Remove unneeded inclusions of <sys/cdefs.h> throughout the tree.
[games.git] / sbin / fsck_msdosfs / fat.c
1 /*
2  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
3  * Copyright (c) 1995 Martin Husemann
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Martin Husemann
16  *      and Wolfgang Solfrank.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $
33  * $FreeBSD: src/sbin/fsck_msdosfs/fat.c,v 1.1.2.1 2001/08/01 05:47:56 obrien Exp $
34  */
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <unistd.h>
41
42 #include "ext.h"
43 #include "fsutil.h"
44
45 static int checkclnum(struct bootblock *, int, cl_t, cl_t *);
46 static int clustdiffer(cl_t, cl_t *, cl_t *, int);
47 static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
48 static int _readfat(int, struct bootblock *, int, u_char **);
49
50 /*
51  * Check a cluster number for valid value
52  */
53 static int
54 checkclnum(struct bootblock *boot, int fat, cl_t cl, cl_t *next)
55 {
56         if (*next >= (CLUST_RSRVD&boot->ClustMask))
57                 *next |= ~boot->ClustMask;
58         if (*next == CLUST_FREE) {
59                 boot->NumFree++;
60                 return FSOK;
61         }
62         if (*next == CLUST_BAD) {
63                 boot->NumBad++;
64                 return FSOK;
65         }
66         if (*next < CLUST_FIRST
67             || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
68                 pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n",
69                       cl, fat,
70                       *next < CLUST_RSRVD ? "out of range" : "reserved",
71                       *next&boot->ClustMask);
72                 if (ask(0, "Truncate")) {
73                         *next = CLUST_EOF;
74                         return FSFATMOD;
75                 }
76                 return FSERROR;
77         }
78         return FSOK;
79 }
80
81 /*
82  * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
83  */
84 static int
85 _readfat(int fs, struct bootblock *boot, int no, u_char **buffer)
86 {
87         off_t off;
88
89         *buffer = malloc(boot->FATsecs * boot->BytesPerSec);
90         if (*buffer == NULL) {
91                 perror("No space for FAT");
92                 return 0;
93         }
94
95         off = boot->ResSectors + no * boot->FATsecs;
96         off *= boot->BytesPerSec;
97
98         if (lseek(fs, off, SEEK_SET) != off) {
99                 perror("Unable to read FAT");
100                 goto err;
101         }
102
103         if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec)
104             != boot->FATsecs * boot->BytesPerSec) {
105                 perror("Unable to read FAT");
106                 goto err;
107         }
108
109         return 1;
110
111     err:
112         free(*buffer);
113         return 0;
114 }
115
116 /*
117  * Read a FAT and decode it into internal format
118  */
119 int
120 readfat(int fs, struct bootblock *boot, int no, struct fatEntry **fp)
121 {
122         struct fatEntry *fat;
123         u_char *buffer, *p;
124         cl_t cl;
125         int ret = FSOK;
126
127         boot->NumFree = boot->NumBad = 0;
128
129         if (!_readfat(fs, boot, no, &buffer))
130                 return FSFATAL;
131                 
132         fat = calloc(boot->NumClusters, sizeof(struct fatEntry));
133         if (fat == NULL) {
134                 perror("No space for FAT");
135                 free(buffer);
136                 return FSFATAL;
137         }
138
139         if (buffer[0] != boot->Media
140             || buffer[1] != 0xff || buffer[2] != 0xff
141             || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
142             || (boot->ClustMask == CLUST32_MASK
143                 && ((buffer[3]&0x0f) != 0x0f
144                     || buffer[4] != 0xff || buffer[5] != 0xff
145                     || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
146
147                 /* Windows 95 OSR2 (and possibly any later) changes
148                  * the FAT signature to 0xXXffff7f for FAT16 and to
149                  * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
150                  * filesystem is dirty if it doesn't reboot cleanly.
151                  * Check this special condition before errorring out.
152                  */
153                 if (buffer[0] == boot->Media && buffer[1] == 0xff
154                     && buffer[2] == 0xff
155                     && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
156                         || (boot->ClustMask == CLUST32_MASK
157                             && buffer[3] == 0x0f && buffer[4] == 0xff
158                             && buffer[5] == 0xff && buffer[6] == 0xff
159                             && buffer[7] == 0x07)))
160                         ret |= FSDIRTY;
161                 else {
162                         /* just some odd byte sequence in FAT */
163                                 
164                         switch (boot->ClustMask) {
165                         case CLUST32_MASK:
166                                 pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
167                                       "FAT starts with odd byte sequence",
168                                       buffer[0], buffer[1], buffer[2], buffer[3],
169                                       buffer[4], buffer[5], buffer[6], buffer[7]);
170                                 break;
171                         case CLUST16_MASK:
172                                 pwarn("%s (%02x%02x%02x%02x)\n",
173                                     "FAT starts with odd byte sequence",
174                                     buffer[0], buffer[1], buffer[2], buffer[3]);
175                                 break;
176                         default:
177                                 pwarn("%s (%02x%02x%02x)\n",
178                                     "FAT starts with odd byte sequence",
179                                     buffer[0], buffer[1], buffer[2]);
180                                 break;
181                         }
182
183         
184                         if (ask(1, "Correct"))
185                                 ret |= FSFIXFAT;
186                 }
187         }
188         switch (boot->ClustMask) {
189         case CLUST32_MASK:
190                 p = buffer + 8;
191                 break;
192         case CLUST16_MASK:
193                 p = buffer + 4;
194                 break;
195         default:
196                 p = buffer + 3;
197                 break;
198         }
199         for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
200                 switch (boot->ClustMask) {
201                 case CLUST32_MASK:
202                         fat[cl].next = p[0] + (p[1] << 8)
203                                        + (p[2] << 16) + (p[3] << 24);
204                         fat[cl].next &= boot->ClustMask;
205                         ret |= checkclnum(boot, no, cl, &fat[cl].next);
206                         cl++;
207                         p += 4;
208                         break;
209                 case CLUST16_MASK:
210                         fat[cl].next = p[0] + (p[1] << 8);
211                         ret |= checkclnum(boot, no, cl, &fat[cl].next);
212                         cl++;
213                         p += 2;
214                         break;
215                 default:
216                         fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff;
217                         ret |= checkclnum(boot, no, cl, &fat[cl].next);
218                         cl++;
219                         if (cl >= boot->NumClusters)
220                                 break;
221                         fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff;
222                         ret |= checkclnum(boot, no, cl, &fat[cl].next);
223                         cl++;
224                         p += 3;
225                         break;
226                 }
227         }
228
229         free(buffer);
230         *fp = fat;
231         return ret;
232 }
233
234 /*
235  * Get type of reserved cluster
236  */
237 char *
238 rsrvdcltype(cl_t cl)
239 {
240         if (cl == CLUST_FREE)
241                 return "free";
242         if (cl < CLUST_BAD)
243                 return "reserved";
244         if (cl > CLUST_BAD)
245                 return "as EOF";
246         return "bad";
247 }
248
249 static int
250 clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, int fatnum)
251 {
252         if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
253                 if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
254                         if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD
255                              && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD)
256                             || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
257                                 pwarn("Cluster %u is marked %s with different indicators, ",
258                                       cl, rsrvdcltype(*cp1));
259                                 if (ask(1, "fix")) {
260                                         *cp2 = *cp1;
261                                         return FSFATMOD;
262                                 }
263                                 return FSFATAL;
264                         }
265                         pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n",
266                               cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
267                         if (ask(0, "use FAT 0's entry")) {
268                                 *cp2 = *cp1;
269                                 return FSFATMOD;
270                         }
271                         if (ask(0, "use FAT %d's entry", fatnum)) {
272                                 *cp1 = *cp2;
273                                 return FSFATMOD;
274                         }
275                         return FSFATAL;
276                 }
277                 pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
278                       cl, rsrvdcltype(*cp1), *cp2, fatnum);
279                 if (ask(0, "Use continuation from FAT %d", fatnum)) {
280                         *cp1 = *cp2;
281                         return FSFATMOD;
282                 }
283                 if (ask(0, "Use mark from FAT 0")) {
284                         *cp2 = *cp1;
285                         return FSFATMOD;
286                 }
287                 return FSFATAL;
288         }
289         if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
290                 pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n",
291                       cl, *cp1, rsrvdcltype(*cp2), fatnum);
292                 if (ask(0, "Use continuation from FAT 0")) {
293                         *cp2 = *cp1;
294                         return FSFATMOD;
295                 }
296                 if (ask(0, "Use mark from FAT %d", fatnum)) {
297                         *cp1 = *cp2;
298                         return FSFATMOD;
299                 }
300                 return FSERROR;
301         }
302         pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n",
303               cl, *cp1, *cp2, fatnum);
304         if (ask(0, "Use continuation from FAT 0")) {
305                 *cp2 = *cp1;
306                 return FSFATMOD;
307         }
308         if (ask(0, "Use continuation from FAT %d", fatnum)) {
309                 *cp1 = *cp2;
310                 return FSFATMOD;
311         }
312         return FSERROR;
313 }
314
315 /*
316  * Compare two FAT copies in memory. Resolve any conflicts and merge them
317  * into the first one.
318  */
319 int
320 comparefat(struct bootblock *boot, struct fatEntry *first, 
321            struct fatEntry *second, int fatnum)
322 {
323         cl_t cl;
324         int ret = FSOK;
325
326         for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++)
327                 if (first[cl].next != second[cl].next)
328                         ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum);
329         return ret;
330 }
331
332 void
333 clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
334 {
335         cl_t p, q;
336
337         for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) {
338                 if (fat[p].head != head)
339                         break;
340                 q = fat[p].next;
341                 fat[p].next = fat[p].head = CLUST_FREE;
342                 fat[p].length = 0;
343         }
344 }
345
346 int
347 tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc)
348 {
349         if (ask(0, "Clear chain starting at %u", head)) {
350                 clearchain(boot, fat, head);
351                 return FSFATMOD;
352         } else if (ask(0, "Truncate")) {
353                 *trunc = CLUST_EOF;
354                 return FSFATMOD;
355         } else
356                 return FSERROR;
357 }
358
359 /*
360  * Check a complete FAT in-memory for crosslinks
361  */
362 int
363 checkfat(struct bootblock *boot, struct fatEntry *fat)
364 {
365         cl_t head, p, h, n;
366         u_int len;
367         int ret = 0;
368         int conf;
369
370         /*
371          * pass 1: figure out the cluster chains.
372          */
373         for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
374                 /* find next untravelled chain */
375                 if (fat[head].head != 0         /* cluster already belongs to some chain */
376                     || fat[head].next == CLUST_FREE
377                     || fat[head].next == CLUST_BAD)
378                         continue;               /* skip it. */
379
380                 /* follow the chain and mark all clusters on the way */
381                 for (len = 0, p = head;
382                      p >= CLUST_FIRST && p < boot->NumClusters;
383                      p = fat[p].next) {
384                         fat[p].head = head;
385                         len++;
386                 }
387
388                 /* the head record gets the length */
389                 fat[head].length = fat[head].next == CLUST_FREE ? 0 : len;
390         }
391
392         /*
393          * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because
394          * we didn't know the real start of the chain then - would have treated partial
395          * chains as interlinked with their main chain)
396          */
397         for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
398                 /* find next untravelled chain */
399                 if (fat[head].head != head)
400                         continue;
401
402                 /* follow the chain to its end (hopefully) */
403                 for (p = head;
404                      (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
405                      p = n)
406                         if (fat[n].head != head)
407                                 break;
408                 if (n >= CLUST_EOFS)
409                         continue;
410
411                 if (n == CLUST_FREE || n >= CLUST_RSRVD) {
412                         pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
413                               head, rsrvdcltype(n));
414                         ret |= tryclear(boot, fat, head, &fat[p].next);
415                         continue;
416                 }
417                 if (n < CLUST_FIRST || n >= boot->NumClusters) {
418                         pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
419                               head, n);
420                         ret |= tryclear(boot, fat, head, &fat[p].next);
421                         continue;
422                 }
423                 pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
424                       head, fat[n].head, n);
425                 conf = tryclear(boot, fat, head, &fat[p].next);
426                 if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
427                         if (conf == FSERROR) {
428                                 /*
429                                  * Transfer the common chain to the one not cleared above.
430                                  */
431                                 for (p = n;
432                                      p >= CLUST_FIRST && p < boot->NumClusters;
433                                      p = fat[p].next) {
434                                         if (h != fat[p].head) {
435                                                 /*
436                                                  * Have to reexamine this chain.
437                                                  */
438                                                 head--;
439                                                 break;
440                                         }
441                                         fat[p].head = head;
442                                 }
443                         }
444                         clearchain(boot, fat, h);
445                         conf |= FSFATMOD;
446                 }
447                 ret |= conf;
448         }
449
450         return ret;
451 }
452
453 /*
454  * Write out FATs encoding them from the internal format
455  */
456 int
457 writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
458 {
459         u_char *buffer, *p;
460         cl_t cl;
461         int i;
462         u_int32_t fatsz;
463         off_t off;
464         int ret = FSOK;
465
466         buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
467         if (buffer == NULL) {
468                 perror("No space for FAT");
469                 return FSFATAL;
470         }
471         memset(buffer, 0, fatsz);
472         boot->NumFree = 0;
473         p = buffer;
474         if (correct_fat) {
475                 *p++ = (u_char)boot->Media;
476                 *p++ = 0xff;
477                 *p++ = 0xff;
478                 switch (boot->ClustMask) {
479                 case CLUST16_MASK:
480                         *p++ = 0xff;
481                         break;
482                 case CLUST32_MASK:
483                         *p++ = 0x0f;
484                         *p++ = 0xff;
485                         *p++ = 0xff;
486                         *p++ = 0xff;
487                         *p++ = 0x0f;
488                         break;
489                 }
490         } else {
491                 /* use same FAT signature as the old FAT has */
492                 int count;
493                 u_char *old_fat;
494
495                 switch (boot->ClustMask) {
496                 case CLUST32_MASK:
497                         count = 8;
498                         break;
499                 case CLUST16_MASK:
500                         count = 4;
501                         break;
502                 default:
503                         count = 3;
504                         break;
505                 }
506
507                 if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0,
508                                          &old_fat)) {
509                         free(buffer);
510                         return FSFATAL;
511                 }
512
513                 memcpy(p, old_fat, count);
514                 free(old_fat);
515                 p += count;
516         }
517                         
518         for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
519                 switch (boot->ClustMask) {
520                 case CLUST32_MASK:
521                         if (fat[cl].next == CLUST_FREE)
522                                 boot->NumFree++;
523                         *p++ = (u_char)fat[cl].next;
524                         *p++ = (u_char)(fat[cl].next >> 8);
525                         *p++ = (u_char)(fat[cl].next >> 16);
526                         *p &= 0xf0;
527                         *p++ |= (fat[cl].next >> 24)&0x0f;
528                         break;
529                 case CLUST16_MASK:
530                         if (fat[cl].next == CLUST_FREE)
531                                 boot->NumFree++;
532                         *p++ = (u_char)fat[cl].next;
533                         *p++ = (u_char)(fat[cl].next >> 8);
534                         break;
535                 default:
536                         if (fat[cl].next == CLUST_FREE)
537                                 boot->NumFree++;
538                         if (cl + 1 < boot->NumClusters
539                             && fat[cl + 1].next == CLUST_FREE)
540                                 boot->NumFree++;
541                         *p++ = (u_char)fat[cl].next;
542                         *p++ = (u_char)((fat[cl].next >> 8) & 0xf)
543                                |(u_char)(fat[cl+1].next << 4);
544                         *p++ = (u_char)(fat[++cl].next >> 4);
545                         break;
546                 }
547         }
548         for (i = 0; i < boot->FATs; i++) {
549                 off = boot->ResSectors + i * boot->FATsecs;
550                 off *= boot->BytesPerSec;
551                 if (lseek(fs, off, SEEK_SET) != off
552                     || write(fs, buffer, fatsz) != fatsz) {
553                         perror("Unable to write FAT");
554                         ret = FSFATAL; /* Return immediately?           XXX */
555                 }
556         }
557         free(buffer);
558         return ret;
559 }
560
561 /*
562  * Check a complete in-memory FAT for lost cluster chains
563  */
564 int
565 checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
566 {
567         cl_t head;
568         int mod = FSOK;
569         int ret;
570         
571         for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
572                 /* find next untravelled chain */
573                 if (fat[head].head != head
574                     || fat[head].next == CLUST_FREE
575                     || (fat[head].next >= CLUST_RSRVD
576                         && fat[head].next < CLUST_EOFS)
577                     || (fat[head].flags & FAT_USED))
578                         continue;
579
580                 pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
581                       head, fat[head].length);
582                 mod |= ret = reconnect(dosfs, boot, fat, head);
583                 if (mod & FSFATAL)
584                         break;
585                 if (ret == FSERROR && ask(0, "Clear")) {
586                         clearchain(boot, fat, head);
587                         mod |= FSFATMOD;
588                 }
589         }
590         finishlf();
591
592         if (boot->FSInfo) {
593                 ret = 0;
594                 if (boot->FSFree != boot->NumFree) {
595                         pwarn("Free space in FSInfo block (%d) not correct (%d)\n",
596                               boot->FSFree, boot->NumFree);
597                         if (ask(1, "fix")) {
598                                 boot->FSFree = boot->NumFree;
599                                 ret = 1;
600                         }
601                 }
602                 if (boot->NumFree && (boot->FSNext >= boot->NumClusters ||
603                                       fat[boot->FSNext].next != CLUST_FREE)) {
604                         pwarn("Next free cluster in FSInfo block (%u) not free\n",
605                               boot->FSNext);
606                         if (ask(1, "fix"))
607                                 for (head = CLUST_FIRST; head < boot->NumClusters; head++)
608                                         if (fat[head].next == CLUST_FREE) {
609                                                 boot->FSNext = head;
610                                                 ret = 1;
611                                                 break;
612                                         }
613                 }
614                 if (ret)
615                         mod |= writefsinfo(dosfs, boot);
616         }
617
618         return mod;
619 }