fat.c revision 79455
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 79455 2001-07-09 10:35:18Z obrien $"; 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 5079455Sobrienstatic int checkclnum __P((struct bootblock *, int, cl_t, cl_t *)); 5179455Sobrienstatic int clustdiffer __P((cl_t, cl_t *, cl_t *, int)); 5279455Sobrienstatic int tryclear __P((struct bootblock *, struct fatEntry *, cl_t, cl_t *)); 5379455Sobrienstatic int _readfat __P((int, struct bootblock *, int, u_char **)); 5479455Sobrien 5579455Sobrien/* 5679455Sobrien * Check a cluster number for valid value 5779455Sobrien */ 5879455Sobrienstatic int 5979455Sobriencheckclnum(boot, fat, cl, next) 6079455Sobrien struct bootblock *boot; 6179455Sobrien int fat; 6279455Sobrien cl_t cl; 6379455Sobrien cl_t *next; 6479455Sobrien{ 6579455Sobrien if (*next >= (CLUST_RSRVD&boot->ClustMask)) 6679455Sobrien *next |= ~boot->ClustMask; 6779455Sobrien if (*next == CLUST_FREE) { 6879455Sobrien boot->NumFree++; 6979455Sobrien return FSOK; 7079455Sobrien } 7179455Sobrien if (*next == CLUST_BAD) { 7279455Sobrien boot->NumBad++; 7379455Sobrien return FSOK; 7479455Sobrien } 7579455Sobrien if (*next < CLUST_FIRST 7679455Sobrien || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { 7779455Sobrien pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", 7879455Sobrien cl, fat, 7979455Sobrien *next < CLUST_RSRVD ? "out of range" : "reserved", 8079455Sobrien *next&boot->ClustMask); 8179455Sobrien if (ask(0, "Truncate")) { 8279455Sobrien *next = CLUST_EOF; 8379455Sobrien return FSFATMOD; 8479455Sobrien } 8579455Sobrien return FSERROR; 8679455Sobrien } 8779455Sobrien return FSOK; 8879455Sobrien} 8979455Sobrien 9079455Sobrien/* 9179455Sobrien * Read a FAT from disk. Returns 1 if successful, 0 otherwise. 9279455Sobrien */ 9379455Sobrienstatic int 9479455Sobrien_readfat(fs, boot, no, buffer) 9579455Sobrien int fs; 9679455Sobrien struct bootblock *boot; 9779455Sobrien int no; 9879455Sobrien u_char **buffer; 9979455Sobrien{ 10079455Sobrien off_t off; 10179455Sobrien 10279455Sobrien *buffer = malloc(boot->FATsecs * boot->BytesPerSec); 10379455Sobrien if (*buffer == NULL) { 10479455Sobrien perror("No space for FAT"); 10579455Sobrien return 0; 10679455Sobrien } 10779455Sobrien 10879455Sobrien off = boot->ResSectors + no * boot->FATsecs; 10979455Sobrien off *= boot->BytesPerSec; 11079455Sobrien 11179455Sobrien if (lseek(fs, off, SEEK_SET) != off) { 11279455Sobrien perror("Unable to read FAT"); 11379455Sobrien goto err; 11479455Sobrien } 11579455Sobrien 11679455Sobrien if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec) 11779455Sobrien != boot->FATsecs * boot->BytesPerSec) { 11879455Sobrien perror("Unable to read FAT"); 11979455Sobrien goto err; 12079455Sobrien } 12179455Sobrien 12279455Sobrien return 1; 12379455Sobrien 12479455Sobrien err: 12579455Sobrien free(*buffer); 12679455Sobrien return 0; 12779455Sobrien} 12879455Sobrien 12979455Sobrien/* 13079455Sobrien * Read a FAT and decode it into internal format 13179455Sobrien */ 13279455Sobrienint 13379455Sobrienreadfat(fs, boot, no, fp) 13479455Sobrien int fs; 13579455Sobrien struct bootblock *boot; 13679455Sobrien int no; 13779455Sobrien struct fatEntry **fp; 13879455Sobrien{ 13979455Sobrien struct fatEntry *fat; 14079455Sobrien u_char *buffer, *p; 14179455Sobrien cl_t cl; 14279455Sobrien int ret = FSOK; 14379455Sobrien 14479455Sobrien boot->NumFree = boot->NumBad = 0; 14579455Sobrien 14679455Sobrien if (!_readfat(fs, boot, no, &buffer)) 14779455Sobrien return FSFATAL; 14879455Sobrien 14979455Sobrien fat = calloc(boot->NumClusters, sizeof(struct fatEntry)); 15079455Sobrien if (fat == NULL) { 15179455Sobrien perror("No space for FAT"); 15279455Sobrien free(buffer); 15379455Sobrien return FSFATAL; 15479455Sobrien } 15579455Sobrien 15679455Sobrien if (buffer[0] != boot->Media 15779455Sobrien || buffer[1] != 0xff || buffer[2] != 0xff 15879455Sobrien || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) 15979455Sobrien || (boot->ClustMask == CLUST32_MASK 16079455Sobrien && ((buffer[3]&0x0f) != 0x0f 16179455Sobrien || buffer[4] != 0xff || buffer[5] != 0xff 16279455Sobrien || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { 16379455Sobrien 16479455Sobrien /* Windows 95 OSR2 (and possibly any later) changes 16579455Sobrien * the FAT signature to 0xXXffff7f for FAT16 and to 16679455Sobrien * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the 16779455Sobrien * filesystem is dirty if it doesn't reboot cleanly. 16879455Sobrien * Check this special condition before errorring out. 16979455Sobrien */ 17079455Sobrien if (buffer[0] == boot->Media && buffer[1] == 0xff 17179455Sobrien && buffer[2] == 0xff 17279455Sobrien && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) 17379455Sobrien || (boot->ClustMask == CLUST32_MASK 17479455Sobrien && buffer[3] == 0x0f && buffer[4] == 0xff 17579455Sobrien && buffer[5] == 0xff && buffer[6] == 0xff 17679455Sobrien && buffer[7] == 0x07))) 17779455Sobrien ret |= FSDIRTY; 17879455Sobrien else { 17979455Sobrien /* just some odd byte sequence in FAT */ 18079455Sobrien 18179455Sobrien switch (boot->ClustMask) { 18279455Sobrien case CLUST32_MASK: 18379455Sobrien pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", 18479455Sobrien "FAT starts with odd byte sequence", 18579455Sobrien buffer[0], buffer[1], buffer[2], buffer[3], 18679455Sobrien buffer[4], buffer[5], buffer[6], buffer[7]); 18779455Sobrien break; 18879455Sobrien case CLUST16_MASK: 18979455Sobrien pwarn("%s (%02x%02x%02x%02x)\n", 19079455Sobrien "FAT starts with odd byte sequence", 19179455Sobrien buffer[0], buffer[1], buffer[2], buffer[3]); 19279455Sobrien break; 19379455Sobrien default: 19479455Sobrien pwarn("%s (%02x%02x%02x)\n", 19579455Sobrien "FAT starts with odd byte sequence", 19679455Sobrien buffer[0], buffer[1], buffer[2]); 19779455Sobrien break; 19879455Sobrien } 19979455Sobrien 20079455Sobrien 20179455Sobrien if (ask(1, "Correct")) 20279455Sobrien ret |= FSFIXFAT; 20379455Sobrien } 20479455Sobrien } 20579455Sobrien switch (boot->ClustMask) { 20679455Sobrien case CLUST32_MASK: 20779455Sobrien p = buffer + 8; 20879455Sobrien break; 20979455Sobrien case CLUST16_MASK: 21079455Sobrien p = buffer + 4; 21179455Sobrien break; 21279455Sobrien default: 21379455Sobrien p = buffer + 3; 21479455Sobrien break; 21579455Sobrien } 21679455Sobrien for (cl = CLUST_FIRST; cl < boot->NumClusters;) { 21779455Sobrien switch (boot->ClustMask) { 21879455Sobrien case CLUST32_MASK: 21979455Sobrien fat[cl].next = p[0] + (p[1] << 8) 22079455Sobrien + (p[2] << 16) + (p[3] << 24); 22179455Sobrien fat[cl].next &= boot->ClustMask; 22279455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 22379455Sobrien cl++; 22479455Sobrien p += 4; 22579455Sobrien break; 22679455Sobrien case CLUST16_MASK: 22779455Sobrien fat[cl].next = p[0] + (p[1] << 8); 22879455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 22979455Sobrien cl++; 23079455Sobrien p += 2; 23179455Sobrien break; 23279455Sobrien default: 23379455Sobrien fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; 23479455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 23579455Sobrien cl++; 23679455Sobrien if (cl >= boot->NumClusters) 23779455Sobrien break; 23879455Sobrien fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; 23979455Sobrien ret |= checkclnum(boot, no, cl, &fat[cl].next); 24079455Sobrien cl++; 24179455Sobrien p += 3; 24279455Sobrien break; 24379455Sobrien } 24479455Sobrien } 24579455Sobrien 24679455Sobrien free(buffer); 24779455Sobrien *fp = fat; 24879455Sobrien return ret; 24979455Sobrien} 25079455Sobrien 25179455Sobrien/* 25279455Sobrien * Get type of reserved cluster 25379455Sobrien */ 25479455Sobrienchar * 25579455Sobrienrsrvdcltype(cl) 25679455Sobrien cl_t cl; 25779455Sobrien{ 25879455Sobrien if (cl == CLUST_FREE) 25979455Sobrien return "free"; 26079455Sobrien if (cl < CLUST_BAD) 26179455Sobrien return "reserved"; 26279455Sobrien if (cl > CLUST_BAD) 26379455Sobrien return "as EOF"; 26479455Sobrien return "bad"; 26579455Sobrien} 26679455Sobrien 26779455Sobrienstatic int 26879455Sobrienclustdiffer(cl, cp1, cp2, fatnum) 26979455Sobrien cl_t cl; 27079455Sobrien cl_t *cp1; 27179455Sobrien cl_t *cp2; 27279455Sobrien int fatnum; 27379455Sobrien{ 27479455Sobrien if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { 27579455Sobrien if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 27679455Sobrien if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD 27779455Sobrien && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) 27879455Sobrien || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { 27979455Sobrien pwarn("Cluster %u is marked %s with different indicators, ", 28079455Sobrien cl, rsrvdcltype(*cp1)); 28179455Sobrien if (ask(1, "fix")) { 28279455Sobrien *cp2 = *cp1; 28379455Sobrien return FSFATMOD; 28479455Sobrien } 28579455Sobrien return FSFATAL; 28679455Sobrien } 28779455Sobrien pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n", 28879455Sobrien cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); 28979455Sobrien if (ask(0, "use FAT 0's entry")) { 29079455Sobrien *cp2 = *cp1; 29179455Sobrien return FSFATMOD; 29279455Sobrien } 29379455Sobrien if (ask(0, "use FAT %d's entry", fatnum)) { 29479455Sobrien *cp1 = *cp2; 29579455Sobrien return FSFATMOD; 29679455Sobrien } 29779455Sobrien return FSFATAL; 29879455Sobrien } 29979455Sobrien pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", 30079455Sobrien cl, rsrvdcltype(*cp1), *cp2, fatnum); 30179455Sobrien if (ask(0, "Use continuation from FAT %d", fatnum)) { 30279455Sobrien *cp1 = *cp2; 30379455Sobrien return FSFATMOD; 30479455Sobrien } 30579455Sobrien if (ask(0, "Use mark from FAT 0")) { 30679455Sobrien *cp2 = *cp1; 30779455Sobrien return FSFATMOD; 30879455Sobrien } 30979455Sobrien return FSFATAL; 31079455Sobrien } 31179455Sobrien if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { 31279455Sobrien pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n", 31379455Sobrien cl, *cp1, rsrvdcltype(*cp2), fatnum); 31479455Sobrien if (ask(0, "Use continuation from FAT 0")) { 31579455Sobrien *cp2 = *cp1; 31679455Sobrien return FSFATMOD; 31779455Sobrien } 31879455Sobrien if (ask(0, "Use mark from FAT %d", fatnum)) { 31979455Sobrien *cp1 = *cp2; 32079455Sobrien return FSFATMOD; 32179455Sobrien } 32279455Sobrien return FSERROR; 32379455Sobrien } 32479455Sobrien pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n", 32579455Sobrien cl, *cp1, *cp2, fatnum); 32679455Sobrien if (ask(0, "Use continuation from FAT 0")) { 32779455Sobrien *cp2 = *cp1; 32879455Sobrien return FSFATMOD; 32979455Sobrien } 33079455Sobrien if (ask(0, "Use continuation from FAT %d", fatnum)) { 33179455Sobrien *cp1 = *cp2; 33279455Sobrien return FSFATMOD; 33379455Sobrien } 33479455Sobrien return FSERROR; 33579455Sobrien} 33679455Sobrien 33779455Sobrien/* 33879455Sobrien * Compare two FAT copies in memory. Resolve any conflicts and merge them 33979455Sobrien * into the first one. 34079455Sobrien */ 34179455Sobrienint 34279455Sobriencomparefat(boot, first, second, fatnum) 34379455Sobrien struct bootblock *boot; 34479455Sobrien struct fatEntry *first; 34579455Sobrien struct fatEntry *second; 34679455Sobrien int fatnum; 34779455Sobrien{ 34879455Sobrien cl_t cl; 34979455Sobrien int ret = FSOK; 35079455Sobrien 35179455Sobrien for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) 35279455Sobrien if (first[cl].next != second[cl].next) 35379455Sobrien ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); 35479455Sobrien return ret; 35579455Sobrien} 35679455Sobrien 35779455Sobrienvoid 35879455Sobrienclearchain(boot, fat, head) 35979455Sobrien struct bootblock *boot; 36079455Sobrien struct fatEntry *fat; 36179455Sobrien cl_t head; 36279455Sobrien{ 36379455Sobrien cl_t p, q; 36479455Sobrien 36579455Sobrien for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { 36679455Sobrien if (fat[p].head != head) 36779455Sobrien break; 36879455Sobrien q = fat[p].next; 36979455Sobrien fat[p].next = fat[p].head = CLUST_FREE; 37079455Sobrien fat[p].length = 0; 37179455Sobrien } 37279455Sobrien} 37379455Sobrien 37479455Sobrienint 37579455Sobrientryclear(boot, fat, head, trunc) 37679455Sobrien struct bootblock *boot; 37779455Sobrien struct fatEntry *fat; 37879455Sobrien cl_t head; 37979455Sobrien cl_t *trunc; 38079455Sobrien{ 38179455Sobrien if (ask(0, "Clear chain starting at %u", head)) { 38279455Sobrien clearchain(boot, fat, head); 38379455Sobrien return FSFATMOD; 38479455Sobrien } else if (ask(0, "Truncate")) { 38579455Sobrien *trunc = CLUST_EOF; 38679455Sobrien return FSFATMOD; 38779455Sobrien } else 38879455Sobrien return FSERROR; 38979455Sobrien} 39079455Sobrien 39179455Sobrien/* 39279455Sobrien * Check a complete FAT in-memory for crosslinks 39379455Sobrien */ 39479455Sobrienint 39579455Sobriencheckfat(boot, fat) 39679455Sobrien struct bootblock *boot; 39779455Sobrien struct fatEntry *fat; 39879455Sobrien{ 39979455Sobrien cl_t head, p, h, n; 40079455Sobrien u_int len; 40179455Sobrien int ret = 0; 40279455Sobrien int conf; 40379455Sobrien 40479455Sobrien /* 40579455Sobrien * pass 1: figure out the cluster chains. 40679455Sobrien */ 40779455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 40879455Sobrien /* find next untravelled chain */ 40979455Sobrien if (fat[head].head != 0 /* cluster already belongs to some chain */ 41079455Sobrien || fat[head].next == CLUST_FREE 41179455Sobrien || fat[head].next == CLUST_BAD) 41279455Sobrien continue; /* skip it. */ 41379455Sobrien 41479455Sobrien /* follow the chain and mark all clusters on the way */ 41579455Sobrien for (len = 0, p = head; 41679455Sobrien p >= CLUST_FIRST && p < boot->NumClusters; 41779455Sobrien p = fat[p].next) { 41879455Sobrien fat[p].head = head; 41979455Sobrien len++; 42079455Sobrien } 42179455Sobrien 42279455Sobrien /* the head record gets the length */ 42379455Sobrien fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; 42479455Sobrien } 42579455Sobrien 42679455Sobrien /* 42779455Sobrien * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because 42879455Sobrien * we didn't know the real start of the chain then - would have treated partial 42979455Sobrien * chains as interlinked with their main chain) 43079455Sobrien */ 43179455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 43279455Sobrien /* find next untravelled chain */ 43379455Sobrien if (fat[head].head != head) 43479455Sobrien continue; 43579455Sobrien 43679455Sobrien /* follow the chain to its end (hopefully) */ 43779455Sobrien for (p = head; 43879455Sobrien (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; 43979455Sobrien p = n) 44079455Sobrien if (fat[n].head != head) 44179455Sobrien break; 44279455Sobrien if (n >= CLUST_EOFS) 44379455Sobrien continue; 44479455Sobrien 44579455Sobrien if (n == CLUST_FREE || n >= CLUST_RSRVD) { 44679455Sobrien pwarn("Cluster chain starting at %u ends with cluster marked %s\n", 44779455Sobrien head, rsrvdcltype(n)); 44879455Sobrien ret |= tryclear(boot, fat, head, &fat[p].next); 44979455Sobrien continue; 45079455Sobrien } 45179455Sobrien if (n < CLUST_FIRST || n >= boot->NumClusters) { 45279455Sobrien pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", 45379455Sobrien head, n); 45479455Sobrien ret |= tryclear(boot, fat, head, &fat[p].next); 45579455Sobrien continue; 45679455Sobrien } 45779455Sobrien pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", 45879455Sobrien head, fat[n].head, n); 45979455Sobrien conf = tryclear(boot, fat, head, &fat[p].next); 46079455Sobrien if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { 46179455Sobrien if (conf == FSERROR) { 46279455Sobrien /* 46379455Sobrien * Transfer the common chain to the one not cleared above. 46479455Sobrien */ 46579455Sobrien for (p = n; 46679455Sobrien p >= CLUST_FIRST && p < boot->NumClusters; 46779455Sobrien p = fat[p].next) { 46879455Sobrien if (h != fat[p].head) { 46979455Sobrien /* 47079455Sobrien * Have to reexamine this chain. 47179455Sobrien */ 47279455Sobrien head--; 47379455Sobrien break; 47479455Sobrien } 47579455Sobrien fat[p].head = head; 47679455Sobrien } 47779455Sobrien } 47879455Sobrien clearchain(boot, fat, h); 47979455Sobrien conf |= FSFATMOD; 48079455Sobrien } 48179455Sobrien ret |= conf; 48279455Sobrien } 48379455Sobrien 48479455Sobrien return ret; 48579455Sobrien} 48679455Sobrien 48779455Sobrien/* 48879455Sobrien * Write out FATs encoding them from the internal format 48979455Sobrien */ 49079455Sobrienint 49179455Sobrienwritefat(fs, boot, fat, correct_fat) 49279455Sobrien int fs; 49379455Sobrien struct bootblock *boot; 49479455Sobrien struct fatEntry *fat; 49579455Sobrien int correct_fat; 49679455Sobrien{ 49779455Sobrien u_char *buffer, *p; 49879455Sobrien cl_t cl; 49979455Sobrien int i; 50079455Sobrien u_int32_t fatsz; 50179455Sobrien off_t off; 50279455Sobrien int ret = FSOK; 50379455Sobrien 50479455Sobrien buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); 50579455Sobrien if (buffer == NULL) { 50679455Sobrien perror("No space for FAT"); 50779455Sobrien return FSFATAL; 50879455Sobrien } 50979455Sobrien memset(buffer, 0, fatsz); 51079455Sobrien boot->NumFree = 0; 51179455Sobrien p = buffer; 51279455Sobrien if (correct_fat) { 51379455Sobrien *p++ = (u_char)boot->Media; 51479455Sobrien *p++ = 0xff; 51579455Sobrien *p++ = 0xff; 51679455Sobrien switch (boot->ClustMask) { 51779455Sobrien case CLUST16_MASK: 51879455Sobrien *p++ = 0xff; 51979455Sobrien break; 52079455Sobrien case CLUST32_MASK: 52179455Sobrien *p++ = 0x0f; 52279455Sobrien *p++ = 0xff; 52379455Sobrien *p++ = 0xff; 52479455Sobrien *p++ = 0xff; 52579455Sobrien *p++ = 0x0f; 52679455Sobrien break; 52779455Sobrien } 52879455Sobrien } else { 52979455Sobrien /* use same FAT signature as the old FAT has */ 53079455Sobrien int count; 53179455Sobrien u_char *old_fat; 53279455Sobrien 53379455Sobrien switch (boot->ClustMask) { 53479455Sobrien case CLUST32_MASK: 53579455Sobrien count = 8; 53679455Sobrien break; 53779455Sobrien case CLUST16_MASK: 53879455Sobrien count = 4; 53979455Sobrien break; 54079455Sobrien default: 54179455Sobrien count = 3; 54279455Sobrien break; 54379455Sobrien } 54479455Sobrien 54579455Sobrien if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, 54679455Sobrien &old_fat)) { 54779455Sobrien free(buffer); 54879455Sobrien return FSFATAL; 54979455Sobrien } 55079455Sobrien 55179455Sobrien memcpy(p, old_fat, count); 55279455Sobrien free(old_fat); 55379455Sobrien p += count; 55479455Sobrien } 55579455Sobrien 55679455Sobrien for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { 55779455Sobrien switch (boot->ClustMask) { 55879455Sobrien case CLUST32_MASK: 55979455Sobrien if (fat[cl].next == CLUST_FREE) 56079455Sobrien boot->NumFree++; 56179455Sobrien *p++ = (u_char)fat[cl].next; 56279455Sobrien *p++ = (u_char)(fat[cl].next >> 8); 56379455Sobrien *p++ = (u_char)(fat[cl].next >> 16); 56479455Sobrien *p &= 0xf0; 56579455Sobrien *p++ |= (fat[cl].next >> 24)&0x0f; 56679455Sobrien break; 56779455Sobrien case CLUST16_MASK: 56879455Sobrien if (fat[cl].next == CLUST_FREE) 56979455Sobrien boot->NumFree++; 57079455Sobrien *p++ = (u_char)fat[cl].next; 57179455Sobrien *p++ = (u_char)(fat[cl].next >> 8); 57279455Sobrien break; 57379455Sobrien default: 57479455Sobrien if (fat[cl].next == CLUST_FREE) 57579455Sobrien boot->NumFree++; 57679455Sobrien if (cl + 1 < boot->NumClusters 57779455Sobrien && fat[cl + 1].next == CLUST_FREE) 57879455Sobrien boot->NumFree++; 57979455Sobrien *p++ = (u_char)fat[cl].next; 58079455Sobrien *p++ = (u_char)((fat[cl].next >> 8) & 0xf) 58179455Sobrien |(u_char)(fat[cl+1].next << 4); 58279455Sobrien *p++ = (u_char)(fat[++cl].next >> 4); 58379455Sobrien break; 58479455Sobrien } 58579455Sobrien } 58679455Sobrien for (i = 0; i < boot->FATs; i++) { 58779455Sobrien off = boot->ResSectors + i * boot->FATsecs; 58879455Sobrien off *= boot->BytesPerSec; 58979455Sobrien if (lseek(fs, off, SEEK_SET) != off 59079455Sobrien || write(fs, buffer, fatsz) != fatsz) { 59179455Sobrien perror("Unable to write FAT"); 59279455Sobrien ret = FSFATAL; /* Return immediately? XXX */ 59379455Sobrien } 59479455Sobrien } 59579455Sobrien free(buffer); 59679455Sobrien return ret; 59779455Sobrien} 59879455Sobrien 59979455Sobrien/* 60079455Sobrien * Check a complete in-memory FAT for lost cluster chains 60179455Sobrien */ 60279455Sobrienint 60379455Sobrienchecklost(dosfs, boot, fat) 60479455Sobrien int dosfs; 60579455Sobrien struct bootblock *boot; 60679455Sobrien struct fatEntry *fat; 60779455Sobrien{ 60879455Sobrien cl_t head; 60979455Sobrien int mod = FSOK; 61079455Sobrien int ret; 61179455Sobrien 61279455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) { 61379455Sobrien /* find next untravelled chain */ 61479455Sobrien if (fat[head].head != head 61579455Sobrien || fat[head].next == CLUST_FREE 61679455Sobrien || (fat[head].next >= CLUST_RSRVD 61779455Sobrien && fat[head].next < CLUST_EOFS) 61879455Sobrien || (fat[head].flags & FAT_USED)) 61979455Sobrien continue; 62079455Sobrien 62179455Sobrien pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", 62279455Sobrien head, fat[head].length); 62379455Sobrien mod |= ret = reconnect(dosfs, boot, fat, head); 62479455Sobrien if (mod & FSFATAL) 62579455Sobrien break; 62679455Sobrien if (ret == FSERROR && ask(0, "Clear")) { 62779455Sobrien clearchain(boot, fat, head); 62879455Sobrien mod |= FSFATMOD; 62979455Sobrien } 63079455Sobrien } 63179455Sobrien finishlf(); 63279455Sobrien 63379455Sobrien if (boot->FSInfo) { 63479455Sobrien ret = 0; 63579455Sobrien if (boot->FSFree != boot->NumFree) { 63679455Sobrien pwarn("Free space in FSInfo block (%d) not correct (%d)\n", 63779455Sobrien boot->FSFree, boot->NumFree); 63879455Sobrien if (ask(1, "fix")) { 63979455Sobrien boot->FSFree = boot->NumFree; 64079455Sobrien ret = 1; 64179455Sobrien } 64279455Sobrien } 64379455Sobrien if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) { 64479455Sobrien pwarn("Next free cluster in FSInfo block (%u) not free\n", 64579455Sobrien boot->FSNext); 64679455Sobrien if (ask(1, "fix")) 64779455Sobrien for (head = CLUST_FIRST; head < boot->NumClusters; head++) 64879455Sobrien if (fat[head].next == CLUST_FREE) { 64979455Sobrien boot->FSNext = head; 65079455Sobrien ret = 1; 65179455Sobrien break; 65279455Sobrien } 65379455Sobrien } 65479455Sobrien if (ret) 65579455Sobrien mod |= writefsinfo(dosfs, boot); 65679455Sobrien } 65779455Sobrien 65879455Sobrien return mod; 65979455Sobrien} 660