fat.c revision 1.5 1 /* $NetBSD: fat.c,v 1.5 1997/01/03 14:32:49 ws Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996 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 #ifndef lint
37 static char rcsid[] = "$NetBSD: fat.c,v 1.5 1997/01/03 14:32:49 ws Exp $";
38 #endif /* not lint */
39
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <stdio.h>
44 #include <unistd.h>
45
46 #include "ext.h"
47 #include "fsutil.h"
48
49 static int checkclnum __P((struct bootblock *, int, cl_t, cl_t *));
50 static int clustdiffer __P((cl_t, cl_t *, cl_t *, int));
51
52 /*
53 * Check a cluster number for valid value
54 */
55 static int
56 checkclnum(boot, fat, cl, next)
57 struct bootblock *boot;
58 int fat;
59 cl_t cl;
60 cl_t *next;
61 {
62 if (!boot->Is16BitFat && *next >= (CLUST_RSRVD&0xfff))
63 *next |= 0xf000;
64 if (*next == CLUST_FREE) {
65 boot->NumFree++;
66 return FSOK;
67 }
68 if (*next == CLUST_BAD) {
69 boot->NumBad++;
70 return FSOK;
71 }
72 if (*next < CLUST_FIRST
73 || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
74 pwarn("Cluster %d in FAT %d continues with %s cluster number %d\n",
75 cl, fat,
76 *next < CLUST_RSRVD ? "out of range" : "reserved",
77 *next);
78 if (ask(0, "Truncate")) {
79 *next = CLUST_EOF;
80 return FSFATMOD;
81 }
82 return FSERROR;
83 }
84 return FSOK;
85 }
86
87 /*
88 * Read a FAT and decode it into internal format
89 */
90 int
91 readfat(fs, boot, no, fp)
92 int fs;
93 struct bootblock *boot;
94 int no;
95 struct fatEntry **fp;
96 {
97 struct fatEntry *fat;
98 u_char *buffer, *p;
99 cl_t cl;
100 off_t off;
101 int size;
102 int ret = FSOK;
103
104 boot->NumFree = boot->NumBad = 0;
105 fat = malloc(sizeof(struct fatEntry) * boot->NumClusters);
106 buffer = malloc(boot->FATsecs * boot->BytesPerSec);
107 if (fat == NULL || buffer == NULL) {
108 perror("No space for FAT");
109 if (fat)
110 free(fat);
111 return FSFATAL;
112 }
113
114 memset(fat, 0, sizeof(struct fatEntry) * boot->NumClusters);
115
116 off = boot->ResSectors + no * boot->FATsecs;
117 off *= boot->BytesPerSec;
118
119 if (lseek(fs, off, SEEK_SET) != off) {
120 perror("Unable to read FAT");
121 free(buffer);
122 free(fat);
123 return FSFATAL;
124 }
125
126 if ((size = read(fs, buffer, boot->FATsecs * boot->BytesPerSec))
127 != boot->FATsecs * boot->BytesPerSec) {
128 if (size < 0)
129 perror("Unable to read FAT");
130 else
131 pfatal("Short FAT?");
132 free(buffer);
133 free(fat);
134 return FSFATAL;
135 }
136
137 /*
138 * Remember start of FAT to allow keeping it in write_fat.
139 */
140 fat[0].length = buffer[0]|(buffer[1] << 8)|(buffer[2] << 16);
141 if (boot->Is16BitFat)
142 fat[0].length |= buffer[3] << 24;
143 if (buffer[1] != 0xff || buffer[2] != 0xff
144 || (boot->Is16BitFat && buffer[3] != 0xff)) {
145 char *msg = boot->Is16BitFat
146 ? "FAT starts with odd byte sequence (%02x%02x%02x%02x)\n"
147 : "FAT starts with odd byte sequence (%02x%02x%02x)\n";
148 pwarn(msg, buffer[0], buffer[1], buffer[2], buffer[3]);
149 if (ask(1, "Correct")) {
150 fat[0].length = boot->Media|0xffffff;
151 ret |= FSFATMOD;
152 }
153 }
154 p = buffer + (boot->Is16BitFat ? 4 : 3);
155 for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
156 if (boot->Is16BitFat) {
157 fat[cl].next = p[0] + (p[1] << 8);
158 ret |= checkclnum(boot, no, cl, &fat[cl].next);
159 cl++;
160 p += 2;
161 } else {
162 fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
163 ret |= checkclnum(boot, no, cl, &fat[cl].next);
164 cl++;
165 if (cl >= boot->NumClusters)
166 break;
167 fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
168 ret |= checkclnum(boot, no, cl, &fat[cl].next);
169 cl++;
170 p += 3;
171 }
172 }
173
174 free(buffer);
175 *fp = fat;
176 return ret;
177 }
178
179 /*
180 * Get type of reserved cluster
181 */
182 char *
183 rsrvdcltype(cl)
184 cl_t cl;
185 {
186 if (cl < CLUST_BAD)
187 return "reserved";
188 if (cl > CLUST_BAD)
189 return "as EOF";
190 return "bad";
191 }
192
193 static int
194 clustdiffer(cl, cp1, cp2, fatnum)
195 cl_t cl;
196 cl_t *cp1;
197 cl_t *cp2;
198 int fatnum;
199 {
200 if (*cp1 >= CLUST_RSRVD) {
201 if (*cp2 >= CLUST_RSRVD) {
202 if ((*cp1 < CLUST_BAD && *cp2 < CLUST_BAD)
203 || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
204 pwarn("Cluster %d is marked %s with different indicators, ",
205 cl, rsrvdcltype(*cp1));
206 if (ask(1, "fix")) {
207 *cp2 = *cp1;
208 return FSFATMOD;
209 }
210 return FSFATAL;
211 }
212 pwarn("Cluster %d is marked %s in FAT 1, %s in FAT %d\n",
213 cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
214 if (ask(0, "use FAT #1's entry")) {
215 *cp2 = *cp1;
216 return FSFATMOD;
217 }
218 if (ask(0, "use FAT #%d's entry", fatnum)) {
219 *cp1 = *cp2;
220 return FSFATMOD;
221 }
222 return FSFATAL;
223 }
224 pwarn("Cluster %d is marked %s in FAT 1, but continues with cluster %d in FAT %d\n",
225 cl, rsrvdcltype(*cp1), *cp2, fatnum);
226 if (ask(0, "Use continuation from FAT %d", fatnum)) {
227 *cp1 = *cp2;
228 return FSFATMOD;
229 }
230 if (ask(0, "Use mark from FAT 1")) {
231 *cp2 = *cp1;
232 return FSFATMOD;
233 }
234 return FSFATAL;
235 }
236 if (*cp2 >= CLUST_RSRVD) {
237 pwarn("Cluster %d continues with cluster %d in FAT 1, but is marked %s in FAT %d\n",
238 cl, *cp1, rsrvdcltype(*cp2), fatnum);
239 if (ask(0, "Use continuation from FAT 1")) {
240 *cp2 = *cp1;
241 return FSFATMOD;
242 }
243 if (ask(0, "Use mark from FAT %d", fatnum)) {
244 *cp1 = *cp2;
245 return FSFATMOD;
246 }
247 return FSERROR;
248 }
249 pwarn("Cluster %d continues with cluster %d in FAT 1, but with cluster %d in FAT %d\n",
250 cl, *cp1, *cp2, fatnum);
251 if (ask(0, "Use continuation from FAT 1")) {
252 *cp2 = *cp1;
253 return FSFATMOD;
254 }
255 if (ask(0, "Use continuation from FAT %d", fatnum)) {
256 *cp1 = *cp2;
257 return FSFATMOD;
258 }
259 return FSERROR;
260 }
261
262 /*
263 * Compare two FAT copies in memory. Resolve any conflicts and merge them
264 * into the first one.
265 */
266 int
267 comparefat(boot, first, second, fatnum)
268 struct bootblock *boot;
269 struct fatEntry *first;
270 struct fatEntry *second;
271 int fatnum;
272 {
273 cl_t cl;
274 int ret = FSOK;
275
276 if (first[0].next != second[0].next) {
277 pwarn("Media bytes in cluster 1(%02x) and %d(%02x) differ\n",
278 first[0].next, fatnum, second[0].next);
279 if (ask(1, "Use media byte from FAT 1")) {
280 second[0].next = first[0].next;
281 ret |= FSFATMOD;
282 } else if (ask(0, "Use media byte from FAT %d", fatnum)) {
283 first[0].next = second[0].next;
284 ret |= FSFATMOD;
285 } else
286 ret |= FSERROR;
287 }
288 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
289 if (first[cl].next != second[cl].next)
290 ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
291 return ret;
292 }
293
294 void
295 clearchain(boot, fat, head)
296 struct bootblock *boot;
297 struct fatEntry *fat;
298 cl_t head;
299 {
300 cl_t p, q;
301
302 for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
303 if (fat[p].head != head)
304 break;
305 q = fat[p].next;
306 fat[p].next = fat[p].head = CLUST_FREE;
307 fat[p].length = 0;
308 }
309 }
310
311 /*
312 * Check a complete FAT in-memory for crosslinks
313 */
314 int
315 checkfat(boot, fat)
316 struct bootblock *boot;
317 struct fatEntry *fat;
318 {
319 cl_t head, p, h;
320 u_int len;
321 int ret = 0;
322 int conf;
323
324 /*
325 * pass 1: figure out the cluster chains.
326 */
327 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
328 /* find next untraveled chain */
329 if (fat[head].head != 0 /* cluster already belongs to some chain*/
330 || fat[head].next == CLUST_FREE
331 || fat[head].next == CLUST_BAD)
332 continue; /* skip it. */
333
334 /* follow the chain and mark all clusters on the way */
335 for (len = 0, p = head;
336 p >= CLUST_FIRST && p < boot->NumClusters;
337 p = fat[p].next) {
338 fat[p].head = head;
339 len++;
340 }
341
342 /* the head record gets the length */
343 fat[head].length = len;
344 }
345
346 /*
347 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
348 * we didn't know the real start of the chain then - would have treated partial
349 * chains as interlinked with their main chain)
350 */
351 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
352 /* find next untraveled chain */
353 if (fat[head].head != head)
354 continue;
355
356 /* follow the chain to its end (hopefully) */
357 for (p = head;
358 fat[p].next >= CLUST_FIRST && fat[p].next < boot->NumClusters;
359 p = fat[p].next)
360 if (fat[fat[p].next].head != head)
361 break;
362 if (fat[p].next >= CLUST_EOFS)
363 continue;
364
365 if (fat[p].next == 0) {
366 pwarn("Cluster chain starting at %d ends with free cluster\n", head);
367 if (ask(0, "Clear chain starting at %d", head)) {
368 clearchain(boot, fat, head);
369 ret |= FSFATMOD;
370 } else
371 ret |= FSERROR;
372 continue;
373 }
374 if (fat[p].next >= CLUST_RSRVD) {
375 pwarn("Cluster chain starting at %d ends with cluster marked %s\n",
376 head, rsrvdcltype(fat[p].next));
377 if (ask(0, "Clear chain starting at %d", head)) {
378 clearchain(boot, fat, head);
379 ret |= FSFATMOD;
380 } else
381 ret |= FSERROR;
382 continue;
383 }
384 if (fat[p].next < CLUST_FIRST || fat[p].next >= boot->NumClusters) {
385 pwarn("Cluster chain starting at %d ends with cluster out of range (%d)\n",
386 head, fat[p].next);
387 if (ask(0, "Clear chain starting at %d", head)) {
388 clearchain(boot, fat, head);
389 ret |= FSFATMOD;
390 } else
391 ret |= FSERROR;
392 }
393 pwarn("Cluster chains starting at %d and %d are linked at cluster %d\n",
394 head, fat[p].head, p);
395 conf = FSERROR;
396 if (ask(0, "Clear chain starting at %d", head)) {
397 clearchain(boot, fat, head);
398 conf = FSFATMOD;
399 }
400 if (ask(0, "Clear chain starting at %d", h = fat[p].head)) {
401 if (conf == FSERROR) {
402 /*
403 * Transfer the common chain to the one not cleared above.
404 */
405 for (; p >= CLUST_FIRST && p < boot->NumClusters;
406 p = fat[p].next) {
407 if (h != fat[p].head) {
408 /*
409 * Have to reexamine this chain.
410 */
411 head--;
412 break;
413 }
414 fat[p].head = head;
415 }
416 }
417 clearchain(boot, fat, h);
418 conf |= FSFATMOD;
419 }
420 ret |= conf;
421 }
422
423 return ret;
424 }
425
426 /*
427 * Write out FATs encoding them from the internal format
428 */
429 int
430 writefat(fs, boot, fat)
431 int fs;
432 struct bootblock *boot;
433 struct fatEntry *fat;
434 {
435 u_char *buffer, *p;
436 cl_t cl;
437 int i;
438 u_int32_t fatsz;
439 off_t off;
440 int ret = FSOK;
441
442 buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
443 if (buffer == NULL) {
444 perror("No space for FAT");
445 return FSFATAL;
446 }
447 memset(buffer, 0, fatsz);
448 boot->NumFree = 0;
449 buffer[0] = (u_char)fat[0].length;
450 buffer[1] = (u_char)(fat[0].length >> 8);
451 if (boot->Is16BitFat)
452 buffer[3] = (u_char)(fat[0].length >> 24);
453 for (cl = CLUST_FIRST, p = buffer; cl < boot->NumClusters;) {
454 if (boot->Is16BitFat) {
455 p[0] = (u_char)fat[cl].next;
456 if (fat[cl].next == CLUST_FREE)
457 boot->NumFree++;
458 p[1] = (u_char)(fat[cl++].next >> 8);
459 p += 2;
460 } else {
461 if (fat[cl].next == CLUST_FREE)
462 boot->NumFree++;
463 if (cl + 1 < boot->NumClusters
464 && fat[cl + 1].next == CLUST_FREE)
465 boot->NumFree++;
466 p[0] = (u_char)fat[cl].next;
467 p[1] = (u_char)((fat[cl].next >> 8) & 0xf)
468 |(u_char)(fat[cl+1].next << 4);
469 p[2] = (u_char)(fat[cl++].next >> 8);
470 p += 3;
471 }
472 }
473 for (i = 0; i < boot->FATs; i++) {
474 off = boot->ResSectors + i * boot->FATsecs;
475 off *= boot->BytesPerSec;
476 if (lseek(fs, off, SEEK_SET) != off
477 || write(fs, buffer, fatsz) != fatsz) {
478 perror("Unable to write FAT");
479 ret = FSFATAL; /* Return immediately? XXX */
480 }
481 }
482 free(buffer);
483 return ret;
484 }
485
486 /*
487 * Check a complete in-memory FAT for lost cluster chains
488 */
489 int
490 checklost(dosfs, boot, fat)
491 int dosfs;
492 struct bootblock *boot;
493 struct fatEntry *fat;
494 {
495 cl_t head;
496 int mod = FSOK;
497
498 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
499 /* find next untraveled chain */
500 if (fat[head].head != head
501 || fat[head].next == CLUST_FREE
502 || (fat[head].next >= CLUST_RSRVD
503 && fat[head].next < CLUST_EOFS)
504 || (fat[head].flags & FAT_USED))
505 continue;
506
507 pwarn("Lost cluster chain at cluster 0x%04x\n%d Cluster(s) lost\n",
508 head, fat[head].length);
509 mod |= reconnect(dosfs, boot, fat, head);
510 if (mod & FSFATAL)
511 break;
512 }
513 finishlf();
514
515 return mod;
516 }
517