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