xref: /freebsd/sbin/fsck_msdosfs/fat.c (revision 206b73d0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
5  * Copyright (c) 1995 Martin Husemann
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 
29 #include <sys/cdefs.h>
30 #ifndef lint
31 __RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
32 static const char rcsid[] =
33   "$FreeBSD$";
34 #endif /* not lint */
35 
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <unistd.h>
41 
42 #include "ext.h"
43 #include "fsutil.h"
44 
45 static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
46 static int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
47 static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
48 static int _readfat(int, struct bootblock *, u_int, u_char **);
49 
50 /*-
51  * The first 2 FAT entries contain pseudo-cluster numbers with the following
52  * layout:
53  *
54  * 31...... ........ ........ .......0
55  * rrrr1111 11111111 11111111 mmmmmmmm         FAT32 entry 0
56  * rrrrsh11 11111111 11111111 11111xxx         FAT32 entry 1
57  *
58  *                   11111111 mmmmmmmm         FAT16 entry 0
59  *                   sh111111 11111xxx         FAT16 entry 1
60  *
61  * r = reserved
62  * m = BPB media ID byte
63  * s = clean flag (1 = dismounted; 0 = still mounted)
64  * h = hard error flag (1 = ok; 0 = I/O error)
65  * x = any value ok
66  */
67 
68 int
69 checkdirty(int fs, struct bootblock *boot)
70 {
71 	off_t off;
72 	u_char *buffer;
73 	int ret = 0;
74 	size_t len;
75 
76 	if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
77 		return 0;
78 
79 	off = boot->bpbResSectors;
80 	off *= boot->bpbBytesPerSec;
81 
82 	buffer = malloc(len = boot->bpbBytesPerSec);
83 	if (buffer == NULL) {
84 		perr("No space for FAT sectors (%zu)", len);
85 		return 1;
86 	}
87 
88 	if (lseek(fs, off, SEEK_SET) != off) {
89 		perr("Unable to read FAT");
90 		goto err;
91 	}
92 
93 	if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
94 	    boot->bpbBytesPerSec) {
95 		perr("Unable to read FAT");
96 		goto err;
97 	}
98 
99 	/*
100 	 * If we don't understand the FAT, then the file system must be
101 	 * assumed to be unclean.
102 	 */
103 	if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
104 		goto err;
105 	if (boot->ClustMask == CLUST16_MASK) {
106 		if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
107 			goto err;
108 	} else {
109 		if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
110 		    || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
111 		    || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
112 			goto err;
113 	}
114 
115 	/*
116 	 * Now check the actual clean flag (and the no-error flag).
117 	 */
118 	if (boot->ClustMask == CLUST16_MASK) {
119 		if ((buffer[3] & 0xc0) == 0xc0)
120 			ret = 1;
121 	} else {
122 		if ((buffer[7] & 0x0c) == 0x0c)
123 			ret = 1;
124 	}
125 
126 err:
127 	free(buffer);
128 	return ret;
129 }
130 
131 /*
132  * Check a cluster number for valid value
133  */
134 static int
135 checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
136 {
137 	if (*next >= (CLUST_RSRVD&boot->ClustMask))
138 		*next |= ~boot->ClustMask;
139 	if (*next == CLUST_FREE) {
140 		boot->NumFree++;
141 		return FSOK;
142 	}
143 	if (*next == CLUST_BAD) {
144 		boot->NumBad++;
145 		return FSOK;
146 	}
147 	if (*next < CLUST_FIRST
148 	    || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
149 		pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
150 		      cl, fat,
151 		      *next < CLUST_RSRVD ? "out of range" : "reserved",
152 		      *next&boot->ClustMask);
153 		if (ask(0, "Truncate")) {
154 			*next = CLUST_EOF;
155 			return FSFATMOD;
156 		}
157 		return FSERROR;
158 	}
159 	return FSOK;
160 }
161 
162 /*
163  * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
164  */
165 static int
166 _readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
167 {
168 	off_t off;
169 
170 	*buffer = calloc(boot->FATsecs, boot->bpbBytesPerSec);
171 	if (*buffer == NULL) {
172 		perr("No space for FAT sectors (%zu)",
173 		    (size_t)boot->FATsecs);
174 		return 0;
175 	}
176 
177 	off = boot->bpbResSectors + no * boot->FATsecs;
178 	off *= boot->bpbBytesPerSec;
179 
180 	if (lseek(fs, off, SEEK_SET) != off) {
181 		perr("Unable to read FAT");
182 		goto err;
183 	}
184 
185 	if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
186 	    != boot->FATsecs * boot->bpbBytesPerSec) {
187 		perr("Unable to read FAT");
188 		goto err;
189 	}
190 
191 	return 1;
192 
193     err:
194 	free(*buffer);
195 	return 0;
196 }
197 
198 /*
199  * Read a FAT and decode it into internal format
200  */
201 int
202 readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
203 {
204 	struct fatEntry *fat;
205 	u_char *buffer, *p;
206 	cl_t cl;
207 	int ret = FSOK;
208 
209 	boot->NumFree = boot->NumBad = 0;
210 
211 	if (!_readfat(fs, boot, no, &buffer))
212 		return FSFATAL;
213 
214 	fat = calloc(boot->NumClusters, sizeof(struct fatEntry));
215 	if (fat == NULL) {
216 		perr("No space for FAT clusters (%zu)",
217 		    (size_t)boot->NumClusters);
218 		free(buffer);
219 		return FSFATAL;
220 	}
221 
222 	if (buffer[0] != boot->bpbMedia
223 	    || buffer[1] != 0xff || buffer[2] != 0xff
224 	    || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
225 	    || (boot->ClustMask == CLUST32_MASK
226 		&& ((buffer[3]&0x0f) != 0x0f
227 		    || buffer[4] != 0xff || buffer[5] != 0xff
228 		    || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
229 
230 		/* Windows 95 OSR2 (and possibly any later) changes
231 		 * the FAT signature to 0xXXffff7f for FAT16 and to
232 		 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
233 		 * file system is dirty if it doesn't reboot cleanly.
234 		 * Check this special condition before errorring out.
235 		 */
236 		if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
237 		    && buffer[2] == 0xff
238 		    && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
239 			|| (boot->ClustMask == CLUST32_MASK
240 			    && buffer[3] == 0x0f && buffer[4] == 0xff
241 			    && buffer[5] == 0xff && buffer[6] == 0xff
242 			    && buffer[7] == 0x07)))
243 			ret |= FSDIRTY;
244 		else {
245 			/* just some odd byte sequence in FAT */
246 
247 			switch (boot->ClustMask) {
248 			case CLUST32_MASK:
249 				pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
250 				      "FAT starts with odd byte sequence",
251 				      buffer[0], buffer[1], buffer[2], buffer[3],
252 				      buffer[4], buffer[5], buffer[6], buffer[7]);
253 				break;
254 			case CLUST16_MASK:
255 				pwarn("%s (%02x%02x%02x%02x)\n",
256 				    "FAT starts with odd byte sequence",
257 				    buffer[0], buffer[1], buffer[2], buffer[3]);
258 				break;
259 			default:
260 				pwarn("%s (%02x%02x%02x)\n",
261 				    "FAT starts with odd byte sequence",
262 				    buffer[0], buffer[1], buffer[2]);
263 				break;
264 			}
265 
266 
267 			if (ask(1, "Correct"))
268 				ret |= FSFIXFAT;
269 		}
270 	}
271 	switch (boot->ClustMask) {
272 	case CLUST32_MASK:
273 		p = buffer + 8;
274 		break;
275 	case CLUST16_MASK:
276 		p = buffer + 4;
277 		break;
278 	default:
279 		p = buffer + 3;
280 		break;
281 	}
282 	for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
283 		switch (boot->ClustMask) {
284 		case CLUST32_MASK:
285 			fat[cl].next = p[0] + (p[1] << 8)
286 				       + (p[2] << 16) + (p[3] << 24);
287 			fat[cl].next &= boot->ClustMask;
288 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
289 			cl++;
290 			p += 4;
291 			break;
292 		case CLUST16_MASK:
293 			fat[cl].next = p[0] + (p[1] << 8);
294 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
295 			cl++;
296 			p += 2;
297 			break;
298 		default:
299 			fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
300 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
301 			cl++;
302 			if (cl >= boot->NumClusters)
303 				break;
304 			fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
305 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
306 			cl++;
307 			p += 3;
308 			break;
309 		}
310 	}
311 
312 	free(buffer);
313 	if (ret & FSFATAL) {
314 		free(fat);
315 		*fp = NULL;
316 	} else
317 		*fp = fat;
318 	return ret;
319 }
320 
321 /*
322  * Get type of reserved cluster
323  */
324 const char *
325 rsrvdcltype(cl_t cl)
326 {
327 	if (cl == CLUST_FREE)
328 		return "free";
329 	if (cl < CLUST_BAD)
330 		return "reserved";
331 	if (cl > CLUST_BAD)
332 		return "as EOF";
333 	return "bad";
334 }
335 
336 static int
337 clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
338 {
339 	if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
340 		if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
341 			if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
342 			     && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
343 			    || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
344 				pwarn("Cluster %u is marked %s with different indicators\n",
345 				      cl, rsrvdcltype(*cp1));
346 				if (ask(1, "Fix")) {
347 					*cp2 = *cp1;
348 					return FSFATMOD;
349 				}
350 				return FSFATAL;
351 			}
352 			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
353 			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
354 			if (ask(0, "Use FAT 0's entry")) {
355 				*cp2 = *cp1;
356 				return FSFATMOD;
357 			}
358 			if (ask(0, "Use FAT %u's entry", fatnum)) {
359 				*cp1 = *cp2;
360 				return FSFATMOD;
361 			}
362 			return FSFATAL;
363 		}
364 		pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
365 		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
366 		if (ask(0, "Use continuation from FAT %u", fatnum)) {
367 			*cp1 = *cp2;
368 			return FSFATMOD;
369 		}
370 		if (ask(0, "Use mark from FAT 0")) {
371 			*cp2 = *cp1;
372 			return FSFATMOD;
373 		}
374 		return FSFATAL;
375 	}
376 	if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
377 		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
378 		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
379 		if (ask(0, "Use continuation from FAT 0")) {
380 			*cp2 = *cp1;
381 			return FSFATMOD;
382 		}
383 		if (ask(0, "Use mark from FAT %d", fatnum)) {
384 			*cp1 = *cp2;
385 			return FSFATMOD;
386 		}
387 		return FSERROR;
388 	}
389 	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
390 	      cl, *cp1, *cp2, fatnum);
391 	if (ask(0, "Use continuation from FAT 0")) {
392 		*cp2 = *cp1;
393 		return FSFATMOD;
394 	}
395 	if (ask(0, "Use continuation from FAT %u", fatnum)) {
396 		*cp1 = *cp2;
397 		return FSFATMOD;
398 	}
399 	return FSERROR;
400 }
401 
402 /*
403  * Compare two FAT copies in memory. Resolve any conflicts and merge them
404  * into the first one.
405  */
406 int
407 comparefat(struct bootblock *boot, struct fatEntry *first,
408     struct fatEntry *second, u_int fatnum)
409 {
410 	cl_t cl;
411 	int ret = FSOK;
412 
413 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
414 		if (first[cl].next != second[cl].next)
415 			ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
416 	return ret;
417 }
418 
419 void
420 clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
421 {
422 	cl_t p, q;
423 
424 	for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
425 		if (fat[p].head != head)
426 			break;
427 		q = fat[p].next;
428 		fat[p].next = fat[p].head = CLUST_FREE;
429 		fat[p].length = 0;
430 	}
431 }
432 
433 int
434 tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
435 {
436 	if (ask(0, "Clear chain starting at %u", head)) {
437 		clearchain(boot, fat, head);
438 		return FSFATMOD;
439 	} else if (ask(0, "Truncate")) {
440 		uint32_t len;
441 		cl_t p;
442 
443 		for (p = head, len = 0;
444 		    p >= CLUST_FIRST && p < boot->NumClusters;
445 		    p = fat[p].next, len++)
446 			continue;
447 		*truncp = CLUST_EOF;
448 		fat[head].length = len;
449 		return FSFATMOD;
450 	} else
451 		return FSERROR;
452 }
453 
454 /*
455  * Check a complete FAT in-memory for crosslinks
456  */
457 int
458 checkfat(struct bootblock *boot, struct fatEntry *fat)
459 {
460 	cl_t head, p, h, n;
461 	u_int len;
462 	int ret = 0;
463 	int conf;
464 
465 	/*
466 	 * pass 1: figure out the cluster chains.
467 	 */
468 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
469 		/* find next untravelled chain */
470 		if (fat[head].head != 0		/* cluster already belongs to some chain */
471 		    || fat[head].next == CLUST_FREE
472 		    || fat[head].next == CLUST_BAD)
473 			continue;		/* skip it. */
474 
475 		/* follow the chain and mark all clusters on the way */
476 		for (len = 0, p = head;
477 		     p >= CLUST_FIRST && p < boot->NumClusters &&
478 		     fat[p].head != head;
479 		     p = fat[p].next) {
480 			fat[p].head = head;
481 			len++;
482 		}
483 
484 		/* the head record gets the length */
485 		fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
486 	}
487 
488 	/*
489 	 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
490 	 * we didn't know the real start of the chain then - would have treated partial
491 	 * chains as interlinked with their main chain)
492 	 */
493 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
494 		/* find next untravelled chain */
495 		if (fat[head].head != head)
496 			continue;
497 
498 		/* follow the chain to its end (hopefully) */
499 		for (len = fat[head].length, p = head;
500 		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
501 		     p = n)
502 			if (fat[n].head != head || len-- < 2)
503 				break;
504 		if (n >= CLUST_EOFS)
505 			continue;
506 
507 		if (n == CLUST_FREE || n >= CLUST_RSRVD) {
508 			pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
509 			      head, rsrvdcltype(n));
510 clear:
511 			ret |= tryclear(boot, fat, head, &fat[p].next);
512 			continue;
513 		}
514 		if (n < CLUST_FIRST || n >= boot->NumClusters) {
515 			pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
516 			    head, n);
517 			goto clear;
518 		}
519 		if (head == fat[n].head) {
520 			pwarn("Cluster chain starting at %u loops at cluster %u\n",
521 
522 			    head, p);
523 			goto clear;
524 		}
525 		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
526 		      head, fat[n].head, n);
527 		conf = tryclear(boot, fat, head, &fat[p].next);
528 		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
529 			if (conf == FSERROR) {
530 				/*
531 				 * Transfer the common chain to the one not cleared above.
532 				 */
533 				for (p = n;
534 				     p >= CLUST_FIRST && p < boot->NumClusters;
535 				     p = fat[p].next) {
536 					if (h != fat[p].head) {
537 						/*
538 						 * Have to reexamine this chain.
539 						 */
540 						head--;
541 						break;
542 					}
543 					fat[p].head = head;
544 				}
545 			}
546 			clearchain(boot, fat, h);
547 			conf |= FSFATMOD;
548 		}
549 		ret |= conf;
550 	}
551 
552 	return ret;
553 }
554 
555 /*
556  * Write out FATs encoding them from the internal format
557  */
558 int
559 writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
560 {
561 	u_char *buffer, *p;
562 	cl_t cl;
563 	u_int i;
564 	size_t fatsz;
565 	off_t off;
566 	int ret = FSOK;
567 
568 	fatsz = boot->FATsecs * boot->bpbBytesPerSec;
569 	buffer = calloc(boot->FATsecs, boot->bpbBytesPerSec);
570 	if (buffer == NULL) {
571 		perr("No space for FAT sectors (%zu)",
572 		    (size_t)boot->FATsecs);
573 		return FSFATAL;
574 	}
575 	boot->NumFree = 0;
576 	p = buffer;
577 	if (correct_fat) {
578 		*p++ = (u_char)boot->bpbMedia;
579 		*p++ = 0xff;
580 		*p++ = 0xff;
581 		switch (boot->ClustMask) {
582 		case CLUST16_MASK:
583 			*p++ = 0xff;
584 			break;
585 		case CLUST32_MASK:
586 			*p++ = 0x0f;
587 			*p++ = 0xff;
588 			*p++ = 0xff;
589 			*p++ = 0xff;
590 			*p++ = 0x0f;
591 			break;
592 		}
593 	} else {
594 		/* use same FAT signature as the old FAT has */
595 		int count;
596 		u_char *old_fat;
597 
598 		switch (boot->ClustMask) {
599 		case CLUST32_MASK:
600 			count = 8;
601 			break;
602 		case CLUST16_MASK:
603 			count = 4;
604 			break;
605 		default:
606 			count = 3;
607 			break;
608 		}
609 
610 		if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
611 					 &old_fat)) {
612 			free(buffer);
613 			return FSFATAL;
614 		}
615 
616 		memcpy(p, old_fat, count);
617 		free(old_fat);
618 		p += count;
619 	}
620 
621 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
622 		switch (boot->ClustMask) {
623 		case CLUST32_MASK:
624 			if (fat[cl].next == CLUST_FREE)
625 				boot->NumFree++;
626 			*p++ = (u_char)fat[cl].next;
627 			*p++ = (u_char)(fat[cl].next >> 8);
628 			*p++ = (u_char)(fat[cl].next >> 16);
629 			*p &= 0xf0;
630 			*p++ |= (fat[cl].next >> 24)&0x0f;
631 			break;
632 		case CLUST16_MASK:
633 			if (fat[cl].next == CLUST_FREE)
634 				boot->NumFree++;
635 			*p++ = (u_char)fat[cl].next;
636 			*p++ = (u_char)(fat[cl].next >> 8);
637 			break;
638 		default:
639 			if (fat[cl].next == CLUST_FREE)
640 				boot->NumFree++;
641 			*p++ = (u_char)fat[cl].next;
642 			*p = (u_char)((fat[cl].next >> 8) & 0xf);
643 			cl++;
644 			if (cl >= boot->NumClusters)
645 				break;
646 			if (fat[cl].next == CLUST_FREE)
647 				boot->NumFree++;
648 			*p++ |= (u_char)(fat[cl].next << 4);
649 			*p++ = (u_char)(fat[cl].next >> 4);
650 			break;
651 		}
652 	}
653 	for (i = 0; i < boot->bpbFATs; i++) {
654 		off = boot->bpbResSectors + i * boot->FATsecs;
655 		off *= boot->bpbBytesPerSec;
656 		if (lseek(fs, off, SEEK_SET) != off
657 		    || (size_t)write(fs, buffer, fatsz) != fatsz) {
658 			perr("Unable to write FAT");
659 			ret = FSFATAL; /* Return immediately?		XXX */
660 		}
661 	}
662 	free(buffer);
663 	return ret;
664 }
665 
666 /*
667  * Check a complete in-memory FAT for lost cluster chains
668  */
669 int
670 checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
671 {
672 	cl_t head;
673 	int mod = FSOK;
674 	int ret;
675 
676 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
677 		/* find next untravelled chain */
678 		if (fat[head].head != head
679 		    || fat[head].next == CLUST_FREE
680 		    || (fat[head].next >= CLUST_RSRVD
681 			&& fat[head].next < CLUST_EOFS)
682 		    || (fat[head].flags & FAT_USED))
683 			continue;
684 
685 		pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
686 		      head, fat[head].length);
687 		mod |= ret = reconnect(dosfs, boot, fat, head);
688 		if (mod & FSFATAL)
689 			break;
690 		if (ret == FSERROR && ask(0, "Clear")) {
691 			clearchain(boot, fat, head);
692 			mod |= FSFATMOD;
693 		}
694 	}
695 	finishlf();
696 
697 	if (boot->bpbFSInfo) {
698 		ret = 0;
699 		if (boot->FSFree != 0xffffffffU &&
700 		    boot->FSFree != boot->NumFree) {
701 			pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
702 			      boot->FSFree, boot->NumFree);
703 			if (ask(1, "Fix")) {
704 				boot->FSFree = boot->NumFree;
705 				ret = 1;
706 			}
707 		}
708 		if (boot->FSNext != 0xffffffffU &&
709 		    (boot->FSNext >= boot->NumClusters ||
710 		    (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) {
711 			pwarn("Next free cluster in FSInfo block (%u) %s\n",
712 			      boot->FSNext,
713 			      (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free");
714 			if (ask(1, "fix"))
715 				for (head = CLUST_FIRST; head < boot->NumClusters; head++)
716 					if (fat[head].next == CLUST_FREE) {
717 						boot->FSNext = head;
718 						ret = 1;
719 						break;
720 					}
721 		}
722 		if (ret)
723 			mod |= writefsinfo(dosfs, boot);
724 	}
725 
726 	return mod;
727 }
728