1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 5 * Copyright (c) 1995 Martin Husemann 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <stdlib.h> 29 #include <string.h> 30 #include <ctype.h> 31 #include <stdio.h> 32 #include <unistd.h> 33 34 #include "ext.h" 35 #include "fsutil.h" 36 37 static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *); 38 static int clustdiffer(cl_t, cl_t *, cl_t *, u_int); 39 static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 40 static int _readfat(int, struct bootblock *, u_int, u_char **); 41 42 /* 43 * Check a cluster number for valid value 44 */ 45 static int 46 checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next) 47 { 48 if (*next >= (CLUST_RSRVD&boot->ClustMask)) 49 *next |= ~boot->ClustMask; 50 if (*next == CLUST_FREE) { 51 boot->NumFree++; 52 return FSOK; 53 } 54 if (*next == CLUST_BAD) { 55 boot->NumBad++; 56 return FSOK; 57 } 58 if (*next < CLUST_FIRST 59 || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 60 pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 61 cl, fat, 62 *next < CLUST_RSRVD ? "out of range" : "reserved", 63 *next&boot->ClustMask); 64 if (ask(0, "Truncate")) { 65 *next = CLUST_EOF; 66 return FSFATMOD; 67 } 68 return FSERROR; 69 } 70 return FSOK; 71 } 72 73 /* 74 * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 75 */ 76 static int 77 _readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer) 78 { 79 off_t off; 80 81 *buffer = calloc(boot->FATsecs, boot->bpbBytesPerSec); 82 if (*buffer == NULL) { 83 perr("No space for FAT sectors (%zu)", 84 (size_t)boot->FATsecs); 85 return 0; 86 } 87 88 off = boot->bpbResSectors + no * boot->FATsecs; 89 off *= boot->bpbBytesPerSec; 90 91 if (lseek(fs, off, SEEK_SET) != off) { 92 perr("Unable to read FAT"); 93 goto err; 94 } 95 96 if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec) 97 != boot->FATsecs * boot->bpbBytesPerSec) { 98 perr("Unable to read FAT"); 99 goto err; 100 } 101 102 return 1; 103 104 err: 105 free(*buffer); 106 return 0; 107 } 108 109 /* 110 * Read a FAT and decode it into internal format 111 */ 112 int 113 readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp) 114 { 115 struct fatEntry *fat; 116 u_char *buffer, *p; 117 cl_t cl; 118 int ret = FSOK; 119 120 boot->NumFree = boot->NumBad = 0; 121 122 if (!_readfat(fs, boot, no, &buffer)) 123 return FSFATAL; 124 125 fat = calloc(boot->NumClusters, sizeof(struct fatEntry)); 126 if (fat == NULL) { 127 perr("No space for FAT clusters (%zu)", 128 (size_t)boot->NumClusters); 129 free(buffer); 130 return FSFATAL; 131 } 132 133 if (buffer[0] != boot->bpbMedia 134 || buffer[1] != 0xff || buffer[2] != 0xff 135 || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 136 || (boot->ClustMask == CLUST32_MASK 137 && ((buffer[3]&0x0f) != 0x0f 138 || buffer[4] != 0xff || buffer[5] != 0xff 139 || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 140 141 /* Windows 95 OSR2 (and possibly any later) changes 142 * the FAT signature to 0xXXffff7f for FAT16 and to 143 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 144 * file system is dirty if it doesn't reboot cleanly. 145 * Check this special condition before errorring out. 146 */ 147 if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff 148 && buffer[2] == 0xff 149 && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 150 || (boot->ClustMask == CLUST32_MASK 151 && buffer[3] == 0x0f && buffer[4] == 0xff 152 && buffer[5] == 0xff && buffer[6] == 0xff 153 && buffer[7] == 0x07))) 154 ret |= FSDIRTY; 155 else { 156 /* just some odd byte sequence in FAT */ 157 switch (boot->ClustMask) { 158 case CLUST32_MASK: 159 pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 160 "FAT starts with odd byte sequence", 161 buffer[0], buffer[1], buffer[2], buffer[3], 162 buffer[4], buffer[5], buffer[6], buffer[7]); 163 break; 164 case CLUST16_MASK: 165 pwarn("%s (%02x%02x%02x%02x)\n", 166 "FAT starts with odd byte sequence", 167 buffer[0], buffer[1], buffer[2], buffer[3]); 168 break; 169 default: 170 pwarn("%s (%02x%02x%02x)\n", 171 "FAT starts with odd byte sequence", 172 buffer[0], buffer[1], buffer[2]); 173 break; 174 } 175 176 if (ask(1, "Correct")) 177 ret |= FSFIXFAT; 178 } 179 } 180 switch (boot->ClustMask) { 181 case CLUST32_MASK: 182 p = buffer + 8; 183 break; 184 case CLUST16_MASK: 185 p = buffer + 4; 186 break; 187 default: 188 p = buffer + 3; 189 break; 190 } 191 for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 192 switch (boot->ClustMask) { 193 case CLUST32_MASK: 194 fat[cl].next = p[0] + (p[1] << 8) 195 + (p[2] << 16) + (p[3] << 24); 196 fat[cl].next &= boot->ClustMask; 197 ret |= checkclnum(boot, no, cl, &fat[cl].next); 198 cl++; 199 p += 4; 200 break; 201 case CLUST16_MASK: 202 fat[cl].next = p[0] + (p[1] << 8); 203 ret |= checkclnum(boot, no, cl, &fat[cl].next); 204 cl++; 205 p += 2; 206 break; 207 default: 208 fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 209 ret |= checkclnum(boot, no, cl, &fat[cl].next); 210 cl++; 211 if (cl >= boot->NumClusters) 212 break; 213 fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 214 ret |= checkclnum(boot, no, cl, &fat[cl].next); 215 cl++; 216 p += 3; 217 break; 218 } 219 } 220 221 free(buffer); 222 if (ret & FSFATAL) { 223 free(fat); 224 *fp = NULL; 225 } else 226 *fp = fat; 227 return ret; 228 } 229 230 /* 231 * Get type of reserved cluster 232 */ 233 const char * 234 rsrvdcltype(cl_t cl) 235 { 236 if (cl == CLUST_FREE) 237 return "free"; 238 if (cl < CLUST_BAD) 239 return "reserved"; 240 if (cl > CLUST_BAD) 241 return "as EOF"; 242 return "bad"; 243 } 244 245 static int 246 clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum) 247 { 248 if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 249 if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 250 if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 251 && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 252 || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 253 pwarn("Cluster %u is marked %s with different indicators\n", 254 cl, rsrvdcltype(*cp1)); 255 if (ask(1, "Fix")) { 256 *cp2 = *cp1; 257 return FSFATMOD; 258 } 259 return FSFATAL; 260 } 261 pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n", 262 cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 263 if (ask(0, "Use FAT 0's entry")) { 264 *cp2 = *cp1; 265 return FSFATMOD; 266 } 267 if (ask(0, "Use FAT %u's entry", fatnum)) { 268 *cp1 = *cp2; 269 return FSFATMOD; 270 } 271 return FSFATAL; 272 } 273 pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 274 cl, rsrvdcltype(*cp1), *cp2, fatnum); 275 if (ask(0, "Use continuation from FAT %u", fatnum)) { 276 *cp1 = *cp2; 277 return FSFATMOD; 278 } 279 if (ask(0, "Use mark from FAT 0")) { 280 *cp2 = *cp1; 281 return FSFATMOD; 282 } 283 return FSFATAL; 284 } 285 if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 286 pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n", 287 cl, *cp1, rsrvdcltype(*cp2), fatnum); 288 if (ask(0, "Use continuation from FAT 0")) { 289 *cp2 = *cp1; 290 return FSFATMOD; 291 } 292 if (ask(0, "Use mark from FAT %d", fatnum)) { 293 *cp1 = *cp2; 294 return FSFATMOD; 295 } 296 return FSERROR; 297 } 298 pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n", 299 cl, *cp1, *cp2, fatnum); 300 if (ask(0, "Use continuation from FAT 0")) { 301 *cp2 = *cp1; 302 return FSFATMOD; 303 } 304 if (ask(0, "Use continuation from FAT %u", fatnum)) { 305 *cp1 = *cp2; 306 return FSFATMOD; 307 } 308 return FSERROR; 309 } 310 311 /* 312 * Compare two FAT copies in memory. Resolve any conflicts and merge them 313 * into the first one. 314 */ 315 int 316 comparefat(struct bootblock *boot, struct fatEntry *first, 317 struct fatEntry *second, u_int fatnum) 318 { 319 cl_t cl; 320 int ret = FSOK; 321 322 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 323 if (first[cl].next != second[cl].next) 324 ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 325 return ret; 326 } 327 328 void 329 clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 330 { 331 cl_t p, q; 332 333 for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 334 if (fat[p].head != head) 335 break; 336 q = fat[p].next; 337 fat[p].next = fat[p].head = CLUST_FREE; 338 fat[p].length = 0; 339 } 340 } 341 342 int 343 tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp) 344 { 345 if (ask(0, "Clear chain starting at %u", head)) { 346 clearchain(boot, fat, head); 347 return FSFATMOD; 348 } else if (ask(0, "Truncate")) { 349 uint32_t len; 350 cl_t p; 351 352 for (p = head, len = 0; 353 p >= CLUST_FIRST && p < boot->NumClusters; 354 p = fat[p].next, len++) 355 continue; 356 *truncp = CLUST_EOF; 357 fat[head].length = len; 358 return FSFATMOD; 359 } else 360 return FSERROR; 361 } 362 363 /* 364 * Check a complete FAT in-memory for crosslinks 365 */ 366 int 367 checkfat(struct bootblock *boot, struct fatEntry *fat) 368 { 369 cl_t head, p, h, n; 370 u_int len; 371 int ret = 0; 372 int conf; 373 374 /* 375 * pass 1: figure out the cluster chains. 376 */ 377 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 378 /* find next untravelled chain */ 379 if (fat[head].head != 0 /* cluster already belongs to some chain */ 380 || fat[head].next == CLUST_FREE 381 || fat[head].next == CLUST_BAD) 382 continue; /* skip it. */ 383 384 /* follow the chain and mark all clusters on the way */ 385 for (len = 0, p = head; 386 p >= CLUST_FIRST && p < boot->NumClusters && 387 fat[p].head != head; 388 p = fat[p].next) { 389 fat[p].head = head; 390 len++; 391 } 392 393 /* the head record gets the length */ 394 fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 395 } 396 397 /* 398 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 399 * we didn't know the real start of the chain then - would have treated partial 400 * chains as interlinked with their main chain) 401 */ 402 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 403 /* find next untravelled chain */ 404 if (fat[head].head != head) 405 continue; 406 407 /* follow the chain to its end (hopefully) */ 408 for (len = fat[head].length, p = head; 409 (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 410 p = n) 411 if (fat[n].head != head || len-- < 2) 412 break; 413 if (n >= CLUST_EOFS) 414 continue; 415 416 if (n == CLUST_FREE || n >= CLUST_RSRVD) { 417 pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 418 head, rsrvdcltype(n)); 419 clear: 420 ret |= tryclear(boot, fat, head, &fat[p].next); 421 continue; 422 } 423 if (n < CLUST_FIRST || n >= boot->NumClusters) { 424 pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 425 head, n); 426 goto clear; 427 } 428 if (head == fat[n].head) { 429 pwarn("Cluster chain starting at %u loops at cluster %u\n", 430 head, p); 431 goto clear; 432 } 433 pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 434 head, fat[n].head, n); 435 conf = tryclear(boot, fat, head, &fat[p].next); 436 if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 437 if (conf == FSERROR) { 438 /* 439 * Transfer the common chain to the one not cleared above. 440 */ 441 for (p = n; 442 p >= CLUST_FIRST && p < boot->NumClusters; 443 p = fat[p].next) { 444 if (h != fat[p].head) { 445 /* 446 * Have to reexamine this chain. 447 */ 448 head--; 449 break; 450 } 451 fat[p].head = head; 452 } 453 } 454 clearchain(boot, fat, h); 455 conf |= FSFATMOD; 456 } 457 ret |= conf; 458 } 459 460 return ret; 461 } 462 463 /* 464 * Write out FATs encoding them from the internal format 465 */ 466 int 467 writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 468 { 469 u_char *buffer, *p; 470 cl_t cl; 471 u_int i; 472 size_t fatsz; 473 off_t off; 474 int ret = FSOK; 475 476 fatsz = boot->FATsecs * boot->bpbBytesPerSec; 477 buffer = calloc(boot->FATsecs, boot->bpbBytesPerSec); 478 if (buffer == NULL) { 479 perr("No space for FAT sectors (%zu)", 480 (size_t)boot->FATsecs); 481 return FSFATAL; 482 } 483 boot->NumFree = 0; 484 p = buffer; 485 if (correct_fat) { 486 *p++ = (u_char)boot->bpbMedia; 487 *p++ = 0xff; 488 *p++ = 0xff; 489 switch (boot->ClustMask) { 490 case CLUST16_MASK: 491 *p++ = 0xff; 492 break; 493 case CLUST32_MASK: 494 *p++ = 0x0f; 495 *p++ = 0xff; 496 *p++ = 0xff; 497 *p++ = 0xff; 498 *p++ = 0x0f; 499 break; 500 } 501 } else { 502 /* use same FAT signature as the old FAT has */ 503 int count; 504 u_char *old_fat; 505 506 switch (boot->ClustMask) { 507 case CLUST32_MASK: 508 count = 8; 509 break; 510 case CLUST16_MASK: 511 count = 4; 512 break; 513 default: 514 count = 3; 515 break; 516 } 517 518 if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 519 &old_fat)) { 520 free(buffer); 521 return FSFATAL; 522 } 523 524 memcpy(p, old_fat, count); 525 free(old_fat); 526 p += count; 527 } 528 529 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 530 switch (boot->ClustMask) { 531 case CLUST32_MASK: 532 if (fat[cl].next == CLUST_FREE) 533 boot->NumFree++; 534 *p++ = (u_char)fat[cl].next; 535 *p++ = (u_char)(fat[cl].next >> 8); 536 *p++ = (u_char)(fat[cl].next >> 16); 537 *p &= 0xf0; 538 *p++ |= (fat[cl].next >> 24)&0x0f; 539 break; 540 case CLUST16_MASK: 541 if (fat[cl].next == CLUST_FREE) 542 boot->NumFree++; 543 *p++ = (u_char)fat[cl].next; 544 *p++ = (u_char)(fat[cl].next >> 8); 545 break; 546 default: 547 if (fat[cl].next == CLUST_FREE) 548 boot->NumFree++; 549 *p++ = (u_char)fat[cl].next; 550 *p = (u_char)((fat[cl].next >> 8) & 0xf); 551 cl++; 552 if (cl >= boot->NumClusters) 553 break; 554 if (fat[cl].next == CLUST_FREE) 555 boot->NumFree++; 556 *p++ |= (u_char)(fat[cl].next << 4); 557 *p++ = (u_char)(fat[cl].next >> 4); 558 break; 559 } 560 } 561 for (i = 0; i < boot->bpbFATs; i++) { 562 off = boot->bpbResSectors + i * boot->FATsecs; 563 off *= boot->bpbBytesPerSec; 564 if (lseek(fs, off, SEEK_SET) != off 565 || (size_t)write(fs, buffer, fatsz) != fatsz) { 566 perr("Unable to write FAT"); 567 ret = FSFATAL; /* Return immediately? XXX */ 568 } 569 } 570 free(buffer); 571 return ret; 572 } 573 574 /* 575 * Check a complete in-memory FAT for lost cluster chains 576 */ 577 int 578 checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) 579 { 580 cl_t head; 581 int mod = FSOK; 582 int ret; 583 584 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 585 /* find next untravelled chain */ 586 if (fat[head].head != head 587 || fat[head].next == CLUST_FREE 588 || (fat[head].next >= CLUST_RSRVD 589 && fat[head].next < CLUST_EOFS) 590 || (fat[head].flags & FAT_USED)) 591 continue; 592 593 pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 594 head, fat[head].length); 595 mod |= ret = reconnect(dosfs, boot, fat, head); 596 if (mod & FSFATAL) 597 break; 598 if (ret == FSERROR && ask(0, "Clear")) { 599 clearchain(boot, fat, head); 600 mod |= FSFATMOD; 601 } 602 } 603 finishlf(); 604 605 if (boot->bpbFSInfo) { 606 ret = 0; 607 if (boot->FSFree != 0xffffffffU && 608 boot->FSFree != boot->NumFree) { 609 pwarn("Free space in FSInfo block (%u) not correct (%u)\n", 610 boot->FSFree, boot->NumFree); 611 if (ask(1, "Fix")) { 612 boot->FSFree = boot->NumFree; 613 ret = 1; 614 } 615 } 616 if (boot->FSNext != 0xffffffffU && 617 (boot->FSNext >= boot->NumClusters || 618 (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) { 619 pwarn("Next free cluster in FSInfo block (%u) %s\n", 620 boot->FSNext, 621 (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free"); 622 if (ask(1, "fix")) 623 for (head = CLUST_FIRST; head < boot->NumClusters; head++) 624 if (fat[head].next == CLUST_FREE) { 625 boot->FSNext = head; 626 ret = 1; 627 break; 628 } 629 } 630 if (ret) 631 mod |= writefsinfo(dosfs, boot); 632 } 633 634 return mod; 635 } 636