xref: /freebsd/sbin/fsck_msdosfs/fat.c (revision 5ab1c5846ff41be24b1f6beb0317bf8258cd4409)
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 			    head, p);
522 			goto clear;
523 		}
524 		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
525 		      head, fat[n].head, n);
526 		conf = tryclear(boot, fat, head, &fat[p].next);
527 		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
528 			if (conf == FSERROR) {
529 				/*
530 				 * Transfer the common chain to the one not cleared above.
531 				 */
532 				for (p = n;
533 				     p >= CLUST_FIRST && p < boot->NumClusters;
534 				     p = fat[p].next) {
535 					if (h != fat[p].head) {
536 						/*
537 						 * Have to reexamine this chain.
538 						 */
539 						head--;
540 						break;
541 					}
542 					fat[p].head = head;
543 				}
544 			}
545 			clearchain(boot, fat, h);
546 			conf |= FSFATMOD;
547 		}
548 		ret |= conf;
549 	}
550 
551 	return ret;
552 }
553 
554 /*
555  * Write out FATs encoding them from the internal format
556  */
557 int
558 writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
559 {
560 	u_char *buffer, *p;
561 	cl_t cl;
562 	u_int i;
563 	size_t fatsz;
564 	off_t off;
565 	int ret = FSOK;
566 
567 	fatsz = boot->FATsecs * boot->bpbBytesPerSec;
568 	buffer = calloc(boot->FATsecs, boot->bpbBytesPerSec);
569 	if (buffer == NULL) {
570 		perr("No space for FAT sectors (%zu)",
571 		    (size_t)boot->FATsecs);
572 		return FSFATAL;
573 	}
574 	boot->NumFree = 0;
575 	p = buffer;
576 	if (correct_fat) {
577 		*p++ = (u_char)boot->bpbMedia;
578 		*p++ = 0xff;
579 		*p++ = 0xff;
580 		switch (boot->ClustMask) {
581 		case CLUST16_MASK:
582 			*p++ = 0xff;
583 			break;
584 		case CLUST32_MASK:
585 			*p++ = 0x0f;
586 			*p++ = 0xff;
587 			*p++ = 0xff;
588 			*p++ = 0xff;
589 			*p++ = 0x0f;
590 			break;
591 		}
592 	} else {
593 		/* use same FAT signature as the old FAT has */
594 		int count;
595 		u_char *old_fat;
596 
597 		switch (boot->ClustMask) {
598 		case CLUST32_MASK:
599 			count = 8;
600 			break;
601 		case CLUST16_MASK:
602 			count = 4;
603 			break;
604 		default:
605 			count = 3;
606 			break;
607 		}
608 
609 		if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
610 					 &old_fat)) {
611 			free(buffer);
612 			return FSFATAL;
613 		}
614 
615 		memcpy(p, old_fat, count);
616 		free(old_fat);
617 		p += count;
618 	}
619 
620 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
621 		switch (boot->ClustMask) {
622 		case CLUST32_MASK:
623 			if (fat[cl].next == CLUST_FREE)
624 				boot->NumFree++;
625 			*p++ = (u_char)fat[cl].next;
626 			*p++ = (u_char)(fat[cl].next >> 8);
627 			*p++ = (u_char)(fat[cl].next >> 16);
628 			*p &= 0xf0;
629 			*p++ |= (fat[cl].next >> 24)&0x0f;
630 			break;
631 		case CLUST16_MASK:
632 			if (fat[cl].next == CLUST_FREE)
633 				boot->NumFree++;
634 			*p++ = (u_char)fat[cl].next;
635 			*p++ = (u_char)(fat[cl].next >> 8);
636 			break;
637 		default:
638 			if (fat[cl].next == CLUST_FREE)
639 				boot->NumFree++;
640 			*p++ = (u_char)fat[cl].next;
641 			*p = (u_char)((fat[cl].next >> 8) & 0xf);
642 			cl++;
643 			if (cl >= boot->NumClusters)
644 				break;
645 			if (fat[cl].next == CLUST_FREE)
646 				boot->NumFree++;
647 			*p++ |= (u_char)(fat[cl].next << 4);
648 			*p++ = (u_char)(fat[cl].next >> 4);
649 			break;
650 		}
651 	}
652 	for (i = 0; i < boot->bpbFATs; i++) {
653 		off = boot->bpbResSectors + i * boot->FATsecs;
654 		off *= boot->bpbBytesPerSec;
655 		if (lseek(fs, off, SEEK_SET) != off
656 		    || (size_t)write(fs, buffer, fatsz) != fatsz) {
657 			perr("Unable to write FAT");
658 			ret = FSFATAL; /* Return immediately?		XXX */
659 		}
660 	}
661 	free(buffer);
662 	return ret;
663 }
664 
665 /*
666  * Check a complete in-memory FAT for lost cluster chains
667  */
668 int
669 checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
670 {
671 	cl_t head;
672 	int mod = FSOK;
673 	int ret;
674 
675 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
676 		/* find next untravelled chain */
677 		if (fat[head].head != head
678 		    || fat[head].next == CLUST_FREE
679 		    || (fat[head].next >= CLUST_RSRVD
680 			&& fat[head].next < CLUST_EOFS)
681 		    || (fat[head].flags & FAT_USED))
682 			continue;
683 
684 		pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
685 		      head, fat[head].length);
686 		mod |= ret = reconnect(dosfs, boot, fat, head);
687 		if (mod & FSFATAL)
688 			break;
689 		if (ret == FSERROR && ask(0, "Clear")) {
690 			clearchain(boot, fat, head);
691 			mod |= FSFATMOD;
692 		}
693 	}
694 	finishlf();
695 
696 	if (boot->bpbFSInfo) {
697 		ret = 0;
698 		if (boot->FSFree != 0xffffffffU &&
699 		    boot->FSFree != boot->NumFree) {
700 			pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
701 			      boot->FSFree, boot->NumFree);
702 			if (ask(1, "Fix")) {
703 				boot->FSFree = boot->NumFree;
704 				ret = 1;
705 			}
706 		}
707 		if (boot->FSNext != 0xffffffffU &&
708 		    (boot->FSNext >= boot->NumClusters ||
709 		    (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) {
710 			pwarn("Next free cluster in FSInfo block (%u) %s\n",
711 			      boot->FSNext,
712 			      (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free");
713 			if (ask(1, "fix"))
714 				for (head = CLUST_FIRST; head < boot->NumClusters; head++)
715 					if (fat[head].next == CLUST_FREE) {
716 						boot->FSNext = head;
717 						ret = 1;
718 						break;
719 					}
720 		}
721 		if (ret)
722 			mod |= writefsinfo(dosfs, boot);
723 	}
724 
725 	return mod;
726 }
727