fat.c revision 1.6 1 /* $NetBSD: fat.c,v 1.6 1997/09/08 14:05:31 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.6 1997/09/08 14:05:31 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[0] != boot->Media
144 || buffer[1] != 0xff || buffer[2] != 0xff
145 || (boot->Is16BitFat && buffer[3] != 0xff)) {
146 char *msg = boot->Is16BitFat
147 ? "FAT starts with odd byte sequence (%02x%02x%02x%02x)\n"
148 : "FAT starts with odd byte sequence (%02x%02x%02x)\n";
149 pwarn(msg, buffer[0], buffer[1], buffer[2], buffer[3]);
150 if (ask(1, "Correct")) {
151 fat[0].length = boot->Media|0xffffff;
152 ret |= FSFATMOD;
153 }
154 }
155 p = buffer + (boot->Is16BitFat ? 4 : 3);
156 for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
157 if (boot->Is16BitFat) {
158 fat[cl].next = p[0] + (p[1] << 8);
159 ret |= checkclnum(boot, no, cl, &fat[cl].next);
160 cl++;
161 p += 2;
162 } else {
163 fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
164 ret |= checkclnum(boot, no, cl, &fat[cl].next);
165 cl++;
166 if (cl >= boot->NumClusters)
167 break;
168 fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
169 ret |= checkclnum(boot, no, cl, &fat[cl].next);
170 cl++;
171 p += 3;
172 }
173 }
174
175 free(buffer);
176 *fp = fat;
177 return ret;
178 }
179
180 /*
181 * Get type of reserved cluster
182 */
183 char *
184 rsrvdcltype(cl)
185 cl_t cl;
186 {
187 if (cl < CLUST_BAD)
188 return "reserved";
189 if (cl > CLUST_BAD)
190 return "as EOF";
191 return "bad";
192 }
193
194 static int
195 clustdiffer(cl, cp1, cp2, fatnum)
196 cl_t cl;
197 cl_t *cp1;
198 cl_t *cp2;
199 int fatnum;
200 {
201 if (*cp1 >= CLUST_RSRVD) {
202 if (*cp2 >= CLUST_RSRVD) {
203 if ((*cp1 < CLUST_BAD && *cp2 < CLUST_BAD)
204 || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
205 pwarn("Cluster %d is marked %s with different indicators, ",
206 cl, rsrvdcltype(*cp1));
207 if (ask(1, "fix")) {
208 *cp2 = *cp1;
209 return FSFATMOD;
210 }
211 return FSFATAL;
212 }
213 pwarn("Cluster %d is marked %s in FAT 1, %s in FAT %d\n",
214 cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
215 if (ask(0, "use FAT #1's entry")) {
216 *cp2 = *cp1;
217 return FSFATMOD;
218 }
219 if (ask(0, "use FAT #%d's entry", fatnum)) {
220 *cp1 = *cp2;
221 return FSFATMOD;
222 }
223 return FSFATAL;
224 }
225 pwarn("Cluster %d is marked %s in FAT 1, but continues with cluster %d in FAT %d\n",
226 cl, rsrvdcltype(*cp1), *cp2, fatnum);
227 if (ask(0, "Use continuation from FAT %d", fatnum)) {
228 *cp1 = *cp2;
229 return FSFATMOD;
230 }
231 if (ask(0, "Use mark from FAT 1")) {
232 *cp2 = *cp1;
233 return FSFATMOD;
234 }
235 return FSFATAL;
236 }
237 if (*cp2 >= CLUST_RSRVD) {
238 pwarn("Cluster %d continues with cluster %d in FAT 1, but is marked %s in FAT %d\n",
239 cl, *cp1, rsrvdcltype(*cp2), fatnum);
240 if (ask(0, "Use continuation from FAT 1")) {
241 *cp2 = *cp1;
242 return FSFATMOD;
243 }
244 if (ask(0, "Use mark from FAT %d", fatnum)) {
245 *cp1 = *cp2;
246 return FSFATMOD;
247 }
248 return FSERROR;
249 }
250 pwarn("Cluster %d continues with cluster %d in FAT 1, but with cluster %d in FAT %d\n",
251 cl, *cp1, *cp2, fatnum);
252 if (ask(0, "Use continuation from FAT 1")) {
253 *cp2 = *cp1;
254 return FSFATMOD;
255 }
256 if (ask(0, "Use continuation from FAT %d", fatnum)) {
257 *cp1 = *cp2;
258 return FSFATMOD;
259 }
260 return FSERROR;
261 }
262
263 /*
264 * Compare two FAT copies in memory. Resolve any conflicts and merge them
265 * into the first one.
266 */
267 int
268 comparefat(boot, first, second, fatnum)
269 struct bootblock *boot;
270 struct fatEntry *first;
271 struct fatEntry *second;
272 int fatnum;
273 {
274 cl_t cl;
275 int ret = FSOK;
276
277 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
278 if (first[cl].next != second[cl].next)
279 ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
280 return ret;
281 }
282
283 void
284 clearchain(boot, fat, head)
285 struct bootblock *boot;
286 struct fatEntry *fat;
287 cl_t head;
288 {
289 cl_t p, q;
290
291 for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
292 if (fat[p].head != head)
293 break;
294 q = fat[p].next;
295 fat[p].next = fat[p].head = CLUST_FREE;
296 fat[p].length = 0;
297 }
298 }
299
300 /*
301 * Check a complete FAT in-memory for crosslinks
302 */
303 int
304 checkfat(boot, fat)
305 struct bootblock *boot;
306 struct fatEntry *fat;
307 {
308 cl_t head, p, h;
309 u_int len;
310 int ret = 0;
311 int conf;
312
313 /*
314 * pass 1: figure out the cluster chains.
315 */
316 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
317 /* find next untraveled chain */
318 if (fat[head].head != 0 /* cluster already belongs to some chain */
319 || fat[head].next == CLUST_FREE
320 || fat[head].next == CLUST_BAD)
321 continue; /* skip it. */
322
323 /* follow the chain and mark all clusters on the way */
324 for (len = 0, p = head;
325 p >= CLUST_FIRST && p < boot->NumClusters;
326 p = fat[p].next) {
327 fat[p].head = head;
328 len++;
329 }
330
331 /* the head record gets the length */
332 fat[head].length = len;
333 }
334
335 /*
336 * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
337 * we didn't know the real start of the chain then - would have treated partial
338 * chains as interlinked with their main chain)
339 */
340 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
341 /* find next untraveled chain */
342 if (fat[head].head != head)
343 continue;
344
345 /* follow the chain to its end (hopefully) */
346 for (p = head;
347 fat[p].next >= CLUST_FIRST && fat[p].next < boot->NumClusters;
348 p = fat[p].next)
349 if (fat[fat[p].next].head != head)
350 break;
351 if (fat[p].next >= CLUST_EOFS)
352 continue;
353
354 if (fat[p].next == 0) {
355 pwarn("Cluster chain starting at %d ends with free cluster\n", head);
356 if (ask(0, "Clear chain starting at %d", head)) {
357 clearchain(boot, fat, head);
358 ret |= FSFATMOD;
359 } else
360 ret |= FSERROR;
361 continue;
362 }
363 if (fat[p].next >= CLUST_RSRVD) {
364 pwarn("Cluster chain starting at %d ends with cluster marked %s\n",
365 head, rsrvdcltype(fat[p].next));
366 if (ask(0, "Clear chain starting at %d", head)) {
367 clearchain(boot, fat, head);
368 ret |= FSFATMOD;
369 } else
370 ret |= FSERROR;
371 continue;
372 }
373 if (fat[p].next < CLUST_FIRST || fat[p].next >= boot->NumClusters) {
374 pwarn("Cluster chain starting at %d ends with cluster out of range (%d)\n",
375 head, fat[p].next);
376 if (ask(0, "Clear chain starting at %d", head)) {
377 clearchain(boot, fat, head);
378 ret |= FSFATMOD;
379 } else
380 ret |= FSERROR;
381 }
382 pwarn("Cluster chains starting at %d and %d are linked at cluster %d\n",
383 head, fat[p].head, p);
384 conf = FSERROR;
385 if (ask(0, "Clear chain starting at %d", head)) {
386 clearchain(boot, fat, head);
387 conf = FSFATMOD;
388 }
389 if (ask(0, "Clear chain starting at %d", h = fat[p].head)) {
390 if (conf == FSERROR) {
391 /*
392 * Transfer the common chain to the one not cleared above.
393 */
394 for (; p >= CLUST_FIRST && p < boot->NumClusters;
395 p = fat[p].next) {
396 if (h != fat[p].head) {
397 /*
398 * Have to reexamine this chain.
399 */
400 head--;
401 break;
402 }
403 fat[p].head = head;
404 }
405 }
406 clearchain(boot, fat, h);
407 conf |= FSFATMOD;
408 }
409 ret |= conf;
410 }
411
412 return ret;
413 }
414
415 /*
416 * Write out FATs encoding them from the internal format
417 */
418 int
419 writefat(fs, boot, fat)
420 int fs;
421 struct bootblock *boot;
422 struct fatEntry *fat;
423 {
424 u_char *buffer, *p;
425 cl_t cl;
426 int i;
427 u_int32_t fatsz;
428 off_t off;
429 int ret = FSOK;
430
431 buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
432 if (buffer == NULL) {
433 perror("No space for FAT");
434 return FSFATAL;
435 }
436 memset(buffer, 0, fatsz);
437 boot->NumFree = 0;
438 p = buffer;
439 *p++ = (u_char)fat[0].length;
440 *p++ = (u_char)(fat[0].length >> 8);
441 *p++ = (u_char)(fat[0].length >> 16);
442 if (boot->Is16BitFat)
443 *p++ = (u_char)(fat[0].length >> 24);
444 for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
445 if (boot->Is16BitFat) {
446 p[0] = (u_char)fat[cl].next;
447 if (fat[cl].next == CLUST_FREE)
448 boot->NumFree++;
449 p[1] = (u_char)(fat[cl].next >> 8);
450 p += 2;
451 } else {
452 if (fat[cl].next == CLUST_FREE)
453 boot->NumFree++;
454 if (cl + 1 < boot->NumClusters
455 && fat[cl + 1].next == CLUST_FREE)
456 boot->NumFree++;
457 p[0] = (u_char)fat[cl].next;
458 p[1] = (u_char)((fat[cl].next >> 8) & 0xf)
459 |(u_char)(fat[cl+1].next << 4);
460 p[2] = (u_char)(fat[++cl].next >> 4);
461 p += 3;
462 }
463 }
464 for (i = 0; i < boot->FATs; i++) {
465 off = boot->ResSectors + i * boot->FATsecs;
466 off *= boot->BytesPerSec;
467 if (lseek(fs, off, SEEK_SET) != off
468 || write(fs, buffer, fatsz) != fatsz) {
469 perror("Unable to write FAT");
470 ret = FSFATAL; /* Return immediately? XXX */
471 }
472 }
473 free(buffer);
474 return ret;
475 }
476
477 /*
478 * Check a complete in-memory FAT for lost cluster chains
479 */
480 int
481 checklost(dosfs, boot, fat)
482 int dosfs;
483 struct bootblock *boot;
484 struct fatEntry *fat;
485 {
486 cl_t head;
487 int mod = FSOK;
488
489 for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
490 /* find next untraveled chain */
491 if (fat[head].head != head
492 || fat[head].next == CLUST_FREE
493 || (fat[head].next >= CLUST_RSRVD
494 && fat[head].next < CLUST_EOFS)
495 || (fat[head].flags & FAT_USED))
496 continue;
497
498 pwarn("Lost cluster chain at cluster 0x%04x\n%d Cluster(s) lost\n",
499 head, fat[head].length);
500 mod |= reconnect(dosfs, boot, fat, head);
501 if (mod & FSFATAL)
502 break;
503 }
504 finishlf();
505
506 return mod;
507 }
508