1*70318244Schristos /* $NetBSD: fat.c,v 1.30 2019/06/04 00:08:00 christos Exp $ */
26ae4c91aSws
36ae4c91aSws /*
4d445160eSws * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
56ae4c91aSws * Copyright (c) 1995 Martin Husemann
66ae4c91aSws *
76ae4c91aSws * Redistribution and use in source and binary forms, with or without
86ae4c91aSws * modification, are permitted provided that the following conditions
96ae4c91aSws * are met:
106ae4c91aSws * 1. Redistributions of source code must retain the above copyright
116ae4c91aSws * notice, this list of conditions and the following disclaimer.
126ae4c91aSws * 2. Redistributions in binary form must reproduce the above copyright
136ae4c91aSws * notice, this list of conditions and the following disclaimer in the
146ae4c91aSws * documentation and/or other materials provided with the distribution.
156ae4c91aSws *
166ae4c91aSws * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
176ae4c91aSws * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
186ae4c91aSws * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
196ae4c91aSws * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
206ae4c91aSws * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
216ae4c91aSws * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
226ae4c91aSws * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
236ae4c91aSws * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
246ae4c91aSws * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
256ae4c91aSws * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
266ae4c91aSws */
276ae4c91aSws
286ae4c91aSws
295a94f674Slukem #include <sys/cdefs.h>
306ae4c91aSws #ifndef lint
31*70318244Schristos __RCSID("$NetBSD: fat.c,v 1.30 2019/06/04 00:08:00 christos Exp $");
326ae4c91aSws #endif /* not lint */
336ae4c91aSws
346ae4c91aSws #include <stdlib.h>
356ae4c91aSws #include <string.h>
366ae4c91aSws #include <ctype.h>
376ae4c91aSws #include <stdio.h>
386ae4c91aSws #include <unistd.h>
396ae4c91aSws
406ae4c91aSws #include "ext.h"
41a0c84e7dSchristos #include "fsutil.h"
42442314abSchristos
43a2b5923eSlukem static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
44a2b5923eSlukem static int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
4565e67723Sxtraeme static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
46a2b5923eSlukem static int _readfat(int, struct bootblock *, u_int, u_char **);
476ae4c91aSws
486ae4c91aSws /*
496ae4c91aSws * Check a cluster number for valid value
506ae4c91aSws */
516ae4c91aSws static int
checkclnum(struct bootblock * boot,u_int fat,cl_t cl,cl_t * next)52a2b5923eSlukem checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
536ae4c91aSws {
54d445160eSws if (*next >= (CLUST_RSRVD&boot->ClustMask))
55d445160eSws *next |= ~boot->ClustMask;
566ae4c91aSws if (*next == CLUST_FREE) {
576ae4c91aSws boot->NumFree++;
586ae4c91aSws return FSOK;
596ae4c91aSws }
60fdbcbfc2Sws if (*next == CLUST_BAD) {
61fdbcbfc2Sws boot->NumBad++;
62fdbcbfc2Sws return FSOK;
63fdbcbfc2Sws }
646ae4c91aSws if (*next < CLUST_FIRST
656ae4c91aSws || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
66a2b5923eSlukem pwarn("Cluster %u in FAT %u continues with %s cluster number %u\n",
676ae4c91aSws cl, fat,
686ae4c91aSws *next < CLUST_RSRVD ? "out of range" : "reserved",
69d445160eSws *next&boot->ClustMask);
706ae4c91aSws if (ask(0, "Truncate")) {
716ae4c91aSws *next = CLUST_EOF;
726ae4c91aSws return FSFATMOD;
736ae4c91aSws }
746ae4c91aSws return FSERROR;
756ae4c91aSws }
766ae4c91aSws return FSOK;
776ae4c91aSws }
786ae4c91aSws
796ae4c91aSws /*
804ab49897Sjdolecek * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
814ab49897Sjdolecek */
82fe6381bfSjdolecek static int
_readfat(int fs,struct bootblock * boot,u_int no,u_char ** buffer)83a2b5923eSlukem _readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
844ab49897Sjdolecek {
854ab49897Sjdolecek off_t off;
8615f3040fSchristos size_t len;
874ab49897Sjdolecek
8815f3040fSchristos *buffer = malloc(len = boot->FATsecs * boot->BytesPerSec);
894ab49897Sjdolecek if (*buffer == NULL) {
9015f3040fSchristos perr("No space for FAT sectors (%zu)", len);
914ab49897Sjdolecek return 0;
924ab49897Sjdolecek }
934ab49897Sjdolecek
944ab49897Sjdolecek off = boot->ResSectors + no * boot->FATsecs;
954ab49897Sjdolecek off *= boot->BytesPerSec;
964ab49897Sjdolecek
974ab49897Sjdolecek if (lseek(fs, off, SEEK_SET) != off) {
9815f3040fSchristos perr("Unable to read FAT");
994ab49897Sjdolecek goto err;
1004ab49897Sjdolecek }
1014ab49897Sjdolecek
102a2b5923eSlukem if ((size_t)read(fs, *buffer, boot->FATsecs * boot->BytesPerSec)
1034ab49897Sjdolecek != boot->FATsecs * boot->BytesPerSec) {
10415f3040fSchristos perr("Unable to read FAT");
1054ab49897Sjdolecek goto err;
1064ab49897Sjdolecek }
1074ab49897Sjdolecek
1084ab49897Sjdolecek return 1;
1094ab49897Sjdolecek
1104ab49897Sjdolecek err:
1114ab49897Sjdolecek free(*buffer);
1124ab49897Sjdolecek return 0;
1134ab49897Sjdolecek }
1144ab49897Sjdolecek
1154ab49897Sjdolecek /*
1166ae4c91aSws * Read a FAT and decode it into internal format
1176ae4c91aSws */
1186ae4c91aSws int
readfat(int fs,struct bootblock * boot,u_int no,struct fatEntry ** fp)119a2b5923eSlukem readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
1206ae4c91aSws {
1216ae4c91aSws struct fatEntry *fat;
1226ae4c91aSws u_char *buffer, *p;
1236ae4c91aSws cl_t cl;
1246ae4c91aSws int ret = FSOK;
12515f3040fSchristos size_t len;
1266ae4c91aSws
127fdbcbfc2Sws boot->NumFree = boot->NumBad = 0;
1284ab49897Sjdolecek
1294ab49897Sjdolecek if (!_readfat(fs, boot, no, &buffer))
1304ab49897Sjdolecek return FSFATAL;
1314ab49897Sjdolecek
13215f3040fSchristos fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
1334ab49897Sjdolecek if (fat == NULL) {
13415f3040fSchristos perr("No space for FAT clusters (%zu)", len);
1356ae4c91aSws free(buffer);
1366ae4c91aSws return FSFATAL;
1376ae4c91aSws }
13815f3040fSchristos (void)memset(fat, 0, len);
1396ae4c91aSws
140daa7d68fSws if (buffer[0] != boot->Media
141daa7d68fSws || buffer[1] != 0xff || buffer[2] != 0xff
142d445160eSws || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
143d445160eSws || (boot->ClustMask == CLUST32_MASK
144d445160eSws && ((buffer[3]&0x0f) != 0x0f
145d445160eSws || buffer[4] != 0xff || buffer[5] != 0xff
146d445160eSws || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
1474ab49897Sjdolecek
1484ab49897Sjdolecek /* Windows 95 OSR2 (and possibly any later) changes
1494ab49897Sjdolecek * the FAT signature to 0xXXffff7f for FAT16 and to
1504ab49897Sjdolecek * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
1514ab49897Sjdolecek * filesystem is dirty if it doesn't reboot cleanly.
1524ab49897Sjdolecek * Check this special condition before errorring out.
1534ab49897Sjdolecek */
1544ab49897Sjdolecek if (buffer[0] == boot->Media && buffer[1] == 0xff
1554ab49897Sjdolecek && buffer[2] == 0xff
1564ab49897Sjdolecek && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
1574ab49897Sjdolecek || (boot->ClustMask == CLUST32_MASK
1584ab49897Sjdolecek && buffer[3] == 0x0f && buffer[4] == 0xff
1594ab49897Sjdolecek && buffer[5] == 0xff && buffer[6] == 0xff
1604ab49897Sjdolecek && buffer[7] == 0x07)))
1614ab49897Sjdolecek ret |= FSDIRTY;
1624ab49897Sjdolecek else {
1634ab49897Sjdolecek /* just some odd byte sequence in FAT */
164d445160eSws
165d445160eSws switch (boot->ClustMask) {
166d445160eSws case CLUST32_MASK:
1679979da6cSis pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
1689979da6cSis "FAT starts with odd byte sequence",
1699979da6cSis buffer[0], buffer[1], buffer[2], buffer[3],
1709979da6cSis buffer[4], buffer[5], buffer[6], buffer[7]);
171d445160eSws break;
172d445160eSws case CLUST16_MASK:
173ca484833Slukem pwarn("%s (%02x%02x%02x%02x)\n",
174ca484833Slukem "FAT starts with odd byte sequence",
1759979da6cSis buffer[0], buffer[1], buffer[2], buffer[3]);
176d445160eSws break;
177d445160eSws default:
178ca484833Slukem pwarn("%s (%02x%02x%02x)\n",
179ca484833Slukem "FAT starts with odd byte sequence",
1809979da6cSis buffer[0], buffer[1], buffer[2]);
181d445160eSws break;
182d445160eSws }
1834ab49897Sjdolecek
1844ab49897Sjdolecek
185d445160eSws if (ask(1, "Correct"))
1864ab49897Sjdolecek ret |= FSFIXFAT;
1874ab49897Sjdolecek }
1886ae4c91aSws }
189d445160eSws switch (boot->ClustMask) {
190d445160eSws case CLUST32_MASK:
191d445160eSws p = buffer + 8;
192d445160eSws break;
193d445160eSws case CLUST16_MASK:
194d445160eSws p = buffer + 4;
195d445160eSws break;
196d445160eSws default:
197d445160eSws p = buffer + 3;
198d445160eSws break;
1996ae4c91aSws }
2006ae4c91aSws for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
201d445160eSws switch (boot->ClustMask) {
202d445160eSws case CLUST32_MASK:
203d445160eSws fat[cl].next = p[0] + (p[1] << 8)
204d445160eSws + (p[2] << 16) + (p[3] << 24);
205d445160eSws fat[cl].next &= boot->ClustMask;
206d445160eSws ret |= checkclnum(boot, no, cl, &fat[cl].next);
207d445160eSws cl++;
208d445160eSws p += 4;
209d445160eSws break;
210d445160eSws case CLUST16_MASK:
2116ae4c91aSws fat[cl].next = p[0] + (p[1] << 8);
2126ae4c91aSws ret |= checkclnum(boot, no, cl, &fat[cl].next);
2136ae4c91aSws cl++;
2146ae4c91aSws p += 2;
215d445160eSws break;
216d445160eSws default:
2176ae4c91aSws fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
2186ae4c91aSws ret |= checkclnum(boot, no, cl, &fat[cl].next);
2196ae4c91aSws cl++;
2206ae4c91aSws if (cl >= boot->NumClusters)
2216ae4c91aSws break;
2226ae4c91aSws fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
2236ae4c91aSws ret |= checkclnum(boot, no, cl, &fat[cl].next);
2246ae4c91aSws cl++;
2256ae4c91aSws p += 3;
226d445160eSws break;
2276ae4c91aSws }
2286ae4c91aSws }
2296ae4c91aSws
2306ae4c91aSws free(buffer);
2314b15f35aSchristos if (ret & FSFATAL) {
2324b15f35aSchristos free(fat);
2334b15f35aSchristos *fp = NULL;
2344b15f35aSchristos } else
2356ae4c91aSws *fp = fat;
2366ae4c91aSws return ret;
2376ae4c91aSws }
2386ae4c91aSws
2396ae4c91aSws /*
2406ae4c91aSws * Get type of reserved cluster
2416ae4c91aSws */
2428e78c802Swiz const char *
rsrvdcltype(cl_t cl)24365e67723Sxtraeme rsrvdcltype(cl_t cl)
2446ae4c91aSws {
245c17a22a4Sws if (cl == CLUST_FREE)
246c17a22a4Sws return "free";
2476ae4c91aSws if (cl < CLUST_BAD)
2486ae4c91aSws return "reserved";
2496ae4c91aSws if (cl > CLUST_BAD)
2506ae4c91aSws return "as EOF";
2516ae4c91aSws return "bad";
2526ae4c91aSws }
2536ae4c91aSws
2546ae4c91aSws static int
clustdiffer(cl_t cl,cl_t * cp1,cl_t * cp2,u_int fatnum)255a2b5923eSlukem clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
2566ae4c91aSws {
257c17a22a4Sws if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
258c17a22a4Sws if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
259c17a22a4Sws if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
260c17a22a4Sws && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
2616ae4c91aSws || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
262d445160eSws pwarn("Cluster %u is marked %s with different indicators, ",
2636ae4c91aSws cl, rsrvdcltype(*cp1));
2646ae4c91aSws if (ask(1, "fix")) {
2656ae4c91aSws *cp2 = *cp1;
2666ae4c91aSws return FSFATMOD;
2676ae4c91aSws }
2686ae4c91aSws return FSFATAL;
2696ae4c91aSws }
270a2b5923eSlukem pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
2716ae4c91aSws cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
272d445160eSws if (ask(0, "use FAT 0's entry")) {
2736ae4c91aSws *cp2 = *cp1;
2746ae4c91aSws return FSFATMOD;
2756ae4c91aSws }
276a2b5923eSlukem if (ask(0, "use FAT %u's entry", fatnum)) {
2776ae4c91aSws *cp1 = *cp2;
2786ae4c91aSws return FSFATMOD;
2796ae4c91aSws }
2806ae4c91aSws return FSFATAL;
2816ae4c91aSws }
282a2b5923eSlukem pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %u\n",
2836ae4c91aSws cl, rsrvdcltype(*cp1), *cp2, fatnum);
284a2b5923eSlukem if (ask(0, "Use continuation from FAT %u", fatnum)) {
2856ae4c91aSws *cp1 = *cp2;
2866ae4c91aSws return FSFATMOD;
2876ae4c91aSws }
288d445160eSws if (ask(0, "Use mark from FAT 0")) {
2896ae4c91aSws *cp2 = *cp1;
2906ae4c91aSws return FSFATMOD;
2916ae4c91aSws }
2926ae4c91aSws return FSFATAL;
2936ae4c91aSws }
294c17a22a4Sws if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
295a2b5923eSlukem pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
2966ae4c91aSws cl, *cp1, rsrvdcltype(*cp2), fatnum);
297d445160eSws if (ask(0, "Use continuation from FAT 0")) {
2986ae4c91aSws *cp2 = *cp1;
2996ae4c91aSws return FSFATMOD;
3006ae4c91aSws }
301a2b5923eSlukem if (ask(0, "Use mark from FAT %u", fatnum)) {
3026ae4c91aSws *cp1 = *cp2;
3036ae4c91aSws return FSFATMOD;
3046ae4c91aSws }
3056ae4c91aSws return FSERROR;
3066ae4c91aSws }
307a2b5923eSlukem pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
3086ae4c91aSws cl, *cp1, *cp2, fatnum);
309d445160eSws if (ask(0, "Use continuation from FAT 0")) {
3106ae4c91aSws *cp2 = *cp1;
3116ae4c91aSws return FSFATMOD;
3126ae4c91aSws }
313a2b5923eSlukem if (ask(0, "Use continuation from FAT %u", fatnum)) {
3146ae4c91aSws *cp1 = *cp2;
3156ae4c91aSws return FSFATMOD;
3166ae4c91aSws }
3176ae4c91aSws return FSERROR;
3186ae4c91aSws }
3196ae4c91aSws
3206ae4c91aSws /*
3216ae4c91aSws * Compare two FAT copies in memory. Resolve any conflicts and merge them
3226ae4c91aSws * into the first one.
3236ae4c91aSws */
3246ae4c91aSws int
comparefat(struct bootblock * boot,struct fatEntry * first,struct fatEntry * second,u_int fatnum)32565e67723Sxtraeme comparefat(struct bootblock *boot, struct fatEntry *first,
326a2b5923eSlukem struct fatEntry *second, u_int fatnum)
3276ae4c91aSws {
3286ae4c91aSws cl_t cl;
3296ae4c91aSws int ret = FSOK;
3306ae4c91aSws
3316ae4c91aSws for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
3326ae4c91aSws if (first[cl].next != second[cl].next)
3336ae4c91aSws ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
3346ae4c91aSws return ret;
3356ae4c91aSws }
3366ae4c91aSws
3376ae4c91aSws void
clearchain(struct bootblock * boot,struct fatEntry * fat,cl_t head)33865e67723Sxtraeme clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
3396ae4c91aSws {
3406ae4c91aSws cl_t p, q;
3416ae4c91aSws
3426ae4c91aSws for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
3436ae4c91aSws if (fat[p].head != head)
3446ae4c91aSws break;
3456ae4c91aSws q = fat[p].next;
3466ae4c91aSws fat[p].next = fat[p].head = CLUST_FREE;
3476ae4c91aSws fat[p].length = 0;
3486ae4c91aSws }
3496ae4c91aSws }
3506ae4c91aSws
351c17a22a4Sws int
tryclear(struct bootblock * boot,struct fatEntry * fat,cl_t head,cl_t * truncp)35265e67723Sxtraeme tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
353c17a22a4Sws {
354c17a22a4Sws if (ask(0, "Clear chain starting at %u", head)) {
355c17a22a4Sws clearchain(boot, fat, head);
356c17a22a4Sws return FSFATMOD;
357c17a22a4Sws } else if (ask(0, "Truncate")) {
358be4e2ccbSchristos uint32_t len;
359be4e2ccbSchristos cl_t p;
360be4e2ccbSchristos
361be4e2ccbSchristos for (p = head, len = 0;
362be4e2ccbSchristos p >= CLUST_FIRST && p < boot->NumClusters;
363be4e2ccbSchristos p = fat[p].next, len++)
364be4e2ccbSchristos continue;
36525b98a10Smatt *truncp = CLUST_EOF;
366be4e2ccbSchristos fat[head].length = len;
367c17a22a4Sws return FSFATMOD;
368c17a22a4Sws } else
369c17a22a4Sws return FSERROR;
370c17a22a4Sws }
371c17a22a4Sws
3726ae4c91aSws /*
3736ae4c91aSws * Check a complete FAT in-memory for crosslinks
3746ae4c91aSws */
3756ae4c91aSws int
checkfat(struct bootblock * boot,struct fatEntry * fat)37665e67723Sxtraeme checkfat(struct bootblock *boot, struct fatEntry *fat)
3776ae4c91aSws {
378c17a22a4Sws cl_t head, p, h, n;
3796ae4c91aSws u_int len;
3806ae4c91aSws int ret = 0;
3816ae4c91aSws int conf;
3826ae4c91aSws
3836ae4c91aSws /*
3846ae4c91aSws * pass 1: figure out the cluster chains.
3856ae4c91aSws */
3866ae4c91aSws for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
387d445160eSws /* find next untravelled chain */
3886ae4c91aSws if (fat[head].head != 0 /* cluster already belongs to some chain */
389fdbcbfc2Sws || fat[head].next == CLUST_FREE
390fdbcbfc2Sws || fat[head].next == CLUST_BAD)
3916ae4c91aSws continue; /* skip it. */
3926ae4c91aSws
3936ae4c91aSws /* follow the chain and mark all clusters on the way */
3946ae4c91aSws for (len = 0, p = head;
395699f31f8Schristos p >= CLUST_FIRST && p < boot->NumClusters &&
396699f31f8Schristos fat[p].head != head;
3976ae4c91aSws p = fat[p].next) {
3986ae4c91aSws fat[p].head = head;
3996ae4c91aSws len++;
4006ae4c91aSws }
4016ae4c91aSws
4026ae4c91aSws /* the head record gets the length */
403d445160eSws fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
4046ae4c91aSws }
4056ae4c91aSws
4066ae4c91aSws /*
4076ae4c91aSws * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
4086ae4c91aSws * we didn't know the real start of the chain then - would have treated partial
4096ae4c91aSws * chains as interlinked with their main chain)
4106ae4c91aSws */
4116ae4c91aSws for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
412d445160eSws /* find next untravelled chain */
4136ae4c91aSws if (fat[head].head != head)
4146ae4c91aSws continue;
4156ae4c91aSws
4166ae4c91aSws /* follow the chain to its end (hopefully) */
41746875337Schristos for (len = fat[head].length, p = head;
418c17a22a4Sws (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
419c17a22a4Sws p = n)
42046875337Schristos if (fat[n].head != head || len-- < 2)
4216ae4c91aSws break;
422c17a22a4Sws if (n >= CLUST_EOFS)
4236ae4c91aSws continue;
4246ae4c91aSws
425c17a22a4Sws if (n == CLUST_FREE || n >= CLUST_RSRVD) {
426d445160eSws pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
427c17a22a4Sws head, rsrvdcltype(n));
42846875337Schristos clear:
429c17a22a4Sws ret |= tryclear(boot, fat, head, &fat[p].next);
4306ae4c91aSws continue;
4316ae4c91aSws }
432c17a22a4Sws if (n < CLUST_FIRST || n >= boot->NumClusters) {
433d445160eSws pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
434c17a22a4Sws head, n);
43546875337Schristos goto clear;
43646875337Schristos }
43746875337Schristos if (head == fat[n].head) {
43846875337Schristos pwarn("Cluster chain starting at %u loops at cluster %u\n",
43946875337Schristos
44046875337Schristos head, p);
44146875337Schristos goto clear;
4426ae4c91aSws }
443d445160eSws pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
444c17a22a4Sws head, fat[n].head, n);
445c17a22a4Sws conf = tryclear(boot, fat, head, &fat[p].next);
446c17a22a4Sws if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
4476ae4c91aSws if (conf == FSERROR) {
4486ae4c91aSws /*
4496ae4c91aSws * Transfer the common chain to the one not cleared above.
4506ae4c91aSws */
451c17a22a4Sws for (p = n;
452c17a22a4Sws p >= CLUST_FIRST && p < boot->NumClusters;
4536ae4c91aSws p = fat[p].next) {
4546ae4c91aSws if (h != fat[p].head) {
4556ae4c91aSws /*
4566ae4c91aSws * Have to reexamine this chain.
4576ae4c91aSws */
4586ae4c91aSws head--;
4596ae4c91aSws break;
4606ae4c91aSws }
4616ae4c91aSws fat[p].head = head;
4626ae4c91aSws }
4636ae4c91aSws }
4646ae4c91aSws clearchain(boot, fat, h);
4656ae4c91aSws conf |= FSFATMOD;
4666ae4c91aSws }
4676ae4c91aSws ret |= conf;
4686ae4c91aSws }
4696ae4c91aSws
4706ae4c91aSws return ret;
4716ae4c91aSws }
4726ae4c91aSws
4736ae4c91aSws /*
4746ae4c91aSws * Write out FATs encoding them from the internal format
4756ae4c91aSws */
4766ae4c91aSws int
writefat(int fs,struct bootblock * boot,struct fatEntry * fat,int correct_fat)47765e67723Sxtraeme writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
4786ae4c91aSws {
4796ae4c91aSws u_char *buffer, *p;
4806ae4c91aSws cl_t cl;
481a2b5923eSlukem u_int i;
482c4c1a29bSchristos size_t fatsz;
4836ae4c91aSws off_t off;
4846ae4c91aSws int ret = FSOK;
4856ae4c91aSws
4866ae4c91aSws buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
4876ae4c91aSws if (buffer == NULL) {
48815f3040fSchristos perr("No space for FAT sectors (%zu)", fatsz);
4896ae4c91aSws return FSFATAL;
4906ae4c91aSws }
4916ae4c91aSws memset(buffer, 0, fatsz);
4926ae4c91aSws boot->NumFree = 0;
493daa7d68fSws p = buffer;
4944ab49897Sjdolecek if (correct_fat) {
495d445160eSws *p++ = (u_char)boot->Media;
496d445160eSws *p++ = 0xff;
497d445160eSws *p++ = 0xff;
498d445160eSws switch (boot->ClustMask) {
499d445160eSws case CLUST16_MASK:
500d445160eSws *p++ = 0xff;
501d445160eSws break;
502d445160eSws case CLUST32_MASK:
503d445160eSws *p++ = 0x0f;
504d445160eSws *p++ = 0xff;
505d445160eSws *p++ = 0xff;
506d445160eSws *p++ = 0xff;
507d445160eSws *p++ = 0x0f;
508d445160eSws break;
509d445160eSws }
5104ab49897Sjdolecek } else {
5114ab49897Sjdolecek /* use same FAT signature as the old FAT has */
5124ab49897Sjdolecek int count;
5134ab49897Sjdolecek u_char *old_fat;
5144ab49897Sjdolecek
5154ab49897Sjdolecek switch (boot->ClustMask) {
5164ab49897Sjdolecek case CLUST32_MASK:
5174ab49897Sjdolecek count = 8;
5184ab49897Sjdolecek break;
5194ab49897Sjdolecek case CLUST16_MASK:
5204ab49897Sjdolecek count = 4;
5214ab49897Sjdolecek break;
5224ab49897Sjdolecek default:
5234ab49897Sjdolecek count = 3;
5244ab49897Sjdolecek break;
5254ab49897Sjdolecek }
5264ab49897Sjdolecek
5274ab49897Sjdolecek if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
5284ab49897Sjdolecek &old_fat)) {
5294ab49897Sjdolecek free(buffer);
5304ab49897Sjdolecek return FSFATAL;
5314ab49897Sjdolecek }
5324ab49897Sjdolecek
5334ab49897Sjdolecek memcpy(p, old_fat, count);
5344ab49897Sjdolecek free(old_fat);
5354ab49897Sjdolecek p += count;
5364ab49897Sjdolecek }
5374ab49897Sjdolecek
538daa7d68fSws for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
539d445160eSws switch (boot->ClustMask) {
540d445160eSws case CLUST32_MASK:
5416ae4c91aSws if (fat[cl].next == CLUST_FREE)
5426ae4c91aSws boot->NumFree++;
543d445160eSws *p++ = (u_char)fat[cl].next;
544d445160eSws *p++ = (u_char)(fat[cl].next >> 8);
545d445160eSws *p++ = (u_char)(fat[cl].next >> 16);
546d445160eSws *p &= 0xf0;
547d445160eSws *p++ |= (fat[cl].next >> 24)&0x0f;
548d445160eSws break;
549d445160eSws case CLUST16_MASK:
550d445160eSws if (fat[cl].next == CLUST_FREE)
551d445160eSws boot->NumFree++;
552d445160eSws *p++ = (u_char)fat[cl].next;
553d445160eSws *p++ = (u_char)(fat[cl].next >> 8);
554d445160eSws break;
555d445160eSws default:
5566ae4c91aSws if (fat[cl].next == CLUST_FREE)
5576ae4c91aSws boot->NumFree++;
558d445160eSws *p++ = (u_char)fat[cl].next;
559ffdedd86Schristos *p = (u_char)((fat[cl].next >> 8) & 0xf);
560d7660c36Schristos cl++;
561d7660c36Schristos if (cl >= boot->NumClusters)
562d7660c36Schristos break;
563d7660c36Schristos if (fat[cl].next == CLUST_FREE)
564d7660c36Schristos boot->NumFree++;
565*70318244Schristos *p++ |= (u_char)(fat[cl].next << 4);
566*70318244Schristos *p++ = (u_char)(fat[cl].next >> 4);
567d445160eSws break;
5686ae4c91aSws }
5696ae4c91aSws }
5706ae4c91aSws for (i = 0; i < boot->FATs; i++) {
5716ae4c91aSws off = boot->ResSectors + i * boot->FATsecs;
5726ae4c91aSws off *= boot->BytesPerSec;
5736ae4c91aSws if (lseek(fs, off, SEEK_SET) != off
574a2b5923eSlukem || (size_t)write(fs, buffer, fatsz) != fatsz) {
57515f3040fSchristos perr("Unable to write FAT");
5766ae4c91aSws ret = FSFATAL; /* Return immediately? XXX */
5776ae4c91aSws }
5786ae4c91aSws }
5796ae4c91aSws free(buffer);
5806ae4c91aSws return ret;
5816ae4c91aSws }
5826ae4c91aSws
5836ae4c91aSws /*
5846ae4c91aSws * Check a complete in-memory FAT for lost cluster chains
5856ae4c91aSws */
5866ae4c91aSws int
checklost(int dosfs,struct bootblock * boot,struct fatEntry * fat)58765e67723Sxtraeme checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
5886ae4c91aSws {
5896ae4c91aSws cl_t head;
5906ae4c91aSws int mod = FSOK;
591d445160eSws int ret;
5926ae4c91aSws
5936ae4c91aSws for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
594d445160eSws /* find next untravelled chain */
5956ae4c91aSws if (fat[head].head != head
5966ae4c91aSws || fat[head].next == CLUST_FREE
5976ae4c91aSws || (fat[head].next >= CLUST_RSRVD
59825e3d62eSws && fat[head].next < CLUST_EOFS)
59925e3d62eSws || (fat[head].flags & FAT_USED))
6006ae4c91aSws continue;
6016ae4c91aSws
602d445160eSws pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
6036ae4c91aSws head, fat[head].length);
604d445160eSws mod |= ret = reconnect(dosfs, boot, fat, head);
6056ae4c91aSws if (mod & FSFATAL)
6066ae4c91aSws break;
607d445160eSws if (ret == FSERROR && ask(0, "Clear")) {
608d445160eSws clearchain(boot, fat, head);
609d445160eSws mod |= FSFATMOD;
610d445160eSws }
6116ae4c91aSws }
6126ae4c91aSws finishlf();
6136ae4c91aSws
614d445160eSws if (boot->FSInfo) {
615d445160eSws ret = 0;
616da6f2443Sjakllsch if (boot->FSFree != 0xffffffffU &&
617da6f2443Sjakllsch boot->FSFree != boot->NumFree) {
618da6f2443Sjakllsch pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
619d445160eSws boot->FSFree, boot->NumFree);
620d445160eSws if (ask(1, "fix")) {
621d445160eSws boot->FSFree = boot->NumFree;
622d445160eSws ret = 1;
623d445160eSws }
624d445160eSws }
625ec5e5d47Sjakllsch if (boot->FSNext != 0xffffffffU &&
626ec5e5d47Sjakllsch (boot->FSNext >= boot->NumClusters ||
627ec5e5d47Sjakllsch (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) {
628f0f72818Smatthias pwarn("Next free cluster in FSInfo block (%u) %s\n",
629f0f72818Smatthias boot->FSNext,
630f0f72818Smatthias (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free");
631d445160eSws if (ask(1, "fix"))
632d445160eSws for (head = CLUST_FIRST; head < boot->NumClusters; head++)
633d445160eSws if (fat[head].next == CLUST_FREE) {
634d445160eSws boot->FSNext = head;
635d445160eSws ret = 1;
636d445160eSws break;
637d445160eSws }
638d445160eSws }
639d445160eSws if (ret)
640d445160eSws mod |= writefsinfo(dosfs, boot);
641d445160eSws }
642d445160eSws
6436ae4c91aSws return mod;
6446ae4c91aSws }
645