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 29 #include <sys/cdefs.h> 30 #ifndef lint 31 __RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $"); 32 static const char rcsid[] = 33 "$FreeBSD$"; 34 #endif /* not lint */ 35 36 #include <stdlib.h> 37 #include <string.h> 38 #include <ctype.h> 39 #include <stdio.h> 40 #include <unistd.h> 41 42 #include "ext.h" 43 #include "fsutil.h" 44 45 static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *); 46 static int clustdiffer(cl_t, cl_t *, cl_t *, u_int); 47 static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 48 static int _readfat(int, struct bootblock *, u_int, u_char **); 49 50 /*- 51 * The first 2 FAT entries contain pseudo-cluster numbers with the following 52 * layout: 53 * 54 * 31...... ........ ........ .......0 55 * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0 56 * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1 57 * 58 * 11111111 mmmmmmmm FAT16 entry 0 59 * sh111111 11111xxx FAT16 entry 1 60 * 61 * r = reserved 62 * m = BPB media ID byte 63 * s = clean flag (1 = dismounted; 0 = still mounted) 64 * h = hard error flag (1 = ok; 0 = I/O error) 65 * x = any value ok 66 */ 67 68 int 69 checkdirty(int fs, struct bootblock *boot) 70 { 71 off_t off; 72 u_char *buffer; 73 int ret = 0; 74 size_t len; 75 76 if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK) 77 return 0; 78 79 off = boot->bpbResSectors; 80 off *= boot->bpbBytesPerSec; 81 82 buffer = malloc(len = boot->bpbBytesPerSec); 83 if (buffer == NULL) { 84 perr("No space for FAT sectors (%zu)", len); 85 return 1; 86 } 87 88 if (lseek(fs, off, SEEK_SET) != off) { 89 perr("Unable to read FAT"); 90 goto err; 91 } 92 93 if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) != 94 boot->bpbBytesPerSec) { 95 perr("Unable to read FAT"); 96 goto err; 97 } 98 99 /* 100 * If we don't understand the FAT, then the file system must be 101 * assumed to be unclean. 102 */ 103 if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff) 104 goto err; 105 if (boot->ClustMask == CLUST16_MASK) { 106 if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f) 107 goto err; 108 } else { 109 if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f 110 || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff 111 || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03) 112 goto err; 113 } 114 115 /* 116 * Now check the actual clean flag (and the no-error flag). 117 */ 118 if (boot->ClustMask == CLUST16_MASK) { 119 if ((buffer[3] & 0xc0) == 0xc0) 120 ret = 1; 121 } else { 122 if ((buffer[7] & 0x0c) == 0x0c) 123 ret = 1; 124 } 125 126 err: 127 free(buffer); 128 return ret; 129 } 130 131 /* 132 * Check a cluster number for valid value 133 */ 134 static int 135 checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next) 136 { 137 if (*next >= (CLUST_RSRVD&boot->ClustMask)) 138 *next |= ~boot->ClustMask; 139 if (*next == CLUST_FREE) { 140 boot->NumFree++; 141 return FSOK; 142 } 143 if (*next == CLUST_BAD) { 144 boot->NumBad++; 145 return FSOK; 146 } 147 if (*next < CLUST_FIRST 148 || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 149 pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 150 cl, fat, 151 *next < CLUST_RSRVD ? "out of range" : "reserved", 152 *next&boot->ClustMask); 153 if (ask(0, "Truncate")) { 154 *next = CLUST_EOF; 155 return FSFATMOD; 156 } 157 return FSERROR; 158 } 159 return FSOK; 160 } 161 162 /* 163 * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 164 */ 165 static int 166 _readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer) 167 { 168 off_t off; 169 size_t len; 170 171 *buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec); 172 if (*buffer == NULL) { 173 perr("No space for FAT sectors (%zu)", len); 174 return 0; 175 } 176 177 off = boot->bpbResSectors + no * boot->FATsecs; 178 off *= boot->bpbBytesPerSec; 179 180 if (lseek(fs, off, SEEK_SET) != off) { 181 perr("Unable to read FAT"); 182 goto err; 183 } 184 185 if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec) 186 != boot->FATsecs * boot->bpbBytesPerSec) { 187 perr("Unable to read FAT"); 188 goto err; 189 } 190 191 return 1; 192 193 err: 194 free(*buffer); 195 return 0; 196 } 197 198 /* 199 * Read a FAT and decode it into internal format 200 */ 201 int 202 readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp) 203 { 204 struct fatEntry *fat; 205 u_char *buffer, *p; 206 cl_t cl; 207 int ret = FSOK; 208 size_t len; 209 210 boot->NumFree = boot->NumBad = 0; 211 212 if (!_readfat(fs, boot, no, &buffer)) 213 return FSFATAL; 214 215 fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry)); 216 if (fat == NULL) { 217 perr("No space for FAT clusters (%zu)", len); 218 free(buffer); 219 return FSFATAL; 220 } 221 (void)memset(fat, 0, len); 222 223 if (buffer[0] != boot->bpbMedia 224 || buffer[1] != 0xff || buffer[2] != 0xff 225 || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 226 || (boot->ClustMask == CLUST32_MASK 227 && ((buffer[3]&0x0f) != 0x0f 228 || buffer[4] != 0xff || buffer[5] != 0xff 229 || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 230 231 /* Windows 95 OSR2 (and possibly any later) changes 232 * the FAT signature to 0xXXffff7f for FAT16 and to 233 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 234 * file system is dirty if it doesn't reboot cleanly. 235 * Check this special condition before errorring out. 236 */ 237 if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff 238 && buffer[2] == 0xff 239 && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 240 || (boot->ClustMask == CLUST32_MASK 241 && buffer[3] == 0x0f && buffer[4] == 0xff 242 && buffer[5] == 0xff && buffer[6] == 0xff 243 && buffer[7] == 0x07))) 244 ret |= FSDIRTY; 245 else { 246 /* just some odd byte sequence in FAT */ 247 248 switch (boot->ClustMask) { 249 case CLUST32_MASK: 250 pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 251 "FAT starts with odd byte sequence", 252 buffer[0], buffer[1], buffer[2], buffer[3], 253 buffer[4], buffer[5], buffer[6], buffer[7]); 254 break; 255 case CLUST16_MASK: 256 pwarn("%s (%02x%02x%02x%02x)\n", 257 "FAT starts with odd byte sequence", 258 buffer[0], buffer[1], buffer[2], buffer[3]); 259 break; 260 default: 261 pwarn("%s (%02x%02x%02x)\n", 262 "FAT starts with odd byte sequence", 263 buffer[0], buffer[1], buffer[2]); 264 break; 265 } 266 267 268 if (ask(1, "Correct")) 269 ret |= FSFIXFAT; 270 } 271 } 272 switch (boot->ClustMask) { 273 case CLUST32_MASK: 274 p = buffer + 8; 275 break; 276 case CLUST16_MASK: 277 p = buffer + 4; 278 break; 279 default: 280 p = buffer + 3; 281 break; 282 } 283 for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 284 switch (boot->ClustMask) { 285 case CLUST32_MASK: 286 fat[cl].next = p[0] + (p[1] << 8) 287 + (p[2] << 16) + (p[3] << 24); 288 fat[cl].next &= boot->ClustMask; 289 ret |= checkclnum(boot, no, cl, &fat[cl].next); 290 cl++; 291 p += 4; 292 break; 293 case CLUST16_MASK: 294 fat[cl].next = p[0] + (p[1] << 8); 295 ret |= checkclnum(boot, no, cl, &fat[cl].next); 296 cl++; 297 p += 2; 298 break; 299 default: 300 fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 301 ret |= checkclnum(boot, no, cl, &fat[cl].next); 302 cl++; 303 if (cl >= boot->NumClusters) 304 break; 305 fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 306 ret |= checkclnum(boot, no, cl, &fat[cl].next); 307 cl++; 308 p += 3; 309 break; 310 } 311 } 312 313 free(buffer); 314 if (ret & FSFATAL) { 315 free(fat); 316 *fp = NULL; 317 } else 318 *fp = fat; 319 return ret; 320 } 321 322 /* 323 * Get type of reserved cluster 324 */ 325 const char * 326 rsrvdcltype(cl_t cl) 327 { 328 if (cl == CLUST_FREE) 329 return "free"; 330 if (cl < CLUST_BAD) 331 return "reserved"; 332 if (cl > CLUST_BAD) 333 return "as EOF"; 334 return "bad"; 335 } 336 337 static int 338 clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum) 339 { 340 if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 341 if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 342 if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 343 && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 344 || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 345 pwarn("Cluster %u is marked %s with different indicators\n", 346 cl, rsrvdcltype(*cp1)); 347 if (ask(1, "Fix")) { 348 *cp2 = *cp1; 349 return FSFATMOD; 350 } 351 return FSFATAL; 352 } 353 pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n", 354 cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 355 if (ask(0, "Use FAT 0's entry")) { 356 *cp2 = *cp1; 357 return FSFATMOD; 358 } 359 if (ask(0, "Use FAT %u's entry", fatnum)) { 360 *cp1 = *cp2; 361 return FSFATMOD; 362 } 363 return FSFATAL; 364 } 365 pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 366 cl, rsrvdcltype(*cp1), *cp2, fatnum); 367 if (ask(0, "Use continuation from FAT %u", fatnum)) { 368 *cp1 = *cp2; 369 return FSFATMOD; 370 } 371 if (ask(0, "Use mark from FAT 0")) { 372 *cp2 = *cp1; 373 return FSFATMOD; 374 } 375 return FSFATAL; 376 } 377 if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 378 pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n", 379 cl, *cp1, rsrvdcltype(*cp2), fatnum); 380 if (ask(0, "Use continuation from FAT 0")) { 381 *cp2 = *cp1; 382 return FSFATMOD; 383 } 384 if (ask(0, "Use mark from FAT %d", fatnum)) { 385 *cp1 = *cp2; 386 return FSFATMOD; 387 } 388 return FSERROR; 389 } 390 pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n", 391 cl, *cp1, *cp2, fatnum); 392 if (ask(0, "Use continuation from FAT 0")) { 393 *cp2 = *cp1; 394 return FSFATMOD; 395 } 396 if (ask(0, "Use continuation from FAT %u", fatnum)) { 397 *cp1 = *cp2; 398 return FSFATMOD; 399 } 400 return FSERROR; 401 } 402 403 /* 404 * Compare two FAT copies in memory. Resolve any conflicts and merge them 405 * into the first one. 406 */ 407 int 408 comparefat(struct bootblock *boot, struct fatEntry *first, 409 struct fatEntry *second, u_int fatnum) 410 { 411 cl_t cl; 412 int ret = FSOK; 413 414 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 415 if (first[cl].next != second[cl].next) 416 ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 417 return ret; 418 } 419 420 void 421 clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 422 { 423 cl_t p, q; 424 425 for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 426 if (fat[p].head != head) 427 break; 428 q = fat[p].next; 429 fat[p].next = fat[p].head = CLUST_FREE; 430 fat[p].length = 0; 431 } 432 } 433 434 int 435 tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp) 436 { 437 if (ask(0, "Clear chain starting at %u", head)) { 438 clearchain(boot, fat, head); 439 return FSFATMOD; 440 } else if (ask(0, "Truncate")) { 441 uint32_t len; 442 cl_t p; 443 444 for (p = head, len = 0; 445 p >= CLUST_FIRST && p < boot->NumClusters; 446 p = fat[p].next, len++) 447 continue; 448 *truncp = CLUST_EOF; 449 fat[head].length = len; 450 return FSFATMOD; 451 } else 452 return FSERROR; 453 } 454 455 /* 456 * Check a complete FAT in-memory for crosslinks 457 */ 458 int 459 checkfat(struct bootblock *boot, struct fatEntry *fat) 460 { 461 cl_t head, p, h, n; 462 u_int len; 463 int ret = 0; 464 int conf; 465 466 /* 467 * pass 1: figure out the cluster chains. 468 */ 469 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 470 /* find next untravelled chain */ 471 if (fat[head].head != 0 /* cluster already belongs to some chain */ 472 || fat[head].next == CLUST_FREE 473 || fat[head].next == CLUST_BAD) 474 continue; /* skip it. */ 475 476 /* follow the chain and mark all clusters on the way */ 477 for (len = 0, p = head; 478 p >= CLUST_FIRST && p < boot->NumClusters && 479 fat[p].head != head; 480 p = fat[p].next) { 481 fat[p].head = head; 482 len++; 483 } 484 485 /* the head record gets the length */ 486 fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 487 } 488 489 /* 490 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 491 * we didn't know the real start of the chain then - would have treated partial 492 * chains as interlinked with their main chain) 493 */ 494 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 495 /* find next untravelled chain */ 496 if (fat[head].head != head) 497 continue; 498 499 /* follow the chain to its end (hopefully) */ 500 for (len = fat[head].length, p = head; 501 (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 502 p = n) 503 if (fat[n].head != head || len-- < 2) 504 break; 505 if (n >= CLUST_EOFS) 506 continue; 507 508 if (n == CLUST_FREE || n >= CLUST_RSRVD) { 509 pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 510 head, rsrvdcltype(n)); 511 clear: 512 ret |= tryclear(boot, fat, head, &fat[p].next); 513 continue; 514 } 515 if (n < CLUST_FIRST || n >= boot->NumClusters) { 516 pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 517 head, n); 518 goto clear; 519 } 520 if (head == fat[n].head) { 521 pwarn("Cluster chain starting at %u loops at cluster %u\n", 522 523 head, p); 524 goto clear; 525 } 526 pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 527 head, fat[n].head, n); 528 conf = tryclear(boot, fat, head, &fat[p].next); 529 if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 530 if (conf == FSERROR) { 531 /* 532 * Transfer the common chain to the one not cleared above. 533 */ 534 for (p = n; 535 p >= CLUST_FIRST && p < boot->NumClusters; 536 p = fat[p].next) { 537 if (h != fat[p].head) { 538 /* 539 * Have to reexamine this chain. 540 */ 541 head--; 542 break; 543 } 544 fat[p].head = head; 545 } 546 } 547 clearchain(boot, fat, h); 548 conf |= FSFATMOD; 549 } 550 ret |= conf; 551 } 552 553 return ret; 554 } 555 556 /* 557 * Write out FATs encoding them from the internal format 558 */ 559 int 560 writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 561 { 562 u_char *buffer, *p; 563 cl_t cl; 564 u_int i; 565 size_t fatsz; 566 off_t off; 567 int ret = FSOK; 568 569 buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec); 570 if (buffer == NULL) { 571 perr("No space for FAT sectors (%zu)", fatsz); 572 return FSFATAL; 573 } 574 memset(buffer, 0, fatsz); 575 boot->NumFree = 0; 576 p = buffer; 577 if (correct_fat) { 578 *p++ = (u_char)boot->bpbMedia; 579 *p++ = 0xff; 580 *p++ = 0xff; 581 switch (boot->ClustMask) { 582 case CLUST16_MASK: 583 *p++ = 0xff; 584 break; 585 case CLUST32_MASK: 586 *p++ = 0x0f; 587 *p++ = 0xff; 588 *p++ = 0xff; 589 *p++ = 0xff; 590 *p++ = 0x0f; 591 break; 592 } 593 } else { 594 /* use same FAT signature as the old FAT has */ 595 int count; 596 u_char *old_fat; 597 598 switch (boot->ClustMask) { 599 case CLUST32_MASK: 600 count = 8; 601 break; 602 case CLUST16_MASK: 603 count = 4; 604 break; 605 default: 606 count = 3; 607 break; 608 } 609 610 if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 611 &old_fat)) { 612 free(buffer); 613 return FSFATAL; 614 } 615 616 memcpy(p, old_fat, count); 617 free(old_fat); 618 p += count; 619 } 620 621 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 622 switch (boot->ClustMask) { 623 case CLUST32_MASK: 624 if (fat[cl].next == CLUST_FREE) 625 boot->NumFree++; 626 *p++ = (u_char)fat[cl].next; 627 *p++ = (u_char)(fat[cl].next >> 8); 628 *p++ = (u_char)(fat[cl].next >> 16); 629 *p &= 0xf0; 630 *p++ |= (fat[cl].next >> 24)&0x0f; 631 break; 632 case CLUST16_MASK: 633 if (fat[cl].next == CLUST_FREE) 634 boot->NumFree++; 635 *p++ = (u_char)fat[cl].next; 636 *p++ = (u_char)(fat[cl].next >> 8); 637 break; 638 default: 639 if (fat[cl].next == CLUST_FREE) 640 boot->NumFree++; 641 *p++ = (u_char)fat[cl].next; 642 *p = (u_char)((fat[cl].next >> 8) & 0xf); 643 cl++; 644 if (cl >= boot->NumClusters) 645 break; 646 if (fat[cl].next == CLUST_FREE) 647 boot->NumFree++; 648 *p++ |= (u_char)(fat[cl + 1].next << 4); 649 *p++ = (u_char)(fat[cl + 1].next >> 4); 650 break; 651 } 652 } 653 for (i = 0; i < boot->bpbFATs; i++) { 654 off = boot->bpbResSectors + i * boot->FATsecs; 655 off *= boot->bpbBytesPerSec; 656 if (lseek(fs, off, SEEK_SET) != off 657 || (size_t)write(fs, buffer, fatsz) != fatsz) { 658 perr("Unable to write FAT"); 659 ret = FSFATAL; /* Return immediately? XXX */ 660 } 661 } 662 free(buffer); 663 return ret; 664 } 665 666 /* 667 * Check a complete in-memory FAT for lost cluster chains 668 */ 669 int 670 checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) 671 { 672 cl_t head; 673 int mod = FSOK; 674 int ret; 675 676 for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 677 /* find next untravelled chain */ 678 if (fat[head].head != head 679 || fat[head].next == CLUST_FREE 680 || (fat[head].next >= CLUST_RSRVD 681 && fat[head].next < CLUST_EOFS) 682 || (fat[head].flags & FAT_USED)) 683 continue; 684 685 pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 686 head, fat[head].length); 687 mod |= ret = reconnect(dosfs, boot, fat, head); 688 if (mod & FSFATAL) 689 break; 690 if (ret == FSERROR && ask(0, "Clear")) { 691 clearchain(boot, fat, head); 692 mod |= FSFATMOD; 693 } 694 } 695 finishlf(); 696 697 if (boot->bpbFSInfo) { 698 ret = 0; 699 if (boot->FSFree != 0xffffffffU && 700 boot->FSFree != boot->NumFree) { 701 pwarn("Free space in FSInfo block (%u) not correct (%u)\n", 702 boot->FSFree, boot->NumFree); 703 if (ask(1, "Fix")) { 704 boot->FSFree = boot->NumFree; 705 ret = 1; 706 } 707 } 708 if (ret) 709 mod |= writefsinfo(dosfs, boot); 710 } 711 712 return mod; 713 } 714