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