fat.c revision 125469
179455Sobrien/* 279455Sobrien * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 379455Sobrien * Copyright (c) 1995 Martin Husemann 479455Sobrien * 579455Sobrien * Redistribution and use in source and binary forms, with or without 679455Sobrien * modification, are permitted provided that the following conditions 779455Sobrien * are met: 879455Sobrien * 1. Redistributions of source code must retain the above copyright 979455Sobrien * notice, this list of conditions and the following disclaimer. 1079455Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1179455Sobrien * notice, this list of conditions and the following disclaimer in the 1279455Sobrien * documentation and/or other materials provided with the distribution. 1379455Sobrien * 3. All advertising materials mentioning features or use of this software 1479455Sobrien * must display the following acknowledgement: 1579455Sobrien * This product includes software developed by Martin Husemann 1679455Sobrien * and Wolfgang Solfrank. 1779455Sobrien * 4. Neither the name of the University nor the names of its contributors 1879455Sobrien * may be used to endorse or promote products derived from this software 1979455Sobrien * without specific prior written permission. 2079455Sobrien * 2179455Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 2279455Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2379455Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2479455Sobrien * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2579455Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2679455Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2779455Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2879455Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2979455Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3079455Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3179455Sobrien */ 3279455Sobrien 3379455Sobrien 3479455Sobrien#include <sys/cdefs.h> 3579455Sobrien#ifndef lint 3679455Sobrien__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $"); 3779455Sobrienstatic const char rcsid[] = 3879455Sobrien "$FreeBSD: head/sbin/fsck_msdosfs/fat.c 125469 2004-02-05 06:32:16Z bde $"; 3979455Sobrien#endif /* not lint */ 4079455Sobrien 4179455Sobrien#include <stdlib.h> 4279455Sobrien#include <string.h> 4379455Sobrien#include <ctype.h> 4479455Sobrien#include <stdio.h> 4579455Sobrien#include <unistd.h> 4679455Sobrien 4779455Sobrien#include "ext.h" 4879455Sobrien#include "fsutil.h" 4979455Sobrien 5092839Simpstatic int checkclnum(struct bootblock *, int, cl_t, cl_t *); 5192839Simpstatic int clustdiffer(cl_t, cl_t *, cl_t *, int); 5292839Simpstatic int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *); 5392839Simpstatic int _readfat(int, struct bootblock *, int, u_char **); 5479455Sobrien 55125469Sbdeint 56125469Sbdecheckdirty(int fs, struct bootblock *boot) 57125469Sbde{ 58125469Sbde off_t off; 59125469Sbde u_char *buffer; 60125469Sbde int ret = 0; 61125469Sbde 62125469Sbde if (boot->ClustMask == CLUST12_MASK) 63125469Sbde return 0; 64125469Sbde 65125469Sbde off = boot->ResSectors; 66125469Sbde off *= boot->BytesPerSec; 67125469Sbde 68125469Sbde buffer = malloc(boot->BytesPerSec); 69125469Sbde if (buffer == NULL) { 70125469Sbde perror("No space for FAT"); 71125469Sbde return 1; 72125469Sbde } 73125469Sbde 74125469Sbde if (lseek(fs, off, SEEK_SET) != off) { 75125469Sbde perror("Unable to read FAT"); 76125469Sbde goto err; 77125469Sbde } 78125469Sbde 79125469Sbde if (read(fs, buffer, boot->BytesPerSec) != boot->BytesPerSec) { 80125469Sbde perror("Unable to read FAT"); 81125469Sbde goto err; 82125469Sbde } 83125469Sbde 84125469Sbde if (buffer[0] == boot->Media && buffer[1] == 0xff && buffer[2] == 0xff 85125469Sbde && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 86125469Sbde || (boot->ClustMask == CLUST32_MASK && buffer[3] == 0x0f 87125469Sbde && buffer[4] == 0xff && buffer[5] == 0xff 88125469Sbde && buffer[6] == 0xff && buffer[7] == 0x07))) 89125469Sbde ret = 0; 90125469Sbde else 91125469Sbde ret = 1; 92125469Sbde 93125469Sbdeerr: 94125469Sbde free(buffer); 95125469Sbde return ret; 96125469Sbde} 97125469Sbde 9879455Sobrien/* 9979455Sobrien * Check a cluster number for valid value 10079455Sobrien */ 10179455Sobrienstatic int 10292839Simpcheckclnum(struct bootblock *boot, int fat, cl_t cl, cl_t *next) 10379455Sobrien{ 10479455Sobrien if (*next >= (CLUST_RSRVD&boot->ClustMask)) 10579455Sobrien *next |= ~boot->ClustMask; 10679455Sobrien if (*next == CLUST_FREE) { 10779455Sobrien boot->NumFree++; 10879455Sobrien return FSOK; 10979455Sobrien } 11079455Sobrien if (*next == CLUST_BAD) { 11179455Sobrien boot->NumBad++; 11279455Sobrien return FSOK; 11379455Sobrien } 11479455Sobrien if (*next < CLUST_FIRST 11579455Sobrien || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 11679455Sobrien pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 11779455Sobrien cl, fat, 11879455Sobrien *next < CLUST_RSRVD ? "out of range" : "reserved", 11979455Sobrien *next&boot->ClustMask); 12079455Sobrien if (ask(0, "Truncate")) { 12179455Sobrien *next = CLUST_EOF; 12279455Sobrien return FSFATMOD; 12379455Sobrien } 12479455Sobrien return FSERROR; 12579455Sobrien } 12679455Sobrien return FSOK; 12779455Sobrien} 12879455Sobrien 12979455Sobrien/* 13079455Sobrien * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 13179455Sobrien */ 13279455Sobrienstatic int 13392839Simp_readfat(int fs, struct bootblock *boot, int no, u_char **buffer) 13479455Sobrien{ 13579455Sobrien off_t off; 13679455Sobrien 13779455Sobrien *buffer = malloc(boot->FATsecs * boot->BytesPerSec); 13879455Sobrien if (*buffer == NULL) { 13979455Sobrien perror("No space for FAT"); 14079455Sobrien return 0; 14179455Sobrien } 14279455Sobrien 14379455Sobrien off = boot->ResSectors + no * boot->FATsecs; 14479455Sobrien off *= boot->BytesPerSec; 14579455Sobrien 14679455Sobrien if (lseek(fs, off, SEEK_SET) != off) { 14779455Sobrien perror("Unable to read FAT"); 14879455Sobrien goto err; 14979455Sobrien } 15079455Sobrien 15179455Sobrien if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec) 15279455Sobrien != boot->FATsecs * boot->BytesPerSec) { 15379455Sobrien perror("Unable to read FAT"); 15479455Sobrien goto err; 15579455Sobrien } 15679455Sobrien 15779455Sobrien return 1; 15879455Sobrien 15979455Sobrien err: 16079455Sobrien free(*buffer); 16179455Sobrien return 0; 16279455Sobrien} 16379455Sobrien 16479455Sobrien/* 16579455Sobrien * Read a FAT and decode it into internal format 16679455Sobrien */ 16779455Sobrienint 16892839Simpreadfat(int fs, struct bootblock *boot, int no, struct fatEntry **fp) 16979455Sobrien{ 17079455Sobrien struct fatEntry *fat; 17179455Sobrien u_char *buffer, *p; 17279455Sobrien cl_t cl; 17379455Sobrien int ret = FSOK; 17479455Sobrien 17579455Sobrien boot->NumFree = boot->NumBad = 0; 17679455Sobrien 17779455Sobrien if (!_readfat(fs, boot, no, &buffer)) 17879455Sobrien return FSFATAL; 17979455Sobrien 18079455Sobrien fat = calloc(boot->NumClusters, sizeof(struct fatEntry)); 18179455Sobrien if (fat == NULL) { 18279455Sobrien perror("No space for FAT"); 18379455Sobrien free(buffer); 18479455Sobrien return FSFATAL; 18579455Sobrien } 18679455Sobrien 18779455Sobrien if (buffer[0] != boot->Media 18879455Sobrien || buffer[1] != 0xff || buffer[2] != 0xff 18979455Sobrien || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 19079455Sobrien || (boot->ClustMask == CLUST32_MASK 19179455Sobrien && ((buffer[3]&0x0f) != 0x0f 19279455Sobrien || buffer[4] != 0xff || buffer[5] != 0xff 19379455Sobrien || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 19479455Sobrien 19579455Sobrien /* Windows 95 OSR2 (and possibly any later) changes 19679455Sobrien * the FAT signature to 0xXXffff7f for FAT16 and to 19779455Sobrien * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 198102231Strhodes * file system is dirty if it doesn't reboot cleanly. 19979455Sobrien * Check this special condition before errorring out. 20079455Sobrien */ 20179455Sobrien if (buffer[0] == boot->Media && buffer[1] == 0xff 20279455Sobrien && buffer[2] == 0xff 20379455Sobrien && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 20479455Sobrien || (boot->ClustMask == CLUST32_MASK 20579455Sobrien && buffer[3] == 0x0f && buffer[4] == 0xff 20679455Sobrien && buffer[5] == 0xff && buffer[6] == 0xff 20779455Sobrien && buffer[7] == 0x07))) 20879455Sobrien ret |= FSDIRTY; 20979455Sobrien else { 21079455Sobrien /* just some odd byte sequence in FAT */ 21179455Sobrien 21279455Sobrien switch (boot->ClustMask) { 21379455Sobrien case CLUST32_MASK: 21479455Sobrien pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 21579455Sobrien "FAT starts with odd byte sequence", 21679455Sobrien buffer[0], buffer[1], buffer[2], buffer[3], 21779455Sobrien buffer[4], buffer[5], buffer[6], buffer[7]); 21879455Sobrien break; 21979455Sobrien case CLUST16_MASK: 22079455Sobrien pwarn("%s (%02x%02x%02x%02x)\n", 22179455Sobrien "FAT starts with odd byte sequence", 22279455Sobrien buffer[0], buffer[1], buffer[2], buffer[3]); 22379455Sobrien break; 22479455Sobrien default: 22579455Sobrien pwarn("%s (%02x%02x%02x)\n", 22679455Sobrien "FAT starts with odd byte sequence", 22779455Sobrien buffer[0], buffer[1], buffer[2]); 22879455Sobrien break; 22979455Sobrien } 23079455Sobrien 23179455Sobrien 23279455Sobrien if (ask(1, "Correct")) 23379455Sobrien ret |= FSFIXFAT; 23479455Sobrien } 23579455Sobrien } 23679455Sobrien switch (boot->ClustMask) { 23779455Sobrien case CLUST32_MASK: 23879455Sobrien p = buffer + 8; 23979455Sobrien break; 24079455Sobrien case CLUST16_MASK: 24179455Sobrien p = buffer + 4; 24279455Sobrien break; 24379455Sobrien default: 24479455Sobrien p = buffer + 3; 24579455Sobrien break; 24679455Sobrien } 24779455Sobrien for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 24879455Sobrien switch (boot->ClustMask) { 24979455Sobrien case CLUST32_MASK: 25079455Sobrien fat[cl].next = p[0] + (p[1] << 8) 25179455Sobrien + (p[2] << 16) + (p[3] << 24); 25279455Sobrien fat[cl].next &= boot->ClustMask; 25379455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 25479455Sobrien cl++; 25579455Sobrien p += 4; 25679455Sobrien break; 25779455Sobrien case CLUST16_MASK: 25879455Sobrien fat[cl].next = p[0] + (p[1] << 8); 25979455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 26079455Sobrien cl++; 26179455Sobrien p += 2; 26279455Sobrien break; 26379455Sobrien default: 26479455Sobrien fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 26579455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 26679455Sobrien cl++; 26779455Sobrien if (cl >= boot->NumClusters) 26879455Sobrien break; 26979455Sobrien fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 27079455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 27179455Sobrien cl++; 27279455Sobrien p += 3; 27379455Sobrien break; 27479455Sobrien } 27579455Sobrien } 27679455Sobrien 27779455Sobrien free(buffer); 27879455Sobrien *fp = fat; 27979455Sobrien return ret; 28079455Sobrien} 28179455Sobrien 28279455Sobrien/* 28379455Sobrien * Get type of reserved cluster 28479455Sobrien */ 28579455Sobrienchar * 28692839Simprsrvdcltype(cl_t cl) 28779455Sobrien{ 28879455Sobrien if (cl == CLUST_FREE) 28979455Sobrien return "free"; 29079455Sobrien if (cl < CLUST_BAD) 29179455Sobrien return "reserved"; 29279455Sobrien if (cl > CLUST_BAD) 29379455Sobrien return "as EOF"; 29479455Sobrien return "bad"; 29579455Sobrien} 29679455Sobrien 29779455Sobrienstatic int 29892839Simpclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, int fatnum) 29979455Sobrien{ 30079455Sobrien if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 30179455Sobrien if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 30279455Sobrien if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 30379455Sobrien && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 30479455Sobrien || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 30579455Sobrien pwarn("Cluster %u is marked %s with different indicators, ", 30679455Sobrien cl, rsrvdcltype(*cp1)); 30779455Sobrien if (ask(1, "fix")) { 30879455Sobrien *cp2 = *cp1; 30979455Sobrien return FSFATMOD; 31079455Sobrien } 31179455Sobrien return FSFATAL; 31279455Sobrien } 31379455Sobrien pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n", 31479455Sobrien cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 31579455Sobrien if (ask(0, "use FAT 0's entry")) { 31679455Sobrien *cp2 = *cp1; 31779455Sobrien return FSFATMOD; 31879455Sobrien } 31979455Sobrien if (ask(0, "use FAT %d's entry", fatnum)) { 32079455Sobrien *cp1 = *cp2; 32179455Sobrien return FSFATMOD; 32279455Sobrien } 32379455Sobrien return FSFATAL; 32479455Sobrien } 32579455Sobrien pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 32679455Sobrien cl, rsrvdcltype(*cp1), *cp2, fatnum); 32779455Sobrien if (ask(0, "Use continuation from FAT %d", fatnum)) { 32879455Sobrien *cp1 = *cp2; 32979455Sobrien return FSFATMOD; 33079455Sobrien } 33179455Sobrien if (ask(0, "Use mark from FAT 0")) { 33279455Sobrien *cp2 = *cp1; 33379455Sobrien return FSFATMOD; 33479455Sobrien } 33579455Sobrien return FSFATAL; 33679455Sobrien } 33779455Sobrien if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 33879455Sobrien pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n", 33979455Sobrien cl, *cp1, rsrvdcltype(*cp2), fatnum); 34079455Sobrien if (ask(0, "Use continuation from FAT 0")) { 34179455Sobrien *cp2 = *cp1; 34279455Sobrien return FSFATMOD; 34379455Sobrien } 34479455Sobrien if (ask(0, "Use mark from FAT %d", fatnum)) { 34579455Sobrien *cp1 = *cp2; 34679455Sobrien return FSFATMOD; 34779455Sobrien } 34879455Sobrien return FSERROR; 34979455Sobrien } 35079455Sobrien pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n", 35179455Sobrien cl, *cp1, *cp2, fatnum); 35279455Sobrien if (ask(0, "Use continuation from FAT 0")) { 35379455Sobrien *cp2 = *cp1; 35479455Sobrien return FSFATMOD; 35579455Sobrien } 35679455Sobrien if (ask(0, "Use continuation from FAT %d", fatnum)) { 35779455Sobrien *cp1 = *cp2; 35879455Sobrien return FSFATMOD; 35979455Sobrien } 36079455Sobrien return FSERROR; 36179455Sobrien} 36279455Sobrien 36379455Sobrien/* 36479455Sobrien * Compare two FAT copies in memory. Resolve any conflicts and merge them 36579455Sobrien * into the first one. 36679455Sobrien */ 36779455Sobrienint 36892839Simpcomparefat(struct bootblock *boot, struct fatEntry *first, 36992839Simp struct fatEntry *second, int fatnum) 37079455Sobrien{ 37179455Sobrien cl_t cl; 37279455Sobrien int ret = FSOK; 37379455Sobrien 37479455Sobrien for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 37579455Sobrien if (first[cl].next != second[cl].next) 37679455Sobrien ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 37779455Sobrien return ret; 37879455Sobrien} 37979455Sobrien 38079455Sobrienvoid 38192839Simpclearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head) 38279455Sobrien{ 38379455Sobrien cl_t p, q; 38479455Sobrien 38579455Sobrien for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 38679455Sobrien if (fat[p].head != head) 38779455Sobrien break; 38879455Sobrien q = fat[p].next; 38979455Sobrien fat[p].next = fat[p].head = CLUST_FREE; 39079455Sobrien fat[p].length = 0; 39179455Sobrien } 39279455Sobrien} 39379455Sobrien 39479455Sobrienint 39592839Simptryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc) 39679455Sobrien{ 39779455Sobrien if (ask(0, "Clear chain starting at %u", head)) { 39879455Sobrien clearchain(boot, fat, head); 39979455Sobrien return FSFATMOD; 40079455Sobrien } else if (ask(0, "Truncate")) { 40179455Sobrien *trunc = CLUST_EOF; 40279455Sobrien return FSFATMOD; 40379455Sobrien } else 40479455Sobrien return FSERROR; 40579455Sobrien} 40679455Sobrien 40779455Sobrien/* 40879455Sobrien * Check a complete FAT in-memory for crosslinks 40979455Sobrien */ 41079455Sobrienint 41192839Simpcheckfat(struct bootblock *boot, struct fatEntry *fat) 41279455Sobrien{ 41379455Sobrien cl_t head, p, h, n; 41479455Sobrien u_int len; 41579455Sobrien int ret = 0; 41679455Sobrien int conf; 41779455Sobrien 41879455Sobrien /* 41979455Sobrien * pass 1: figure out the cluster chains. 42079455Sobrien */ 42179455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 42279455Sobrien /* find next untravelled chain */ 42379455Sobrien if (fat[head].head != 0 /* cluster already belongs to some chain */ 42479455Sobrien || fat[head].next == CLUST_FREE 42579455Sobrien || fat[head].next == CLUST_BAD) 42679455Sobrien continue; /* skip it. */ 42779455Sobrien 42879455Sobrien /* follow the chain and mark all clusters on the way */ 42979455Sobrien for (len = 0, p = head; 43079455Sobrien p >= CLUST_FIRST && p < boot->NumClusters; 43179455Sobrien p = fat[p].next) { 43279455Sobrien fat[p].head = head; 43379455Sobrien len++; 43479455Sobrien } 43579455Sobrien 43679455Sobrien /* the head record gets the length */ 43779455Sobrien fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 43879455Sobrien } 43979455Sobrien 44079455Sobrien /* 44179455Sobrien * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 44279455Sobrien * we didn't know the real start of the chain then - would have treated partial 44379455Sobrien * chains as interlinked with their main chain) 44479455Sobrien */ 44579455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 44679455Sobrien /* find next untravelled chain */ 44779455Sobrien if (fat[head].head != head) 44879455Sobrien continue; 44979455Sobrien 45079455Sobrien /* follow the chain to its end (hopefully) */ 45179455Sobrien for (p = head; 45279455Sobrien (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 45379455Sobrien p = n) 45479455Sobrien if (fat[n].head != head) 45579455Sobrien break; 45679455Sobrien if (n >= CLUST_EOFS) 45779455Sobrien continue; 45879455Sobrien 45979455Sobrien if (n == CLUST_FREE || n >= CLUST_RSRVD) { 46079455Sobrien pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 46179455Sobrien head, rsrvdcltype(n)); 46279455Sobrien ret |= tryclear(boot, fat, head, &fat[p].next); 46379455Sobrien continue; 46479455Sobrien } 46579455Sobrien if (n < CLUST_FIRST || n >= boot->NumClusters) { 46679455Sobrien pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 46779455Sobrien head, n); 46879455Sobrien ret |= tryclear(boot, fat, head, &fat[p].next); 46979455Sobrien continue; 47079455Sobrien } 47179455Sobrien pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 47279455Sobrien head, fat[n].head, n); 47379455Sobrien conf = tryclear(boot, fat, head, &fat[p].next); 47479455Sobrien if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 47579455Sobrien if (conf == FSERROR) { 47679455Sobrien /* 47779455Sobrien * Transfer the common chain to the one not cleared above. 47879455Sobrien */ 47979455Sobrien for (p = n; 48079455Sobrien p >= CLUST_FIRST && p < boot->NumClusters; 48179455Sobrien p = fat[p].next) { 48279455Sobrien if (h != fat[p].head) { 48379455Sobrien /* 48479455Sobrien * Have to reexamine this chain. 48579455Sobrien */ 48679455Sobrien head--; 48779455Sobrien break; 48879455Sobrien } 48979455Sobrien fat[p].head = head; 49079455Sobrien } 49179455Sobrien } 49279455Sobrien clearchain(boot, fat, h); 49379455Sobrien conf |= FSFATMOD; 49479455Sobrien } 49579455Sobrien ret |= conf; 49679455Sobrien } 49779455Sobrien 49879455Sobrien return ret; 49979455Sobrien} 50079455Sobrien 50179455Sobrien/* 50279455Sobrien * Write out FATs encoding them from the internal format 50379455Sobrien */ 50479455Sobrienint 50592839Simpwritefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat) 50679455Sobrien{ 50779455Sobrien u_char *buffer, *p; 50879455Sobrien cl_t cl; 50979455Sobrien int i; 51079455Sobrien u_int32_t fatsz; 51179455Sobrien off_t off; 51279455Sobrien int ret = FSOK; 51379455Sobrien 51479455Sobrien buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); 51579455Sobrien if (buffer == NULL) { 51679455Sobrien perror("No space for FAT"); 51779455Sobrien return FSFATAL; 51879455Sobrien } 51979455Sobrien memset(buffer, 0, fatsz); 52079455Sobrien boot->NumFree = 0; 52179455Sobrien p = buffer; 52279455Sobrien if (correct_fat) { 52379455Sobrien *p++ = (u_char)boot->Media; 52479455Sobrien *p++ = 0xff; 52579455Sobrien *p++ = 0xff; 52679455Sobrien switch (boot->ClustMask) { 52779455Sobrien case CLUST16_MASK: 52879455Sobrien *p++ = 0xff; 52979455Sobrien break; 53079455Sobrien case CLUST32_MASK: 53179455Sobrien *p++ = 0x0f; 53279455Sobrien *p++ = 0xff; 53379455Sobrien *p++ = 0xff; 53479455Sobrien *p++ = 0xff; 53579455Sobrien *p++ = 0x0f; 53679455Sobrien break; 53779455Sobrien } 53879455Sobrien } else { 53979455Sobrien /* use same FAT signature as the old FAT has */ 54079455Sobrien int count; 54179455Sobrien u_char *old_fat; 54279455Sobrien 54379455Sobrien switch (boot->ClustMask) { 54479455Sobrien case CLUST32_MASK: 54579455Sobrien count = 8; 54679455Sobrien break; 54779455Sobrien case CLUST16_MASK: 54879455Sobrien count = 4; 54979455Sobrien break; 55079455Sobrien default: 55179455Sobrien count = 3; 55279455Sobrien break; 55379455Sobrien } 55479455Sobrien 55579455Sobrien if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 55679455Sobrien &old_fat)) { 55779455Sobrien free(buffer); 55879455Sobrien return FSFATAL; 55979455Sobrien } 56079455Sobrien 56179455Sobrien memcpy(p, old_fat, count); 56279455Sobrien free(old_fat); 56379455Sobrien p += count; 56479455Sobrien } 56579455Sobrien 56679455Sobrien for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 56779455Sobrien switch (boot->ClustMask) { 56879455Sobrien case CLUST32_MASK: 56979455Sobrien if (fat[cl].next == CLUST_FREE) 57079455Sobrien boot->NumFree++; 57179455Sobrien *p++ = (u_char)fat[cl].next; 57279455Sobrien *p++ = (u_char)(fat[cl].next >> 8); 57379455Sobrien *p++ = (u_char)(fat[cl].next >> 16); 57479455Sobrien *p &= 0xf0; 57579455Sobrien *p++ |= (fat[cl].next >> 24)&0x0f; 57679455Sobrien break; 57779455Sobrien case CLUST16_MASK: 57879455Sobrien if (fat[cl].next == CLUST_FREE) 57979455Sobrien boot->NumFree++; 58079455Sobrien *p++ = (u_char)fat[cl].next; 58179455Sobrien *p++ = (u_char)(fat[cl].next >> 8); 58279455Sobrien break; 58379455Sobrien default: 58479455Sobrien if (fat[cl].next == CLUST_FREE) 58579455Sobrien boot->NumFree++; 58679455Sobrien if (cl + 1 < boot->NumClusters 58779455Sobrien && fat[cl + 1].next == CLUST_FREE) 58879455Sobrien boot->NumFree++; 58979455Sobrien *p++ = (u_char)fat[cl].next; 59079455Sobrien *p++ = (u_char)((fat[cl].next >> 8) & 0xf) 59179455Sobrien |(u_char)(fat[cl+1].next << 4); 59279455Sobrien *p++ = (u_char)(fat[++cl].next >> 4); 59379455Sobrien break; 59479455Sobrien } 59579455Sobrien } 59679455Sobrien for (i = 0; i < boot->FATs; i++) { 59779455Sobrien off = boot->ResSectors + i * boot->FATsecs; 59879455Sobrien off *= boot->BytesPerSec; 59979455Sobrien if (lseek(fs, off, SEEK_SET) != off 60079455Sobrien || write(fs, buffer, fatsz) != fatsz) { 60179455Sobrien perror("Unable to write FAT"); 60279455Sobrien ret = FSFATAL; /* Return immediately? XXX */ 60379455Sobrien } 60479455Sobrien } 60579455Sobrien free(buffer); 60679455Sobrien return ret; 60779455Sobrien} 60879455Sobrien 60979455Sobrien/* 61079455Sobrien * Check a complete in-memory FAT for lost cluster chains 61179455Sobrien */ 61279455Sobrienint 61392839Simpchecklost(int dosfs, struct bootblock *boot, struct fatEntry *fat) 61479455Sobrien{ 61579455Sobrien cl_t head; 61679455Sobrien int mod = FSOK; 61779455Sobrien int ret; 61879455Sobrien 61979455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 62079455Sobrien /* find next untravelled chain */ 62179455Sobrien if (fat[head].head != head 62279455Sobrien || fat[head].next == CLUST_FREE 62379455Sobrien || (fat[head].next >= CLUST_RSRVD 62479455Sobrien && fat[head].next < CLUST_EOFS) 62579455Sobrien || (fat[head].flags & FAT_USED)) 62679455Sobrien continue; 62779455Sobrien 62879455Sobrien pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 62979455Sobrien head, fat[head].length); 63079455Sobrien mod |= ret = reconnect(dosfs, boot, fat, head); 63179455Sobrien if (mod & FSFATAL) 63279455Sobrien break; 63379455Sobrien if (ret == FSERROR && ask(0, "Clear")) { 63479455Sobrien clearchain(boot, fat, head); 63579455Sobrien mod |= FSFATMOD; 63679455Sobrien } 63779455Sobrien } 63879455Sobrien finishlf(); 63979455Sobrien 64079455Sobrien if (boot->FSInfo) { 64179455Sobrien ret = 0; 64279455Sobrien if (boot->FSFree != boot->NumFree) { 64379455Sobrien pwarn("Free space in FSInfo block (%d) not correct (%d)\n", 64479455Sobrien boot->FSFree, boot->NumFree); 64579455Sobrien if (ask(1, "fix")) { 64679455Sobrien boot->FSFree = boot->NumFree; 64779455Sobrien ret = 1; 64879455Sobrien } 64979455Sobrien } 65079455Sobrien if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) { 65179455Sobrien pwarn("Next free cluster in FSInfo block (%u) not free\n", 65279455Sobrien boot->FSNext); 65379455Sobrien if (ask(1, "fix")) 65479455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) 65579455Sobrien if (fat[head].next == CLUST_FREE) { 65679455Sobrien boot->FSNext = head; 65779455Sobrien ret = 1; 65879455Sobrien break; 65979455Sobrien } 66079455Sobrien } 66179455Sobrien if (ret) 66279455Sobrien mod |= writefsinfo(dosfs, boot); 66379455Sobrien } 66479455Sobrien 66579455Sobrien return mod; 66679455Sobrien} 667