fat.c revision 1.11 1 /* $NetBSD: fat.c,v 1.11 2000/04/26 16:45:02 jdolecek 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.11 2000/04/26 16:45:02 jdolecek 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 const char *msg;
164
165 /* Windows 95 OSR2 (and possibly any later) changes
166 * the FAT signature to 0xXXffff7f for FAT16 and to
167 * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
168 * filesystem is dirty if it doesn't reboot cleanly.
169 * Check this special condition before errorring out.
170 */
171 if (buffer[0] == boot->Media && buffer[1] == 0xff
172 && buffer[2] == 0xff
173 && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
174 || (boot->ClustMask == CLUST32_MASK
175 && buffer[3] == 0x0f && buffer[4] == 0xff
176 && buffer[5] == 0xff && buffer[6] == 0xff
177 && buffer[7] == 0x07)))
178 ret |= FSDIRTY;
179 else {
180 /* just some odd byte sequence in FAT */
181
182 switch (boot->ClustMask) {
183 case CLUST32_MASK:
184 msg = "%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n";
185 break;
186 case CLUST16_MASK:
187 msg = "%s (%02x%02x%02x%02x)\n";
188 break;
189 default:
190 msg = "%s (%02x%02x%02x)\n";
191 break;
192 }
193
194 pwarn(msg, "FAT starts with odd byte sequence",
195 buffer[0], buffer[1], buffer[2], buffer[3],
196 buffer[4], buffer[5], buffer[6], buffer[7]);
197
198 if (ask(1, "Correct"))
199 ret |= FSFIXFAT;
200 }
201 }
202 switch (boot->ClustMask) {
203 case CLUST32_MASK:
204 p = buffer + 8;
205 break;
206 case CLUST16_MASK:
207 p = buffer + 4;
208 break;
209 default:
210 p = buffer + 3;
211 break;
212 }
213 for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
214 switch (boot->ClustMask) {
215 case CLUST32_MASK:
216 fat[cl].next = p[0] + (p[1] << 8)
217 + (p[2] << 16) + (p[3] << 24);
218 fat[cl].next &= boot->ClustMask;
219 ret |= checkclnum(boot, no, cl, &fat[cl].next);
220 cl++;
221 p += 4;
222 break;
223 case CLUST16_MASK:
224 fat[cl].next = p[0] + (p[1] << 8);
225 ret |= checkclnum(boot, no, cl, &fat[cl].next);
226 cl++;
227 p += 2;
228 break;
229 default:
230 fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
231 ret |= checkclnum(boot, no, cl, &fat[cl].next);
232 cl++;
233 if (cl >= boot->NumClusters)
234 break;
235 fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
236 ret |= checkclnum(boot, no, cl, &fat[cl].next);
237 cl++;
238 p += 3;
239 break;
240 }
241 }
242
243 free(buffer);
244 *fp = fat;
245 return ret;
246 }
247
248 /*
249 * Get type of reserved cluster
250 */
251 char *
252 rsrvdcltype(cl)
253 cl_t cl;
254 {
255 if (cl == CLUST_FREE)
256 return "free";
257 if (cl < CLUST_BAD)
258 return "reserved";
259 if (cl > CLUST_BAD)
260 return "as EOF";
261 return "bad";
262 }
263
264 static int
265 clustdiffer(cl, cp1, cp2, fatnum)
266 cl_t cl;
267 cl_t *cp1;
268 cl_t *cp2;
269 int fatnum;
270 {
271 if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
272 if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
273 if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
274 && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
275 || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
276 pwarn("Cluster %u is marked %s with different indicators, ",
277 cl, rsrvdcltype(*cp1));
278 if (ask(1, "fix")) {
279 *cp2 = *cp1;
280 return FSFATMOD;
281 }
282 return FSFATAL;
283 }
284 pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n",
285 cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
286 if (ask(0, "use FAT 0's entry")) {
287 *cp2 = *cp1;
288 return FSFATMOD;
289 }
290 if (ask(0, "use FAT %d's entry", fatnum)) {
291 *cp1 = *cp2;
292 return FSFATMOD;
293 }
294 return FSFATAL;
295 }
296 pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
297 cl, rsrvdcltype(*cp1), *cp2, fatnum);
298 if (ask(0, "Use continuation from FAT %d", fatnum)) {
299 *cp1 = *cp2;
300 return FSFATMOD;
301 }
302 if (ask(0, "Use mark from FAT 0")) {
303 *cp2 = *cp1;
304 return FSFATMOD;
305 }
306 return FSFATAL;
307 }
308 if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
309 pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n",
310 cl, *cp1, rsrvdcltype(*cp2), fatnum);
311 if (ask(0, "Use continuation from FAT 0")) {
312 *cp2 = *cp1;
313 return FSFATMOD;
314 }
315 if (ask(0, "Use mark from FAT %d", fatnum)) {
316 *cp1 = *cp2;
317 return FSFATMOD;
318 }
319 return FSERROR;
320 }
321 pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n",
322 cl, *cp1, *cp2, fatnum);
323 if (ask(0, "Use continuation from FAT 0")) {
324 *cp2 = *cp1;
325 return FSFATMOD;
326 }
327 if (ask(0, "Use continuation from FAT %d", fatnum)) {
328 *cp1 = *cp2;
329 return FSFATMOD;
330 }
331 return FSERROR;
332 }
333
334 /*
335 * Compare two FAT copies in memory. Resolve any conflicts and merge them
336 * into the first one.
337 */
338 int
339 comparefat(boot, first, second, fatnum)
340 struct bootblock *boot;
341 struct fatEntry *first;
342 struct fatEntry *second;
343 int fatnum;
344 {
345 cl_t cl;
346 int ret = FSOK;
347
348 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
349 if (first[cl].next != second[cl].next)
350 ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
351 return ret;
352 }
353
354 void
355 clearchain(boot, fat, head)
356 struct bootblock *boot;
357 struct fatEntry *fat;
358 cl_t head;
359 {
360 cl_t p, q;
361
362 for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
363 if (fat[p].head != head)
364 break;
365 q = fat[p].next;
366 fat[p].next = fat[p].head = CLUST_FREE;
367 fat[p].length = 0;
368 }
369 }
370
371 int
372 tryclear(boot, fat, head, trunc)
373 struct bootblock *boot;
374 struct fatEntry *fat;
375 cl_t head;
376 cl_t *trunc;
377 {
378 if (ask(0, "Clear chain starting at %u", head)) {
379 clearchain(boot, fat, head);
380 return FSFATMOD;
381 } else if (ask(0, "Truncate")) {
382 *trunc = CLUST_EOF;
383 return FSFATMOD;
384 } else
385 return FSERROR;
386 }
387
388 /*
389 * Check a complete FAT in-memory for crosslinks
390 */
391 int
392 checkfat(boot, fat)
393 struct bootblock *boot;
394 struct fatEntry *fat;
395 {
396 cl_t head, p, h, n;
397 u_int len;
398 int ret = 0;
399 int conf;
400
401 /*
402 * pass 1: figure out the cluster chains.
403 */
404 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
405 /* find next untravelled chain */
406 if (fat[head].head != 0 /* cluster already belongs to some chain */
407 || fat[head].next == CLUST_FREE
408 || fat[head].next == CLUST_BAD)
409 continue; /* skip it. */
410
411 /* follow the chain and mark all clusters on the way */
412 for (len = 0, p = head;
413 p >= CLUST_FIRST && p < boot->NumClusters;
414 p = fat[p].next) {
415 fat[p].head = head;
416 len++;
417 }
418
419 /* the head record gets the length */
420 fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
421 }
422
423 /*
424 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
425 * we didn't know the real start of the chain then - would have treated partial
426 * chains as interlinked with their main chain)
427 */
428 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
429 /* find next untravelled chain */
430 if (fat[head].head != head)
431 continue;
432
433 /* follow the chain to its end (hopefully) */
434 for (p = head;
435 (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
436 p = n)
437 if (fat[n].head != head)
438 break;
439 if (n >= CLUST_EOFS)
440 continue;
441
442 if (n == CLUST_FREE || n >= CLUST_RSRVD) {
443 pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
444 head, rsrvdcltype(n));
445 ret |= tryclear(boot, fat, head, &fat[p].next);
446 continue;
447 }
448 if (n < CLUST_FIRST || n >= boot->NumClusters) {
449 pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
450 head, n);
451 ret |= tryclear(boot, fat, head, &fat[p].next);
452 continue;
453 }
454 pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
455 head, fat[n].head, n);
456 conf = tryclear(boot, fat, head, &fat[p].next);
457 if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
458 if (conf == FSERROR) {
459 /*
460 * Transfer the common chain to the one not cleared above.
461 */
462 for (p = n;
463 p >= CLUST_FIRST && p < boot->NumClusters;
464 p = fat[p].next) {
465 if (h != fat[p].head) {
466 /*
467 * Have to reexamine this chain.
468 */
469 head--;
470 break;
471 }
472 fat[p].head = head;
473 }
474 }
475 clearchain(boot, fat, h);
476 conf |= FSFATMOD;
477 }
478 ret |= conf;
479 }
480
481 return ret;
482 }
483
484 /*
485 * Write out FATs encoding them from the internal format
486 */
487 int
488 writefat(fs, boot, fat, correct_fat)
489 int fs;
490 struct bootblock *boot;
491 struct fatEntry *fat;
492 int correct_fat;
493 {
494 u_char *buffer, *p;
495 cl_t cl;
496 int i;
497 u_int32_t fatsz;
498 off_t off;
499 int ret = FSOK;
500
501 buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
502 if (buffer == NULL) {
503 perror("No space for FAT");
504 return FSFATAL;
505 }
506 memset(buffer, 0, fatsz);
507 boot->NumFree = 0;
508 p = buffer;
509 if (correct_fat) {
510 *p++ = (u_char)boot->Media;
511 *p++ = 0xff;
512 *p++ = 0xff;
513 switch (boot->ClustMask) {
514 case CLUST16_MASK:
515 *p++ = 0xff;
516 break;
517 case CLUST32_MASK:
518 *p++ = 0x0f;
519 *p++ = 0xff;
520 *p++ = 0xff;
521 *p++ = 0xff;
522 *p++ = 0x0f;
523 break;
524 }
525 } else {
526 /* use same FAT signature as the old FAT has */
527 int count;
528 u_char *old_fat;
529
530 switch (boot->ClustMask) {
531 case CLUST32_MASK:
532 count = 8;
533 break;
534 case CLUST16_MASK:
535 count = 4;
536 break;
537 default:
538 count = 3;
539 break;
540 }
541
542 if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
543 &old_fat)) {
544 free(buffer);
545 return FSFATAL;
546 }
547
548 memcpy(p, old_fat, count);
549 free(old_fat);
550 p += count;
551 }
552
553 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
554 switch (boot->ClustMask) {
555 case CLUST32_MASK:
556 if (fat[cl].next == CLUST_FREE)
557 boot->NumFree++;
558 *p++ = (u_char)fat[cl].next;
559 *p++ = (u_char)(fat[cl].next >> 8);
560 *p++ = (u_char)(fat[cl].next >> 16);
561 *p &= 0xf0;
562 *p++ |= (fat[cl].next >> 24)&0x0f;
563 break;
564 case CLUST16_MASK:
565 if (fat[cl].next == CLUST_FREE)
566 boot->NumFree++;
567 *p++ = (u_char)fat[cl].next;
568 *p++ = (u_char)(fat[cl].next >> 8);
569 break;
570 default:
571 if (fat[cl].next == CLUST_FREE)
572 boot->NumFree++;
573 if (cl + 1 < boot->NumClusters
574 && fat[cl + 1].next == CLUST_FREE)
575 boot->NumFree++;
576 *p++ = (u_char)fat[cl].next;
577 *p++ = (u_char)((fat[cl].next >> 8) & 0xf)
578 |(u_char)(fat[cl+1].next << 4);
579 *p++ = (u_char)(fat[++cl].next >> 4);
580 break;
581 }
582 }
583 for (i = 0; i < boot->FATs; i++) {
584 off = boot->ResSectors + i * boot->FATsecs;
585 off *= boot->BytesPerSec;
586 if (lseek(fs, off, SEEK_SET) != off
587 || write(fs, buffer, fatsz) != fatsz) {
588 perror("Unable to write FAT");
589 ret = FSFATAL; /* Return immediately? XXX */
590 }
591 }
592 free(buffer);
593 return ret;
594 }
595
596 /*
597 * Check a complete in-memory FAT for lost cluster chains
598 */
599 int
600 checklost(dosfs, boot, fat)
601 int dosfs;
602 struct bootblock *boot;
603 struct fatEntry *fat;
604 {
605 cl_t head;
606 int mod = FSOK;
607 int ret;
608
609 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
610 /* find next untravelled chain */
611 if (fat[head].head != head
612 || fat[head].next == CLUST_FREE
613 || (fat[head].next >= CLUST_RSRVD
614 && fat[head].next < CLUST_EOFS)
615 || (fat[head].flags & FAT_USED))
616 continue;
617
618 pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
619 head, fat[head].length);
620 mod |= ret = reconnect(dosfs, boot, fat, head);
621 if (mod & FSFATAL)
622 break;
623 if (ret == FSERROR && ask(0, "Clear")) {
624 clearchain(boot, fat, head);
625 mod |= FSFATMOD;
626 }
627 }
628 finishlf();
629
630 if (boot->FSInfo) {
631 ret = 0;
632 if (boot->FSFree != boot->NumFree) {
633 pwarn("Free space in FSInfo block (%d) not correct (%d)\n",
634 boot->FSFree, boot->NumFree);
635 if (ask(1, "fix")) {
636 boot->FSFree = boot->NumFree;
637 ret = 1;
638 }
639 }
640 if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) {
641 pwarn("Next free cluster in FSInfo block (%u) not free\n",
642 boot->FSNext);
643 if (ask(1, "fix"))
644 for (head = CLUST_FIRST; head < boot->NumClusters; head++)
645 if (fat[head].next == CLUST_FREE) {
646 boot->FSNext = head;
647 ret = 1;
648 break;
649 }
650 }
651 if (ret)
652 mod |= writefsinfo(dosfs, boot);
653 }
654
655 return mod;
656 }
657