fat.c revision 125469
1193267Sjkim/* 2193267Sjkim * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 3193267Sjkim * Copyright (c) 1995 Martin Husemann 4193267Sjkim * 5193267Sjkim * Redistribution and use in source and binary forms, with or without 6193267Sjkim * modification, are permitted provided that the following conditions 7217365Sjkim * are met: 8217365Sjkim * 1. Redistributions of source code must retain the above copyright 9193267Sjkim * notice, this list of conditions and the following disclaimer. 10193267Sjkim * 2. Redistributions in binary form must reproduce the above copyright 11217365Sjkim * notice, this list of conditions and the following disclaimer in the 12217365Sjkim * documentation and/or other materials provided with the distribution. 13217365Sjkim * 3. All advertising materials mentioning features or use of this software 14217365Sjkim * must display the following acknowledgement: 15217365Sjkim * This product includes software developed by Martin Husemann 16217365Sjkim * and Wolfgang Solfrank. 17217365Sjkim * 4. Neither the name of the University nor the names of its contributors 18217365Sjkim * may be used to endorse or promote products derived from this software 19217365Sjkim * without specific prior written permission. 20217365Sjkim * 21217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 22217365Sjkim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23217365Sjkim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24217365Sjkim * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25193267Sjkim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26217365Sjkim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27217365Sjkim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28217365Sjkim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29193267Sjkim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30217365Sjkim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31217365Sjkim */ 32217365Sjkim 33217365Sjkim 34217365Sjkim#include <sys/cdefs.h> 35217365Sjkim#ifndef lint 36217365Sjkim__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $"); 37217365Sjkimstatic const char rcsid[] = 38217365Sjkim "$FreeBSD: head/sbin/fsck_msdosfs/fat.c 125469 2004-02-05 06:32:16Z bde $"; 39217365Sjkim#endif /* not lint */ 40217365Sjkim 41217365Sjkim#include <stdlib.h> 42217365Sjkim#include <string.h> 43193267Sjkim#include <ctype.h> 44193267Sjkim#include <stdio.h> 45193267Sjkim#include <unistd.h> 46193267Sjkim 47193267Sjkim#include "ext.h" 48193267Sjkim#include "fsutil.h" 49193267Sjkim 50193267Sjkimstatic int checkclnum(struct bootblock *, int, cl_t, cl_t *); 51193267Sjkimstatic int clustdiffer(cl_t, cl_t *, cl_t *, int); 52193267Sjkimstatic int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 53193267Sjkimstatic int _readfat(int, struct bootblock *, int, u_char **); 54193267Sjkim 55193267Sjkimint 56193267Sjkimcheckdirty(int fs, struct bootblock *boot) 57193267Sjkim{ 58193267Sjkim off_t off; 59193267Sjkim u_char *buffer; 60193267Sjkim int ret = 0; 61193267Sjkim 62193267Sjkim if (boot->ClustMask == CLUST12_MASK) 63193267Sjkim return 0; 64193267Sjkim 65193267Sjkim off = boot->ResSectors; 66193267Sjkim off *= boot->BytesPerSec; 67197104Sjkim 68197104Sjkim buffer = malloc(boot->BytesPerSec); 69193267Sjkim if (buffer == NULL) { 70193267Sjkim perror("No space for FAT"); 71193267Sjkim return 1; 72193267Sjkim } 73193267Sjkim 74193267Sjkim if (lseek(fs, off, SEEK_SET) != off) { 75193267Sjkim perror("Unable to read FAT"); 76193267Sjkim goto err; 77193267Sjkim } 78193267Sjkim 79193267Sjkim if (read(fs, buffer, boot->BytesPerSec) != boot->BytesPerSec) { 80193267Sjkim perror("Unable to read FAT"); 81193267Sjkim goto err; 82193267Sjkim } 83193267Sjkim 84193267Sjkim if (buffer[0] == boot->Media && buffer[1] == 0xff && buffer[2] == 0xff 85193267Sjkim && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 86193267Sjkim || (boot->ClustMask == CLUST32_MASK && buffer[3] == 0x0f 87193267Sjkim && buffer[4] == 0xff && buffer[5] == 0xff 88197104Sjkim && buffer[6] == 0xff && buffer[7] == 0x07))) 89193267Sjkim ret = 0; 90193267Sjkim else 91193267Sjkim ret = 1; 92193267Sjkim 93193267Sjkimerr: 94197104Sjkim free(buffer); 95197104Sjkim return ret; 96197104Sjkim} 97228110Sjkim 98228110Sjkim/* 99228110Sjkim * Check a cluster number for valid value 100228110Sjkim */ 101228110Sjkimstatic int 102228110Sjkimcheckclnum(struct bootblock *boot, int fat, cl_t cl, cl_t *next) 103228110Sjkim{ 104228110Sjkim if (*next >= (CLUST_RSRVD&boot->ClustMask)) 105193267Sjkim *next |= ~boot->ClustMask; 106193267Sjkim if (*next == CLUST_FREE) { 107193267Sjkim boot->NumFree++; 108193267Sjkim return FSOK; 109193267Sjkim } 110193267Sjkim if (*next == CLUST_BAD) { 111193267Sjkim boot->NumBad++; 112193267Sjkim return FSOK; 113193267Sjkim } 114193267Sjkim if (*next < CLUST_FIRST 115193267Sjkim || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 116197104Sjkim pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 117228110Sjkim cl, fat, 118228110Sjkim *next < CLUST_RSRVD ? "out of range" : "reserved", 119193267Sjkim *next&boot->ClustMask); 120193267Sjkim if (ask(0, "Truncate")) { 121193267Sjkim *next = CLUST_EOF; 122197104Sjkim return FSFATMOD; 123193267Sjkim } 124193267Sjkim return FSERROR; 125193267Sjkim } 126193267Sjkim return FSOK; 127193267Sjkim} 128193267Sjkim 129193267Sjkim/* 130193267Sjkim * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 131193267Sjkim */ 132193267Sjkimstatic int 133197104Sjkim_readfat(int fs, struct bootblock *boot, int no, u_char **buffer) 134193267Sjkim{ 135193267Sjkim off_t off; 136193267Sjkim 137193267Sjkim *buffer = malloc(boot->FATsecs * boot->BytesPerSec); 138193267Sjkim if (*buffer == NULL) { 139193267Sjkim perror("No space for FAT"); 140193267Sjkim return 0; 141193267Sjkim } 142193267Sjkim 143193267Sjkim off = boot->ResSectors + no * boot->FATsecs; 144193267Sjkim off *= boot->BytesPerSec; 145193267Sjkim 146193267Sjkim if (lseek(fs, off, SEEK_SET) != off) { 147193267Sjkim perror("Unable to read FAT"); 148193267Sjkim goto err; 149193267Sjkim } 150193267Sjkim 151193267Sjkim if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec) 152193267Sjkim != boot->FATsecs * boot->BytesPerSec) { 153193267Sjkim perror("Unable to read FAT"); 154193267Sjkim goto err; 155193267Sjkim } 156193267Sjkim 157193267Sjkim return 1; 158193267Sjkim 159193267Sjkim err: 160193267Sjkim free(*buffer); 161193267Sjkim return 0; 162193267Sjkim} 163193267Sjkim 164193267Sjkim/* 165193267Sjkim * Read a FAT and decode it into internal format 166193267Sjkim */ 167193267Sjkimint 168193267Sjkimreadfat(int fs, struct bootblock *boot, int no, struct fatEntry **fp) 169193267Sjkim{ 170193267Sjkim struct fatEntry *fat; 171228110Sjkim u_char *buffer, *p; 172193267Sjkim cl_t cl; 173193267Sjkim int ret = FSOK; 174193267Sjkim 175193267Sjkim boot->NumFree = boot->NumBad = 0; 176193267Sjkim 177193267Sjkim if (!_readfat(fs, boot, no, &buffer)) 178193267Sjkim return FSFATAL; 179193267Sjkim 180193267Sjkim fat = calloc(boot->NumClusters, sizeof(struct fatEntry)); 181193267Sjkim if (fat == NULL) { 182193267Sjkim perror("No space for FAT"); 183193267Sjkim free(buffer); 184193267Sjkim return FSFATAL; 185193267Sjkim } 186193267Sjkim 187193267Sjkim if (buffer[0] != boot->Media 188193267Sjkim || buffer[1] != 0xff || buffer[2] != 0xff 189193267Sjkim || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 190193267Sjkim || (boot->ClustMask == CLUST32_MASK 191193267Sjkim && ((buffer[3]&0x0f) != 0x0f 192193267Sjkim || buffer[4] != 0xff || buffer[5] != 0xff 193193267Sjkim || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 194193267Sjkim 195193267Sjkim /* Windows 95 OSR2 (and possibly any later) changes 196193267Sjkim * the FAT signature to 0xXXffff7f for FAT16 and to 197193267Sjkim * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 198193267Sjkim * file system is dirty if it doesn't reboot cleanly. 199193267Sjkim * Check this special condition before errorring out. 200193267Sjkim */ 201193267Sjkim if (buffer[0] == boot->Media && buffer[1] == 0xff 202193267Sjkim && buffer[2] == 0xff 203193267Sjkim && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 204193267Sjkim || (boot->ClustMask == CLUST32_MASK 205193267Sjkim && buffer[3] == 0x0f && buffer[4] == 0xff 206193267Sjkim && buffer[5] == 0xff && buffer[6] == 0xff 207193267Sjkim && buffer[7] == 0x07))) 208193267Sjkim ret |= FSDIRTY; 209197104Sjkim else { 210197104Sjkim /* just some odd byte sequence in FAT */ 211197104Sjkim 212193267Sjkim switch (boot->ClustMask) { 213193267Sjkim case CLUST32_MASK: 214193267Sjkim pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 215193267Sjkim "FAT starts with odd byte sequence", 216193267Sjkim buffer[0], buffer[1], buffer[2], buffer[3], 217197104Sjkim buffer[4], buffer[5], buffer[6], buffer[7]); 218193267Sjkim break; 219193267Sjkim case CLUST16_MASK: 220193267Sjkim pwarn("%s (%02x%02x%02x%02x)\n", 221193267Sjkim "FAT starts with odd byte sequence", 222193267Sjkim buffer[0], buffer[1], buffer[2], buffer[3]); 223197104Sjkim break; 224197104Sjkim default: 225197104Sjkim pwarn("%s (%02x%02x%02x)\n", 226193267Sjkim "FAT starts with odd byte sequence", 227197104Sjkim buffer[0], buffer[1], buffer[2]); 228193267Sjkim break; 229193267Sjkim } 230193267Sjkim 231193267Sjkim 232197104Sjkim if (ask(1, "Correct")) 233193267Sjkim ret |= FSFIXFAT; 234193267Sjkim } 235193267Sjkim } 236193267Sjkim switch (boot->ClustMask) { 237193267Sjkim case CLUST32_MASK: 238193267Sjkim p = buffer + 8; 239193267Sjkim break; 240197104Sjkim case CLUST16_MASK: 241193267Sjkim p = buffer + 4; 242193267Sjkim break; 243193267Sjkim default: 244228110Sjkim p = buffer + 3; 245228110Sjkim break; 246228110Sjkim } 247228110Sjkim for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 248228110Sjkim switch (boot->ClustMask) { 249228110Sjkim case CLUST32_MASK: 250193267Sjkim fat[cl].next = p[0] + (p[1] << 8) 251193267Sjkim + (p[2] << 16) + (p[3] << 24); 252193267Sjkim fat[cl].next &= boot->ClustMask; 253193267Sjkim ret |= checkclnum(boot, no, cl, &fat[cl].next); 254193267Sjkim cl++; 255193267Sjkim p += 4; 256193267Sjkim break; 257193267Sjkim case CLUST16_MASK: 258228110Sjkim fat[cl].next = p[0] + (p[1] << 8); 259193267Sjkim ret |= checkclnum(boot, no, cl, &fat[cl].next); 260193267Sjkim cl++; 261193267Sjkim p += 2; 262193267Sjkim break; 263228110Sjkim default: 264228110Sjkim fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 265228110Sjkim ret |= checkclnum(boot, no, cl, &fat[cl].next); 266193267Sjkim cl++; 267193267Sjkim if (cl >= boot->NumClusters) 268228110Sjkim break; 269228110Sjkim fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 270228110Sjkim ret |= checkclnum(boot, no, cl, &fat[cl].next); 271228110Sjkim cl++; 272193267Sjkim p += 3; 273193267Sjkim break; 274193267Sjkim } 275193267Sjkim } 276193267Sjkim 277193267Sjkim free(buffer); 278193267Sjkim *fp = fat; 279193267Sjkim return ret; 280197104Sjkim} 281193267Sjkim 282193267Sjkim/* 283193267Sjkim * Get type of reserved cluster 284193267Sjkim */ 285193267Sjkimchar * 286193267Sjkimrsrvdcltype(cl_t cl) 287193267Sjkim{ 288193267Sjkim if (cl == CLUST_FREE) 289193267Sjkim return "free"; 290193267Sjkim if (cl < CLUST_BAD) 291228110Sjkim return "reserved"; 292228110Sjkim if (cl > CLUST_BAD) 293193267Sjkim return "as EOF"; 294193267Sjkim return "bad"; 295193267Sjkim} 296193267Sjkim 297193267Sjkimstatic int 298197104Sjkimclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, int fatnum) 299197104Sjkim{ 300197104Sjkim if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 301193267Sjkim if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 302193267Sjkim if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 303193267Sjkim && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 304197104Sjkim || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 305197104Sjkim pwarn("Cluster %u is marked %s with different indicators, ", 306197104Sjkim cl, rsrvdcltype(*cp1)); 307197104Sjkim if (ask(1, "fix")) { 308197104Sjkim *cp2 = *cp1; 309197104Sjkim return FSFATMOD; 310197104Sjkim } 311197104Sjkim return FSFATAL; 312197104Sjkim } 313228110Sjkim pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n", 314197104Sjkim cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 315193267Sjkim if (ask(0, "use FAT 0's entry")) { 316193267Sjkim *cp2 = *cp1; 317193267Sjkim return FSFATMOD; 318228110Sjkim } 319193267Sjkim if (ask(0, "use FAT %d's entry", fatnum)) { 320193267Sjkim *cp1 = *cp2; 321193267Sjkim return FSFATMOD; 322193267Sjkim } 323228110Sjkim return FSFATAL; 324193267Sjkim } 325193267Sjkim pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 326193267Sjkim cl, rsrvdcltype(*cp1), *cp2, fatnum); 327193267Sjkim if (ask(0, "Use continuation from FAT %d", fatnum)) { 328193267Sjkim *cp1 = *cp2; 329193267Sjkim return FSFATMOD; 330193267Sjkim } 331193267Sjkim if (ask(0, "Use mark from FAT 0")) { 332193267Sjkim *cp2 = *cp1; 333193267Sjkim return FSFATMOD; 334193267Sjkim } 335193267Sjkim return FSFATAL; 336193267Sjkim } 337193267Sjkim if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 338228110Sjkim pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n", 339193267Sjkim cl, *cp1, rsrvdcltype(*cp2), fatnum); 340193267Sjkim if (ask(0, "Use continuation from FAT 0")) { 341193267Sjkim *cp2 = *cp1; 342193267Sjkim return FSFATMOD; 343193267Sjkim } 344193267Sjkim if (ask(0, "Use mark from FAT %d", fatnum)) { 345197104Sjkim *cp1 = *cp2; 346197104Sjkim return FSFATMOD; 347197104Sjkim } 348193267Sjkim return FSERROR; 349193267Sjkim } 350193267Sjkim pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n", 351193267Sjkim cl, *cp1, *cp2, fatnum); 352197104Sjkim if (ask(0, "Use continuation from FAT 0")) { 353197104Sjkim *cp2 = *cp1; 354193267Sjkim return FSFATMOD; 355193267Sjkim } 356193267Sjkim if (ask(0, "Use continuation from FAT %d", fatnum)) { 357193267Sjkim *cp1 = *cp2; 358193267Sjkim return FSFATMOD; 359197104Sjkim } 360193267Sjkim return FSERROR; 361193267Sjkim} 362193267Sjkim 363193267Sjkim/* 364193267Sjkim * Compare two FAT copies in memory. Resolve any conflicts and merge them 365193267Sjkim * into the first one. 366193267Sjkim */ 367197104Sjkimint 368193267Sjkimcomparefat(struct bootblock *boot, struct fatEntry *first, 369197104Sjkim struct fatEntry *second, int fatnum) 370197104Sjkim{ 371197104Sjkim cl_t cl; 372193267Sjkim int ret = FSOK; 373193267Sjkim 374193267Sjkim for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 375197104Sjkim if (first[cl].next != second[cl].next) 376197104Sjkim ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 377197104Sjkim return ret; 378197104Sjkim} 379197104Sjkim 380197104Sjkimvoid 381197104Sjkimclearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 382193267Sjkim{ 383193267Sjkim cl_t p, q; 384193267Sjkim 385193267Sjkim for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 386193267Sjkim if (fat[p].head != head) 387193267Sjkim break; 388193267Sjkim q = fat[p].next; 389193267Sjkim fat[p].next = fat[p].head = CLUST_FREE; 390193267Sjkim fat[p].length = 0; 391193267Sjkim } 392193267Sjkim} 393197104Sjkim 394197104Sjkimint 395197104Sjkimtryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc) 396228110Sjkim{ 397228110Sjkim if (ask(0, "Clear chain starting at %u", head)) { 398228110Sjkim clearchain(boot, fat, head); 399197104Sjkim return FSFATMOD; 400197104Sjkim } else if (ask(0, "Truncate")) { 401197104Sjkim *trunc = CLUST_EOF; 402193267Sjkim return FSFATMOD; 403193267Sjkim } else 404193267Sjkim return FSERROR; 405197104Sjkim} 406197104Sjkim 407197104Sjkim/* 408197104Sjkim * Check a complete FAT in-memory for crosslinks 409197104Sjkim */ 410197104Sjkimint 411193267Sjkimcheckfat(struct bootblock *boot, struct fatEntry *fat) 412193267Sjkim{ 413193267Sjkim cl_t head, p, h, n; 414197104Sjkim u_int len; 415197104Sjkim int ret = 0; 416193267Sjkim int conf; 417193267Sjkim 418193267Sjkim /* 419193267Sjkim * pass 1: figure out the cluster chains. 420193267Sjkim */ 421193267Sjkim for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 422193267Sjkim /* find next untravelled chain */ 423193267Sjkim if (fat[head].head != 0 /* cluster already belongs to some chain */ 424193267Sjkim || fat[head].next == CLUST_FREE 425193267Sjkim || fat[head].next == CLUST_BAD) 426193267Sjkim continue; /* skip it. */ 427193267Sjkim 428193267Sjkim /* follow the chain and mark all clusters on the way */ 429228110Sjkim for (len = 0, p = head; 430193267Sjkim p >= CLUST_FIRST && p < boot->NumClusters; 431193267Sjkim p = fat[p].next) { 432193267Sjkim fat[p].head = head; 433193267Sjkim len++; 434193267Sjkim } 435193267Sjkim 436193267Sjkim /* the head record gets the length */ 437193267Sjkim fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 438193267Sjkim } 439193267Sjkim 440193267Sjkim /* 441193267Sjkim * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 442197104Sjkim * we didn't know the real start of the chain then - would have treated partial 443193267Sjkim * chains as interlinked with their main chain) 444197104Sjkim */ 445197104Sjkim for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 446197104Sjkim /* find next untravelled chain */ 447193267Sjkim if (fat[head].head != head) 448193267Sjkim continue; 449193267Sjkim 450193267Sjkim /* follow the chain to its end (hopefully) */ 451193267Sjkim for (p = head; 452193267Sjkim (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 453193267Sjkim p = n) 454193267Sjkim if (fat[n].head != head) 455193267Sjkim break; 456193267Sjkim if (n >= CLUST_EOFS) 457193267Sjkim continue; 458193267Sjkim 459193267Sjkim if (n == CLUST_FREE || n >= CLUST_RSRVD) { 460193267Sjkim pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 461193267Sjkim head, rsrvdcltype(n)); 462193267Sjkim ret |= tryclear(boot, fat, head, &fat[p].next); 463193267Sjkim continue; 464193267Sjkim } 465193267Sjkim if (n < CLUST_FIRST || n >= boot->NumClusters) { 466193267Sjkim pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 467193267Sjkim head, n); 468193267Sjkim ret |= tryclear(boot, fat, head, &fat[p].next); 469193267Sjkim continue; 470193267Sjkim } 471193267Sjkim pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 472193267Sjkim head, fat[n].head, n); 473193267Sjkim conf = tryclear(boot, fat, head, &fat[p].next); 474193267Sjkim if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 475193267Sjkim if (conf == FSERROR) { 476193267Sjkim /* 477193267Sjkim * Transfer the common chain to the one not cleared above. 478193267Sjkim */ 479193267Sjkim for (p = n; 480193267Sjkim p >= CLUST_FIRST && p < boot->NumClusters; 481193267Sjkim p = fat[p].next) { 482193267Sjkim if (h != fat[p].head) { 483193267Sjkim /* 484193267Sjkim * Have to reexamine this chain. 485193267Sjkim */ 486193267Sjkim head--; 487193267Sjkim break; 488193267Sjkim } 489193267Sjkim fat[p].head = head; 490193267Sjkim } 491193267Sjkim } 492197104Sjkim clearchain(boot, fat, h); 493193267Sjkim conf |= FSFATMOD; 494193267Sjkim } 495193267Sjkim ret |= conf; 496228110Sjkim } 497193267Sjkim 498193267Sjkim return ret; 499193267Sjkim} 500193267Sjkim 501197104Sjkim/* 502193267Sjkim * Write out FATs encoding them from the internal format 503197104Sjkim */ 504228110Sjkimint 505193267Sjkimwritefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 506193267Sjkim{ 507193267Sjkim u_char *buffer, *p; 508193267Sjkim cl_t cl; 509222544Sjkim int i; 510197104Sjkim u_int32_t fatsz; 511197104Sjkim off_t off; 512193267Sjkim int ret = FSOK; 513193267Sjkim 514193267Sjkim buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); 515193267Sjkim if (buffer == NULL) { 516193267Sjkim perror("No space for FAT"); 517193267Sjkim return FSFATAL; 518193267Sjkim } 519193267Sjkim memset(buffer, 0, fatsz); 520193267Sjkim boot->NumFree = 0; 521193267Sjkim p = buffer; 522193267Sjkim if (correct_fat) { 523193267Sjkim *p++ = (u_char)boot->Media; 524193267Sjkim *p++ = 0xff; 525193267Sjkim *p++ = 0xff; 526193267Sjkim switch (boot->ClustMask) { 527193267Sjkim case CLUST16_MASK: 528193267Sjkim *p++ = 0xff; 529193267Sjkim break; 530193267Sjkim case CLUST32_MASK: 531193267Sjkim *p++ = 0x0f; 532193267Sjkim *p++ = 0xff; 533193267Sjkim *p++ = 0xff; 534193267Sjkim *p++ = 0xff; 535193267Sjkim *p++ = 0x0f; 536193267Sjkim break; 537193267Sjkim } 538193267Sjkim } else { 539193267Sjkim /* use same FAT signature as the old FAT has */ 540193267Sjkim int count; 541193267Sjkim u_char *old_fat; 542193267Sjkim 543193267Sjkim switch (boot->ClustMask) { 544193267Sjkim case CLUST32_MASK: 545209746Sjkim count = 8; 546209746Sjkim break; 547209746Sjkim case CLUST16_MASK: 548209746Sjkim count = 4; 549209746Sjkim break; 550193267Sjkim default: 551193267Sjkim count = 3; 552193267Sjkim break; 553193267Sjkim } 554193267Sjkim 555193267Sjkim if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 556193267Sjkim &old_fat)) { 557193267Sjkim free(buffer); 558193267Sjkim return FSFATAL; 559193267Sjkim } 560193267Sjkim 561193267Sjkim memcpy(p, old_fat, count); 562193267Sjkim free(old_fat); 563193267Sjkim p += count; 564197104Sjkim } 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