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