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