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