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