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 *
1479455Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
1579455Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1679455Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1779455Sobrien * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1879455Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1979455Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2079455Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2179455Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2279455Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2379455Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2479455Sobrien */
2579455Sobrien
2679455Sobrien
2779455Sobrien#include <sys/cdefs.h>
2879455Sobrien#ifndef lint
29241806Suqs__RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
3079455Sobrienstatic const char rcsid[] =
3179455Sobrien  "$FreeBSD$";
3279455Sobrien#endif /* not lint */
3379455Sobrien
3479455Sobrien#include <stdlib.h>
3579455Sobrien#include <string.h>
3679455Sobrien#include <ctype.h>
3779455Sobrien#include <stdio.h>
3879455Sobrien#include <unistd.h>
3979455Sobrien
4079455Sobrien#include "ext.h"
4179455Sobrien#include "fsutil.h"
4279455Sobrien
43203872Skibstatic int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
44203872Skibstatic int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
4592839Simpstatic int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
46203872Skibstatic int _readfat(int, struct bootblock *, u_int, u_char **);
4779455Sobrien
48125471Sbde/*-
49125471Sbde * The first 2 FAT entries contain pseudo-cluster numbers with the following
50125471Sbde * layout:
51125471Sbde *
52125471Sbde * 31...... ........ ........ .......0
53125471Sbde * rrrr1111 11111111 11111111 mmmmmmmm         FAT32 entry 0
54125471Sbde * rrrrsh11 11111111 11111111 11111xxx         FAT32 entry 1
55125471Sbde *
56125471Sbde *                   11111111 mmmmmmmm         FAT16 entry 0
57125471Sbde *                   sh111111 11111xxx         FAT16 entry 1
58125471Sbde *
59125471Sbde * r = reserved
60125471Sbde * m = BPB media ID byte
61125471Sbde * s = clean flag (1 = dismounted; 0 = still mounted)
62125471Sbde * h = hard error flag (1 = ok; 0 = I/O error)
63125471Sbde * x = any value ok
64125471Sbde */
65125471Sbde
66125469Sbdeint
67125469Sbdecheckdirty(int fs, struct bootblock *boot)
68125469Sbde{
69125469Sbde	off_t off;
70125469Sbde	u_char *buffer;
71125469Sbde	int ret = 0;
72241806Suqs	size_t len;
73125469Sbde
74125485Sbde	if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
75125469Sbde		return 0;
76125469Sbde
77203874Skib	off = boot->bpbResSectors;
78203874Skib	off *= boot->bpbBytesPerSec;
79125469Sbde
80241806Suqs	buffer = malloc(len = boot->bpbBytesPerSec);
81125469Sbde	if (buffer == NULL) {
82241806Suqs		perr("No space for FAT sectors (%zu)", len);
83125469Sbde		return 1;
84125469Sbde	}
85125469Sbde
86125469Sbde	if (lseek(fs, off, SEEK_SET) != off) {
87241806Suqs		perr("Unable to read FAT");
88125469Sbde		goto err;
89125469Sbde	}
90125469Sbde
91209364Sbrian	if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
92209364Sbrian	    boot->bpbBytesPerSec) {
93241806Suqs		perr("Unable to read FAT");
94125469Sbde		goto err;
95125469Sbde	}
96125469Sbde
97125485Sbde	/*
98125485Sbde	 * If we don't understand the FAT, then the file system must be
99125485Sbde	 * assumed to be unclean.
100125485Sbde	 */
101203874Skib	if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
102125485Sbde		goto err;
103125485Sbde	if (boot->ClustMask == CLUST16_MASK) {
104125485Sbde		if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
105125485Sbde			goto err;
106125485Sbde	} else {
107125485Sbde		if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
108125485Sbde		    || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
109125485Sbde		    || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
110125485Sbde			goto err;
111125485Sbde	}
112125469Sbde
113125485Sbde	/*
114125485Sbde	 * Now check the actual clean flag (and the no-error flag).
115125485Sbde	 */
116125485Sbde	if (boot->ClustMask == CLUST16_MASK) {
117125485Sbde		if ((buffer[3] & 0xc0) == 0xc0)
118125485Sbde			ret = 1;
119125485Sbde	} else {
120125485Sbde		if ((buffer[7] & 0x0c) == 0x0c)
121125485Sbde			ret = 1;
122125485Sbde	}
123125485Sbde
124125469Sbdeerr:
125125469Sbde	free(buffer);
126125469Sbde	return ret;
127125469Sbde}
128125469Sbde
12979455Sobrien/*
13079455Sobrien * Check a cluster number for valid value
13179455Sobrien */
13279455Sobrienstatic int
133203872Skibcheckclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
13479455Sobrien{
13579455Sobrien	if (*next >= (CLUST_RSRVD&boot->ClustMask))
13679455Sobrien		*next |= ~boot->ClustMask;
13779455Sobrien	if (*next == CLUST_FREE) {
13879455Sobrien		boot->NumFree++;
13979455Sobrien		return FSOK;
14079455Sobrien	}
14179455Sobrien	if (*next == CLUST_BAD) {
14279455Sobrien		boot->NumBad++;
14379455Sobrien		return FSOK;
14479455Sobrien	}
14579455Sobrien	if (*next < CLUST_FIRST
14679455Sobrien	    || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
14779455Sobrien		pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
14879455Sobrien		      cl, fat,
14979455Sobrien		      *next < CLUST_RSRVD ? "out of range" : "reserved",
15079455Sobrien		      *next&boot->ClustMask);
15179455Sobrien		if (ask(0, "Truncate")) {
15279455Sobrien			*next = CLUST_EOF;
15379455Sobrien			return FSFATMOD;
15479455Sobrien		}
15579455Sobrien		return FSERROR;
15679455Sobrien	}
15779455Sobrien	return FSOK;
15879455Sobrien}
15979455Sobrien
16079455Sobrien/*
16179455Sobrien * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
16279455Sobrien */
16379455Sobrienstatic int
164203872Skib_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
16579455Sobrien{
16679455Sobrien	off_t off;
167241806Suqs	size_t len;
16879455Sobrien
169241806Suqs	*buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec);
17079455Sobrien	if (*buffer == NULL) {
171241806Suqs		perr("No space for FAT sectors (%zu)", len);
17279455Sobrien		return 0;
17379455Sobrien	}
17479455Sobrien
175203874Skib	off = boot->bpbResSectors + no * boot->FATsecs;
176203874Skib	off *= boot->bpbBytesPerSec;
17779455Sobrien
17879455Sobrien	if (lseek(fs, off, SEEK_SET) != off) {
179241806Suqs		perr("Unable to read FAT");
18079455Sobrien		goto err;
18179455Sobrien	}
18279455Sobrien
183203874Skib	if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
184203874Skib	    != boot->FATsecs * boot->bpbBytesPerSec) {
185241806Suqs		perr("Unable to read FAT");
18679455Sobrien		goto err;
18779455Sobrien	}
18879455Sobrien
18979455Sobrien	return 1;
19079455Sobrien
19179455Sobrien    err:
19279455Sobrien	free(*buffer);
19379455Sobrien	return 0;
19479455Sobrien}
19579455Sobrien
19679455Sobrien/*
19779455Sobrien * Read a FAT and decode it into internal format
19879455Sobrien */
19979455Sobrienint
200203872Skibreadfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
20179455Sobrien{
20279455Sobrien	struct fatEntry *fat;
20379455Sobrien	u_char *buffer, *p;
20479455Sobrien	cl_t cl;
20579455Sobrien	int ret = FSOK;
206203872Skib	size_t len;
20779455Sobrien
20879455Sobrien	boot->NumFree = boot->NumBad = 0;
20979455Sobrien
21079455Sobrien	if (!_readfat(fs, boot, no, &buffer))
21179455Sobrien		return FSFATAL;
212241806Suqs
213203872Skib	fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
21479455Sobrien	if (fat == NULL) {
215241806Suqs		perr("No space for FAT clusters (%zu)", len);
21679455Sobrien		free(buffer);
21779455Sobrien		return FSFATAL;
21879455Sobrien	}
219203872Skib	(void)memset(fat, 0, len);
22079455Sobrien
221203874Skib	if (buffer[0] != boot->bpbMedia
22279455Sobrien	    || buffer[1] != 0xff || buffer[2] != 0xff
22379455Sobrien	    || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
22479455Sobrien	    || (boot->ClustMask == CLUST32_MASK
22579455Sobrien		&& ((buffer[3]&0x0f) != 0x0f
22679455Sobrien		    || buffer[4] != 0xff || buffer[5] != 0xff
22779455Sobrien		    || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
22879455Sobrien
22979455Sobrien		/* Windows 95 OSR2 (and possibly any later) changes
23079455Sobrien		 * the FAT signature to 0xXXffff7f for FAT16 and to
23179455Sobrien		 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
232102231Strhodes		 * file system is dirty if it doesn't reboot cleanly.
23379455Sobrien		 * Check this special condition before errorring out.
23479455Sobrien		 */
235203874Skib		if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
23679455Sobrien		    && buffer[2] == 0xff
23779455Sobrien		    && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
23879455Sobrien			|| (boot->ClustMask == CLUST32_MASK
23979455Sobrien			    && buffer[3] == 0x0f && buffer[4] == 0xff
24079455Sobrien			    && buffer[5] == 0xff && buffer[6] == 0xff
24179455Sobrien			    && buffer[7] == 0x07)))
24279455Sobrien			ret |= FSDIRTY;
24379455Sobrien		else {
24479455Sobrien			/* just some odd byte sequence in FAT */
245268784Spfg
24679455Sobrien			switch (boot->ClustMask) {
24779455Sobrien			case CLUST32_MASK:
24879455Sobrien				pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
24979455Sobrien				      "FAT starts with odd byte sequence",
25079455Sobrien				      buffer[0], buffer[1], buffer[2], buffer[3],
25179455Sobrien				      buffer[4], buffer[5], buffer[6], buffer[7]);
25279455Sobrien				break;
25379455Sobrien			case CLUST16_MASK:
25479455Sobrien				pwarn("%s (%02x%02x%02x%02x)\n",
25579455Sobrien				    "FAT starts with odd byte sequence",
25679455Sobrien				    buffer[0], buffer[1], buffer[2], buffer[3]);
25779455Sobrien				break;
25879455Sobrien			default:
25979455Sobrien				pwarn("%s (%02x%02x%02x)\n",
26079455Sobrien				    "FAT starts with odd byte sequence",
26179455Sobrien				    buffer[0], buffer[1], buffer[2]);
26279455Sobrien				break;
26379455Sobrien			}
26479455Sobrien
265268784Spfg
26679455Sobrien			if (ask(1, "Correct"))
26779455Sobrien				ret |= FSFIXFAT;
26879455Sobrien		}
26979455Sobrien	}
27079455Sobrien	switch (boot->ClustMask) {
27179455Sobrien	case CLUST32_MASK:
27279455Sobrien		p = buffer + 8;
27379455Sobrien		break;
27479455Sobrien	case CLUST16_MASK:
27579455Sobrien		p = buffer + 4;
27679455Sobrien		break;
27779455Sobrien	default:
27879455Sobrien		p = buffer + 3;
27979455Sobrien		break;
28079455Sobrien	}
28179455Sobrien	for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
28279455Sobrien		switch (boot->ClustMask) {
28379455Sobrien		case CLUST32_MASK:
28479455Sobrien			fat[cl].next = p[0] + (p[1] << 8)
28579455Sobrien				       + (p[2] << 16) + (p[3] << 24);
28679455Sobrien			fat[cl].next &= boot->ClustMask;
28779455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
28879455Sobrien			cl++;
28979455Sobrien			p += 4;
29079455Sobrien			break;
29179455Sobrien		case CLUST16_MASK:
29279455Sobrien			fat[cl].next = p[0] + (p[1] << 8);
29379455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
29479455Sobrien			cl++;
29579455Sobrien			p += 2;
29679455Sobrien			break;
29779455Sobrien		default:
29879455Sobrien			fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
29979455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
30079455Sobrien			cl++;
30179455Sobrien			if (cl >= boot->NumClusters)
30279455Sobrien				break;
30379455Sobrien			fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
30479455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
30579455Sobrien			cl++;
30679455Sobrien			p += 3;
30779455Sobrien			break;
30879455Sobrien		}
30979455Sobrien	}
31079455Sobrien
31179455Sobrien	free(buffer);
312203872Skib	if (ret & FSFATAL) {
313203872Skib		free(fat);
314203872Skib		*fp = NULL;
315203872Skib	} else
316203872Skib		*fp = fat;
31779455Sobrien	return ret;
31879455Sobrien}
31979455Sobrien
32079455Sobrien/*
32179455Sobrien * Get type of reserved cluster
32279455Sobrien */
323241807Suqsconst char *
32492839Simprsrvdcltype(cl_t cl)
32579455Sobrien{
32679455Sobrien	if (cl == CLUST_FREE)
32779455Sobrien		return "free";
32879455Sobrien	if (cl < CLUST_BAD)
32979455Sobrien		return "reserved";
33079455Sobrien	if (cl > CLUST_BAD)
33179455Sobrien		return "as EOF";
33279455Sobrien	return "bad";
33379455Sobrien}
33479455Sobrien
33579455Sobrienstatic int
336203872Skibclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
33779455Sobrien{
33879455Sobrien	if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
33979455Sobrien		if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
34079455Sobrien			if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
34179455Sobrien			     && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
34279455Sobrien			    || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
343175853Syar				pwarn("Cluster %u is marked %s with different indicators\n",
34479455Sobrien				      cl, rsrvdcltype(*cp1));
345175853Syar				if (ask(1, "Fix")) {
34679455Sobrien					*cp2 = *cp1;
34779455Sobrien					return FSFATMOD;
34879455Sobrien				}
34979455Sobrien				return FSFATAL;
35079455Sobrien			}
351203872Skib			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
35279455Sobrien			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
353175854Syar			if (ask(0, "Use FAT 0's entry")) {
35479455Sobrien				*cp2 = *cp1;
35579455Sobrien				return FSFATMOD;
35679455Sobrien			}
357203872Skib			if (ask(0, "Use FAT %u's entry", fatnum)) {
35879455Sobrien				*cp1 = *cp2;
35979455Sobrien				return FSFATMOD;
36079455Sobrien			}
36179455Sobrien			return FSFATAL;
36279455Sobrien		}
36379455Sobrien		pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
36479455Sobrien		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
365203872Skib		if (ask(0, "Use continuation from FAT %u", fatnum)) {
36679455Sobrien			*cp1 = *cp2;
36779455Sobrien			return FSFATMOD;
36879455Sobrien		}
36979455Sobrien		if (ask(0, "Use mark from FAT 0")) {
37079455Sobrien			*cp2 = *cp1;
37179455Sobrien			return FSFATMOD;
37279455Sobrien		}
37379455Sobrien		return FSFATAL;
37479455Sobrien	}
37579455Sobrien	if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
376203872Skib		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
37779455Sobrien		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
37879455Sobrien		if (ask(0, "Use continuation from FAT 0")) {
37979455Sobrien			*cp2 = *cp1;
38079455Sobrien			return FSFATMOD;
38179455Sobrien		}
38279455Sobrien		if (ask(0, "Use mark from FAT %d", fatnum)) {
38379455Sobrien			*cp1 = *cp2;
38479455Sobrien			return FSFATMOD;
38579455Sobrien		}
38679455Sobrien		return FSERROR;
38779455Sobrien	}
388203872Skib	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
38979455Sobrien	      cl, *cp1, *cp2, fatnum);
39079455Sobrien	if (ask(0, "Use continuation from FAT 0")) {
39179455Sobrien		*cp2 = *cp1;
39279455Sobrien		return FSFATMOD;
39379455Sobrien	}
394203872Skib	if (ask(0, "Use continuation from FAT %u", fatnum)) {
39579455Sobrien		*cp1 = *cp2;
39679455Sobrien		return FSFATMOD;
39779455Sobrien	}
39879455Sobrien	return FSERROR;
39979455Sobrien}
40079455Sobrien
40179455Sobrien/*
40279455Sobrien * Compare two FAT copies in memory. Resolve any conflicts and merge them
40379455Sobrien * into the first one.
40479455Sobrien */
40579455Sobrienint
406203872Skibcomparefat(struct bootblock *boot, struct fatEntry *first,
407203872Skib    struct fatEntry *second, u_int fatnum)
40879455Sobrien{
40979455Sobrien	cl_t cl;
41079455Sobrien	int ret = FSOK;
41179455Sobrien
41279455Sobrien	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
41379455Sobrien		if (first[cl].next != second[cl].next)
41479455Sobrien			ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
41579455Sobrien	return ret;
41679455Sobrien}
41779455Sobrien
41879455Sobrienvoid
41992839Simpclearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
42079455Sobrien{
42179455Sobrien	cl_t p, q;
42279455Sobrien
42379455Sobrien	for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
42479455Sobrien		if (fat[p].head != head)
42579455Sobrien			break;
42679455Sobrien		q = fat[p].next;
42779455Sobrien		fat[p].next = fat[p].head = CLUST_FREE;
42879455Sobrien		fat[p].length = 0;
42979455Sobrien	}
43079455Sobrien}
43179455Sobrien
43279455Sobrienint
433241806Suqstryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
43479455Sobrien{
43579455Sobrien	if (ask(0, "Clear chain starting at %u", head)) {
43679455Sobrien		clearchain(boot, fat, head);
43779455Sobrien		return FSFATMOD;
43879455Sobrien	} else if (ask(0, "Truncate")) {
439268968Spfg		uint32_t len;
440268968Spfg		cl_t p;
441268968Spfg
442268968Spfg		for (p = head, len = 0;
443268968Spfg		    p >= CLUST_FIRST && p < boot->NumClusters;
444268968Spfg		    p = fat[p].next, len++)
445268968Spfg			continue;
446241806Suqs		*truncp = CLUST_EOF;
447268968Spfg		fat[head].length = len;
44879455Sobrien		return FSFATMOD;
44979455Sobrien	} else
45079455Sobrien		return FSERROR;
45179455Sobrien}
45279455Sobrien
45379455Sobrien/*
45479455Sobrien * Check a complete FAT in-memory for crosslinks
45579455Sobrien */
45679455Sobrienint
45792839Simpcheckfat(struct bootblock *boot, struct fatEntry *fat)
45879455Sobrien{
45979455Sobrien	cl_t head, p, h, n;
46079455Sobrien	u_int len;
46179455Sobrien	int ret = 0;
46279455Sobrien	int conf;
46379455Sobrien
46479455Sobrien	/*
46579455Sobrien	 * pass 1: figure out the cluster chains.
46679455Sobrien	 */
46779455Sobrien	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
46879455Sobrien		/* find next untravelled chain */
46979455Sobrien		if (fat[head].head != 0		/* cluster already belongs to some chain */
47079455Sobrien		    || fat[head].next == CLUST_FREE
47179455Sobrien		    || fat[head].next == CLUST_BAD)
47279455Sobrien			continue;		/* skip it. */
47379455Sobrien
47479455Sobrien		/* follow the chain and mark all clusters on the way */
47579455Sobrien		for (len = 0, p = head;
476268968Spfg		     p >= CLUST_FIRST && p < boot->NumClusters &&
477268968Spfg		     fat[p].head != head;
47879455Sobrien		     p = fat[p].next) {
47979455Sobrien			fat[p].head = head;
48079455Sobrien			len++;
48179455Sobrien		}
48279455Sobrien
48379455Sobrien		/* the head record gets the length */
48479455Sobrien		fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
48579455Sobrien	}
48679455Sobrien
48779455Sobrien	/*
48879455Sobrien	 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
48979455Sobrien	 * we didn't know the real start of the chain then - would have treated partial
49079455Sobrien	 * chains as interlinked with their main chain)
49179455Sobrien	 */
49279455Sobrien	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
49379455Sobrien		/* find next untravelled chain */
49479455Sobrien		if (fat[head].head != head)
49579455Sobrien			continue;
49679455Sobrien
49779455Sobrien		/* follow the chain to its end (hopefully) */
498268968Spfg		for (len = fat[head].length, p = head;
49979455Sobrien		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
50079455Sobrien		     p = n)
501268968Spfg			if (fat[n].head != head || len-- < 2)
50279455Sobrien				break;
50379455Sobrien		if (n >= CLUST_EOFS)
50479455Sobrien			continue;
50579455Sobrien
50679455Sobrien		if (n == CLUST_FREE || n >= CLUST_RSRVD) {
50779455Sobrien			pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
50879455Sobrien			      head, rsrvdcltype(n));
509268968Spfgclear:
51079455Sobrien			ret |= tryclear(boot, fat, head, &fat[p].next);
51179455Sobrien			continue;
51279455Sobrien		}
51379455Sobrien		if (n < CLUST_FIRST || n >= boot->NumClusters) {
51479455Sobrien			pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
515268968Spfg			    head, n);
516268968Spfg			goto clear;
51779455Sobrien		}
518268968Spfg		if (head == fat[n].head) {
519268968Spfg			pwarn("Cluster chain starting at %u loops at cluster %u\n",
520268968Spfg
521268968Spfg			    head, p);
522268968Spfg			goto clear;
523268968Spfg		}
52479455Sobrien		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
52579455Sobrien		      head, fat[n].head, n);
52679455Sobrien		conf = tryclear(boot, fat, head, &fat[p].next);
52779455Sobrien		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
52879455Sobrien			if (conf == FSERROR) {
52979455Sobrien				/*
53079455Sobrien				 * Transfer the common chain to the one not cleared above.
53179455Sobrien				 */
53279455Sobrien				for (p = n;
53379455Sobrien				     p >= CLUST_FIRST && p < boot->NumClusters;
53479455Sobrien				     p = fat[p].next) {
53579455Sobrien					if (h != fat[p].head) {
53679455Sobrien						/*
53779455Sobrien						 * Have to reexamine this chain.
53879455Sobrien						 */
53979455Sobrien						head--;
54079455Sobrien						break;
54179455Sobrien					}
54279455Sobrien					fat[p].head = head;
54379455Sobrien				}
54479455Sobrien			}
54579455Sobrien			clearchain(boot, fat, h);
54679455Sobrien			conf |= FSFATMOD;
54779455Sobrien		}
54879455Sobrien		ret |= conf;
54979455Sobrien	}
55079455Sobrien
55179455Sobrien	return ret;
55279455Sobrien}
55379455Sobrien
55479455Sobrien/*
55579455Sobrien * Write out FATs encoding them from the internal format
55679455Sobrien */
55779455Sobrienint
55892839Simpwritefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
55979455Sobrien{
56079455Sobrien	u_char *buffer, *p;
56179455Sobrien	cl_t cl;
562203872Skib	u_int i;
563203872Skib	size_t fatsz;
56479455Sobrien	off_t off;
56579455Sobrien	int ret = FSOK;
56679455Sobrien
567203874Skib	buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec);
56879455Sobrien	if (buffer == NULL) {
569241806Suqs		perr("No space for FAT sectors (%zu)", fatsz);
57079455Sobrien		return FSFATAL;
57179455Sobrien	}
57279455Sobrien	memset(buffer, 0, fatsz);
57379455Sobrien	boot->NumFree = 0;
57479455Sobrien	p = buffer;
57579455Sobrien	if (correct_fat) {
576203874Skib		*p++ = (u_char)boot->bpbMedia;
57779455Sobrien		*p++ = 0xff;
57879455Sobrien		*p++ = 0xff;
57979455Sobrien		switch (boot->ClustMask) {
58079455Sobrien		case CLUST16_MASK:
58179455Sobrien			*p++ = 0xff;
58279455Sobrien			break;
58379455Sobrien		case CLUST32_MASK:
58479455Sobrien			*p++ = 0x0f;
58579455Sobrien			*p++ = 0xff;
58679455Sobrien			*p++ = 0xff;
58779455Sobrien			*p++ = 0xff;
58879455Sobrien			*p++ = 0x0f;
58979455Sobrien			break;
59079455Sobrien		}
59179455Sobrien	} else {
59279455Sobrien		/* use same FAT signature as the old FAT has */
59379455Sobrien		int count;
59479455Sobrien		u_char *old_fat;
59579455Sobrien
59679455Sobrien		switch (boot->ClustMask) {
59779455Sobrien		case CLUST32_MASK:
59879455Sobrien			count = 8;
59979455Sobrien			break;
60079455Sobrien		case CLUST16_MASK:
60179455Sobrien			count = 4;
60279455Sobrien			break;
60379455Sobrien		default:
60479455Sobrien			count = 3;
60579455Sobrien			break;
60679455Sobrien		}
60779455Sobrien
60879455Sobrien		if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
60979455Sobrien					 &old_fat)) {
61079455Sobrien			free(buffer);
61179455Sobrien			return FSFATAL;
61279455Sobrien		}
61379455Sobrien
61479455Sobrien		memcpy(p, old_fat, count);
61579455Sobrien		free(old_fat);
61679455Sobrien		p += count;
61779455Sobrien	}
618241806Suqs
61979455Sobrien	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
62079455Sobrien		switch (boot->ClustMask) {
62179455Sobrien		case CLUST32_MASK:
62279455Sobrien			if (fat[cl].next == CLUST_FREE)
62379455Sobrien				boot->NumFree++;
62479455Sobrien			*p++ = (u_char)fat[cl].next;
62579455Sobrien			*p++ = (u_char)(fat[cl].next >> 8);
62679455Sobrien			*p++ = (u_char)(fat[cl].next >> 16);
62779455Sobrien			*p &= 0xf0;
62879455Sobrien			*p++ |= (fat[cl].next >> 24)&0x0f;
62979455Sobrien			break;
63079455Sobrien		case CLUST16_MASK:
63179455Sobrien			if (fat[cl].next == CLUST_FREE)
63279455Sobrien				boot->NumFree++;
63379455Sobrien			*p++ = (u_char)fat[cl].next;
63479455Sobrien			*p++ = (u_char)(fat[cl].next >> 8);
63579455Sobrien			break;
63679455Sobrien		default:
63779455Sobrien			if (fat[cl].next == CLUST_FREE)
63879455Sobrien				boot->NumFree++;
639268968Spfg			*p++ = (u_char)fat[cl].next;
640268968Spfg			*p = (u_char)((fat[cl].next >> 8) & 0xf);
641268968Spfg			cl++;
642268968Spfg			if (cl >= boot->NumClusters)
643268968Spfg				break;
644268968Spfg			if (fat[cl].next == CLUST_FREE)
64579455Sobrien				boot->NumFree++;
646268968Spfg			*p++ |= (u_char)(fat[cl + 1].next << 4);
647268968Spfg			*p++ = (u_char)(fat[cl + 1].next >> 4);
64879455Sobrien			break;
64979455Sobrien		}
65079455Sobrien	}
651203874Skib	for (i = 0; i < boot->bpbFATs; i++) {
652203874Skib		off = boot->bpbResSectors + i * boot->FATsecs;
653203874Skib		off *= boot->bpbBytesPerSec;
65479455Sobrien		if (lseek(fs, off, SEEK_SET) != off
655203872Skib		    || (size_t)write(fs, buffer, fatsz) != fatsz) {
656241806Suqs			perr("Unable to write FAT");
65779455Sobrien			ret = FSFATAL; /* Return immediately?		XXX */
65879455Sobrien		}
65979455Sobrien	}
66079455Sobrien	free(buffer);
66179455Sobrien	return ret;
66279455Sobrien}
66379455Sobrien
66479455Sobrien/*
66579455Sobrien * Check a complete in-memory FAT for lost cluster chains
66679455Sobrien */
66779455Sobrienint
66892839Simpchecklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
66979455Sobrien{
67079455Sobrien	cl_t head;
67179455Sobrien	int mod = FSOK;
67279455Sobrien	int ret;
673268784Spfg
67479455Sobrien	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
67579455Sobrien		/* find next untravelled chain */
67679455Sobrien		if (fat[head].head != head
67779455Sobrien		    || fat[head].next == CLUST_FREE
67879455Sobrien		    || (fat[head].next >= CLUST_RSRVD
67979455Sobrien			&& fat[head].next < CLUST_EOFS)
68079455Sobrien		    || (fat[head].flags & FAT_USED))
68179455Sobrien			continue;
68279455Sobrien
68379455Sobrien		pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
68479455Sobrien		      head, fat[head].length);
68579455Sobrien		mod |= ret = reconnect(dosfs, boot, fat, head);
68679455Sobrien		if (mod & FSFATAL)
68779455Sobrien			break;
68879455Sobrien		if (ret == FSERROR && ask(0, "Clear")) {
68979455Sobrien			clearchain(boot, fat, head);
69079455Sobrien			mod |= FSFATMOD;
69179455Sobrien		}
69279455Sobrien	}
69379455Sobrien	finishlf();
69479455Sobrien
695203874Skib	if (boot->bpbFSInfo) {
69679455Sobrien		ret = 0;
697268784Spfg		if (boot->FSFree != 0xffffffffU &&
698268784Spfg		    boot->FSFree != boot->NumFree) {
699268784Spfg			pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
70079455Sobrien			      boot->FSFree, boot->NumFree);
701175853Syar			if (ask(1, "Fix")) {
70279455Sobrien				boot->FSFree = boot->NumFree;
70379455Sobrien				ret = 1;
70479455Sobrien			}
70579455Sobrien		}
70679455Sobrien		if (ret)
70779455Sobrien			mod |= writefsinfo(dosfs, boot);
70879455Sobrien	}
70979455Sobrien
71079455Sobrien	return mod;
71179455Sobrien}
712