xref: /dragonfly/sbin/fsck_msdosfs/fat.c (revision 207ba670)
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 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 
34 #include "ext.h"
35 #include "fsutil.h"
36 
37 static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
38 static int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
39 static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
40 static int _readfat(int, struct bootblock *, u_int, u_char **);
41 
42 /*
43  * Check a cluster number for valid value
44  */
45 static int
46 checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
47 {
48 	if (*next >= (CLUST_RSRVD&boot->ClustMask))
49 		*next |= ~boot->ClustMask;
50 	if (*next == CLUST_FREE) {
51 		boot->NumFree++;
52 		return FSOK;
53 	}
54 	if (*next == CLUST_BAD) {
55 		boot->NumBad++;
56 		return FSOK;
57 	}
58 	if (*next < CLUST_FIRST
59 	    || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
60 		pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
61 		      cl, fat,
62 		      *next < CLUST_RSRVD ? "out of range" : "reserved",
63 		      *next&boot->ClustMask);
64 		if (ask(0, "Truncate")) {
65 			*next = CLUST_EOF;
66 			return FSFATMOD;
67 		}
68 		return FSERROR;
69 	}
70 	return FSOK;
71 }
72 
73 /*
74  * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
75  */
76 static int
77 _readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
78 {
79 	off_t off;
80 
81 	*buffer = calloc(boot->FATsecs, boot->bpbBytesPerSec);
82 	if (*buffer == NULL) {
83 		perr("No space for FAT sectors (%zu)",
84 		    (size_t)boot->FATsecs);
85 		return 0;
86 	}
87 
88 	off = boot->bpbResSectors + no * boot->FATsecs;
89 	off *= boot->bpbBytesPerSec;
90 
91 	if (lseek(fs, off, SEEK_SET) != off) {
92 		perr("Unable to read FAT");
93 		goto err;
94 	}
95 
96 	if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
97 	    != boot->FATsecs * boot->bpbBytesPerSec) {
98 		perr("Unable to read FAT");
99 		goto err;
100 	}
101 
102 	return 1;
103 
104 err:
105 	free(*buffer);
106 	return 0;
107 }
108 
109 /*
110  * Read a FAT and decode it into internal format
111  */
112 int
113 readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
114 {
115 	struct fatEntry *fat;
116 	u_char *buffer, *p;
117 	cl_t cl;
118 	int ret = FSOK;
119 
120 	boot->NumFree = boot->NumBad = 0;
121 
122 	if (!_readfat(fs, boot, no, &buffer))
123 		return FSFATAL;
124 
125 	fat = calloc(boot->NumClusters, sizeof(struct fatEntry));
126 	if (fat == NULL) {
127 		perr("No space for FAT clusters (%zu)",
128 		    (size_t)boot->NumClusters);
129 		free(buffer);
130 		return FSFATAL;
131 	}
132 
133 	if (buffer[0] != boot->bpbMedia
134 	    || buffer[1] != 0xff || buffer[2] != 0xff
135 	    || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
136 	    || (boot->ClustMask == CLUST32_MASK
137 		&& ((buffer[3]&0x0f) != 0x0f
138 		    || buffer[4] != 0xff || buffer[5] != 0xff
139 		    || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
140 
141 		/* Windows 95 OSR2 (and possibly any later) changes
142 		 * the FAT signature to 0xXXffff7f for FAT16 and to
143 		 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
144 		 * file system is dirty if it doesn't reboot cleanly.
145 		 * Check this special condition before errorring out.
146 		 */
147 		if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
148 		    && buffer[2] == 0xff
149 		    && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
150 			|| (boot->ClustMask == CLUST32_MASK
151 			    && buffer[3] == 0x0f && buffer[4] == 0xff
152 			    && buffer[5] == 0xff && buffer[6] == 0xff
153 			    && buffer[7] == 0x07)))
154 			ret |= FSDIRTY;
155 		else {
156 			/* just some odd byte sequence in FAT */
157 			switch (boot->ClustMask) {
158 			case CLUST32_MASK:
159 				pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
160 				      "FAT starts with odd byte sequence",
161 				      buffer[0], buffer[1], buffer[2], buffer[3],
162 				      buffer[4], buffer[5], buffer[6], buffer[7]);
163 				break;
164 			case CLUST16_MASK:
165 				pwarn("%s (%02x%02x%02x%02x)\n",
166 				    "FAT starts with odd byte sequence",
167 				    buffer[0], buffer[1], buffer[2], buffer[3]);
168 				break;
169 			default:
170 				pwarn("%s (%02x%02x%02x)\n",
171 				    "FAT starts with odd byte sequence",
172 				    buffer[0], buffer[1], buffer[2]);
173 				break;
174 			}
175 
176 			if (ask(1, "Correct"))
177 				ret |= FSFIXFAT;
178 		}
179 	}
180 	switch (boot->ClustMask) {
181 	case CLUST32_MASK:
182 		p = buffer + 8;
183 		break;
184 	case CLUST16_MASK:
185 		p = buffer + 4;
186 		break;
187 	default:
188 		p = buffer + 3;
189 		break;
190 	}
191 	for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
192 		switch (boot->ClustMask) {
193 		case CLUST32_MASK:
194 			fat[cl].next = p[0] + (p[1] << 8)
195 				       + (p[2] << 16) + (p[3] << 24);
196 			fat[cl].next &= boot->ClustMask;
197 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
198 			cl++;
199 			p += 4;
200 			break;
201 		case CLUST16_MASK:
202 			fat[cl].next = p[0] + (p[1] << 8);
203 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
204 			cl++;
205 			p += 2;
206 			break;
207 		default:
208 			fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
209 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
210 			cl++;
211 			if (cl >= boot->NumClusters)
212 				break;
213 			fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
214 			ret |= checkclnum(boot, no, cl, &fat[cl].next);
215 			cl++;
216 			p += 3;
217 			break;
218 		}
219 	}
220 
221 	free(buffer);
222 	if (ret & FSFATAL) {
223 		free(fat);
224 		*fp = NULL;
225 	} else
226 		*fp = fat;
227 	return ret;
228 }
229 
230 /*
231  * Get type of reserved cluster
232  */
233 const char *
234 rsrvdcltype(cl_t cl)
235 {
236 	if (cl == CLUST_FREE)
237 		return "free";
238 	if (cl < CLUST_BAD)
239 		return "reserved";
240 	if (cl > CLUST_BAD)
241 		return "as EOF";
242 	return "bad";
243 }
244 
245 static int
246 clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
247 {
248 	if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
249 		if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
250 			if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
251 			     && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
252 			    || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
253 				pwarn("Cluster %u is marked %s with different indicators\n",
254 				      cl, rsrvdcltype(*cp1));
255 				if (ask(1, "Fix")) {
256 					*cp2 = *cp1;
257 					return FSFATMOD;
258 				}
259 				return FSFATAL;
260 			}
261 			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
262 			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
263 			if (ask(0, "Use FAT 0's entry")) {
264 				*cp2 = *cp1;
265 				return FSFATMOD;
266 			}
267 			if (ask(0, "Use FAT %u's entry", fatnum)) {
268 				*cp1 = *cp2;
269 				return FSFATMOD;
270 			}
271 			return FSFATAL;
272 		}
273 		pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
274 		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
275 		if (ask(0, "Use continuation from FAT %u", fatnum)) {
276 			*cp1 = *cp2;
277 			return FSFATMOD;
278 		}
279 		if (ask(0, "Use mark from FAT 0")) {
280 			*cp2 = *cp1;
281 			return FSFATMOD;
282 		}
283 		return FSFATAL;
284 	}
285 	if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
286 		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
287 		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
288 		if (ask(0, "Use continuation from FAT 0")) {
289 			*cp2 = *cp1;
290 			return FSFATMOD;
291 		}
292 		if (ask(0, "Use mark from FAT %d", fatnum)) {
293 			*cp1 = *cp2;
294 			return FSFATMOD;
295 		}
296 		return FSERROR;
297 	}
298 	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
299 	      cl, *cp1, *cp2, fatnum);
300 	if (ask(0, "Use continuation from FAT 0")) {
301 		*cp2 = *cp1;
302 		return FSFATMOD;
303 	}
304 	if (ask(0, "Use continuation from FAT %u", fatnum)) {
305 		*cp1 = *cp2;
306 		return FSFATMOD;
307 	}
308 	return FSERROR;
309 }
310 
311 /*
312  * Compare two FAT copies in memory. Resolve any conflicts and merge them
313  * into the first one.
314  */
315 int
316 comparefat(struct bootblock *boot, struct fatEntry *first,
317     struct fatEntry *second, u_int fatnum)
318 {
319 	cl_t cl;
320 	int ret = FSOK;
321 
322 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
323 		if (first[cl].next != second[cl].next)
324 			ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
325 	return ret;
326 }
327 
328 void
329 clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
330 {
331 	cl_t p, q;
332 
333 	for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
334 		if (fat[p].head != head)
335 			break;
336 		q = fat[p].next;
337 		fat[p].next = fat[p].head = CLUST_FREE;
338 		fat[p].length = 0;
339 	}
340 }
341 
342 int
343 tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
344 {
345 	if (ask(0, "Clear chain starting at %u", head)) {
346 		clearchain(boot, fat, head);
347 		return FSFATMOD;
348 	} else if (ask(0, "Truncate")) {
349 		uint32_t len;
350 		cl_t p;
351 
352 		for (p = head, len = 0;
353 		    p >= CLUST_FIRST && p < boot->NumClusters;
354 		    p = fat[p].next, len++)
355 			continue;
356 		*truncp = CLUST_EOF;
357 		fat[head].length = len;
358 		return FSFATMOD;
359 	} else
360 		return FSERROR;
361 }
362 
363 /*
364  * Check a complete FAT in-memory for crosslinks
365  */
366 int
367 checkfat(struct bootblock *boot, struct fatEntry *fat)
368 {
369 	cl_t head, p, h, n;
370 	u_int len;
371 	int ret = 0;
372 	int conf;
373 
374 	/*
375 	 * pass 1: figure out the cluster chains.
376 	 */
377 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
378 		/* find next untravelled chain */
379 		if (fat[head].head != 0		/* cluster already belongs to some chain */
380 		    || fat[head].next == CLUST_FREE
381 		    || fat[head].next == CLUST_BAD)
382 			continue;		/* skip it. */
383 
384 		/* follow the chain and mark all clusters on the way */
385 		for (len = 0, p = head;
386 		     p >= CLUST_FIRST && p < boot->NumClusters &&
387 		     fat[p].head != head;
388 		     p = fat[p].next) {
389 			fat[p].head = head;
390 			len++;
391 		}
392 
393 		/* the head record gets the length */
394 		fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
395 	}
396 
397 	/*
398 	 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
399 	 * we didn't know the real start of the chain then - would have treated partial
400 	 * chains as interlinked with their main chain)
401 	 */
402 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
403 		/* find next untravelled chain */
404 		if (fat[head].head != head)
405 			continue;
406 
407 		/* follow the chain to its end (hopefully) */
408 		for (len = fat[head].length, p = head;
409 		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
410 		     p = n)
411 			if (fat[n].head != head || len-- < 2)
412 				break;
413 		if (n >= CLUST_EOFS)
414 			continue;
415 
416 		if (n == CLUST_FREE || n >= CLUST_RSRVD) {
417 			pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
418 			      head, rsrvdcltype(n));
419 clear:
420 			ret |= tryclear(boot, fat, head, &fat[p].next);
421 			continue;
422 		}
423 		if (n < CLUST_FIRST || n >= boot->NumClusters) {
424 			pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
425 			    head, n);
426 			goto clear;
427 		}
428 		if (head == fat[n].head) {
429 			pwarn("Cluster chain starting at %u loops at cluster %u\n",
430 			    head, p);
431 			goto clear;
432 		}
433 		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
434 		      head, fat[n].head, n);
435 		conf = tryclear(boot, fat, head, &fat[p].next);
436 		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
437 			if (conf == FSERROR) {
438 				/*
439 				 * Transfer the common chain to the one not cleared above.
440 				 */
441 				for (p = n;
442 				     p >= CLUST_FIRST && p < boot->NumClusters;
443 				     p = fat[p].next) {
444 					if (h != fat[p].head) {
445 						/*
446 						 * Have to reexamine this chain.
447 						 */
448 						head--;
449 						break;
450 					}
451 					fat[p].head = head;
452 				}
453 			}
454 			clearchain(boot, fat, h);
455 			conf |= FSFATMOD;
456 		}
457 		ret |= conf;
458 	}
459 
460 	return ret;
461 }
462 
463 /*
464  * Write out FATs encoding them from the internal format
465  */
466 int
467 writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
468 {
469 	u_char *buffer, *p;
470 	cl_t cl;
471 	u_int i;
472 	size_t fatsz;
473 	off_t off;
474 	int ret = FSOK;
475 
476 	fatsz = boot->FATsecs * boot->bpbBytesPerSec;
477 	buffer = calloc(boot->FATsecs, boot->bpbBytesPerSec);
478 	if (buffer == NULL) {
479 		perr("No space for FAT sectors (%zu)",
480 		    (size_t)boot->FATsecs);
481 		return FSFATAL;
482 	}
483 	boot->NumFree = 0;
484 	p = buffer;
485 	if (correct_fat) {
486 		*p++ = (u_char)boot->bpbMedia;
487 		*p++ = 0xff;
488 		*p++ = 0xff;
489 		switch (boot->ClustMask) {
490 		case CLUST16_MASK:
491 			*p++ = 0xff;
492 			break;
493 		case CLUST32_MASK:
494 			*p++ = 0x0f;
495 			*p++ = 0xff;
496 			*p++ = 0xff;
497 			*p++ = 0xff;
498 			*p++ = 0x0f;
499 			break;
500 		}
501 	} else {
502 		/* use same FAT signature as the old FAT has */
503 		int count;
504 		u_char *old_fat;
505 
506 		switch (boot->ClustMask) {
507 		case CLUST32_MASK:
508 			count = 8;
509 			break;
510 		case CLUST16_MASK:
511 			count = 4;
512 			break;
513 		default:
514 			count = 3;
515 			break;
516 		}
517 
518 		if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
519 					 &old_fat)) {
520 			free(buffer);
521 			return FSFATAL;
522 		}
523 
524 		memcpy(p, old_fat, count);
525 		free(old_fat);
526 		p += count;
527 	}
528 
529 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
530 		switch (boot->ClustMask) {
531 		case CLUST32_MASK:
532 			if (fat[cl].next == CLUST_FREE)
533 				boot->NumFree++;
534 			*p++ = (u_char)fat[cl].next;
535 			*p++ = (u_char)(fat[cl].next >> 8);
536 			*p++ = (u_char)(fat[cl].next >> 16);
537 			*p &= 0xf0;
538 			*p++ |= (fat[cl].next >> 24)&0x0f;
539 			break;
540 		case CLUST16_MASK:
541 			if (fat[cl].next == CLUST_FREE)
542 				boot->NumFree++;
543 			*p++ = (u_char)fat[cl].next;
544 			*p++ = (u_char)(fat[cl].next >> 8);
545 			break;
546 		default:
547 			if (fat[cl].next == CLUST_FREE)
548 				boot->NumFree++;
549 			*p++ = (u_char)fat[cl].next;
550 			*p = (u_char)((fat[cl].next >> 8) & 0xf);
551 			cl++;
552 			if (cl >= boot->NumClusters)
553 				break;
554 			if (fat[cl].next == CLUST_FREE)
555 				boot->NumFree++;
556 			*p++ |= (u_char)(fat[cl].next << 4);
557 			*p++ = (u_char)(fat[cl].next >> 4);
558 			break;
559 		}
560 	}
561 	for (i = 0; i < boot->bpbFATs; i++) {
562 		off = boot->bpbResSectors + i * boot->FATsecs;
563 		off *= boot->bpbBytesPerSec;
564 		if (lseek(fs, off, SEEK_SET) != off
565 		    || (size_t)write(fs, buffer, fatsz) != fatsz) {
566 			perr("Unable to write FAT");
567 			ret = FSFATAL; /* Return immediately?		XXX */
568 		}
569 	}
570 	free(buffer);
571 	return ret;
572 }
573 
574 /*
575  * Check a complete in-memory FAT for lost cluster chains
576  */
577 int
578 checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
579 {
580 	cl_t head;
581 	int mod = FSOK;
582 	int ret;
583 
584 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
585 		/* find next untravelled chain */
586 		if (fat[head].head != head
587 		    || fat[head].next == CLUST_FREE
588 		    || (fat[head].next >= CLUST_RSRVD
589 			&& fat[head].next < CLUST_EOFS)
590 		    || (fat[head].flags & FAT_USED))
591 			continue;
592 
593 		pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
594 		      head, fat[head].length);
595 		mod |= ret = reconnect(dosfs, boot, fat, head);
596 		if (mod & FSFATAL)
597 			break;
598 		if (ret == FSERROR && ask(0, "Clear")) {
599 			clearchain(boot, fat, head);
600 			mod |= FSFATMOD;
601 		}
602 	}
603 	finishlf();
604 
605 	if (boot->bpbFSInfo) {
606 		ret = 0;
607 		if (boot->FSFree != 0xffffffffU &&
608 		    boot->FSFree != boot->NumFree) {
609 			pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
610 			      boot->FSFree, boot->NumFree);
611 			if (ask(1, "Fix")) {
612 				boot->FSFree = boot->NumFree;
613 				ret = 1;
614 			}
615 		}
616 		if (boot->FSNext != 0xffffffffU &&
617 		    (boot->FSNext >= boot->NumClusters ||
618 		    (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) {
619 			pwarn("Next free cluster in FSInfo block (%u) %s\n",
620 			      boot->FSNext,
621 			      (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free");
622 			if (ask(1, "fix"))
623 				for (head = CLUST_FIRST; head < boot->NumClusters; head++)
624 					if (fat[head].next == CLUST_FREE) {
625 						boot->FSNext = head;
626 						ret = 1;
627 						break;
628 					}
629 		}
630 		if (ret)
631 			mod |= writefsinfo(dosfs, boot);
632 	}
633 
634 	return mod;
635 }
636