fat.c revision 268631
1130812Smarcel/* 2130812Smarcel * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 3130812Smarcel * Copyright (c) 1995 Martin Husemann 4130812Smarcel * 5130812Smarcel * Redistribution and use in source and binary forms, with or without 6130812Smarcel * modification, are permitted provided that the following conditions 7130812Smarcel * are met: 8130812Smarcel * 1. Redistributions of source code must retain the above copyright 9130812Smarcel * notice, this list of conditions and the following disclaimer. 10130812Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11130812Smarcel * notice, this list of conditions and the following disclaimer in the 12130812Smarcel * documentation and/or other materials provided with the distribution. 13130812Smarcel * 14130812Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 15130812Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16130812Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17130812Smarcel * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18130812Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19130812Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20130812Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21130812Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22130812Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23130812Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24130812Smarcel */ 25130812Smarcel 26130812Smarcel 27130812Smarcel#include <sys/cdefs.h> 28130812Smarcel#ifndef lint 29130812Smarcel__RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $"); 30130812Smarcelstatic const char rcsid[] = 31130812Smarcel "$FreeBSD: head/sbin/fsck_msdosfs/fat.c 268631 2014-07-14 20:17:09Z pfg $"; 32130812Smarcel#endif /* not lint */ 33130812Smarcel 34130812Smarcel#include <stdlib.h> 35130812Smarcel#include <string.h> 36130812Smarcel#include <ctype.h> 37130812Smarcel#include <stdio.h> 38130812Smarcel#include <unistd.h> 39130812Smarcel 40130812Smarcel#include "ext.h" 41130812Smarcel#include "fsutil.h" 42130812Smarcel 43130812Smarcelstatic int checkclnum(struct bootblock *, u_int, cl_t, cl_t *); 44130812Smarcelstatic int clustdiffer(cl_t, cl_t *, cl_t *, u_int); 45130812Smarcelstatic int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 46130812Smarcelstatic int _readfat(int, struct bootblock *, u_int, u_char **); 47130812Smarcel 48130812Smarcel/*- 49130812Smarcel * The first 2 FAT entries contain pseudo-cluster numbers with the following 50130812Smarcel * layout: 51130812Smarcel * 52130812Smarcel * 31...... ........ ........ .......0 53130812Smarcel * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0 54130812Smarcel * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1 55130812Smarcel * 56130812Smarcel * 11111111 mmmmmmmm FAT16 entry 0 57130812Smarcel * sh111111 11111xxx FAT16 entry 1 58130812Smarcel * 59130812Smarcel * r = reserved 60130812Smarcel * m = BPB media ID byte 61130812Smarcel * s = clean flag (1 = dismounted; 0 = still mounted) 62130812Smarcel * h = hard error flag (1 = ok; 0 = I/O error) 63130812Smarcel * x = any value ok 64130812Smarcel */ 65130812Smarcel 66130812Smarcelint 67130812Smarcelcheckdirty(int fs, struct bootblock *boot) 68130812Smarcel{ 69130812Smarcel off_t off; 70130812Smarcel u_char *buffer; 71130812Smarcel int ret = 0; 72130812Smarcel size_t len; 73130812Smarcel 74130812Smarcel if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK) 75130812Smarcel return 0; 76130812Smarcel 77130812Smarcel off = boot->bpbResSectors; 78130812Smarcel off *= boot->bpbBytesPerSec; 79130812Smarcel 80130812Smarcel buffer = malloc(len = boot->bpbBytesPerSec); 81130812Smarcel if (buffer == NULL) { 82130812Smarcel perr("No space for FAT sectors (%zu)", len); 83130812Smarcel return 1; 84130812Smarcel } 85130812Smarcel 86130812Smarcel if (lseek(fs, off, SEEK_SET) != off) { 87130812Smarcel perr("Unable to read FAT"); 88130812Smarcel goto err; 89130812Smarcel } 90130812Smarcel 91130812Smarcel if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) != 92130812Smarcel boot->bpbBytesPerSec) { 93130812Smarcel perr("Unable to read FAT"); 94130812Smarcel goto err; 95130812Smarcel } 96130812Smarcel 97130812Smarcel /* 98130812Smarcel * If we don't understand the FAT, then the file system must be 99130812Smarcel * assumed to be unclean. 100130812Smarcel */ 101130812Smarcel if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff) 102130812Smarcel goto err; 103130812Smarcel if (boot->ClustMask == CLUST16_MASK) { 104130812Smarcel if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f) 105130812Smarcel goto err; 106130812Smarcel } else { 107130812Smarcel if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f 108130812Smarcel || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff 109130812Smarcel || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03) 110130812Smarcel goto err; 111130812Smarcel } 112130812Smarcel 113130812Smarcel /* 114130812Smarcel * Now check the actual clean flag (and the no-error flag). 115130812Smarcel */ 116130812Smarcel if (boot->ClustMask == CLUST16_MASK) { 117130812Smarcel if ((buffer[3] & 0xc0) == 0xc0) 118130812Smarcel ret = 1; 119130812Smarcel } else { 120130812Smarcel if ((buffer[7] & 0x0c) == 0x0c) 121130812Smarcel ret = 1; 122130812Smarcel } 123130812Smarcel 124130812Smarcelerr: 125130812Smarcel free(buffer); 126130812Smarcel return ret; 127130812Smarcel} 128130812Smarcel 129130812Smarcel/* 130130812Smarcel * Check a cluster number for valid value 131130812Smarcel */ 132130812Smarcelstatic int 133130812Smarcelcheckclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next) 134130812Smarcel{ 135130812Smarcel if (*next >= (CLUST_RSRVD&boot->ClustMask)) 136130812Smarcel *next |= ~boot->ClustMask; 137130812Smarcel if (*next == CLUST_FREE) { 138130812Smarcel boot->NumFree++; 139130812Smarcel return FSOK; 140130812Smarcel } 141130812Smarcel if (*next == CLUST_BAD) { 142130812Smarcel boot->NumBad++; 143130812Smarcel return FSOK; 144130812Smarcel } 145130812Smarcel if (*next < CLUST_FIRST 146130812Smarcel || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 147130812Smarcel pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 148130812Smarcel cl, fat, 149130812Smarcel *next < CLUST_RSRVD ? "out of range" : "reserved", 150130812Smarcel *next&boot->ClustMask); 151130812Smarcel if (ask(0, "Truncate")) { 152130812Smarcel *next = CLUST_EOF; 153130812Smarcel return FSFATMOD; 154130812Smarcel } 155130812Smarcel return FSERROR; 156130812Smarcel } 157130812Smarcel return FSOK; 158130812Smarcel} 159130812Smarcel 160130812Smarcel/* 161130812Smarcel * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 162130812Smarcel */ 163130812Smarcelstatic int 164130812Smarcel_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer) 165130812Smarcel{ 166130812Smarcel off_t off; 167130812Smarcel size_t len; 168130812Smarcel 169130812Smarcel *buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec); 170130812Smarcel if (*buffer == NULL) { 171130812Smarcel perr("No space for FAT sectors (%zu)", len); 172130812Smarcel return 0; 173130812Smarcel } 174130812Smarcel 175130812Smarcel off = boot->bpbResSectors + no * boot->FATsecs; 176130812Smarcel off *= boot->bpbBytesPerSec; 177130812Smarcel 178130812Smarcel if (lseek(fs, off, SEEK_SET) != off) { 179130812Smarcel perr("Unable to read FAT"); 180130812Smarcel goto err; 181130812Smarcel } 182130812Smarcel 183130812Smarcel if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec) 184130812Smarcel != boot->FATsecs * boot->bpbBytesPerSec) { 185130812Smarcel perr("Unable to read FAT"); 186130812Smarcel goto err; 187130812Smarcel } 188130812Smarcel 189130812Smarcel return 1; 190130812Smarcel 191130812Smarcel err: 192130812Smarcel free(*buffer); 193130812Smarcel return 0; 194130812Smarcel} 195130812Smarcel 196130812Smarcel/* 197130812Smarcel * Read a FAT and decode it into internal format 198130812Smarcel */ 199130812Smarcelint 200130812Smarcelreadfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp) 201130812Smarcel{ 202130812Smarcel struct fatEntry *fat; 203130812Smarcel u_char *buffer, *p; 204130812Smarcel cl_t cl; 205130812Smarcel int ret = FSOK; 206130812Smarcel size_t len; 207130812Smarcel 208130812Smarcel boot->NumFree = boot->NumBad = 0; 209130812Smarcel 210130812Smarcel if (!_readfat(fs, boot, no, &buffer)) 211130812Smarcel return FSFATAL; 212130812Smarcel 213130812Smarcel fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry)); 214130812Smarcel if (fat == NULL) { 215130812Smarcel perr("No space for FAT clusters (%zu)", len); 216130812Smarcel free(buffer); 217130812Smarcel return FSFATAL; 218130812Smarcel } 219130812Smarcel (void)memset(fat, 0, len); 220130812Smarcel 221130812Smarcel if (buffer[0] != boot->bpbMedia 222130812Smarcel || buffer[1] != 0xff || buffer[2] != 0xff 223130812Smarcel || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 224130812Smarcel || (boot->ClustMask == CLUST32_MASK 225130812Smarcel && ((buffer[3]&0x0f) != 0x0f 226130812Smarcel || buffer[4] != 0xff || buffer[5] != 0xff 227130812Smarcel || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 228130812Smarcel 229130812Smarcel /* Windows 95 OSR2 (and possibly any later) changes 230130812Smarcel * the FAT signature to 0xXXffff7f for FAT16 and to 231130812Smarcel * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 232130812Smarcel * file system is dirty if it doesn't reboot cleanly. 233130812Smarcel * Check this special condition before errorring out. 234130812Smarcel */ 235130812Smarcel if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff 236130812Smarcel && buffer[2] == 0xff 237130812Smarcel && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 238130812Smarcel || (boot->ClustMask == CLUST32_MASK 239130812Smarcel && buffer[3] == 0x0f && buffer[4] == 0xff 240130812Smarcel && buffer[5] == 0xff && buffer[6] == 0xff 241130812Smarcel && buffer[7] == 0x07))) 242130812Smarcel ret |= FSDIRTY; 243130812Smarcel else { 244130812Smarcel /* just some odd byte sequence in FAT */ 245130812Smarcel 246130812Smarcel switch (boot->ClustMask) { 247130812Smarcel case CLUST32_MASK: 248130812Smarcel pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 249130812Smarcel "FAT starts with odd byte sequence", 250130812Smarcel buffer[0], buffer[1], buffer[2], buffer[3], 251130812Smarcel buffer[4], buffer[5], buffer[6], buffer[7]); 252130812Smarcel break; 253130812Smarcel case CLUST16_MASK: 254130812Smarcel pwarn("%s (%02x%02x%02x%02x)\n", 255130812Smarcel "FAT starts with odd byte sequence", 256130812Smarcel buffer[0], buffer[1], buffer[2], buffer[3]); 257130812Smarcel break; 258130812Smarcel default: 259130812Smarcel pwarn("%s (%02x%02x%02x)\n", 260130812Smarcel "FAT starts with odd byte sequence", 261130812Smarcel buffer[0], buffer[1], buffer[2]); 262130812Smarcel break; 263130812Smarcel } 264130812Smarcel 265130812Smarcel 266130812Smarcel if (ask(1, "Correct")) 267130812Smarcel ret |= FSFIXFAT; 268130812Smarcel } 269130812Smarcel } 270130812Smarcel switch (boot->ClustMask) { 271130812Smarcel case CLUST32_MASK: 272130812Smarcel p = buffer + 8; 273130812Smarcel break; 274130812Smarcel case CLUST16_MASK: 275130812Smarcel p = buffer + 4; 276130812Smarcel break; 277130812Smarcel default: 278130812Smarcel p = buffer + 3; 279130812Smarcel break; 280130812Smarcel } 281130812Smarcel for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 282130812Smarcel switch (boot->ClustMask) { 283130812Smarcel case CLUST32_MASK: 284130812Smarcel fat[cl].next = p[0] + (p[1] << 8) 285130812Smarcel + (p[2] << 16) + (p[3] << 24); 286130812Smarcel fat[cl].next &= boot->ClustMask; 287130812Smarcel ret |= checkclnum(boot, no, cl, &fat[cl].next); 288130812Smarcel cl++; 289130812Smarcel p += 4; 290130812Smarcel break; 291130812Smarcel case CLUST16_MASK: 292130812Smarcel fat[cl].next = p[0] + (p[1] << 8); 293130812Smarcel ret |= checkclnum(boot, no, cl, &fat[cl].next); 294130812Smarcel cl++; 295130812Smarcel p += 2; 296130812Smarcel break; 297130812Smarcel default: 298130812Smarcel fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 299130812Smarcel ret |= checkclnum(boot, no, cl, &fat[cl].next); 300130812Smarcel cl++; 301130812Smarcel if (cl >= boot->NumClusters) 302130812Smarcel break; 303130812Smarcel fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 304130812Smarcel ret |= checkclnum(boot, no, cl, &fat[cl].next); 305130812Smarcel cl++; 306130812Smarcel p += 3; 307130812Smarcel break; 308130812Smarcel } 309130812Smarcel } 310130812Smarcel 311130812Smarcel free(buffer); 312130812Smarcel if (ret & FSFATAL) { 313130812Smarcel free(fat); 314130812Smarcel *fp = NULL; 315130812Smarcel } else 316130812Smarcel *fp = fat; 317130812Smarcel return ret; 318130812Smarcel} 319130812Smarcel 320130812Smarcel/* 321130812Smarcel * Get type of reserved cluster 322130812Smarcel */ 323130812Smarcelconst char * 324130812Smarcelrsrvdcltype(cl_t cl) 325130812Smarcel{ 326130812Smarcel if (cl == CLUST_FREE) 327130812Smarcel return "free"; 328130812Smarcel if (cl < CLUST_BAD) 329130812Smarcel return "reserved"; 330130812Smarcel if (cl > CLUST_BAD) 331130812Smarcel return "as EOF"; 332130812Smarcel return "bad"; 333130812Smarcel} 334130812Smarcel 335130812Smarcelstatic int 336130812Smarcelclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum) 337130812Smarcel{ 338130812Smarcel if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 339130812Smarcel if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 340130812Smarcel if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 341130812Smarcel && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 342130812Smarcel || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 343130812Smarcel pwarn("Cluster %u is marked %s with different indicators\n", 344130812Smarcel cl, rsrvdcltype(*cp1)); 345130812Smarcel if (ask(1, "Fix")) { 346130812Smarcel *cp2 = *cp1; 347130812Smarcel return FSFATMOD; 348130812Smarcel } 349130812Smarcel return FSFATAL; 350130812Smarcel } 351130812Smarcel pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n", 352130812Smarcel cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 353130812Smarcel if (ask(0, "Use FAT 0's entry")) { 354130812Smarcel *cp2 = *cp1; 355130812Smarcel return FSFATMOD; 356130812Smarcel } 357130812Smarcel if (ask(0, "Use FAT %u's entry", fatnum)) { 358130812Smarcel *cp1 = *cp2; 359130812Smarcel return FSFATMOD; 360130812Smarcel } 361130812Smarcel return FSFATAL; 362130812Smarcel } 363130812Smarcel pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 364130812Smarcel cl, rsrvdcltype(*cp1), *cp2, fatnum); 365130812Smarcel if (ask(0, "Use continuation from FAT %u", fatnum)) { 366130812Smarcel *cp1 = *cp2; 367130812Smarcel return FSFATMOD; 368130812Smarcel } 369130812Smarcel if (ask(0, "Use mark from FAT 0")) { 370130812Smarcel *cp2 = *cp1; 371130812Smarcel return FSFATMOD; 372130812Smarcel } 373130812Smarcel return FSFATAL; 374130812Smarcel } 375130812Smarcel if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 376130812Smarcel pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n", 377130812Smarcel cl, *cp1, rsrvdcltype(*cp2), fatnum); 378130812Smarcel if (ask(0, "Use continuation from FAT 0")) { 379130812Smarcel *cp2 = *cp1; 380130812Smarcel return FSFATMOD; 381130812Smarcel } 382130812Smarcel if (ask(0, "Use mark from FAT %d", fatnum)) { 383130812Smarcel *cp1 = *cp2; 384130812Smarcel return FSFATMOD; 385130812Smarcel } 386130812Smarcel return FSERROR; 387130812Smarcel } 388130812Smarcel pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n", 389130812Smarcel cl, *cp1, *cp2, fatnum); 390130812Smarcel if (ask(0, "Use continuation from FAT 0")) { 391130812Smarcel *cp2 = *cp1; 392130812Smarcel return FSFATMOD; 393130812Smarcel } 394130812Smarcel if (ask(0, "Use continuation from FAT %u", fatnum)) { 395130812Smarcel *cp1 = *cp2; 396130812Smarcel return FSFATMOD; 397130812Smarcel } 398130812Smarcel return FSERROR; 399130812Smarcel} 400130812Smarcel 401130812Smarcel/* 402130812Smarcel * Compare two FAT copies in memory. Resolve any conflicts and merge them 403130812Smarcel * into the first one. 404130812Smarcel */ 405130812Smarcelint 406130812Smarcelcomparefat(struct bootblock *boot, struct fatEntry *first, 407130812Smarcel struct fatEntry *second, u_int fatnum) 408130812Smarcel{ 409130812Smarcel cl_t cl; 410130812Smarcel int ret = FSOK; 411130812Smarcel 412130812Smarcel for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 413130812Smarcel if (first[cl].next != second[cl].next) 414130812Smarcel ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 415130812Smarcel return ret; 416130812Smarcel} 417130812Smarcel 418130812Smarcelvoid 419130812Smarcelclearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 420130812Smarcel{ 421130812Smarcel cl_t p, q; 422130812Smarcel 423130812Smarcel for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 424130812Smarcel if (fat[p].head != head) 425130812Smarcel break; 426130812Smarcel q = fat[p].next; 427130812Smarcel fat[p].next = fat[p].head = CLUST_FREE; 428130812Smarcel fat[p].length = 0; 429130812Smarcel } 430130812Smarcel} 431130812Smarcel 432130812Smarcelint 433130812Smarceltryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp) 434130812Smarcel{ 435130812Smarcel if (ask(0, "Clear chain starting at %u", head)) { 436130812Smarcel clearchain(boot, fat, head); 437130812Smarcel return FSFATMOD; 438130812Smarcel } else if (ask(0, "Truncate")) { 439130812Smarcel *truncp = CLUST_EOF; 440130812Smarcel return FSFATMOD; 441130812Smarcel } else 442130812Smarcel return FSERROR; 443130812Smarcel} 444130812Smarcel 445130812Smarcel/* 446130812Smarcel * Check a complete FAT in-memory for crosslinks 447130812Smarcel */ 448130812Smarcelint 449130812Smarcelcheckfat(struct bootblock *boot, struct fatEntry *fat) 450130812Smarcel{ 451130812Smarcel cl_t head, p, h, n; 452130812Smarcel u_int len; 453130812Smarcel int ret = 0; 454130812Smarcel int conf; 455130812Smarcel 456130812Smarcel /* 457130812Smarcel * pass 1: figure out the cluster chains. 458130812Smarcel */ 459130812Smarcel for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 460130812Smarcel /* find next untravelled chain */ 461130812Smarcel if (fat[head].head != 0 /* cluster already belongs to some chain */ 462130812Smarcel || fat[head].next == CLUST_FREE 463130812Smarcel || fat[head].next == CLUST_BAD) 464130812Smarcel continue; /* skip it. */ 465130812Smarcel 466130812Smarcel /* follow the chain and mark all clusters on the way */ 467130812Smarcel for (len = 0, p = head; 468130812Smarcel p >= CLUST_FIRST && p < boot->NumClusters; 469130812Smarcel p = fat[p].next) { 470130812Smarcel fat[p].head = head; 471130812Smarcel len++; 472130812Smarcel } 473130812Smarcel 474130812Smarcel /* the head record gets the length */ 475130812Smarcel fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 476130812Smarcel } 477130812Smarcel 478130812Smarcel /* 479130812Smarcel * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 480130812Smarcel * we didn't know the real start of the chain then - would have treated partial 481130812Smarcel * chains as interlinked with their main chain) 482130812Smarcel */ 483130812Smarcel for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 484130812Smarcel /* find next untravelled chain */ 485130812Smarcel if (fat[head].head != head) 486130812Smarcel continue; 487130812Smarcel 488130812Smarcel /* follow the chain to its end (hopefully) */ 489130812Smarcel for (p = head; 490130812Smarcel (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 491130812Smarcel p = n) 492130812Smarcel if (fat[n].head != head) 493130812Smarcel break; 494130812Smarcel if (n >= CLUST_EOFS) 495130812Smarcel continue; 496130812Smarcel 497130812Smarcel if (n == CLUST_FREE || n >= CLUST_RSRVD) { 498130812Smarcel pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 499130812Smarcel head, rsrvdcltype(n)); 500130812Smarcel ret |= tryclear(boot, fat, head, &fat[p].next); 501130812Smarcel continue; 502130812Smarcel } 503130812Smarcel if (n < CLUST_FIRST || n >= boot->NumClusters) { 504130812Smarcel pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 505130812Smarcel head, n); 506130812Smarcel ret |= tryclear(boot, fat, head, &fat[p].next); 507130812Smarcel continue; 508130812Smarcel } 509130812Smarcel pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 510130812Smarcel head, fat[n].head, n); 511130812Smarcel conf = tryclear(boot, fat, head, &fat[p].next); 512130812Smarcel if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 513130812Smarcel if (conf == FSERROR) { 514130812Smarcel /* 515130812Smarcel * Transfer the common chain to the one not cleared above. 516130812Smarcel */ 517130812Smarcel for (p = n; 518130812Smarcel p >= CLUST_FIRST && p < boot->NumClusters; 519130812Smarcel p = fat[p].next) { 520130812Smarcel if (h != fat[p].head) { 521130812Smarcel /* 522130812Smarcel * Have to reexamine this chain. 523130812Smarcel */ 524130812Smarcel head--; 525130812Smarcel break; 526130812Smarcel } 527130812Smarcel fat[p].head = head; 528130812Smarcel } 529130812Smarcel } 530130812Smarcel clearchain(boot, fat, h); 531130812Smarcel conf |= FSFATMOD; 532130812Smarcel } 533130812Smarcel ret |= conf; 534130812Smarcel } 535130812Smarcel 536130812Smarcel return ret; 537130812Smarcel} 538130812Smarcel 539130812Smarcel/* 540130812Smarcel * Write out FATs encoding them from the internal format 541130812Smarcel */ 542130812Smarcelint 543130812Smarcelwritefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 544130812Smarcel{ 545130812Smarcel u_char *buffer, *p; 546130812Smarcel cl_t cl; 547130812Smarcel u_int i; 548130812Smarcel size_t fatsz; 549130812Smarcel off_t off; 550130812Smarcel int ret = FSOK; 551130812Smarcel 552130812Smarcel buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec); 553130812Smarcel if (buffer == NULL) { 554130812Smarcel perr("No space for FAT sectors (%zu)", fatsz); 555130812Smarcel return FSFATAL; 556130812Smarcel } 557130812Smarcel memset(buffer, 0, fatsz); 558130812Smarcel boot->NumFree = 0; 559130812Smarcel p = buffer; 560130812Smarcel if (correct_fat) { 561130812Smarcel *p++ = (u_char)boot->bpbMedia; 562130812Smarcel *p++ = 0xff; 563130812Smarcel *p++ = 0xff; 564130812Smarcel switch (boot->ClustMask) { 565130812Smarcel case CLUST16_MASK: 566130812Smarcel *p++ = 0xff; 567130812Smarcel break; 568130812Smarcel case CLUST32_MASK: 569130812Smarcel *p++ = 0x0f; 570130812Smarcel *p++ = 0xff; 571130812Smarcel *p++ = 0xff; 572130812Smarcel *p++ = 0xff; 573130812Smarcel *p++ = 0x0f; 574130812Smarcel break; 575130812Smarcel } 576130812Smarcel } else { 577130812Smarcel /* use same FAT signature as the old FAT has */ 578130812Smarcel int count; 579130812Smarcel u_char *old_fat; 580130812Smarcel 581130812Smarcel switch (boot->ClustMask) { 582130812Smarcel case CLUST32_MASK: 583130812Smarcel count = 8; 584130812Smarcel break; 585130812Smarcel case CLUST16_MASK: 586130812Smarcel count = 4; 587130812Smarcel break; 588130812Smarcel default: 589130812Smarcel count = 3; 590130812Smarcel break; 591130812Smarcel } 592130812Smarcel 593130812Smarcel if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 594130812Smarcel &old_fat)) { 595130812Smarcel free(buffer); 596130812Smarcel return FSFATAL; 597130812Smarcel } 598130812Smarcel 599130812Smarcel memcpy(p, old_fat, count); 600130812Smarcel free(old_fat); 601130812Smarcel p += count; 602130812Smarcel } 603130812Smarcel 604130812Smarcel for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 605130812Smarcel switch (boot->ClustMask) { 606130812Smarcel case CLUST32_MASK: 607130812Smarcel if (fat[cl].next == CLUST_FREE) 608130812Smarcel boot->NumFree++; 609130812Smarcel *p++ = (u_char)fat[cl].next; 610130812Smarcel *p++ = (u_char)(fat[cl].next >> 8); 611130812Smarcel *p++ = (u_char)(fat[cl].next >> 16); 612130812Smarcel *p &= 0xf0; 613130812Smarcel *p++ |= (fat[cl].next >> 24)&0x0f; 614130812Smarcel break; 615130812Smarcel case CLUST16_MASK: 616130812Smarcel if (fat[cl].next == CLUST_FREE) 617130812Smarcel boot->NumFree++; 618130812Smarcel *p++ = (u_char)fat[cl].next; 619130812Smarcel *p++ = (u_char)(fat[cl].next >> 8); 620130812Smarcel break; 621130812Smarcel default: 622130812Smarcel if (fat[cl].next == CLUST_FREE) 623130812Smarcel boot->NumFree++; 624130812Smarcel if (cl + 1 < boot->NumClusters 625130812Smarcel && fat[cl + 1].next == CLUST_FREE) 626130812Smarcel boot->NumFree++; 627130812Smarcel *p++ = (u_char)fat[cl].next; 628130812Smarcel *p++ = (u_char)((fat[cl].next >> 8) & 0xf) 629130812Smarcel |(u_char)(fat[cl+1].next << 4); 630130812Smarcel *p++ = (u_char)(fat[++cl].next >> 4); 631130812Smarcel break; 632130812Smarcel } 633130812Smarcel } 634130812Smarcel for (i = 0; i < boot->bpbFATs; i++) { 635130812Smarcel off = boot->bpbResSectors + i * boot->FATsecs; 636130812Smarcel off *= boot->bpbBytesPerSec; 637130812Smarcel if (lseek(fs, off, SEEK_SET) != off 638130812Smarcel || (size_t)write(fs, buffer, fatsz) != fatsz) { 639130812Smarcel perr("Unable to write FAT"); 640130812Smarcel ret = FSFATAL; /* Return immediately? XXX */ 641130812Smarcel } 642130812Smarcel } 643130812Smarcel free(buffer); 644130812Smarcel return ret; 645130812Smarcel} 646130812Smarcel 647130812Smarcel/* 648130812Smarcel * Check a complete in-memory FAT for lost cluster chains 649130812Smarcel */ 650130812Smarcelint 651130812Smarcelchecklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) 652130812Smarcel{ 653130812Smarcel cl_t head; 654130812Smarcel int mod = FSOK; 655130812Smarcel int ret; 656130812Smarcel 657130812Smarcel for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 658130812Smarcel /* find next untravelled chain */ 659130812Smarcel if (fat[head].head != head 660130812Smarcel || fat[head].next == CLUST_FREE 661130812Smarcel || (fat[head].next >= CLUST_RSRVD 662130812Smarcel && fat[head].next < CLUST_EOFS) 663130812Smarcel || (fat[head].flags & FAT_USED)) 664130812Smarcel continue; 665130812Smarcel 666130812Smarcel pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 667130812Smarcel head, fat[head].length); 668130812Smarcel mod |= ret = reconnect(dosfs, boot, fat, head); 669130812Smarcel if (mod & FSFATAL) 670130812Smarcel break; 671130812Smarcel if (ret == FSERROR && ask(0, "Clear")) { 672130812Smarcel clearchain(boot, fat, head); 673130812Smarcel mod |= FSFATMOD; 674130812Smarcel } 675130812Smarcel } 676130812Smarcel finishlf(); 677130812Smarcel 678130812Smarcel if (boot->bpbFSInfo) { 679130812Smarcel ret = 0; 680130812Smarcel if (boot->FSFree != 0xffffffffU && 681130812Smarcel boot->FSFree != boot->NumFree) { 682130812Smarcel pwarn("Free space in FSInfo block (%u) not correct (%u)\n", 683130812Smarcel boot->FSFree, boot->NumFree); 684130812Smarcel if (ask(1, "Fix")) { 685130812Smarcel boot->FSFree = boot->NumFree; 686130812Smarcel ret = 1; 687130812Smarcel } 688130812Smarcel } 689130812Smarcel if (ret) 690130812Smarcel mod |= writefsinfo(dosfs, boot); 691130812Smarcel } 692130812Smarcel 693130812Smarcel return mod; 694130812Smarcel} 695130812Smarcel