fat.c revision 209364
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
2979455Sobrien__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $");
3079455Sobrienstatic const char rcsid[] =
3179455Sobrien  "$FreeBSD: head/sbin/fsck_msdosfs/fat.c 209364 2010-06-20 09:40:54Z brian $";
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;
72125469Sbde
73125485Sbde	if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
74125469Sbde		return 0;
75125469Sbde
76203874Skib	off = boot->bpbResSectors;
77203874Skib	off *= boot->bpbBytesPerSec;
78125469Sbde
79203874Skib	buffer = malloc(boot->bpbBytesPerSec);
80125469Sbde	if (buffer == NULL) {
81125469Sbde		perror("No space for FAT");
82125469Sbde		return 1;
83125469Sbde	}
84125469Sbde
85125469Sbde	if (lseek(fs, off, SEEK_SET) != off) {
86125469Sbde		perror("Unable to read FAT");
87125469Sbde		goto err;
88125469Sbde	}
89125469Sbde
90209364Sbrian	if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
91209364Sbrian	    boot->bpbBytesPerSec) {
92125469Sbde		perror("Unable to read FAT");
93125469Sbde		goto err;
94125469Sbde	}
95125469Sbde
96125485Sbde	/*
97125485Sbde	 * If we don't understand the FAT, then the file system must be
98125485Sbde	 * assumed to be unclean.
99125485Sbde	 */
100203874Skib	if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
101125485Sbde		goto err;
102125485Sbde	if (boot->ClustMask == CLUST16_MASK) {
103125485Sbde		if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
104125485Sbde			goto err;
105125485Sbde	} else {
106125485Sbde		if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
107125485Sbde		    || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
108125485Sbde		    || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
109125485Sbde			goto err;
110125485Sbde	}
111125469Sbde
112125485Sbde	/*
113125485Sbde	 * Now check the actual clean flag (and the no-error flag).
114125485Sbde	 */
115125485Sbde	if (boot->ClustMask == CLUST16_MASK) {
116125485Sbde		if ((buffer[3] & 0xc0) == 0xc0)
117125485Sbde			ret = 1;
118125485Sbde	} else {
119125485Sbde		if ((buffer[7] & 0x0c) == 0x0c)
120125485Sbde			ret = 1;
121125485Sbde	}
122125485Sbde
123125469Sbdeerr:
124125469Sbde	free(buffer);
125125469Sbde	return ret;
126125469Sbde}
127125469Sbde
12879455Sobrien/*
12979455Sobrien * Check a cluster number for valid value
13079455Sobrien */
13179455Sobrienstatic int
132203872Skibcheckclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
13379455Sobrien{
13479455Sobrien	if (*next >= (CLUST_RSRVD&boot->ClustMask))
13579455Sobrien		*next |= ~boot->ClustMask;
13679455Sobrien	if (*next == CLUST_FREE) {
13779455Sobrien		boot->NumFree++;
13879455Sobrien		return FSOK;
13979455Sobrien	}
14079455Sobrien	if (*next == CLUST_BAD) {
14179455Sobrien		boot->NumBad++;
14279455Sobrien		return FSOK;
14379455Sobrien	}
14479455Sobrien	if (*next < CLUST_FIRST
14579455Sobrien	    || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
14679455Sobrien		pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
14779455Sobrien		      cl, fat,
14879455Sobrien		      *next < CLUST_RSRVD ? "out of range" : "reserved",
14979455Sobrien		      *next&boot->ClustMask);
15079455Sobrien		if (ask(0, "Truncate")) {
15179455Sobrien			*next = CLUST_EOF;
15279455Sobrien			return FSFATMOD;
15379455Sobrien		}
15479455Sobrien		return FSERROR;
15579455Sobrien	}
15679455Sobrien	return FSOK;
15779455Sobrien}
15879455Sobrien
15979455Sobrien/*
16079455Sobrien * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
16179455Sobrien */
16279455Sobrienstatic int
163203872Skib_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
16479455Sobrien{
16579455Sobrien	off_t off;
16679455Sobrien
167203874Skib	*buffer = malloc(boot->FATsecs * boot->bpbBytesPerSec);
16879455Sobrien	if (*buffer == NULL) {
16979455Sobrien		perror("No space for FAT");
17079455Sobrien		return 0;
17179455Sobrien	}
17279455Sobrien
173203874Skib	off = boot->bpbResSectors + no * boot->FATsecs;
174203874Skib	off *= boot->bpbBytesPerSec;
17579455Sobrien
17679455Sobrien	if (lseek(fs, off, SEEK_SET) != off) {
17779455Sobrien		perror("Unable to read FAT");
17879455Sobrien		goto err;
17979455Sobrien	}
18079455Sobrien
181203874Skib	if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
182203874Skib	    != boot->FATsecs * boot->bpbBytesPerSec) {
18379455Sobrien		perror("Unable to read FAT");
18479455Sobrien		goto err;
18579455Sobrien	}
18679455Sobrien
18779455Sobrien	return 1;
18879455Sobrien
18979455Sobrien    err:
19079455Sobrien	free(*buffer);
19179455Sobrien	return 0;
19279455Sobrien}
19379455Sobrien
19479455Sobrien/*
19579455Sobrien * Read a FAT and decode it into internal format
19679455Sobrien */
19779455Sobrienint
198203872Skibreadfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
19979455Sobrien{
20079455Sobrien	struct fatEntry *fat;
20179455Sobrien	u_char *buffer, *p;
20279455Sobrien	cl_t cl;
20379455Sobrien	int ret = FSOK;
204203872Skib	size_t len;
20579455Sobrien
20679455Sobrien	boot->NumFree = boot->NumBad = 0;
20779455Sobrien
20879455Sobrien	if (!_readfat(fs, boot, no, &buffer))
20979455Sobrien		return FSFATAL;
21079455Sobrien
211203872Skib	fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
21279455Sobrien	if (fat == NULL) {
21379455Sobrien		perror("No space for FAT");
21479455Sobrien		free(buffer);
21579455Sobrien		return FSFATAL;
21679455Sobrien	}
217203872Skib	(void)memset(fat, 0, len);
21879455Sobrien
219203874Skib	if (buffer[0] != boot->bpbMedia
22079455Sobrien	    || buffer[1] != 0xff || buffer[2] != 0xff
22179455Sobrien	    || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
22279455Sobrien	    || (boot->ClustMask == CLUST32_MASK
22379455Sobrien		&& ((buffer[3]&0x0f) != 0x0f
22479455Sobrien		    || buffer[4] != 0xff || buffer[5] != 0xff
22579455Sobrien		    || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
22679455Sobrien
22779455Sobrien		/* Windows 95 OSR2 (and possibly any later) changes
22879455Sobrien		 * the FAT signature to 0xXXffff7f for FAT16 and to
22979455Sobrien		 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
230102231Strhodes		 * file system is dirty if it doesn't reboot cleanly.
23179455Sobrien		 * Check this special condition before errorring out.
23279455Sobrien		 */
233203874Skib		if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
23479455Sobrien		    && buffer[2] == 0xff
23579455Sobrien		    && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
23679455Sobrien			|| (boot->ClustMask == CLUST32_MASK
23779455Sobrien			    && buffer[3] == 0x0f && buffer[4] == 0xff
23879455Sobrien			    && buffer[5] == 0xff && buffer[6] == 0xff
23979455Sobrien			    && buffer[7] == 0x07)))
24079455Sobrien			ret |= FSDIRTY;
24179455Sobrien		else {
24279455Sobrien			/* just some odd byte sequence in FAT */
24379455Sobrien
24479455Sobrien			switch (boot->ClustMask) {
24579455Sobrien			case CLUST32_MASK:
24679455Sobrien				pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
24779455Sobrien				      "FAT starts with odd byte sequence",
24879455Sobrien				      buffer[0], buffer[1], buffer[2], buffer[3],
24979455Sobrien				      buffer[4], buffer[5], buffer[6], buffer[7]);
25079455Sobrien				break;
25179455Sobrien			case CLUST16_MASK:
25279455Sobrien				pwarn("%s (%02x%02x%02x%02x)\n",
25379455Sobrien				    "FAT starts with odd byte sequence",
25479455Sobrien				    buffer[0], buffer[1], buffer[2], buffer[3]);
25579455Sobrien				break;
25679455Sobrien			default:
25779455Sobrien				pwarn("%s (%02x%02x%02x)\n",
25879455Sobrien				    "FAT starts with odd byte sequence",
25979455Sobrien				    buffer[0], buffer[1], buffer[2]);
26079455Sobrien				break;
26179455Sobrien			}
26279455Sobrien
26379455Sobrien
26479455Sobrien			if (ask(1, "Correct"))
26579455Sobrien				ret |= FSFIXFAT;
26679455Sobrien		}
26779455Sobrien	}
26879455Sobrien	switch (boot->ClustMask) {
26979455Sobrien	case CLUST32_MASK:
27079455Sobrien		p = buffer + 8;
27179455Sobrien		break;
27279455Sobrien	case CLUST16_MASK:
27379455Sobrien		p = buffer + 4;
27479455Sobrien		break;
27579455Sobrien	default:
27679455Sobrien		p = buffer + 3;
27779455Sobrien		break;
27879455Sobrien	}
27979455Sobrien	for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
28079455Sobrien		switch (boot->ClustMask) {
28179455Sobrien		case CLUST32_MASK:
28279455Sobrien			fat[cl].next = p[0] + (p[1] << 8)
28379455Sobrien				       + (p[2] << 16) + (p[3] << 24);
28479455Sobrien			fat[cl].next &= boot->ClustMask;
28579455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
28679455Sobrien			cl++;
28779455Sobrien			p += 4;
28879455Sobrien			break;
28979455Sobrien		case CLUST16_MASK:
29079455Sobrien			fat[cl].next = p[0] + (p[1] << 8);
29179455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
29279455Sobrien			cl++;
29379455Sobrien			p += 2;
29479455Sobrien			break;
29579455Sobrien		default:
29679455Sobrien			fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
29779455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
29879455Sobrien			cl++;
29979455Sobrien			if (cl >= boot->NumClusters)
30079455Sobrien				break;
30179455Sobrien			fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
30279455Sobrien			ret |= checkclnum(boot, no, cl, &fat[cl].next);
30379455Sobrien			cl++;
30479455Sobrien			p += 3;
30579455Sobrien			break;
30679455Sobrien		}
30779455Sobrien	}
30879455Sobrien
30979455Sobrien	free(buffer);
310203872Skib	if (ret & FSFATAL) {
311203872Skib		free(fat);
312203872Skib		*fp = NULL;
313203872Skib	} else
314203872Skib		*fp = fat;
31579455Sobrien	return ret;
31679455Sobrien}
31779455Sobrien
31879455Sobrien/*
31979455Sobrien * Get type of reserved cluster
32079455Sobrien */
32179455Sobrienchar *
32292839Simprsrvdcltype(cl_t cl)
32379455Sobrien{
32479455Sobrien	if (cl == CLUST_FREE)
32579455Sobrien		return "free";
32679455Sobrien	if (cl < CLUST_BAD)
32779455Sobrien		return "reserved";
32879455Sobrien	if (cl > CLUST_BAD)
32979455Sobrien		return "as EOF";
33079455Sobrien	return "bad";
33179455Sobrien}
33279455Sobrien
33379455Sobrienstatic int
334203872Skibclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
33579455Sobrien{
33679455Sobrien	if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
33779455Sobrien		if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
33879455Sobrien			if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
33979455Sobrien			     && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
34079455Sobrien			    || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
341175853Syar				pwarn("Cluster %u is marked %s with different indicators\n",
34279455Sobrien				      cl, rsrvdcltype(*cp1));
343175853Syar				if (ask(1, "Fix")) {
34479455Sobrien					*cp2 = *cp1;
34579455Sobrien					return FSFATMOD;
34679455Sobrien				}
34779455Sobrien				return FSFATAL;
34879455Sobrien			}
349203872Skib			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
35079455Sobrien			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
351175854Syar			if (ask(0, "Use FAT 0's entry")) {
35279455Sobrien				*cp2 = *cp1;
35379455Sobrien				return FSFATMOD;
35479455Sobrien			}
355203872Skib			if (ask(0, "Use FAT %u's entry", fatnum)) {
35679455Sobrien				*cp1 = *cp2;
35779455Sobrien				return FSFATMOD;
35879455Sobrien			}
35979455Sobrien			return FSFATAL;
36079455Sobrien		}
36179455Sobrien		pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
36279455Sobrien		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
363203872Skib		if (ask(0, "Use continuation from FAT %u", fatnum)) {
36479455Sobrien			*cp1 = *cp2;
36579455Sobrien			return FSFATMOD;
36679455Sobrien		}
36779455Sobrien		if (ask(0, "Use mark from FAT 0")) {
36879455Sobrien			*cp2 = *cp1;
36979455Sobrien			return FSFATMOD;
37079455Sobrien		}
37179455Sobrien		return FSFATAL;
37279455Sobrien	}
37379455Sobrien	if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
374203872Skib		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
37579455Sobrien		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
37679455Sobrien		if (ask(0, "Use continuation from FAT 0")) {
37779455Sobrien			*cp2 = *cp1;
37879455Sobrien			return FSFATMOD;
37979455Sobrien		}
38079455Sobrien		if (ask(0, "Use mark from FAT %d", fatnum)) {
38179455Sobrien			*cp1 = *cp2;
38279455Sobrien			return FSFATMOD;
38379455Sobrien		}
38479455Sobrien		return FSERROR;
38579455Sobrien	}
386203872Skib	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
38779455Sobrien	      cl, *cp1, *cp2, fatnum);
38879455Sobrien	if (ask(0, "Use continuation from FAT 0")) {
38979455Sobrien		*cp2 = *cp1;
39079455Sobrien		return FSFATMOD;
39179455Sobrien	}
392203872Skib	if (ask(0, "Use continuation from FAT %u", fatnum)) {
39379455Sobrien		*cp1 = *cp2;
39479455Sobrien		return FSFATMOD;
39579455Sobrien	}
39679455Sobrien	return FSERROR;
39779455Sobrien}
39879455Sobrien
39979455Sobrien/*
40079455Sobrien * Compare two FAT copies in memory. Resolve any conflicts and merge them
40179455Sobrien * into the first one.
40279455Sobrien */
40379455Sobrienint
404203872Skibcomparefat(struct bootblock *boot, struct fatEntry *first,
405203872Skib    struct fatEntry *second, u_int fatnum)
40679455Sobrien{
40779455Sobrien	cl_t cl;
40879455Sobrien	int ret = FSOK;
40979455Sobrien
41079455Sobrien	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
41179455Sobrien		if (first[cl].next != second[cl].next)
41279455Sobrien			ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
41379455Sobrien	return ret;
41479455Sobrien}
41579455Sobrien
41679455Sobrienvoid
41792839Simpclearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
41879455Sobrien{
41979455Sobrien	cl_t p, q;
42079455Sobrien
42179455Sobrien	for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
42279455Sobrien		if (fat[p].head != head)
42379455Sobrien			break;
42479455Sobrien		q = fat[p].next;
42579455Sobrien		fat[p].next = fat[p].head = CLUST_FREE;
42679455Sobrien		fat[p].length = 0;
42779455Sobrien	}
42879455Sobrien}
42979455Sobrien
43079455Sobrienint
43192839Simptryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc)
43279455Sobrien{
43379455Sobrien	if (ask(0, "Clear chain starting at %u", head)) {
43479455Sobrien		clearchain(boot, fat, head);
43579455Sobrien		return FSFATMOD;
43679455Sobrien	} else if (ask(0, "Truncate")) {
43779455Sobrien		*trunc = CLUST_EOF;
43879455Sobrien		return FSFATMOD;
43979455Sobrien	} else
44079455Sobrien		return FSERROR;
44179455Sobrien}
44279455Sobrien
44379455Sobrien/*
44479455Sobrien * Check a complete FAT in-memory for crosslinks
44579455Sobrien */
44679455Sobrienint
44792839Simpcheckfat(struct bootblock *boot, struct fatEntry *fat)
44879455Sobrien{
44979455Sobrien	cl_t head, p, h, n;
45079455Sobrien	u_int len;
45179455Sobrien	int ret = 0;
45279455Sobrien	int conf;
45379455Sobrien
45479455Sobrien	/*
45579455Sobrien	 * pass 1: figure out the cluster chains.
45679455Sobrien	 */
45779455Sobrien	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
45879455Sobrien		/* find next untravelled chain */
45979455Sobrien		if (fat[head].head != 0		/* cluster already belongs to some chain */
46079455Sobrien		    || fat[head].next == CLUST_FREE
46179455Sobrien		    || fat[head].next == CLUST_BAD)
46279455Sobrien			continue;		/* skip it. */
46379455Sobrien
46479455Sobrien		/* follow the chain and mark all clusters on the way */
46579455Sobrien		for (len = 0, p = head;
46679455Sobrien		     p >= CLUST_FIRST && p < boot->NumClusters;
46779455Sobrien		     p = fat[p].next) {
46879455Sobrien			fat[p].head = head;
46979455Sobrien			len++;
47079455Sobrien		}
47179455Sobrien
47279455Sobrien		/* the head record gets the length */
47379455Sobrien		fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
47479455Sobrien	}
47579455Sobrien
47679455Sobrien	/*
47779455Sobrien	 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
47879455Sobrien	 * we didn't know the real start of the chain then - would have treated partial
47979455Sobrien	 * chains as interlinked with their main chain)
48079455Sobrien	 */
48179455Sobrien	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
48279455Sobrien		/* find next untravelled chain */
48379455Sobrien		if (fat[head].head != head)
48479455Sobrien			continue;
48579455Sobrien
48679455Sobrien		/* follow the chain to its end (hopefully) */
48779455Sobrien		for (p = head;
48879455Sobrien		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
48979455Sobrien		     p = n)
49079455Sobrien			if (fat[n].head != head)
49179455Sobrien				break;
49279455Sobrien		if (n >= CLUST_EOFS)
49379455Sobrien			continue;
49479455Sobrien
49579455Sobrien		if (n == CLUST_FREE || n >= CLUST_RSRVD) {
49679455Sobrien			pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
49779455Sobrien			      head, rsrvdcltype(n));
49879455Sobrien			ret |= tryclear(boot, fat, head, &fat[p].next);
49979455Sobrien			continue;
50079455Sobrien		}
50179455Sobrien		if (n < CLUST_FIRST || n >= boot->NumClusters) {
50279455Sobrien			pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
50379455Sobrien			      head, n);
50479455Sobrien			ret |= tryclear(boot, fat, head, &fat[p].next);
50579455Sobrien			continue;
50679455Sobrien		}
50779455Sobrien		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
50879455Sobrien		      head, fat[n].head, n);
50979455Sobrien		conf = tryclear(boot, fat, head, &fat[p].next);
51079455Sobrien		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
51179455Sobrien			if (conf == FSERROR) {
51279455Sobrien				/*
51379455Sobrien				 * Transfer the common chain to the one not cleared above.
51479455Sobrien				 */
51579455Sobrien				for (p = n;
51679455Sobrien				     p >= CLUST_FIRST && p < boot->NumClusters;
51779455Sobrien				     p = fat[p].next) {
51879455Sobrien					if (h != fat[p].head) {
51979455Sobrien						/*
52079455Sobrien						 * Have to reexamine this chain.
52179455Sobrien						 */
52279455Sobrien						head--;
52379455Sobrien						break;
52479455Sobrien					}
52579455Sobrien					fat[p].head = head;
52679455Sobrien				}
52779455Sobrien			}
52879455Sobrien			clearchain(boot, fat, h);
52979455Sobrien			conf |= FSFATMOD;
53079455Sobrien		}
53179455Sobrien		ret |= conf;
53279455Sobrien	}
53379455Sobrien
53479455Sobrien	return ret;
53579455Sobrien}
53679455Sobrien
53779455Sobrien/*
53879455Sobrien * Write out FATs encoding them from the internal format
53979455Sobrien */
54079455Sobrienint
54192839Simpwritefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
54279455Sobrien{
54379455Sobrien	u_char *buffer, *p;
54479455Sobrien	cl_t cl;
545203872Skib	u_int i;
546203872Skib	size_t fatsz;
54779455Sobrien	off_t off;
54879455Sobrien	int ret = FSOK;
54979455Sobrien
550203874Skib	buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec);
55179455Sobrien	if (buffer == NULL) {
55279455Sobrien		perror("No space for FAT");
55379455Sobrien		return FSFATAL;
55479455Sobrien	}
55579455Sobrien	memset(buffer, 0, fatsz);
55679455Sobrien	boot->NumFree = 0;
55779455Sobrien	p = buffer;
55879455Sobrien	if (correct_fat) {
559203874Skib		*p++ = (u_char)boot->bpbMedia;
56079455Sobrien		*p++ = 0xff;
56179455Sobrien		*p++ = 0xff;
56279455Sobrien		switch (boot->ClustMask) {
56379455Sobrien		case CLUST16_MASK:
56479455Sobrien			*p++ = 0xff;
56579455Sobrien			break;
56679455Sobrien		case CLUST32_MASK:
56779455Sobrien			*p++ = 0x0f;
56879455Sobrien			*p++ = 0xff;
56979455Sobrien			*p++ = 0xff;
57079455Sobrien			*p++ = 0xff;
57179455Sobrien			*p++ = 0x0f;
57279455Sobrien			break;
57379455Sobrien		}
57479455Sobrien	} else {
57579455Sobrien		/* use same FAT signature as the old FAT has */
57679455Sobrien		int count;
57779455Sobrien		u_char *old_fat;
57879455Sobrien
57979455Sobrien		switch (boot->ClustMask) {
58079455Sobrien		case CLUST32_MASK:
58179455Sobrien			count = 8;
58279455Sobrien			break;
58379455Sobrien		case CLUST16_MASK:
58479455Sobrien			count = 4;
58579455Sobrien			break;
58679455Sobrien		default:
58779455Sobrien			count = 3;
58879455Sobrien			break;
58979455Sobrien		}
59079455Sobrien
59179455Sobrien		if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
59279455Sobrien					 &old_fat)) {
59379455Sobrien			free(buffer);
59479455Sobrien			return FSFATAL;
59579455Sobrien		}
59679455Sobrien
59779455Sobrien		memcpy(p, old_fat, count);
59879455Sobrien		free(old_fat);
59979455Sobrien		p += count;
60079455Sobrien	}
60179455Sobrien
60279455Sobrien	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
60379455Sobrien		switch (boot->ClustMask) {
60479455Sobrien		case CLUST32_MASK:
60579455Sobrien			if (fat[cl].next == CLUST_FREE)
60679455Sobrien				boot->NumFree++;
60779455Sobrien			*p++ = (u_char)fat[cl].next;
60879455Sobrien			*p++ = (u_char)(fat[cl].next >> 8);
60979455Sobrien			*p++ = (u_char)(fat[cl].next >> 16);
61079455Sobrien			*p &= 0xf0;
61179455Sobrien			*p++ |= (fat[cl].next >> 24)&0x0f;
61279455Sobrien			break;
61379455Sobrien		case CLUST16_MASK:
61479455Sobrien			if (fat[cl].next == CLUST_FREE)
61579455Sobrien				boot->NumFree++;
61679455Sobrien			*p++ = (u_char)fat[cl].next;
61779455Sobrien			*p++ = (u_char)(fat[cl].next >> 8);
61879455Sobrien			break;
61979455Sobrien		default:
62079455Sobrien			if (fat[cl].next == CLUST_FREE)
62179455Sobrien				boot->NumFree++;
62279455Sobrien			if (cl + 1 < boot->NumClusters
62379455Sobrien			    && fat[cl + 1].next == CLUST_FREE)
62479455Sobrien				boot->NumFree++;
62579455Sobrien			*p++ = (u_char)fat[cl].next;
62679455Sobrien			*p++ = (u_char)((fat[cl].next >> 8) & 0xf)
62779455Sobrien			       |(u_char)(fat[cl+1].next << 4);
62879455Sobrien			*p++ = (u_char)(fat[++cl].next >> 4);
62979455Sobrien			break;
63079455Sobrien		}
63179455Sobrien	}
632203874Skib	for (i = 0; i < boot->bpbFATs; i++) {
633203874Skib		off = boot->bpbResSectors + i * boot->FATsecs;
634203874Skib		off *= boot->bpbBytesPerSec;
63579455Sobrien		if (lseek(fs, off, SEEK_SET) != off
636203872Skib		    || (size_t)write(fs, buffer, fatsz) != fatsz) {
63779455Sobrien			perror("Unable to write FAT");
63879455Sobrien			ret = FSFATAL; /* Return immediately?		XXX */
63979455Sobrien		}
64079455Sobrien	}
64179455Sobrien	free(buffer);
64279455Sobrien	return ret;
64379455Sobrien}
64479455Sobrien
64579455Sobrien/*
64679455Sobrien * Check a complete in-memory FAT for lost cluster chains
64779455Sobrien */
64879455Sobrienint
64992839Simpchecklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
65079455Sobrien{
65179455Sobrien	cl_t head;
65279455Sobrien	int mod = FSOK;
65379455Sobrien	int ret;
65479455Sobrien
65579455Sobrien	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
65679455Sobrien		/* find next untravelled chain */
65779455Sobrien		if (fat[head].head != head
65879455Sobrien		    || fat[head].next == CLUST_FREE
65979455Sobrien		    || (fat[head].next >= CLUST_RSRVD
66079455Sobrien			&& fat[head].next < CLUST_EOFS)
66179455Sobrien		    || (fat[head].flags & FAT_USED))
66279455Sobrien			continue;
66379455Sobrien
66479455Sobrien		pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
66579455Sobrien		      head, fat[head].length);
66679455Sobrien		mod |= ret = reconnect(dosfs, boot, fat, head);
66779455Sobrien		if (mod & FSFATAL)
66879455Sobrien			break;
66979455Sobrien		if (ret == FSERROR && ask(0, "Clear")) {
67079455Sobrien			clearchain(boot, fat, head);
67179455Sobrien			mod |= FSFATMOD;
67279455Sobrien		}
67379455Sobrien	}
67479455Sobrien	finishlf();
67579455Sobrien
676203874Skib	if (boot->bpbFSInfo) {
67779455Sobrien		ret = 0;
67879455Sobrien		if (boot->FSFree != boot->NumFree) {
67979455Sobrien			pwarn("Free space in FSInfo block (%d) not correct (%d)\n",
68079455Sobrien			      boot->FSFree, boot->NumFree);
681175853Syar			if (ask(1, "Fix")) {
68279455Sobrien				boot->FSFree = boot->NumFree;
68379455Sobrien				ret = 1;
68479455Sobrien			}
68579455Sobrien		}
68679455Sobrien		if (ret)
68779455Sobrien			mod |= writefsinfo(dosfs, boot);
68879455Sobrien	}
68979455Sobrien
69079455Sobrien	return mod;
69179455Sobrien}
692