fat.c revision 203872
1116743Ssam/* 2186904Ssam * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 3116743Ssam * Copyright (c) 1995 Martin Husemann 4116743Ssam * 5116743Ssam * Redistribution and use in source and binary forms, with or without 6116743Ssam * modification, are permitted provided that the following conditions 7116743Ssam * are met: 8116743Ssam * 1. Redistributions of source code must retain the above copyright 9116743Ssam * notice, this list of conditions and the following disclaimer. 10116743Ssam * 2. Redistributions in binary form must reproduce the above copyright 11116743Ssam * notice, this list of conditions and the following disclaimer in the 12116743Ssam * documentation and/or other materials provided with the distribution. 13116743Ssam * 14116743Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 15116743Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16116743Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17116743Ssam * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18116743Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19116743Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20116743Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21116743Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22116743Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23116743Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24116743Ssam */ 25116743Ssam 26116743Ssam 27116743Ssam#include <sys/cdefs.h> 28116743Ssam#ifndef lint 29116743Ssam__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $"); 30116743Ssamstatic const char rcsid[] = 31116743Ssam "$FreeBSD: head/sbin/fsck_msdosfs/fat.c 203872 2010-02-14 12:30:30Z kib $"; 32116743Ssam#endif /* not lint */ 33116743Ssam 34116743Ssam#include <stdlib.h> 35116743Ssam#include <string.h> 36116743Ssam#include <ctype.h> 37116743Ssam#include <stdio.h> 38227327Sadrian#include <unistd.h> 39227327Sadrian 40227327Sadrian#include "ext.h" 41227327Sadrian#include "fsutil.h" 42227327Sadrian 43227327Sadrianstatic int checkclnum(struct bootblock *, u_int, cl_t, cl_t *); 44227327Sadrianstatic int clustdiffer(cl_t, cl_t *, cl_t *, u_int); 45227327Sadrianstatic int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 46233989Sadrianstatic int _readfat(int, struct bootblock *, u_int, u_char **); 47227327Sadrian 48227327Sadrian/*- 49234090Sadrian * The first 2 FAT entries contain pseudo-cluster numbers with the following 50234090Sadrian * layout: 51234090Sadrian * 52234090Sadrian * 31...... ........ ........ .......0 53116743Ssam * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0 54116743Ssam * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1 55116743Ssam * 56116743Ssam * 11111111 mmmmmmmm FAT16 entry 0 57155492Ssam * sh111111 11111xxx FAT16 entry 1 58138570Ssam * 59116743Ssam * r = reserved 60116743Ssam * m = BPB media ID byte 61116743Ssam * s = clean flag (1 = dismounted; 0 = still mounted) 62138570Ssam * h = hard error flag (1 = ok; 0 = I/O error) 63116743Ssam * x = any value ok 64138570Ssam */ 65116743Ssam 66116743Ssamint 67116743Ssamcheckdirty(int fs, struct bootblock *boot) 68116743Ssam{ 69116743Ssam off_t off; 70116743Ssam u_char *buffer; 71116743Ssam int ret = 0; 72116743Ssam 73116743Ssam if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK) 74116743Ssam return 0; 75116743Ssam 76116743Ssam off = boot->ResSectors; 77116743Ssam off *= boot->BytesPerSec; 78116743Ssam 79116743Ssam buffer = malloc(boot->BytesPerSec); 80116743Ssam if (buffer == NULL) { 81116743Ssam perror("No space for FAT"); 82116743Ssam return 1; 83116743Ssam } 84116743Ssam 85127779Ssam if (lseek(fs, off, SEEK_SET) != off) { 86127779Ssam perror("Unable to read FAT"); 87170530Ssam goto err; 88170530Ssam } 89116743Ssam 90116743Ssam if (read(fs, buffer, boot->BytesPerSec) != boot->BytesPerSec) { 91116743Ssam perror("Unable to read FAT"); 92116743Ssam goto err; 93116743Ssam } 94116743Ssam 95138570Ssam /* 96116743Ssam * If we don't understand the FAT, then the file system must be 97218689Sadrian * assumed to be unclean. 98119147Ssam */ 99127779Ssam if (buffer[0] != boot->Media || buffer[1] != 0xff) 100138570Ssam goto err; 101138570Ssam if (boot->ClustMask == CLUST16_MASK) { 102119147Ssam if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f) 103138570Ssam goto err; 104138570Ssam } else { 105161187Ssam if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f 106138570Ssam || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff 107116743Ssam || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03) 108116743Ssam goto err; 109116743Ssam } 110116743Ssam 111116743Ssam /* 112116743Ssam * Now check the actual clean flag (and the no-error flag). 113116743Ssam */ 114138570Ssam if (boot->ClustMask == CLUST16_MASK) { 115138570Ssam if ((buffer[3] & 0xc0) == 0xc0) 116138570Ssam ret = 1; 117138570Ssam } else { 118159894Ssam if ((buffer[7] & 0x0c) == 0x0c) 119159894Ssam ret = 1; 120160992Ssam } 121170530Ssam 122170530Ssamerr: 123170530Ssam free(buffer); 124170530Ssam return ret; 125170530Ssam} 126170530Ssam 127186904Ssam/* 128186904Ssam * Check a cluster number for valid value 129186904Ssam */ 130186904Ssamstatic int 131186904Ssamcheckclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next) 132186904Ssam{ 133188195Ssam if (*next >= (CLUST_RSRVD&boot->ClustMask)) 134188195Ssam *next |= ~boot->ClustMask; 135188555Ssam if (*next == CLUST_FREE) { 136211299Sadrian boot->NumFree++; 137217684Sadrian return FSOK; 138218378Sadrian } 139221965Sadrian if (*next == CLUST_BAD) { 140221965Sadrian boot->NumBad++; 141221965Sadrian return FSOK; 142221965Sadrian } 143221965Sadrian if (*next < CLUST_FIRST 144218689Sadrian || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 145218924Sadrian pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 146221965Sadrian cl, fat, 147220772Sadrian *next < CLUST_RSRVD ? "out of range" : "reserved", 148220782Sadrian *next&boot->ClustMask); 149221965Sadrian if (ask(0, "Truncate")) { 150221965Sadrian *next = CLUST_EOF; 151221965Sadrian return FSFATMOD; 152226798Sadrian } 153226798Sadrian return FSERROR; 154226798Sadrian } 155226798Sadrian return FSOK; 156227868Sadrian} 157226798Sadrian 158226798Sadrian/* 159226798Sadrian * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 160226798Sadrian */ 161227868Sadrianstatic int 162227868Sadrian_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer) 163232764Sadrian{ 164238507Sadrian off_t off; 165238507Sadrian 166238507Sadrian *buffer = malloc(boot->FATsecs * boot->BytesPerSec); 167116743Ssam if (*buffer == NULL) { 168116743Ssam perror("No space for FAT"); 169116743Ssam return 0; 170188557Ssam } 171236833Sadrian 172116743Ssam off = boot->ResSectors + no * boot->FATsecs; 173123044Ssam off *= boot->BytesPerSec; 174138570Ssam 175138570Ssam if (lseek(fs, off, SEEK_SET) != off) { 176138570Ssam perror("Unable to read FAT"); 177138570Ssam goto err; 178138570Ssam } 179138570Ssam 180138570Ssam if ((size_t)read(fs, *buffer, boot->FATsecs * boot->BytesPerSec) 181138570Ssam != boot->FATsecs * boot->BytesPerSec) { 182138570Ssam perror("Unable to read FAT"); 183138570Ssam goto err; 184123044Ssam } 185123044Ssam 186123044Ssam return 1; 187224245Sadrian 188123044Ssam err: 189238632Sadrian free(*buffer); 190119783Ssam return 0; 191238632Sadrian} 192238632Sadrian 193238632Sadrian/* 194238632Sadrian * Read a FAT and decode it into internal format 195238632Sadrian */ 196238632Sadrianint 197238632Sadrianreadfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp) 198238632Sadrian{ 199238632Sadrian struct fatEntry *fat; 200238632Sadrian u_char *buffer, *p; 201238632Sadrian cl_t cl; 202238632Sadrian int ret = FSOK; 203238632Sadrian size_t len; 204238632Sadrian 205238632Sadrian boot->NumFree = boot->NumBad = 0; 206238632Sadrian 207238632Sadrian if (!_readfat(fs, boot, no, &buffer)) 208238632Sadrian return FSFATAL; 209238632Sadrian 210238632Sadrian fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry)); 211238632Sadrian if (fat == NULL) { 212238632Sadrian perror("No space for FAT"); 213238632Sadrian free(buffer); 214238632Sadrian return FSFATAL; 215238632Sadrian } 216238632Sadrian (void)memset(fat, 0, len); 217238632Sadrian 218238632Sadrian if (buffer[0] != boot->Media 219238632Sadrian || buffer[1] != 0xff || buffer[2] != 0xff 220238632Sadrian || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 221238632Sadrian || (boot->ClustMask == CLUST32_MASK 222238632Sadrian && ((buffer[3]&0x0f) != 0x0f 223238632Sadrian || buffer[4] != 0xff || buffer[5] != 0xff 224119783Ssam || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 225119783Ssam 226237522Sadrian /* Windows 95 OSR2 (and possibly any later) changes 227154140Ssam * the FAT signature to 0xXXffff7f for FAT16 and to 228119783Ssam * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 229119783Ssam * file system is dirty if it doesn't reboot cleanly. 230123928Ssam * Check this special condition before errorring out. 231154140Ssam */ 232154140Ssam if (buffer[0] == boot->Media && buffer[1] == 0xff 233170530Ssam && buffer[2] == 0xff 234119783Ssam && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 235119783Ssam || (boot->ClustMask == CLUST32_MASK 236237522Sadrian && buffer[3] == 0x0f && buffer[4] == 0xff 237237522Sadrian && buffer[5] == 0xff && buffer[6] == 0xff 238237522Sadrian && buffer[7] == 0x07))) 239237522Sadrian ret |= FSDIRTY; 240237522Sadrian else { 241237522Sadrian /* just some odd byte sequence in FAT */ 242237522Sadrian 243237522Sadrian switch (boot->ClustMask) { 244237522Sadrian case CLUST32_MASK: 245237522Sadrian pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 246237522Sadrian "FAT starts with odd byte sequence", 247237522Sadrian buffer[0], buffer[1], buffer[2], buffer[3], 248237522Sadrian buffer[4], buffer[5], buffer[6], buffer[7]); 249237522Sadrian break; 250237522Sadrian case CLUST16_MASK: 251237522Sadrian pwarn("%s (%02x%02x%02x%02x)\n", 252237522Sadrian "FAT starts with odd byte sequence", 253237522Sadrian buffer[0], buffer[1], buffer[2], buffer[3]); 254237522Sadrian break; 255237522Sadrian default: 256237522Sadrian pwarn("%s (%02x%02x%02x)\n", 257237522Sadrian "FAT starts with odd byte sequence", 258237522Sadrian buffer[0], buffer[1], buffer[2]); 259237522Sadrian break; 260237522Sadrian } 261237522Sadrian 262237522Sadrian 263237522Sadrian if (ask(1, "Correct")) 264237522Sadrian ret |= FSFIXFAT; 265237522Sadrian } 266237522Sadrian } 267237522Sadrian switch (boot->ClustMask) { 268237522Sadrian case CLUST32_MASK: 269237522Sadrian p = buffer + 8; 270237522Sadrian break; 271237522Sadrian case CLUST16_MASK: 272237522Sadrian p = buffer + 4; 273237522Sadrian break; 274237522Sadrian default: 275237522Sadrian p = buffer + 3; 276237522Sadrian break; 277237522Sadrian } 278237522Sadrian for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 279237522Sadrian switch (boot->ClustMask) { 280237522Sadrian case CLUST32_MASK: 281237522Sadrian fat[cl].next = p[0] + (p[1] << 8) 282237522Sadrian + (p[2] << 16) + (p[3] << 24); 283237522Sadrian fat[cl].next &= boot->ClustMask; 284237522Sadrian ret |= checkclnum(boot, no, cl, &fat[cl].next); 285237522Sadrian cl++; 286237522Sadrian p += 4; 287237522Sadrian break; 288237522Sadrian case CLUST16_MASK: 289237522Sadrian fat[cl].next = p[0] + (p[1] << 8); 290237522Sadrian ret |= checkclnum(boot, no, cl, &fat[cl].next); 291237522Sadrian cl++; 292119783Ssam p += 2; 293119783Ssam break; 294237522Sadrian default: 295237522Sadrian fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 296237522Sadrian ret |= checkclnum(boot, no, cl, &fat[cl].next); 297237522Sadrian cl++; 298237522Sadrian if (cl >= boot->NumClusters) 299237522Sadrian break; 300237522Sadrian fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 301237522Sadrian ret |= checkclnum(boot, no, cl, &fat[cl].next); 302237522Sadrian cl++; 303237522Sadrian p += 3; 304237522Sadrian break; 305237522Sadrian } 306237522Sadrian } 307237522Sadrian 308237522Sadrian free(buffer); 309237522Sadrian if (ret & FSFATAL) { 310154140Ssam free(fat); 311154140Ssam *fp = NULL; 312119783Ssam } else 313170530Ssam *fp = fat; 314170530Ssam return ret; 315170530Ssam} 316170530Ssam 317170530Ssam/* 318119783Ssam * Get type of reserved cluster 319170530Ssam */ 320170530Ssamchar * 321237522Sadrianrsrvdcltype(cl_t cl) 322237522Sadrian{ 323237522Sadrian if (cl == CLUST_FREE) 324237522Sadrian return "free"; 325237522Sadrian if (cl < CLUST_BAD) 326237522Sadrian return "reserved"; 327237522Sadrian if (cl > CLUST_BAD) 328237522Sadrian return "as EOF"; 329237522Sadrian return "bad"; 330237522Sadrian} 331237522Sadrian 332237522Sadrianstatic int 333237522Sadrianclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum) 334237522Sadrian{ 335237522Sadrian if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 336237522Sadrian if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 337237522Sadrian if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 338237522Sadrian && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 339237522Sadrian || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 340237522Sadrian pwarn("Cluster %u is marked %s with different indicators\n", 341170530Ssam cl, rsrvdcltype(*cp1)); 342119783Ssam if (ask(1, "Fix")) { 343119783Ssam *cp2 = *cp1; 344154140Ssam return FSFATMOD; 345119783Ssam } 346119783Ssam return FSFATAL; 347123928Ssam } 348123928Ssam pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n", 349170530Ssam cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 350119783Ssam if (ask(0, "Use FAT 0's entry")) { 351119783Ssam *cp2 = *cp1; 352119783Ssam return FSFATMOD; 353119783Ssam } 354154140Ssam if (ask(0, "Use FAT %u's entry", fatnum)) { 355154140Ssam *cp1 = *cp2; 356119783Ssam return FSFATMOD; 357123928Ssam } 358123928Ssam return FSFATAL; 359170530Ssam } 360170530Ssam pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 361170530Ssam cl, rsrvdcltype(*cp1), *cp2, fatnum); 362170530Ssam if (ask(0, "Use continuation from FAT %u", fatnum)) { 363170530Ssam *cp1 = *cp2; 364119783Ssam return FSFATMOD; 365224245Sadrian } 366224245Sadrian if (ask(0, "Use mark from FAT 0")) { 367224245Sadrian *cp2 = *cp1; 368224245Sadrian return FSFATMOD; 369224245Sadrian } 370224245Sadrian return FSFATAL; 371224245Sadrian } 372224245Sadrian if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 373224245Sadrian pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n", 374224245Sadrian cl, *cp1, rsrvdcltype(*cp2), fatnum); 375224245Sadrian if (ask(0, "Use continuation from FAT 0")) { 376224245Sadrian *cp2 = *cp1; 377224245Sadrian return FSFATMOD; 378224245Sadrian } 379224245Sadrian if (ask(0, "Use mark from FAT %d", fatnum)) { 380224245Sadrian *cp1 = *cp2; 381224245Sadrian return FSFATMOD; 382224245Sadrian } 383224245Sadrian return FSERROR; 384224245Sadrian } 385224245Sadrian pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n", 386224245Sadrian cl, *cp1, *cp2, fatnum); 387224245Sadrian if (ask(0, "Use continuation from FAT 0")) { 388224245Sadrian *cp2 = *cp1; 389224245Sadrian return FSFATMOD; 390224245Sadrian } 391224245Sadrian if (ask(0, "Use continuation from FAT %u", fatnum)) { 392224245Sadrian *cp1 = *cp2; 393224245Sadrian return FSFATMOD; 394224245Sadrian } 395116743Ssam return FSERROR; 396} 397 398/* 399 * Compare two FAT copies in memory. Resolve any conflicts and merge them 400 * into the first one. 401 */ 402int 403comparefat(struct bootblock *boot, struct fatEntry *first, 404 struct fatEntry *second, u_int fatnum) 405{ 406 cl_t cl; 407 int ret = FSOK; 408 409 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 410 if (first[cl].next != second[cl].next) 411 ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 412 return ret; 413} 414 415void 416clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 417{ 418 cl_t p, q; 419 420 for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 421 if (fat[p].head != head) 422 break; 423 q = fat[p].next; 424 fat[p].next = fat[p].head = CLUST_FREE; 425 fat[p].length = 0; 426 } 427} 428 429int 430tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc) 431{ 432 if (ask(0, "Clear chain starting at %u", head)) { 433 clearchain(boot, fat, head); 434 return FSFATMOD; 435 } else if (ask(0, "Truncate")) { 436 *trunc = CLUST_EOF; 437 return FSFATMOD; 438 } else 439 return FSERROR; 440} 441 442/* 443 * Check a complete FAT in-memory for crosslinks 444 */ 445int 446checkfat(struct bootblock *boot, struct fatEntry *fat) 447{ 448 cl_t head, p, h, n; 449 u_int len; 450 int ret = 0; 451 int conf; 452 453 /* 454 * pass 1: figure out the cluster chains. 455 */ 456 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 457 /* find next untravelled chain */ 458 if (fat[head].head != 0 /* cluster already belongs to some chain */ 459 || fat[head].next == CLUST_FREE 460 || fat[head].next == CLUST_BAD) 461 continue; /* skip it. */ 462 463 /* follow the chain and mark all clusters on the way */ 464 for (len = 0, p = head; 465 p >= CLUST_FIRST && p < boot->NumClusters; 466 p = fat[p].next) { 467 fat[p].head = head; 468 len++; 469 } 470 471 /* the head record gets the length */ 472 fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 473 } 474 475 /* 476 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 477 * we didn't know the real start of the chain then - would have treated partial 478 * chains as interlinked with their main chain) 479 */ 480 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 481 /* find next untravelled chain */ 482 if (fat[head].head != head) 483 continue; 484 485 /* follow the chain to its end (hopefully) */ 486 for (p = head; 487 (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 488 p = n) 489 if (fat[n].head != head) 490 break; 491 if (n >= CLUST_EOFS) 492 continue; 493 494 if (n == CLUST_FREE || n >= CLUST_RSRVD) { 495 pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 496 head, rsrvdcltype(n)); 497 ret |= tryclear(boot, fat, head, &fat[p].next); 498 continue; 499 } 500 if (n < CLUST_FIRST || n >= boot->NumClusters) { 501 pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 502 head, n); 503 ret |= tryclear(boot, fat, head, &fat[p].next); 504 continue; 505 } 506 pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 507 head, fat[n].head, n); 508 conf = tryclear(boot, fat, head, &fat[p].next); 509 if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 510 if (conf == FSERROR) { 511 /* 512 * Transfer the common chain to the one not cleared above. 513 */ 514 for (p = n; 515 p >= CLUST_FIRST && p < boot->NumClusters; 516 p = fat[p].next) { 517 if (h != fat[p].head) { 518 /* 519 * Have to reexamine this chain. 520 */ 521 head--; 522 break; 523 } 524 fat[p].head = head; 525 } 526 } 527 clearchain(boot, fat, h); 528 conf |= FSFATMOD; 529 } 530 ret |= conf; 531 } 532 533 return ret; 534} 535 536/* 537 * Write out FATs encoding them from the internal format 538 */ 539int 540writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 541{ 542 u_char *buffer, *p; 543 cl_t cl; 544 u_int i; 545 size_t fatsz; 546 off_t off; 547 int ret = FSOK; 548 549 buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); 550 if (buffer == NULL) { 551 perror("No space for FAT"); 552 return FSFATAL; 553 } 554 memset(buffer, 0, fatsz); 555 boot->NumFree = 0; 556 p = buffer; 557 if (correct_fat) { 558 *p++ = (u_char)boot->Media; 559 *p++ = 0xff; 560 *p++ = 0xff; 561 switch (boot->ClustMask) { 562 case CLUST16_MASK: 563 *p++ = 0xff; 564 break; 565 case CLUST32_MASK: 566 *p++ = 0x0f; 567 *p++ = 0xff; 568 *p++ = 0xff; 569 *p++ = 0xff; 570 *p++ = 0x0f; 571 break; 572 } 573 } else { 574 /* use same FAT signature as the old FAT has */ 575 int count; 576 u_char *old_fat; 577 578 switch (boot->ClustMask) { 579 case CLUST32_MASK: 580 count = 8; 581 break; 582 case CLUST16_MASK: 583 count = 4; 584 break; 585 default: 586 count = 3; 587 break; 588 } 589 590 if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 591 &old_fat)) { 592 free(buffer); 593 return FSFATAL; 594 } 595 596 memcpy(p, old_fat, count); 597 free(old_fat); 598 p += count; 599 } 600 601 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 602 switch (boot->ClustMask) { 603 case CLUST32_MASK: 604 if (fat[cl].next == CLUST_FREE) 605 boot->NumFree++; 606 *p++ = (u_char)fat[cl].next; 607 *p++ = (u_char)(fat[cl].next >> 8); 608 *p++ = (u_char)(fat[cl].next >> 16); 609 *p &= 0xf0; 610 *p++ |= (fat[cl].next >> 24)&0x0f; 611 break; 612 case CLUST16_MASK: 613 if (fat[cl].next == CLUST_FREE) 614 boot->NumFree++; 615 *p++ = (u_char)fat[cl].next; 616 *p++ = (u_char)(fat[cl].next >> 8); 617 break; 618 default: 619 if (fat[cl].next == CLUST_FREE) 620 boot->NumFree++; 621 if (cl + 1 < boot->NumClusters 622 && fat[cl + 1].next == CLUST_FREE) 623 boot->NumFree++; 624 *p++ = (u_char)fat[cl].next; 625 *p++ = (u_char)((fat[cl].next >> 8) & 0xf) 626 |(u_char)(fat[cl+1].next << 4); 627 *p++ = (u_char)(fat[++cl].next >> 4); 628 break; 629 } 630 } 631 for (i = 0; i < boot->FATs; i++) { 632 off = boot->ResSectors + i * boot->FATsecs; 633 off *= boot->BytesPerSec; 634 if (lseek(fs, off, SEEK_SET) != off 635 || (size_t)write(fs, buffer, fatsz) != fatsz) { 636 perror("Unable to write FAT"); 637 ret = FSFATAL; /* Return immediately? XXX */ 638 } 639 } 640 free(buffer); 641 return ret; 642} 643 644/* 645 * Check a complete in-memory FAT for lost cluster chains 646 */ 647int 648checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) 649{ 650 cl_t head; 651 int mod = FSOK; 652 int ret; 653 654 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 655 /* find next untravelled chain */ 656 if (fat[head].head != head 657 || fat[head].next == CLUST_FREE 658 || (fat[head].next >= CLUST_RSRVD 659 && fat[head].next < CLUST_EOFS) 660 || (fat[head].flags & FAT_USED)) 661 continue; 662 663 pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 664 head, fat[head].length); 665 mod |= ret = reconnect(dosfs, boot, fat, head); 666 if (mod & FSFATAL) 667 break; 668 if (ret == FSERROR && ask(0, "Clear")) { 669 clearchain(boot, fat, head); 670 mod |= FSFATMOD; 671 } 672 } 673 finishlf(); 674 675 if (boot->FSInfo) { 676 ret = 0; 677 if (boot->FSFree != boot->NumFree) { 678 pwarn("Free space in FSInfo block (%d) not correct (%d)\n", 679 boot->FSFree, boot->NumFree); 680 if (ask(1, "Fix")) { 681 boot->FSFree = boot->NumFree; 682 ret = 1; 683 } 684 } 685 if (ret) 686 mod |= writefsinfo(dosfs, boot); 687 } 688 689 return mod; 690} 691