1 /*
   2     libparted
   3     Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
   4 
   5     This program is free software; you can redistribute it and/or modify
   6     it under the terms of the GNU General Public License as published by
   7     the Free Software Foundation; either version 3 of the License, or
   8     (at your option) any later version.
   9 
  10     This program is distributed in the hope that it will be useful,
  11     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13     GNU General Public License for more details.
  14 
  15     You should have received a copy of the GNU General Public License
  16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18 
  19 #include <config.h>
  20 #include <parted/endian.h>
  21 #include "fat.h"
  22 
  23 #ifndef DISCOVER_ONLY
  24 
  25 FatTable*
  26 fat_table_new (FatType fat_type, FatCluster size)
  27 {
  28         FatTable*       ft;
  29         int             entry_size = fat_table_entry_size (fat_type);
  30 
  31         ft = (FatTable*) ped_malloc (sizeof (FatTable));
  32         if (!ft) return NULL;
  33 
  34         ft->cluster_count = ft->free_cluster_count = size - 2;
  35 
  36 /* ensure there's some free room on the end, to finish off the sector */
  37         ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size;
  38         ft->fat_type = fat_type;
  39         ft->raw_size = ft->size * entry_size;
  40 
  41         ft->table = ped_malloc (ft->raw_size);
  42         if (!ft->table) {
  43                 ped_free (ft);
  44                 return NULL;
  45         }
  46 
  47         fat_table_clear (ft);
  48         return ft;
  49 }
  50 
  51 void
  52 fat_table_destroy (FatTable* ft)
  53 {
  54         ped_free (ft->table);
  55         ped_free (ft);
  56 }
  57 
  58 FatTable*
  59 fat_table_duplicate (const FatTable* ft)
  60 {
  61         FatTable*       dup;
  62 
  63         dup = fat_table_new (ft->fat_type, ft->size);
  64         if (!dup) return NULL;
  65 
  66         dup->cluster_count   = ft->cluster_count;
  67         dup->free_cluster_count      = ft->free_cluster_count;
  68         dup->bad_cluster_count       = ft->bad_cluster_count;
  69         dup->last_alloc              = ft->last_alloc;
  70 
  71         memcpy (dup->table, ft->table, ft->raw_size);
  72 
  73         return dup;
  74 }
  75 
  76 void
  77 fat_table_clear (FatTable* ft)
  78 {
  79         memset (ft->table, 0, ft->raw_size);
  80 
  81         fat_table_set (ft, 0, 0x0ffffff8);
  82         fat_table_set (ft, 1, 0x0fffffff);
  83 
  84         ft->free_cluster_count = ft->cluster_count;
  85         ft->bad_cluster_count = 0;
  86         ft->last_alloc = 1;
  87 }
  88 
  89 int
  90 fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count)
  91 {
  92         PED_ASSERT (new_cluster_count + 2 <= ft->size, return 0);
  93 
  94         ft->cluster_count = new_cluster_count;
  95         return fat_table_count_stats (ft);
  96 }
  97 
  98 int
  99 fat_table_count_stats (FatTable* ft)
 100 {
 101         FatCluster      i;
 102 
 103         PED_ASSERT (ft->cluster_count + 2 <= ft->size, return 0);
 104 
 105         ft->free_cluster_count = 0;
 106         ft->bad_cluster_count = 0;
 107 
 108         for (i=2; i < ft->cluster_count + 2; i++) {
 109                 if (fat_table_is_available (ft, i))
 110                         ft->free_cluster_count++;
 111                 if (fat_table_is_bad (ft, i))
 112                         ft->bad_cluster_count++;
 113         }
 114         return 1;
 115 }
 116 
 117 int
 118 fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num)
 119 {
 120         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 121 
 122         PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0);
 123 
 124         memset (ft->table, 0, ft->raw_size);
 125 
 126         if (!ped_geometry_read (fs->geom, (void *) ft->table,
 127                                 fs_info->fat_offset
 128                                         + table_num * fs_info->fat_sectors,
 129                                 fs_info->fat_sectors))
 130                 return 0;
 131 
 132         if ( *((unsigned char*) ft->table) != fs_info->boot_sector.media) {
 133                 if (ped_exception_throw (
 134                         PED_EXCEPTION_ERROR,
 135                         PED_EXCEPTION_IGNORE_CANCEL,
 136                         _("FAT %d media %x doesn't match the boot sector's "
 137                           "media %x.  You should probably run scandisk."),
 138                         (int) table_num + 1,
 139                         (int) *((unsigned char*) ft->table),
 140                         (int) fs_info->boot_sector.media)
 141                                 != PED_EXCEPTION_IGNORE)
 142                         return 0;
 143         }
 144 
 145         ft->cluster_count = fs_info->cluster_count;
 146 
 147         fat_table_count_stats (ft);
 148 
 149         return 1;
 150 }
 151 
 152 int
 153 fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num)
 154 {
 155         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 156 
 157         PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0);
 158 
 159         if (!ped_geometry_write (fs->geom, (void *) ft->table,
 160                                  fs_info->fat_offset
 161                                         + table_num * fs_info->fat_sectors,
 162                                  fs_info->fat_sectors))
 163                 return 0;
 164         if (!ped_geometry_sync (fs->geom))
 165                 return 0;
 166 
 167         return 1;
 168 }
 169 
 170 int
 171 fat_table_write_all (const FatTable* ft, PedFileSystem* fs)
 172 {
 173         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 174         int             i;
 175 
 176         for (i = 0; i < fs_info->fat_table_count; i++) {
 177                 if (!fat_table_write (ft, fs, i))
 178                         return 0;
 179         }
 180 
 181         return 1;
 182 }
 183 
 184 int
 185 fat_table_compare (const FatTable* a, const FatTable* b)
 186 {
 187         FatCluster      i;
 188 
 189         if (a->cluster_count != b->cluster_count)
 190                 return 0;
 191 
 192         for (i = 0; i < a->cluster_count + 2; i++) {
 193                 if (fat_table_get (a, i) != fat_table_get (b, i))
 194                         return 0;
 195         }
 196 
 197         return 1;
 198 }
 199 
 200 static int
 201 _test_code_available (const FatTable* ft, FatCluster code)
 202 {
 203         return code == 0;
 204 }
 205 
 206 static int
 207 _test_code_bad (const FatTable* ft, FatCluster code)
 208 {
 209         switch (ft->fat_type) {
 210                 case FAT_TYPE_FAT12:
 211                 if (code == 0xff7) return 1;
 212                 break;
 213 
 214                 case FAT_TYPE_FAT16:
 215                 if (code == 0xfff7) return 1;
 216                 break;
 217 
 218                 case FAT_TYPE_FAT32:
 219                 if (code == 0x0ffffff7) return 1;
 220                 break;
 221         }
 222         return 0;
 223 }
 224 
 225 static int
 226 _test_code_eof (const FatTable* ft, FatCluster code)
 227 {
 228         switch (ft->fat_type) {
 229                 case FAT_TYPE_FAT12:
 230                 if (code >= 0xff7) return 1;
 231                 break;
 232 
 233                 case FAT_TYPE_FAT16:
 234                 if (code >= 0xfff7) return 1;
 235                 break;
 236 
 237                 case FAT_TYPE_FAT32:
 238                 if (code >= 0x0ffffff7) return 1;
 239                 break;
 240         }
 241         return 0;
 242 }
 243 
 244 void
 245 _update_stats (FatTable* ft, FatCluster cluster, FatCluster value)
 246 {
 247         if (_test_code_available (ft, value)
 248             && !fat_table_is_available (ft, cluster)) {
 249                 ft->free_cluster_count++;
 250                 if (fat_table_is_bad (ft, cluster))
 251                         ft->bad_cluster_count--;
 252         }
 253 
 254         if (!_test_code_available (ft, value)
 255             && fat_table_is_available (ft, cluster)) {
 256                 ft->free_cluster_count--;
 257                 if (_test_code_bad (ft, cluster))
 258                         ft->bad_cluster_count--;
 259         }
 260 }
 261 
 262 int
 263 fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value)
 264 {
 265         if (cluster >= ft->cluster_count + 2) {
 266                 ped_exception_throw (PED_EXCEPTION_BUG,
 267                                      PED_EXCEPTION_CANCEL,
 268                                      _("fat_table_set: cluster %ld outside "
 269                                        "file system"),
 270                                      (long) cluster);
 271                 return 0;
 272         }
 273 
 274         _update_stats (ft, cluster, value);
 275 
 276         switch (ft->fat_type) {
 277                 case FAT_TYPE_FAT12:
 278                 PED_ASSERT (0, (void) 0);
 279                 break;
 280 
 281                 case FAT_TYPE_FAT16:
 282                 ((unsigned short *) ft->table) [cluster]
 283                         = PED_CPU_TO_LE16 (value);
 284                 break;
 285 
 286                 case FAT_TYPE_FAT32:
 287                 ((unsigned int *) ft->table) [cluster]
 288                         = PED_CPU_TO_LE32 (value);
 289                 break;
 290         }
 291         return 1;
 292 }
 293 
 294 FatCluster 
 295 fat_table_get (const FatTable* ft, FatCluster cluster)
 296 {
 297         if (cluster >= ft->cluster_count + 2) {
 298                 ped_exception_throw (PED_EXCEPTION_BUG,
 299                                      PED_EXCEPTION_CANCEL,
 300                                      _("fat_table_get: cluster %ld outside "
 301                                        "file system"),
 302                                      (long) cluster);
 303                 exit (1);       /* FIXME */
 304         }
 305 
 306         switch (ft->fat_type) {
 307                 case FAT_TYPE_FAT12:
 308                 PED_ASSERT (0, (void) 0);
 309                 break;
 310 
 311                 case FAT_TYPE_FAT16:
 312                 return PED_LE16_TO_CPU
 313                         (((unsigned short *) ft->table) [cluster]);
 314 
 315                 case FAT_TYPE_FAT32:
 316                 return PED_LE32_TO_CPU
 317                         (((unsigned int *) ft->table) [cluster]);
 318         }
 319 
 320         return 0;
 321 }
 322 
 323 FatCluster
 324 fat_table_alloc_cluster (FatTable* ft)
 325 {
 326         FatCluster      i;
 327         FatCluster      cluster;
 328 
 329 /* hack: assumes the first two FAT entries are marked as used (which they
 330  * always should be)
 331  */
 332         for (i=1; i < ft->cluster_count + 1; i++) {
 333                 cluster = (i + ft->last_alloc) % ft->cluster_count;
 334                 if (fat_table_is_available (ft, cluster)) {
 335                         ft->last_alloc = cluster;
 336                         return cluster;
 337                 }
 338         }
 339 
 340         ped_exception_throw (PED_EXCEPTION_ERROR,
 341                              PED_EXCEPTION_CANCEL,
 342                              _("fat_table_alloc_cluster: no free clusters"));
 343         return 0;
 344 }
 345 
 346 FatCluster
 347 fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs)
 348 {
 349         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
 350         FatCluster      result;
 351 
 352         while (1) {
 353                 result = fat_table_alloc_cluster (ft);
 354                 if (!result)
 355                         return 0;
 356                 if (fat_read_cluster (fs, fs_info->buffer, result))
 357                         return result;
 358                 fat_table_set_bad (ft, result);
 359         }
 360 }
 361 
 362 /*
 363     returns true if <cluster> is marked as bad
 364 */
 365 int
 366 fat_table_is_bad (const FatTable* ft, FatCluster cluster)
 367 {
 368         return _test_code_bad (ft, fat_table_get (ft, cluster));
 369 }
 370 
 371 /*
 372     returns true if <cluster> represents an EOF marker
 373 */
 374 int
 375 fat_table_is_eof (const FatTable* ft, FatCluster cluster)
 376 {
 377         return _test_code_eof (ft, cluster);
 378 }
 379 
 380 /*
 381     returns true if <cluster> is available.
 382 */
 383 int
 384 fat_table_is_available (const FatTable* ft, FatCluster cluster)
 385 {
 386         return _test_code_available (ft, fat_table_get (ft, cluster));
 387 }
 388 
 389 /*
 390     returns true if <cluster> is empty.  Note that this includes bad clusters.
 391 */
 392 int
 393 fat_table_is_empty (const FatTable* ft, FatCluster cluster)
 394 {
 395         return fat_table_is_available (ft, cluster)
 396                 || fat_table_is_bad (ft, cluster);
 397 }
 398 
 399 /*
 400     returns true if <cluster> is being used for something constructive.
 401 */
 402 int
 403 fat_table_is_active (const FatTable* ft, FatCluster cluster)
 404 {
 405         return !fat_table_is_bad (ft, cluster)
 406                 && !fat_table_is_available (ft, cluster);
 407 }
 408 
 409 /*
 410     marks <cluster> as the last cluster in the chain
 411 */
 412 int
 413 fat_table_set_eof (FatTable* ft, FatCluster cluster)
 414 {
 415 
 416         switch (ft->fat_type) {
 417                 case FAT_TYPE_FAT12:
 418                 PED_ASSERT (0, (void) 0);
 419                 break;
 420 
 421                 case FAT_TYPE_FAT16:
 422                 return fat_table_set (ft, cluster, 0xfff8);
 423         
 424                 case FAT_TYPE_FAT32:
 425                 return fat_table_set (ft, cluster, 0x0fffffff);
 426         }
 427 
 428         return 0;
 429 }
 430 
 431 /*
 432         Marks a clusters as unusable, due to physical disk damage.
 433 */
 434 int
 435 fat_table_set_bad (FatTable* ft, FatCluster cluster)
 436 {
 437         if (!fat_table_is_bad (ft, cluster))
 438                 ft->bad_cluster_count++;
 439 
 440         switch (ft->fat_type) {
 441                 case FAT_TYPE_FAT12:
 442                 return fat_table_set (ft, cluster, 0xff7);
 443 
 444                 case FAT_TYPE_FAT16:
 445                 return fat_table_set (ft, cluster, 0xfff7);
 446         
 447                 case FAT_TYPE_FAT32:
 448                 return fat_table_set (ft, cluster, 0x0ffffff7);
 449         }
 450 
 451         return 0;
 452 }
 453 
 454 /*
 455     marks <cluster> as unused/free/available
 456 */
 457 int
 458 fat_table_set_avail (FatTable* ft, FatCluster cluster)
 459 {
 460         return fat_table_set (ft, cluster, 0);
 461 }
 462 
 463 #endif /* !DISCOVER_ONLY */
 464 
 465 int
 466 fat_table_entry_size (FatType fat_type)
 467 {
 468         switch (fat_type) {
 469                 case FAT_TYPE_FAT12:
 470                 return 2;               /* FIXME: how? */
 471 
 472                 case FAT_TYPE_FAT16:
 473                 return 2;
 474 
 475                 case FAT_TYPE_FAT32:
 476                 return 4;
 477         }
 478 
 479         return 0;
 480 }
 481