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