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