xref: /freebsd/sbin/fsck_msdosfs/fat.c (revision 4f52dfbb8d6c4d446500c5b097e3806ec219fbd4)
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 	size_t len;
170 
171 	*buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec);
172 	if (*buffer == NULL) {
173 		perr("No space for FAT sectors (%zu)", len);
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 	size_t len;
209 
210 	boot->NumFree = boot->NumBad = 0;
211 
212 	if (!_readfat(fs, boot, no, &buffer))
213 		return FSFATAL;
214 
215 	fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
216 	if (fat == NULL) {
217 		perr("No space for FAT clusters (%zu)", len);
218 		free(buffer);
219 		return FSFATAL;
220 	}
221 	(void)memset(fat, 0, len);
222 
223 	if (buffer[0] != boot->bpbMedia
224 	    || buffer[1] != 0xff || buffer[2] != 0xff
225 	    || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
226 	    || (boot->ClustMask == CLUST32_MASK
227 		&& ((buffer[3]&0x0f) != 0x0f
228 		    || buffer[4] != 0xff || buffer[5] != 0xff
229 		    || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
230 
231 		/* Windows 95 OSR2 (and possibly any later) changes
232 		 * the FAT signature to 0xXXffff7f for FAT16 and to
233 		 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
234 		 * file system is dirty if it doesn't reboot cleanly.
235 		 * Check this special condition before errorring out.
236 		 */
237 		if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
238 		    && buffer[2] == 0xff
239 		    && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
240 			|| (boot->ClustMask == CLUST32_MASK
241 			    && buffer[3] == 0x0f && buffer[4] == 0xff
242 			    && buffer[5] == 0xff && buffer[6] == 0xff
243 			    && buffer[7] == 0x07)))
244 			ret |= FSDIRTY;
245 		else {
246 			/* just some odd byte sequence in FAT */
247 
248 			switch (boot->ClustMask) {
249 			case CLUST32_MASK:
250 				pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
251 				      "FAT starts with odd byte sequence",
252 				      buffer[0], buffer[1], buffer[2], buffer[3],
253 				      buffer[4], buffer[5], buffer[6], buffer[7]);
254 				break;
255 			case CLUST16_MASK:
256 				pwarn("%s (%02x%02x%02x%02x)\n",
257 				    "FAT starts with odd byte sequence",
258 				    buffer[0], buffer[1], buffer[2], buffer[3]);
259 				break;
260 			default:
261 				pwarn("%s (%02x%02x%02x)\n",
262 				    "FAT starts with odd byte sequence",
263 				    buffer[0], buffer[1], buffer[2]);
264 				break;
265 			}
266 
267 
268 			if (ask(1, "Correct"))
269 				ret |= FSFIXFAT;
270 		}
271 	}
272 	switch (boot->ClustMask) {
273 	case CLUST32_MASK:
274 		p = buffer + 8;
275 		break;
276 	case CLUST16_MASK:
277 		p = buffer + 4;
278 		break;
279 	default:
280 		p = buffer + 3;
281 		break;
282 	}
283 	for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
284 		switch (boot->ClustMask) {
285 		case CLUST32_MASK:
286 			fat[cl].next = p[0] + (p[1] << 8)
287 				       + (p[2] << 16) + (p[3] << 24);
288 			fat[cl].next &= boot->ClustMask;
289 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
290 			cl++;
291 			p += 4;
292 			break;
293 		case CLUST16_MASK:
294 			fat[cl].next = p[0] + (p[1] << 8);
295 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
296 			cl++;
297 			p += 2;
298 			break;
299 		default:
300 			fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
301 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
302 			cl++;
303 			if (cl >= boot->NumClusters)
304 				break;
305 			fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
306 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
307 			cl++;
308 			p += 3;
309 			break;
310 		}
311 	}
312 
313 	free(buffer);
314 	if (ret & FSFATAL) {
315 		free(fat);
316 		*fp = NULL;
317 	} else
318 		*fp = fat;
319 	return ret;
320 }
321 
322 /*
323  * Get type of reserved cluster
324  */
325 const char *
326 rsrvdcltype(cl_t cl)
327 {
328 	if (cl == CLUST_FREE)
329 		return "free";
330 	if (cl < CLUST_BAD)
331 		return "reserved";
332 	if (cl > CLUST_BAD)
333 		return "as EOF";
334 	return "bad";
335 }
336 
337 static int
338 clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
339 {
340 	if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
341 		if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
342 			if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
343 			     && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
344 			    || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
345 				pwarn("Cluster %u is marked %s with different indicators\n",
346 				      cl, rsrvdcltype(*cp1));
347 				if (ask(1, "Fix")) {
348 					*cp2 = *cp1;
349 					return FSFATMOD;
350 				}
351 				return FSFATAL;
352 			}
353 			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
354 			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
355 			if (ask(0, "Use FAT 0's entry")) {
356 				*cp2 = *cp1;
357 				return FSFATMOD;
358 			}
359 			if (ask(0, "Use FAT %u's entry", fatnum)) {
360 				*cp1 = *cp2;
361 				return FSFATMOD;
362 			}
363 			return FSFATAL;
364 		}
365 		pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
366 		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
367 		if (ask(0, "Use continuation from FAT %u", fatnum)) {
368 			*cp1 = *cp2;
369 			return FSFATMOD;
370 		}
371 		if (ask(0, "Use mark from FAT 0")) {
372 			*cp2 = *cp1;
373 			return FSFATMOD;
374 		}
375 		return FSFATAL;
376 	}
377 	if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
378 		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
379 		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
380 		if (ask(0, "Use continuation from FAT 0")) {
381 			*cp2 = *cp1;
382 			return FSFATMOD;
383 		}
384 		if (ask(0, "Use mark from FAT %d", fatnum)) {
385 			*cp1 = *cp2;
386 			return FSFATMOD;
387 		}
388 		return FSERROR;
389 	}
390 	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
391 	      cl, *cp1, *cp2, fatnum);
392 	if (ask(0, "Use continuation from FAT 0")) {
393 		*cp2 = *cp1;
394 		return FSFATMOD;
395 	}
396 	if (ask(0, "Use continuation from FAT %u", fatnum)) {
397 		*cp1 = *cp2;
398 		return FSFATMOD;
399 	}
400 	return FSERROR;
401 }
402 
403 /*
404  * Compare two FAT copies in memory. Resolve any conflicts and merge them
405  * into the first one.
406  */
407 int
408 comparefat(struct bootblock *boot, struct fatEntry *first,
409     struct fatEntry *second, u_int fatnum)
410 {
411 	cl_t cl;
412 	int ret = FSOK;
413 
414 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
415 		if (first[cl].next != second[cl].next)
416 			ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
417 	return ret;
418 }
419 
420 void
421 clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
422 {
423 	cl_t p, q;
424 
425 	for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
426 		if (fat[p].head != head)
427 			break;
428 		q = fat[p].next;
429 		fat[p].next = fat[p].head = CLUST_FREE;
430 		fat[p].length = 0;
431 	}
432 }
433 
434 int
435 tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
436 {
437 	if (ask(0, "Clear chain starting at %u", head)) {
438 		clearchain(boot, fat, head);
439 		return FSFATMOD;
440 	} else if (ask(0, "Truncate")) {
441 		uint32_t len;
442 		cl_t p;
443 
444 		for (p = head, len = 0;
445 		    p >= CLUST_FIRST && p < boot->NumClusters;
446 		    p = fat[p].next, len++)
447 			continue;
448 		*truncp = CLUST_EOF;
449 		fat[head].length = len;
450 		return FSFATMOD;
451 	} else
452 		return FSERROR;
453 }
454 
455 /*
456  * Check a complete FAT in-memory for crosslinks
457  */
458 int
459 checkfat(struct bootblock *boot, struct fatEntry *fat)
460 {
461 	cl_t head, p, h, n;
462 	u_int len;
463 	int ret = 0;
464 	int conf;
465 
466 	/*
467 	 * pass 1: figure out the cluster chains.
468 	 */
469 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
470 		/* find next untravelled chain */
471 		if (fat[head].head != 0		/* cluster already belongs to some chain */
472 		    || fat[head].next == CLUST_FREE
473 		    || fat[head].next == CLUST_BAD)
474 			continue;		/* skip it. */
475 
476 		/* follow the chain and mark all clusters on the way */
477 		for (len = 0, p = head;
478 		     p >= CLUST_FIRST && p < boot->NumClusters &&
479 		     fat[p].head != head;
480 		     p = fat[p].next) {
481 			fat[p].head = head;
482 			len++;
483 		}
484 
485 		/* the head record gets the length */
486 		fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
487 	}
488 
489 	/*
490 	 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
491 	 * we didn't know the real start of the chain then - would have treated partial
492 	 * chains as interlinked with their main chain)
493 	 */
494 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
495 		/* find next untravelled chain */
496 		if (fat[head].head != head)
497 			continue;
498 
499 		/* follow the chain to its end (hopefully) */
500 		for (len = fat[head].length, p = head;
501 		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
502 		     p = n)
503 			if (fat[n].head != head || len-- < 2)
504 				break;
505 		if (n >= CLUST_EOFS)
506 			continue;
507 
508 		if (n == CLUST_FREE || n >= CLUST_RSRVD) {
509 			pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
510 			      head, rsrvdcltype(n));
511 clear:
512 			ret |= tryclear(boot, fat, head, &fat[p].next);
513 			continue;
514 		}
515 		if (n < CLUST_FIRST || n >= boot->NumClusters) {
516 			pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
517 			    head, n);
518 			goto clear;
519 		}
520 		if (head == fat[n].head) {
521 			pwarn("Cluster chain starting at %u loops at cluster %u\n",
522 
523 			    head, p);
524 			goto clear;
525 		}
526 		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
527 		      head, fat[n].head, n);
528 		conf = tryclear(boot, fat, head, &fat[p].next);
529 		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
530 			if (conf == FSERROR) {
531 				/*
532 				 * Transfer the common chain to the one not cleared above.
533 				 */
534 				for (p = n;
535 				     p >= CLUST_FIRST && p < boot->NumClusters;
536 				     p = fat[p].next) {
537 					if (h != fat[p].head) {
538 						/*
539 						 * Have to reexamine this chain.
540 						 */
541 						head--;
542 						break;
543 					}
544 					fat[p].head = head;
545 				}
546 			}
547 			clearchain(boot, fat, h);
548 			conf |= FSFATMOD;
549 		}
550 		ret |= conf;
551 	}
552 
553 	return ret;
554 }
555 
556 /*
557  * Write out FATs encoding them from the internal format
558  */
559 int
560 writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
561 {
562 	u_char *buffer, *p;
563 	cl_t cl;
564 	u_int i;
565 	size_t fatsz;
566 	off_t off;
567 	int ret = FSOK;
568 
569 	buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec);
570 	if (buffer == NULL) {
571 		perr("No space for FAT sectors (%zu)", fatsz);
572 		return FSFATAL;
573 	}
574 	memset(buffer, 0, fatsz);
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 + 1].next << 4);
649 			*p++ = (u_char)(fat[cl + 1].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 (ret)
709 			mod |= writefsinfo(dosfs, boot);
710 	}
711 
712 	return mod;
713 }
714