fat.c revision 125469
1219019Sgabor/* 2264497Stijl * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 3219019Sgabor * Copyright (c) 1995 Martin Husemann 4219019Sgabor * 5219019Sgabor * Redistribution and use in source and binary forms, with or without 6219019Sgabor * modification, are permitted provided that the following conditions 7219019Sgabor * are met: 8219019Sgabor * 1. Redistributions of source code must retain the above copyright 9219019Sgabor * notice, this list of conditions and the following disclaimer. 10219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 11219019Sgabor * notice, this list of conditions and the following disclaimer in the 12219019Sgabor * documentation and/or other materials provided with the distribution. 13219019Sgabor * 3. All advertising materials mentioning features or use of this software 14219019Sgabor * must display the following acknowledgement: 15219019Sgabor * This product includes software developed by Martin Husemann 16219019Sgabor * and Wolfgang Solfrank. 17219019Sgabor * 4. Neither the name of the University nor the names of its contributors 18219019Sgabor * may be used to endorse or promote products derived from this software 19219019Sgabor * without specific prior written permission. 20219019Sgabor * 21219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 22219019Sgabor * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23219019Sgabor * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24219019Sgabor * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25219019Sgabor * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26219019Sgabor * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27219019Sgabor * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28219019Sgabor * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29219019Sgabor * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30219019Sgabor * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31219019Sgabor */ 32219019Sgabor 33219019Sgabor 34219019Sgabor#include <sys/cdefs.h> 35219019Sgabor#ifndef lint 36219019Sgabor__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $"); 37219019Sgaborstatic const char rcsid[] = 38219019Sgabor "$FreeBSD: head/sbin/fsck_msdosfs/fat.c 125469 2004-02-05 06:32:16Z bde $"; 39219019Sgabor#endif /* not lint */ 40219019Sgabor 41219019Sgabor#include <stdlib.h> 42219019Sgabor#include <string.h> 43219019Sgabor#include <ctype.h> 44219019Sgabor#include <stdio.h> 45219019Sgabor#include <unistd.h> 46219019Sgabor 47219019Sgabor#include "ext.h" 48219019Sgabor#include "fsutil.h" 49219019Sgabor 50219019Sgaborstatic int checkclnum(struct bootblock *, int, cl_t, cl_t *); 51219019Sgaborstatic int clustdiffer(cl_t, cl_t *, cl_t *, int); 52219019Sgaborstatic int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 53219019Sgaborstatic int _readfat(int, struct bootblock *, int, u_char **); 54219019Sgabor 55219019Sgaborint 56219019Sgaborcheckdirty(int fs, struct bootblock *boot) 57219019Sgabor{ 58219019Sgabor off_t off; 59219019Sgabor u_char *buffer; 60219019Sgabor int ret = 0; 61219019Sgabor 62219019Sgabor if (boot->ClustMask == CLUST12_MASK) 63219019Sgabor return 0; 64219019Sgabor 65219019Sgabor off = boot->ResSectors; 66219019Sgabor off *= boot->BytesPerSec; 67219019Sgabor 68219019Sgabor buffer = malloc(boot->BytesPerSec); 69219019Sgabor if (buffer == NULL) { 70219019Sgabor perror("No space for FAT"); 71219019Sgabor return 1; 72219019Sgabor } 73219019Sgabor 74219019Sgabor if (lseek(fs, off, SEEK_SET) != off) { 75219019Sgabor perror("Unable to read FAT"); 76219019Sgabor goto err; 77219019Sgabor } 78219019Sgabor 79219019Sgabor if (read(fs, buffer, boot->BytesPerSec) != boot->BytesPerSec) { 80219019Sgabor perror("Unable to read FAT"); 81219019Sgabor goto err; 82219019Sgabor } 83219019Sgabor 84219019Sgabor if (buffer[0] == boot->Media && buffer[1] == 0xff && buffer[2] == 0xff 85219019Sgabor && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 86219019Sgabor || (boot->ClustMask == CLUST32_MASK && buffer[3] == 0x0f 87219019Sgabor && buffer[4] == 0xff && buffer[5] == 0xff 88219019Sgabor && buffer[6] == 0xff && buffer[7] == 0x07))) 89219019Sgabor ret = 0; 90219019Sgabor else 91219019Sgabor ret = 1; 92219019Sgabor 93219019Sgaborerr: 94219019Sgabor free(buffer); 95219019Sgabor return ret; 96219019Sgabor} 97219019Sgabor 98219019Sgabor/* 99219019Sgabor * Check a cluster number for valid value 100219019Sgabor */ 101219019Sgaborstatic int 102219019Sgaborcheckclnum(struct bootblock *boot, int fat, cl_t cl, cl_t *next) 103219019Sgabor{ 104219019Sgabor if (*next >= (CLUST_RSRVD&boot->ClustMask)) 105219019Sgabor *next |= ~boot->ClustMask; 106219019Sgabor if (*next == CLUST_FREE) { 107219019Sgabor boot->NumFree++; 108219019Sgabor return FSOK; 109219019Sgabor } 110219019Sgabor if (*next == CLUST_BAD) { 111219019Sgabor boot->NumBad++; 112219019Sgabor return FSOK; 113219019Sgabor } 114219019Sgabor if (*next < CLUST_FIRST 115219019Sgabor || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 116219019Sgabor pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 117219019Sgabor cl, fat, 118219019Sgabor *next < CLUST_RSRVD ? "out of range" : "reserved", 119219019Sgabor *next&boot->ClustMask); 120219019Sgabor if (ask(0, "Truncate")) { 121219019Sgabor *next = CLUST_EOF; 122219019Sgabor return FSFATMOD; 123219019Sgabor } 124219019Sgabor return FSERROR; 125219019Sgabor } 126219019Sgabor return FSOK; 127219019Sgabor} 128219019Sgabor 129219019Sgabor/* 130219019Sgabor * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 131219019Sgabor */ 132219019Sgaborstatic int 133219019Sgabor_readfat(int fs, struct bootblock *boot, int no, u_char **buffer) 134219019Sgabor{ 135219019Sgabor off_t off; 136219019Sgabor 137219019Sgabor *buffer = malloc(boot->FATsecs * boot->BytesPerSec); 138219019Sgabor if (*buffer == NULL) { 139219019Sgabor perror("No space for FAT"); 140219019Sgabor return 0; 141219019Sgabor } 142219019Sgabor 143219019Sgabor off = boot->ResSectors + no * boot->FATsecs; 144219019Sgabor off *= boot->BytesPerSec; 145219019Sgabor 146219019Sgabor if (lseek(fs, off, SEEK_SET) != off) { 147219019Sgabor perror("Unable to read FAT"); 148219019Sgabor goto err; 149219019Sgabor } 150219019Sgabor 151219019Sgabor if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec) 152219019Sgabor != boot->FATsecs * boot->BytesPerSec) { 153219019Sgabor perror("Unable to read FAT"); 154219019Sgabor goto err; 155219019Sgabor } 156219019Sgabor 157219019Sgabor return 1; 158219019Sgabor 159219019Sgabor err: 160219019Sgabor free(*buffer); 161219019Sgabor return 0; 162219019Sgabor} 163219019Sgabor 164219019Sgabor/* 165219019Sgabor * Read a FAT and decode it into internal format 166219019Sgabor */ 167219019Sgaborint 168219019Sgaborreadfat(int fs, struct bootblock *boot, int no, struct fatEntry **fp) 169219019Sgabor{ 170219019Sgabor struct fatEntry *fat; 171219019Sgabor u_char *buffer, *p; 172219019Sgabor cl_t cl; 173219019Sgabor int ret = FSOK; 174219019Sgabor 175219019Sgabor boot->NumFree = boot->NumBad = 0; 176219019Sgabor 177219019Sgabor if (!_readfat(fs, boot, no, &buffer)) 178219019Sgabor return FSFATAL; 179219019Sgabor 180219019Sgabor fat = calloc(boot->NumClusters, sizeof(struct fatEntry)); 181219019Sgabor if (fat == NULL) { 182219019Sgabor perror("No space for FAT"); 183219019Sgabor free(buffer); 184219019Sgabor return FSFATAL; 185219019Sgabor } 186219019Sgabor 187219019Sgabor if (buffer[0] != boot->Media 188219019Sgabor || buffer[1] != 0xff || buffer[2] != 0xff 189219019Sgabor || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 190219019Sgabor || (boot->ClustMask == CLUST32_MASK 191219019Sgabor && ((buffer[3]&0x0f) != 0x0f 192219019Sgabor || buffer[4] != 0xff || buffer[5] != 0xff 193219019Sgabor || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 194219019Sgabor 195219019Sgabor /* Windows 95 OSR2 (and possibly any later) changes 196219019Sgabor * the FAT signature to 0xXXffff7f for FAT16 and to 197219019Sgabor * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 198219019Sgabor * file system is dirty if it doesn't reboot cleanly. 199219019Sgabor * Check this special condition before errorring out. 200219019Sgabor */ 201219019Sgabor if (buffer[0] == boot->Media && buffer[1] == 0xff 202219019Sgabor && buffer[2] == 0xff 203219019Sgabor && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 204219019Sgabor || (boot->ClustMask == CLUST32_MASK 205219019Sgabor && buffer[3] == 0x0f && buffer[4] == 0xff 206219019Sgabor && buffer[5] == 0xff && buffer[6] == 0xff 207219019Sgabor && buffer[7] == 0x07))) 208219019Sgabor ret |= FSDIRTY; 209219019Sgabor else { 210219019Sgabor /* just some odd byte sequence in FAT */ 211219019Sgabor 212219019Sgabor switch (boot->ClustMask) { 213219019Sgabor case CLUST32_MASK: 214219019Sgabor pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 215219019Sgabor "FAT starts with odd byte sequence", 216219019Sgabor buffer[0], buffer[1], buffer[2], buffer[3], 217219019Sgabor buffer[4], buffer[5], buffer[6], buffer[7]); 218219019Sgabor break; 219219019Sgabor case CLUST16_MASK: 220219019Sgabor pwarn("%s (%02x%02x%02x%02x)\n", 221219019Sgabor "FAT starts with odd byte sequence", 222219019Sgabor buffer[0], buffer[1], buffer[2], buffer[3]); 223219019Sgabor break; 224219019Sgabor default: 225219019Sgabor pwarn("%s (%02x%02x%02x)\n", 226219019Sgabor "FAT starts with odd byte sequence", 227219019Sgabor buffer[0], buffer[1], buffer[2]); 228219019Sgabor break; 229219019Sgabor } 230219019Sgabor 231219019Sgabor 232219019Sgabor if (ask(1, "Correct")) 233219019Sgabor ret |= FSFIXFAT; 234219019Sgabor } 235219019Sgabor } 236219019Sgabor switch (boot->ClustMask) { 237219019Sgabor case CLUST32_MASK: 238219019Sgabor p = buffer + 8; 239219019Sgabor break; 240219019Sgabor case CLUST16_MASK: 241219019Sgabor p = buffer + 4; 242219019Sgabor break; 243219019Sgabor default: 244219019Sgabor p = buffer + 3; 245219019Sgabor break; 246219019Sgabor } 247219019Sgabor for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 248219019Sgabor switch (boot->ClustMask) { 249219019Sgabor case CLUST32_MASK: 250219019Sgabor fat[cl].next = p[0] + (p[1] << 8) 251219019Sgabor + (p[2] << 16) + (p[3] << 24); 252219019Sgabor fat[cl].next &= boot->ClustMask; 253219019Sgabor ret |= checkclnum(boot, no, cl, &fat[cl].next); 254219019Sgabor cl++; 255219019Sgabor p += 4; 256219019Sgabor break; 257219019Sgabor case CLUST16_MASK: 258219019Sgabor fat[cl].next = p[0] + (p[1] << 8); 259219019Sgabor ret |= checkclnum(boot, no, cl, &fat[cl].next); 260219019Sgabor cl++; 261219019Sgabor p += 2; 262219019Sgabor break; 263219019Sgabor default: 264219019Sgabor fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 265219019Sgabor ret |= checkclnum(boot, no, cl, &fat[cl].next); 266219019Sgabor cl++; 267219019Sgabor if (cl >= boot->NumClusters) 268219019Sgabor break; 269219019Sgabor fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 270219019Sgabor ret |= checkclnum(boot, no, cl, &fat[cl].next); 271219019Sgabor cl++; 272219019Sgabor p += 3; 273219019Sgabor break; 274219019Sgabor } 275219019Sgabor } 276219019Sgabor 277219019Sgabor free(buffer); 278219019Sgabor *fp = fat; 279219019Sgabor return ret; 280219019Sgabor} 281219019Sgabor 282219019Sgabor/* 283219019Sgabor * Get type of reserved cluster 284219019Sgabor */ 285219019Sgaborchar * 286219019Sgaborrsrvdcltype(cl_t cl) 287219019Sgabor{ 288219019Sgabor if (cl == CLUST_FREE) 289219019Sgabor return "free"; 290219019Sgabor if (cl < CLUST_BAD) 291219019Sgabor return "reserved"; 292219019Sgabor if (cl > CLUST_BAD) 293219019Sgabor return "as EOF"; 294219019Sgabor return "bad"; 295219019Sgabor} 296219019Sgabor 297219019Sgaborstatic int 298219019Sgaborclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, int fatnum) 299219019Sgabor{ 300219019Sgabor if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 301219019Sgabor if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 302219019Sgabor if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 303219019Sgabor && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 304219019Sgabor || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 305219019Sgabor pwarn("Cluster %u is marked %s with different indicators, ", 306219019Sgabor cl, rsrvdcltype(*cp1)); 307219019Sgabor if (ask(1, "fix")) { 308219019Sgabor *cp2 = *cp1; 309219019Sgabor return FSFATMOD; 310219019Sgabor } 311219019Sgabor return FSFATAL; 312219019Sgabor } 313219019Sgabor pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n", 314219019Sgabor cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 315219019Sgabor if (ask(0, "use FAT 0's entry")) { 316219019Sgabor *cp2 = *cp1; 317219019Sgabor return FSFATMOD; 318219019Sgabor } 319219019Sgabor if (ask(0, "use FAT %d's entry", fatnum)) { 320219019Sgabor *cp1 = *cp2; 321219019Sgabor return FSFATMOD; 322219019Sgabor } 323219019Sgabor return FSFATAL; 324219019Sgabor } 325219019Sgabor pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 326219019Sgabor cl, rsrvdcltype(*cp1), *cp2, fatnum); 327219019Sgabor if (ask(0, "Use continuation from FAT %d", fatnum)) { 328219019Sgabor *cp1 = *cp2; 329219019Sgabor return FSFATMOD; 330219019Sgabor } 331219019Sgabor if (ask(0, "Use mark from FAT 0")) { 332219019Sgabor *cp2 = *cp1; 333219019Sgabor return FSFATMOD; 334219019Sgabor } 335219019Sgabor return FSFATAL; 336219019Sgabor } 337219019Sgabor if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 338219019Sgabor pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n", 339219019Sgabor cl, *cp1, rsrvdcltype(*cp2), fatnum); 340 if (ask(0, "Use continuation from FAT 0")) { 341 *cp2 = *cp1; 342 return FSFATMOD; 343 } 344 if (ask(0, "Use mark from FAT %d", fatnum)) { 345 *cp1 = *cp2; 346 return FSFATMOD; 347 } 348 return FSERROR; 349 } 350 pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n", 351 cl, *cp1, *cp2, fatnum); 352 if (ask(0, "Use continuation from FAT 0")) { 353 *cp2 = *cp1; 354 return FSFATMOD; 355 } 356 if (ask(0, "Use continuation from FAT %d", fatnum)) { 357 *cp1 = *cp2; 358 return FSFATMOD; 359 } 360 return FSERROR; 361} 362 363/* 364 * Compare two FAT copies in memory. Resolve any conflicts and merge them 365 * into the first one. 366 */ 367int 368comparefat(struct bootblock *boot, struct fatEntry *first, 369 struct fatEntry *second, int fatnum) 370{ 371 cl_t cl; 372 int ret = FSOK; 373 374 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 375 if (first[cl].next != second[cl].next) 376 ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 377 return ret; 378} 379 380void 381clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 382{ 383 cl_t p, q; 384 385 for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 386 if (fat[p].head != head) 387 break; 388 q = fat[p].next; 389 fat[p].next = fat[p].head = CLUST_FREE; 390 fat[p].length = 0; 391 } 392} 393 394int 395tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc) 396{ 397 if (ask(0, "Clear chain starting at %u", head)) { 398 clearchain(boot, fat, head); 399 return FSFATMOD; 400 } else if (ask(0, "Truncate")) { 401 *trunc = CLUST_EOF; 402 return FSFATMOD; 403 } else 404 return FSERROR; 405} 406 407/* 408 * Check a complete FAT in-memory for crosslinks 409 */ 410int 411checkfat(struct bootblock *boot, struct fatEntry *fat) 412{ 413 cl_t head, p, h, n; 414 u_int len; 415 int ret = 0; 416 int conf; 417 418 /* 419 * pass 1: figure out the cluster chains. 420 */ 421 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 422 /* find next untravelled chain */ 423 if (fat[head].head != 0 /* cluster already belongs to some chain */ 424 || fat[head].next == CLUST_FREE 425 || fat[head].next == CLUST_BAD) 426 continue; /* skip it. */ 427 428 /* follow the chain and mark all clusters on the way */ 429 for (len = 0, p = head; 430 p >= CLUST_FIRST && p < boot->NumClusters; 431 p = fat[p].next) { 432 fat[p].head = head; 433 len++; 434 } 435 436 /* the head record gets the length */ 437 fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 438 } 439 440 /* 441 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 442 * we didn't know the real start of the chain then - would have treated partial 443 * chains as interlinked with their main chain) 444 */ 445 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 446 /* find next untravelled chain */ 447 if (fat[head].head != head) 448 continue; 449 450 /* follow the chain to its end (hopefully) */ 451 for (p = head; 452 (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 453 p = n) 454 if (fat[n].head != head) 455 break; 456 if (n >= CLUST_EOFS) 457 continue; 458 459 if (n == CLUST_FREE || n >= CLUST_RSRVD) { 460 pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 461 head, rsrvdcltype(n)); 462 ret |= tryclear(boot, fat, head, &fat[p].next); 463 continue; 464 } 465 if (n < CLUST_FIRST || n >= boot->NumClusters) { 466 pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 467 head, n); 468 ret |= tryclear(boot, fat, head, &fat[p].next); 469 continue; 470 } 471 pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 472 head, fat[n].head, n); 473 conf = tryclear(boot, fat, head, &fat[p].next); 474 if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 475 if (conf == FSERROR) { 476 /* 477 * Transfer the common chain to the one not cleared above. 478 */ 479 for (p = n; 480 p >= CLUST_FIRST && p < boot->NumClusters; 481 p = fat[p].next) { 482 if (h != fat[p].head) { 483 /* 484 * Have to reexamine this chain. 485 */ 486 head--; 487 break; 488 } 489 fat[p].head = head; 490 } 491 } 492 clearchain(boot, fat, h); 493 conf |= FSFATMOD; 494 } 495 ret |= conf; 496 } 497 498 return ret; 499} 500 501/* 502 * Write out FATs encoding them from the internal format 503 */ 504int 505writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 506{ 507 u_char *buffer, *p; 508 cl_t cl; 509 int i; 510 u_int32_t fatsz; 511 off_t off; 512 int ret = FSOK; 513 514 buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); 515 if (buffer == NULL) { 516 perror("No space for FAT"); 517 return FSFATAL; 518 } 519 memset(buffer, 0, fatsz); 520 boot->NumFree = 0; 521 p = buffer; 522 if (correct_fat) { 523 *p++ = (u_char)boot->Media; 524 *p++ = 0xff; 525 *p++ = 0xff; 526 switch (boot->ClustMask) { 527 case CLUST16_MASK: 528 *p++ = 0xff; 529 break; 530 case CLUST32_MASK: 531 *p++ = 0x0f; 532 *p++ = 0xff; 533 *p++ = 0xff; 534 *p++ = 0xff; 535 *p++ = 0x0f; 536 break; 537 } 538 } else { 539 /* use same FAT signature as the old FAT has */ 540 int count; 541 u_char *old_fat; 542 543 switch (boot->ClustMask) { 544 case CLUST32_MASK: 545 count = 8; 546 break; 547 case CLUST16_MASK: 548 count = 4; 549 break; 550 default: 551 count = 3; 552 break; 553 } 554 555 if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 556 &old_fat)) { 557 free(buffer); 558 return FSFATAL; 559 } 560 561 memcpy(p, old_fat, count); 562 free(old_fat); 563 p += count; 564 } 565 566 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 567 switch (boot->ClustMask) { 568 case CLUST32_MASK: 569 if (fat[cl].next == CLUST_FREE) 570 boot->NumFree++; 571 *p++ = (u_char)fat[cl].next; 572 *p++ = (u_char)(fat[cl].next >> 8); 573 *p++ = (u_char)(fat[cl].next >> 16); 574 *p &= 0xf0; 575 *p++ |= (fat[cl].next >> 24)&0x0f; 576 break; 577 case CLUST16_MASK: 578 if (fat[cl].next == CLUST_FREE) 579 boot->NumFree++; 580 *p++ = (u_char)fat[cl].next; 581 *p++ = (u_char)(fat[cl].next >> 8); 582 break; 583 default: 584 if (fat[cl].next == CLUST_FREE) 585 boot->NumFree++; 586 if (cl + 1 < boot->NumClusters 587 && fat[cl + 1].next == CLUST_FREE) 588 boot->NumFree++; 589 *p++ = (u_char)fat[cl].next; 590 *p++ = (u_char)((fat[cl].next >> 8) & 0xf) 591 |(u_char)(fat[cl+1].next << 4); 592 *p++ = (u_char)(fat[++cl].next >> 4); 593 break; 594 } 595 } 596 for (i = 0; i < boot->FATs; i++) { 597 off = boot->ResSectors + i * boot->FATsecs; 598 off *= boot->BytesPerSec; 599 if (lseek(fs, off, SEEK_SET) != off 600 || write(fs, buffer, fatsz) != fatsz) { 601 perror("Unable to write FAT"); 602 ret = FSFATAL; /* Return immediately? XXX */ 603 } 604 } 605 free(buffer); 606 return ret; 607} 608 609/* 610 * Check a complete in-memory FAT for lost cluster chains 611 */ 612int 613checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) 614{ 615 cl_t head; 616 int mod = FSOK; 617 int ret; 618 619 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 620 /* find next untravelled chain */ 621 if (fat[head].head != head 622 || fat[head].next == CLUST_FREE 623 || (fat[head].next >= CLUST_RSRVD 624 && fat[head].next < CLUST_EOFS) 625 || (fat[head].flags & FAT_USED)) 626 continue; 627 628 pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 629 head, fat[head].length); 630 mod |= ret = reconnect(dosfs, boot, fat, head); 631 if (mod & FSFATAL) 632 break; 633 if (ret == FSERROR && ask(0, "Clear")) { 634 clearchain(boot, fat, head); 635 mod |= FSFATMOD; 636 } 637 } 638 finishlf(); 639 640 if (boot->FSInfo) { 641 ret = 0; 642 if (boot->FSFree != boot->NumFree) { 643 pwarn("Free space in FSInfo block (%d) not correct (%d)\n", 644 boot->FSFree, boot->NumFree); 645 if (ask(1, "fix")) { 646 boot->FSFree = boot->NumFree; 647 ret = 1; 648 } 649 } 650 if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) { 651 pwarn("Next free cluster in FSInfo block (%u) not free\n", 652 boot->FSNext); 653 if (ask(1, "fix")) 654 for (head = CLUST_FIRST; head < boot->NumClusters; head++) 655 if (fat[head].next == CLUST_FREE) { 656 boot->FSNext = head; 657 ret = 1; 658 break; 659 } 660 } 661 if (ret) 662 mod |= writefsinfo(dosfs, boot); 663 } 664 665 return mod; 666} 667