fat.c revision 268631
1130812Smarcel/*
2130812Smarcel * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
3130812Smarcel * Copyright (c) 1995 Martin Husemann
4130812Smarcel *
5130812Smarcel * Redistribution and use in source and binary forms, with or without
6130812Smarcel * modification, are permitted provided that the following conditions
7130812Smarcel * are met:
8130812Smarcel * 1. Redistributions of source code must retain the above copyright
9130812Smarcel *    notice, this list of conditions and the following disclaimer.
10130812Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11130812Smarcel *    notice, this list of conditions and the following disclaimer in the
12130812Smarcel *    documentation and/or other materials provided with the distribution.
13130812Smarcel *
14130812Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
15130812Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16130812Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17130812Smarcel * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18130812Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19130812Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20130812Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21130812Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22130812Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23130812Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24130812Smarcel */
25130812Smarcel
26130812Smarcel
27130812Smarcel#include <sys/cdefs.h>
28130812Smarcel#ifndef lint
29130812Smarcel__RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
30130812Smarcelstatic const char rcsid[] =
31130812Smarcel  "$FreeBSD: head/sbin/fsck_msdosfs/fat.c 268631 2014-07-14 20:17:09Z pfg $";
32130812Smarcel#endif /* not lint */
33130812Smarcel
34130812Smarcel#include <stdlib.h>
35130812Smarcel#include <string.h>
36130812Smarcel#include <ctype.h>
37130812Smarcel#include <stdio.h>
38130812Smarcel#include <unistd.h>
39130812Smarcel
40130812Smarcel#include "ext.h"
41130812Smarcel#include "fsutil.h"
42130812Smarcel
43130812Smarcelstatic int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
44130812Smarcelstatic int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
45130812Smarcelstatic int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
46130812Smarcelstatic int _readfat(int, struct bootblock *, u_int, u_char **);
47130812Smarcel
48130812Smarcel/*-
49130812Smarcel * The first 2 FAT entries contain pseudo-cluster numbers with the following
50130812Smarcel * layout:
51130812Smarcel *
52130812Smarcel * 31...... ........ ........ .......0
53130812Smarcel * rrrr1111 11111111 11111111 mmmmmmmm         FAT32 entry 0
54130812Smarcel * rrrrsh11 11111111 11111111 11111xxx         FAT32 entry 1
55130812Smarcel *
56130812Smarcel *                   11111111 mmmmmmmm         FAT16 entry 0
57130812Smarcel *                   sh111111 11111xxx         FAT16 entry 1
58130812Smarcel *
59130812Smarcel * r = reserved
60130812Smarcel * m = BPB media ID byte
61130812Smarcel * s = clean flag (1 = dismounted; 0 = still mounted)
62130812Smarcel * h = hard error flag (1 = ok; 0 = I/O error)
63130812Smarcel * x = any value ok
64130812Smarcel */
65130812Smarcel
66130812Smarcelint
67130812Smarcelcheckdirty(int fs, struct bootblock *boot)
68130812Smarcel{
69130812Smarcel	off_t off;
70130812Smarcel	u_char *buffer;
71130812Smarcel	int ret = 0;
72130812Smarcel	size_t len;
73130812Smarcel
74130812Smarcel	if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
75130812Smarcel		return 0;
76130812Smarcel
77130812Smarcel	off = boot->bpbResSectors;
78130812Smarcel	off *= boot->bpbBytesPerSec;
79130812Smarcel
80130812Smarcel	buffer = malloc(len = boot->bpbBytesPerSec);
81130812Smarcel	if (buffer == NULL) {
82130812Smarcel		perr("No space for FAT sectors (%zu)", len);
83130812Smarcel		return 1;
84130812Smarcel	}
85130812Smarcel
86130812Smarcel	if (lseek(fs, off, SEEK_SET) != off) {
87130812Smarcel		perr("Unable to read FAT");
88130812Smarcel		goto err;
89130812Smarcel	}
90130812Smarcel
91130812Smarcel	if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
92130812Smarcel	    boot->bpbBytesPerSec) {
93130812Smarcel		perr("Unable to read FAT");
94130812Smarcel		goto err;
95130812Smarcel	}
96130812Smarcel
97130812Smarcel	/*
98130812Smarcel	 * If we don't understand the FAT, then the file system must be
99130812Smarcel	 * assumed to be unclean.
100130812Smarcel	 */
101130812Smarcel	if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
102130812Smarcel		goto err;
103130812Smarcel	if (boot->ClustMask == CLUST16_MASK) {
104130812Smarcel		if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
105130812Smarcel			goto err;
106130812Smarcel	} else {
107130812Smarcel		if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
108130812Smarcel		    || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
109130812Smarcel		    || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
110130812Smarcel			goto err;
111130812Smarcel	}
112130812Smarcel
113130812Smarcel	/*
114130812Smarcel	 * Now check the actual clean flag (and the no-error flag).
115130812Smarcel	 */
116130812Smarcel	if (boot->ClustMask == CLUST16_MASK) {
117130812Smarcel		if ((buffer[3] & 0xc0) == 0xc0)
118130812Smarcel			ret = 1;
119130812Smarcel	} else {
120130812Smarcel		if ((buffer[7] & 0x0c) == 0x0c)
121130812Smarcel			ret = 1;
122130812Smarcel	}
123130812Smarcel
124130812Smarcelerr:
125130812Smarcel	free(buffer);
126130812Smarcel	return ret;
127130812Smarcel}
128130812Smarcel
129130812Smarcel/*
130130812Smarcel * Check a cluster number for valid value
131130812Smarcel */
132130812Smarcelstatic int
133130812Smarcelcheckclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
134130812Smarcel{
135130812Smarcel	if (*next >= (CLUST_RSRVD&boot->ClustMask))
136130812Smarcel		*next |= ~boot->ClustMask;
137130812Smarcel	if (*next == CLUST_FREE) {
138130812Smarcel		boot->NumFree++;
139130812Smarcel		return FSOK;
140130812Smarcel	}
141130812Smarcel	if (*next == CLUST_BAD) {
142130812Smarcel		boot->NumBad++;
143130812Smarcel		return FSOK;
144130812Smarcel	}
145130812Smarcel	if (*next < CLUST_FIRST
146130812Smarcel	    || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
147130812Smarcel		pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
148130812Smarcel		      cl, fat,
149130812Smarcel		      *next < CLUST_RSRVD ? "out of range" : "reserved",
150130812Smarcel		      *next&boot->ClustMask);
151130812Smarcel		if (ask(0, "Truncate")) {
152130812Smarcel			*next = CLUST_EOF;
153130812Smarcel			return FSFATMOD;
154130812Smarcel		}
155130812Smarcel		return FSERROR;
156130812Smarcel	}
157130812Smarcel	return FSOK;
158130812Smarcel}
159130812Smarcel
160130812Smarcel/*
161130812Smarcel * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
162130812Smarcel */
163130812Smarcelstatic int
164130812Smarcel_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
165130812Smarcel{
166130812Smarcel	off_t off;
167130812Smarcel	size_t len;
168130812Smarcel
169130812Smarcel	*buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec);
170130812Smarcel	if (*buffer == NULL) {
171130812Smarcel		perr("No space for FAT sectors (%zu)", len);
172130812Smarcel		return 0;
173130812Smarcel	}
174130812Smarcel
175130812Smarcel	off = boot->bpbResSectors + no * boot->FATsecs;
176130812Smarcel	off *= boot->bpbBytesPerSec;
177130812Smarcel
178130812Smarcel	if (lseek(fs, off, SEEK_SET) != off) {
179130812Smarcel		perr("Unable to read FAT");
180130812Smarcel		goto err;
181130812Smarcel	}
182130812Smarcel
183130812Smarcel	if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
184130812Smarcel	    != boot->FATsecs * boot->bpbBytesPerSec) {
185130812Smarcel		perr("Unable to read FAT");
186130812Smarcel		goto err;
187130812Smarcel	}
188130812Smarcel
189130812Smarcel	return 1;
190130812Smarcel
191130812Smarcel    err:
192130812Smarcel	free(*buffer);
193130812Smarcel	return 0;
194130812Smarcel}
195130812Smarcel
196130812Smarcel/*
197130812Smarcel * Read a FAT and decode it into internal format
198130812Smarcel */
199130812Smarcelint
200130812Smarcelreadfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
201130812Smarcel{
202130812Smarcel	struct fatEntry *fat;
203130812Smarcel	u_char *buffer, *p;
204130812Smarcel	cl_t cl;
205130812Smarcel	int ret = FSOK;
206130812Smarcel	size_t len;
207130812Smarcel
208130812Smarcel	boot->NumFree = boot->NumBad = 0;
209130812Smarcel
210130812Smarcel	if (!_readfat(fs, boot, no, &buffer))
211130812Smarcel		return FSFATAL;
212130812Smarcel
213130812Smarcel	fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
214130812Smarcel	if (fat == NULL) {
215130812Smarcel		perr("No space for FAT clusters (%zu)", len);
216130812Smarcel		free(buffer);
217130812Smarcel		return FSFATAL;
218130812Smarcel	}
219130812Smarcel	(void)memset(fat, 0, len);
220130812Smarcel
221130812Smarcel	if (buffer[0] != boot->bpbMedia
222130812Smarcel	    || buffer[1] != 0xff || buffer[2] != 0xff
223130812Smarcel	    || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
224130812Smarcel	    || (boot->ClustMask == CLUST32_MASK
225130812Smarcel		&& ((buffer[3]&0x0f) != 0x0f
226130812Smarcel		    || buffer[4] != 0xff || buffer[5] != 0xff
227130812Smarcel		    || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
228130812Smarcel
229130812Smarcel		/* Windows 95 OSR2 (and possibly any later) changes
230130812Smarcel		 * the FAT signature to 0xXXffff7f for FAT16 and to
231130812Smarcel		 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
232130812Smarcel		 * file system is dirty if it doesn't reboot cleanly.
233130812Smarcel		 * Check this special condition before errorring out.
234130812Smarcel		 */
235130812Smarcel		if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
236130812Smarcel		    && buffer[2] == 0xff
237130812Smarcel		    && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
238130812Smarcel			|| (boot->ClustMask == CLUST32_MASK
239130812Smarcel			    && buffer[3] == 0x0f && buffer[4] == 0xff
240130812Smarcel			    && buffer[5] == 0xff && buffer[6] == 0xff
241130812Smarcel			    && buffer[7] == 0x07)))
242130812Smarcel			ret |= FSDIRTY;
243130812Smarcel		else {
244130812Smarcel			/* just some odd byte sequence in FAT */
245130812Smarcel
246130812Smarcel			switch (boot->ClustMask) {
247130812Smarcel			case CLUST32_MASK:
248130812Smarcel				pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
249130812Smarcel				      "FAT starts with odd byte sequence",
250130812Smarcel				      buffer[0], buffer[1], buffer[2], buffer[3],
251130812Smarcel				      buffer[4], buffer[5], buffer[6], buffer[7]);
252130812Smarcel				break;
253130812Smarcel			case CLUST16_MASK:
254130812Smarcel				pwarn("%s (%02x%02x%02x%02x)\n",
255130812Smarcel				    "FAT starts with odd byte sequence",
256130812Smarcel				    buffer[0], buffer[1], buffer[2], buffer[3]);
257130812Smarcel				break;
258130812Smarcel			default:
259130812Smarcel				pwarn("%s (%02x%02x%02x)\n",
260130812Smarcel				    "FAT starts with odd byte sequence",
261130812Smarcel				    buffer[0], buffer[1], buffer[2]);
262130812Smarcel				break;
263130812Smarcel			}
264130812Smarcel
265130812Smarcel
266130812Smarcel			if (ask(1, "Correct"))
267130812Smarcel				ret |= FSFIXFAT;
268130812Smarcel		}
269130812Smarcel	}
270130812Smarcel	switch (boot->ClustMask) {
271130812Smarcel	case CLUST32_MASK:
272130812Smarcel		p = buffer + 8;
273130812Smarcel		break;
274130812Smarcel	case CLUST16_MASK:
275130812Smarcel		p = buffer + 4;
276130812Smarcel		break;
277130812Smarcel	default:
278130812Smarcel		p = buffer + 3;
279130812Smarcel		break;
280130812Smarcel	}
281130812Smarcel	for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
282130812Smarcel		switch (boot->ClustMask) {
283130812Smarcel		case CLUST32_MASK:
284130812Smarcel			fat[cl].next = p[0] + (p[1] << 8)
285130812Smarcel				       + (p[2] << 16) + (p[3] << 24);
286130812Smarcel			fat[cl].next &= boot->ClustMask;
287130812Smarcel			ret |= checkclnum(boot, no, cl, &fat[cl].next);
288130812Smarcel			cl++;
289130812Smarcel			p += 4;
290130812Smarcel			break;
291130812Smarcel		case CLUST16_MASK:
292130812Smarcel			fat[cl].next = p[0] + (p[1] << 8);
293130812Smarcel			ret |= checkclnum(boot, no, cl, &fat[cl].next);
294130812Smarcel			cl++;
295130812Smarcel			p += 2;
296130812Smarcel			break;
297130812Smarcel		default:
298130812Smarcel			fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
299130812Smarcel			ret |= checkclnum(boot, no, cl, &fat[cl].next);
300130812Smarcel			cl++;
301130812Smarcel			if (cl >= boot->NumClusters)
302130812Smarcel				break;
303130812Smarcel			fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
304130812Smarcel			ret |= checkclnum(boot, no, cl, &fat[cl].next);
305130812Smarcel			cl++;
306130812Smarcel			p += 3;
307130812Smarcel			break;
308130812Smarcel		}
309130812Smarcel	}
310130812Smarcel
311130812Smarcel	free(buffer);
312130812Smarcel	if (ret & FSFATAL) {
313130812Smarcel		free(fat);
314130812Smarcel		*fp = NULL;
315130812Smarcel	} else
316130812Smarcel		*fp = fat;
317130812Smarcel	return ret;
318130812Smarcel}
319130812Smarcel
320130812Smarcel/*
321130812Smarcel * Get type of reserved cluster
322130812Smarcel */
323130812Smarcelconst char *
324130812Smarcelrsrvdcltype(cl_t cl)
325130812Smarcel{
326130812Smarcel	if (cl == CLUST_FREE)
327130812Smarcel		return "free";
328130812Smarcel	if (cl < CLUST_BAD)
329130812Smarcel		return "reserved";
330130812Smarcel	if (cl > CLUST_BAD)
331130812Smarcel		return "as EOF";
332130812Smarcel	return "bad";
333130812Smarcel}
334130812Smarcel
335130812Smarcelstatic int
336130812Smarcelclustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
337130812Smarcel{
338130812Smarcel	if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
339130812Smarcel		if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
340130812Smarcel			if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
341130812Smarcel			     && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
342130812Smarcel			    || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
343130812Smarcel				pwarn("Cluster %u is marked %s with different indicators\n",
344130812Smarcel				      cl, rsrvdcltype(*cp1));
345130812Smarcel				if (ask(1, "Fix")) {
346130812Smarcel					*cp2 = *cp1;
347130812Smarcel					return FSFATMOD;
348130812Smarcel				}
349130812Smarcel				return FSFATAL;
350130812Smarcel			}
351130812Smarcel			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
352130812Smarcel			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
353130812Smarcel			if (ask(0, "Use FAT 0's entry")) {
354130812Smarcel				*cp2 = *cp1;
355130812Smarcel				return FSFATMOD;
356130812Smarcel			}
357130812Smarcel			if (ask(0, "Use FAT %u's entry", fatnum)) {
358130812Smarcel				*cp1 = *cp2;
359130812Smarcel				return FSFATMOD;
360130812Smarcel			}
361130812Smarcel			return FSFATAL;
362130812Smarcel		}
363130812Smarcel		pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
364130812Smarcel		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
365130812Smarcel		if (ask(0, "Use continuation from FAT %u", fatnum)) {
366130812Smarcel			*cp1 = *cp2;
367130812Smarcel			return FSFATMOD;
368130812Smarcel		}
369130812Smarcel		if (ask(0, "Use mark from FAT 0")) {
370130812Smarcel			*cp2 = *cp1;
371130812Smarcel			return FSFATMOD;
372130812Smarcel		}
373130812Smarcel		return FSFATAL;
374130812Smarcel	}
375130812Smarcel	if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
376130812Smarcel		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
377130812Smarcel		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
378130812Smarcel		if (ask(0, "Use continuation from FAT 0")) {
379130812Smarcel			*cp2 = *cp1;
380130812Smarcel			return FSFATMOD;
381130812Smarcel		}
382130812Smarcel		if (ask(0, "Use mark from FAT %d", fatnum)) {
383130812Smarcel			*cp1 = *cp2;
384130812Smarcel			return FSFATMOD;
385130812Smarcel		}
386130812Smarcel		return FSERROR;
387130812Smarcel	}
388130812Smarcel	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
389130812Smarcel	      cl, *cp1, *cp2, fatnum);
390130812Smarcel	if (ask(0, "Use continuation from FAT 0")) {
391130812Smarcel		*cp2 = *cp1;
392130812Smarcel		return FSFATMOD;
393130812Smarcel	}
394130812Smarcel	if (ask(0, "Use continuation from FAT %u", fatnum)) {
395130812Smarcel		*cp1 = *cp2;
396130812Smarcel		return FSFATMOD;
397130812Smarcel	}
398130812Smarcel	return FSERROR;
399130812Smarcel}
400130812Smarcel
401130812Smarcel/*
402130812Smarcel * Compare two FAT copies in memory. Resolve any conflicts and merge them
403130812Smarcel * into the first one.
404130812Smarcel */
405130812Smarcelint
406130812Smarcelcomparefat(struct bootblock *boot, struct fatEntry *first,
407130812Smarcel    struct fatEntry *second, u_int fatnum)
408130812Smarcel{
409130812Smarcel	cl_t cl;
410130812Smarcel	int ret = FSOK;
411130812Smarcel
412130812Smarcel	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
413130812Smarcel		if (first[cl].next != second[cl].next)
414130812Smarcel			ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
415130812Smarcel	return ret;
416130812Smarcel}
417130812Smarcel
418130812Smarcelvoid
419130812Smarcelclearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
420130812Smarcel{
421130812Smarcel	cl_t p, q;
422130812Smarcel
423130812Smarcel	for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
424130812Smarcel		if (fat[p].head != head)
425130812Smarcel			break;
426130812Smarcel		q = fat[p].next;
427130812Smarcel		fat[p].next = fat[p].head = CLUST_FREE;
428130812Smarcel		fat[p].length = 0;
429130812Smarcel	}
430130812Smarcel}
431130812Smarcel
432130812Smarcelint
433130812Smarceltryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
434130812Smarcel{
435130812Smarcel	if (ask(0, "Clear chain starting at %u", head)) {
436130812Smarcel		clearchain(boot, fat, head);
437130812Smarcel		return FSFATMOD;
438130812Smarcel	} else if (ask(0, "Truncate")) {
439130812Smarcel		*truncp = CLUST_EOF;
440130812Smarcel		return FSFATMOD;
441130812Smarcel	} else
442130812Smarcel		return FSERROR;
443130812Smarcel}
444130812Smarcel
445130812Smarcel/*
446130812Smarcel * Check a complete FAT in-memory for crosslinks
447130812Smarcel */
448130812Smarcelint
449130812Smarcelcheckfat(struct bootblock *boot, struct fatEntry *fat)
450130812Smarcel{
451130812Smarcel	cl_t head, p, h, n;
452130812Smarcel	u_int len;
453130812Smarcel	int ret = 0;
454130812Smarcel	int conf;
455130812Smarcel
456130812Smarcel	/*
457130812Smarcel	 * pass 1: figure out the cluster chains.
458130812Smarcel	 */
459130812Smarcel	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
460130812Smarcel		/* find next untravelled chain */
461130812Smarcel		if (fat[head].head != 0		/* cluster already belongs to some chain */
462130812Smarcel		    || fat[head].next == CLUST_FREE
463130812Smarcel		    || fat[head].next == CLUST_BAD)
464130812Smarcel			continue;		/* skip it. */
465130812Smarcel
466130812Smarcel		/* follow the chain and mark all clusters on the way */
467130812Smarcel		for (len = 0, p = head;
468130812Smarcel		     p >= CLUST_FIRST && p < boot->NumClusters;
469130812Smarcel		     p = fat[p].next) {
470130812Smarcel			fat[p].head = head;
471130812Smarcel			len++;
472130812Smarcel		}
473130812Smarcel
474130812Smarcel		/* the head record gets the length */
475130812Smarcel		fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
476130812Smarcel	}
477130812Smarcel
478130812Smarcel	/*
479130812Smarcel	 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
480130812Smarcel	 * we didn't know the real start of the chain then - would have treated partial
481130812Smarcel	 * chains as interlinked with their main chain)
482130812Smarcel	 */
483130812Smarcel	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
484130812Smarcel		/* find next untravelled chain */
485130812Smarcel		if (fat[head].head != head)
486130812Smarcel			continue;
487130812Smarcel
488130812Smarcel		/* follow the chain to its end (hopefully) */
489130812Smarcel		for (p = head;
490130812Smarcel		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
491130812Smarcel		     p = n)
492130812Smarcel			if (fat[n].head != head)
493130812Smarcel				break;
494130812Smarcel		if (n >= CLUST_EOFS)
495130812Smarcel			continue;
496130812Smarcel
497130812Smarcel		if (n == CLUST_FREE || n >= CLUST_RSRVD) {
498130812Smarcel			pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
499130812Smarcel			      head, rsrvdcltype(n));
500130812Smarcel			ret |= tryclear(boot, fat, head, &fat[p].next);
501130812Smarcel			continue;
502130812Smarcel		}
503130812Smarcel		if (n < CLUST_FIRST || n >= boot->NumClusters) {
504130812Smarcel			pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
505130812Smarcel			      head, n);
506130812Smarcel			ret |= tryclear(boot, fat, head, &fat[p].next);
507130812Smarcel			continue;
508130812Smarcel		}
509130812Smarcel		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
510130812Smarcel		      head, fat[n].head, n);
511130812Smarcel		conf = tryclear(boot, fat, head, &fat[p].next);
512130812Smarcel		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
513130812Smarcel			if (conf == FSERROR) {
514130812Smarcel				/*
515130812Smarcel				 * Transfer the common chain to the one not cleared above.
516130812Smarcel				 */
517130812Smarcel				for (p = n;
518130812Smarcel				     p >= CLUST_FIRST && p < boot->NumClusters;
519130812Smarcel				     p = fat[p].next) {
520130812Smarcel					if (h != fat[p].head) {
521130812Smarcel						/*
522130812Smarcel						 * Have to reexamine this chain.
523130812Smarcel						 */
524130812Smarcel						head--;
525130812Smarcel						break;
526130812Smarcel					}
527130812Smarcel					fat[p].head = head;
528130812Smarcel				}
529130812Smarcel			}
530130812Smarcel			clearchain(boot, fat, h);
531130812Smarcel			conf |= FSFATMOD;
532130812Smarcel		}
533130812Smarcel		ret |= conf;
534130812Smarcel	}
535130812Smarcel
536130812Smarcel	return ret;
537130812Smarcel}
538130812Smarcel
539130812Smarcel/*
540130812Smarcel * Write out FATs encoding them from the internal format
541130812Smarcel */
542130812Smarcelint
543130812Smarcelwritefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
544130812Smarcel{
545130812Smarcel	u_char *buffer, *p;
546130812Smarcel	cl_t cl;
547130812Smarcel	u_int i;
548130812Smarcel	size_t fatsz;
549130812Smarcel	off_t off;
550130812Smarcel	int ret = FSOK;
551130812Smarcel
552130812Smarcel	buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec);
553130812Smarcel	if (buffer == NULL) {
554130812Smarcel		perr("No space for FAT sectors (%zu)", fatsz);
555130812Smarcel		return FSFATAL;
556130812Smarcel	}
557130812Smarcel	memset(buffer, 0, fatsz);
558130812Smarcel	boot->NumFree = 0;
559130812Smarcel	p = buffer;
560130812Smarcel	if (correct_fat) {
561130812Smarcel		*p++ = (u_char)boot->bpbMedia;
562130812Smarcel		*p++ = 0xff;
563130812Smarcel		*p++ = 0xff;
564130812Smarcel		switch (boot->ClustMask) {
565130812Smarcel		case CLUST16_MASK:
566130812Smarcel			*p++ = 0xff;
567130812Smarcel			break;
568130812Smarcel		case CLUST32_MASK:
569130812Smarcel			*p++ = 0x0f;
570130812Smarcel			*p++ = 0xff;
571130812Smarcel			*p++ = 0xff;
572130812Smarcel			*p++ = 0xff;
573130812Smarcel			*p++ = 0x0f;
574130812Smarcel			break;
575130812Smarcel		}
576130812Smarcel	} else {
577130812Smarcel		/* use same FAT signature as the old FAT has */
578130812Smarcel		int count;
579130812Smarcel		u_char *old_fat;
580130812Smarcel
581130812Smarcel		switch (boot->ClustMask) {
582130812Smarcel		case CLUST32_MASK:
583130812Smarcel			count = 8;
584130812Smarcel			break;
585130812Smarcel		case CLUST16_MASK:
586130812Smarcel			count = 4;
587130812Smarcel			break;
588130812Smarcel		default:
589130812Smarcel			count = 3;
590130812Smarcel			break;
591130812Smarcel		}
592130812Smarcel
593130812Smarcel		if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
594130812Smarcel					 &old_fat)) {
595130812Smarcel			free(buffer);
596130812Smarcel			return FSFATAL;
597130812Smarcel		}
598130812Smarcel
599130812Smarcel		memcpy(p, old_fat, count);
600130812Smarcel		free(old_fat);
601130812Smarcel		p += count;
602130812Smarcel	}
603130812Smarcel
604130812Smarcel	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
605130812Smarcel		switch (boot->ClustMask) {
606130812Smarcel		case CLUST32_MASK:
607130812Smarcel			if (fat[cl].next == CLUST_FREE)
608130812Smarcel				boot->NumFree++;
609130812Smarcel			*p++ = (u_char)fat[cl].next;
610130812Smarcel			*p++ = (u_char)(fat[cl].next >> 8);
611130812Smarcel			*p++ = (u_char)(fat[cl].next >> 16);
612130812Smarcel			*p &= 0xf0;
613130812Smarcel			*p++ |= (fat[cl].next >> 24)&0x0f;
614130812Smarcel			break;
615130812Smarcel		case CLUST16_MASK:
616130812Smarcel			if (fat[cl].next == CLUST_FREE)
617130812Smarcel				boot->NumFree++;
618130812Smarcel			*p++ = (u_char)fat[cl].next;
619130812Smarcel			*p++ = (u_char)(fat[cl].next >> 8);
620130812Smarcel			break;
621130812Smarcel		default:
622130812Smarcel			if (fat[cl].next == CLUST_FREE)
623130812Smarcel				boot->NumFree++;
624130812Smarcel			if (cl + 1 < boot->NumClusters
625130812Smarcel			    && fat[cl + 1].next == CLUST_FREE)
626130812Smarcel				boot->NumFree++;
627130812Smarcel			*p++ = (u_char)fat[cl].next;
628130812Smarcel			*p++ = (u_char)((fat[cl].next >> 8) & 0xf)
629130812Smarcel			       |(u_char)(fat[cl+1].next << 4);
630130812Smarcel			*p++ = (u_char)(fat[++cl].next >> 4);
631130812Smarcel			break;
632130812Smarcel		}
633130812Smarcel	}
634130812Smarcel	for (i = 0; i < boot->bpbFATs; i++) {
635130812Smarcel		off = boot->bpbResSectors + i * boot->FATsecs;
636130812Smarcel		off *= boot->bpbBytesPerSec;
637130812Smarcel		if (lseek(fs, off, SEEK_SET) != off
638130812Smarcel		    || (size_t)write(fs, buffer, fatsz) != fatsz) {
639130812Smarcel			perr("Unable to write FAT");
640130812Smarcel			ret = FSFATAL; /* Return immediately?		XXX */
641130812Smarcel		}
642130812Smarcel	}
643130812Smarcel	free(buffer);
644130812Smarcel	return ret;
645130812Smarcel}
646130812Smarcel
647130812Smarcel/*
648130812Smarcel * Check a complete in-memory FAT for lost cluster chains
649130812Smarcel */
650130812Smarcelint
651130812Smarcelchecklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
652130812Smarcel{
653130812Smarcel	cl_t head;
654130812Smarcel	int mod = FSOK;
655130812Smarcel	int ret;
656130812Smarcel
657130812Smarcel	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
658130812Smarcel		/* find next untravelled chain */
659130812Smarcel		if (fat[head].head != head
660130812Smarcel		    || fat[head].next == CLUST_FREE
661130812Smarcel		    || (fat[head].next >= CLUST_RSRVD
662130812Smarcel			&& fat[head].next < CLUST_EOFS)
663130812Smarcel		    || (fat[head].flags & FAT_USED))
664130812Smarcel			continue;
665130812Smarcel
666130812Smarcel		pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
667130812Smarcel		      head, fat[head].length);
668130812Smarcel		mod |= ret = reconnect(dosfs, boot, fat, head);
669130812Smarcel		if (mod & FSFATAL)
670130812Smarcel			break;
671130812Smarcel		if (ret == FSERROR && ask(0, "Clear")) {
672130812Smarcel			clearchain(boot, fat, head);
673130812Smarcel			mod |= FSFATMOD;
674130812Smarcel		}
675130812Smarcel	}
676130812Smarcel	finishlf();
677130812Smarcel
678130812Smarcel	if (boot->bpbFSInfo) {
679130812Smarcel		ret = 0;
680130812Smarcel		if (boot->FSFree != 0xffffffffU &&
681130812Smarcel		    boot->FSFree != boot->NumFree) {
682130812Smarcel			pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
683130812Smarcel			      boot->FSFree, boot->NumFree);
684130812Smarcel			if (ask(1, "Fix")) {
685130812Smarcel				boot->FSFree = boot->NumFree;
686130812Smarcel				ret = 1;
687130812Smarcel			}
688130812Smarcel		}
689130812Smarcel		if (ret)
690130812Smarcel			mod |= writefsinfo(dosfs, boot);
691130812Smarcel	}
692130812Smarcel
693130812Smarcel	return mod;
694130812Smarcel}
695130812Smarcel