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