Imported Upstream version 4.2
[platform/upstream/dosfstools.git] / src / check.c
1 /* check.c - Check and repair a PC/MS-DOS filesystem
2
3    Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4    Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5    Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
6    Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
7    Copyright (C) 2017-2021 Pali Rohár <pali.rohar@gmail.com>
8
9    This program is free software: you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22    The complete text of the GNU General Public License
23    can be found in /usr/share/common-licenses/GPL-3 file.
24 */
25
26 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
27  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <wctype.h>
36
37 #include "common.h"
38 #include "fsck.fat.h"
39 #include "io.h"
40 #include "fat.h"
41 #include "file.h"
42 #include "lfn.h"
43 #include "check.h"
44 #include "boot.h"
45 #include "charconv.h"
46
47
48 /* the longest path on the filesystem that can be handled by path_name() */
49 #define PATH_NAME_MAX 1023
50
51 static DOS_FILE *root;
52
53 /* get start field of a dir entry */
54 #define FSTART(p,fs) \
55   ((uint32_t)le16toh(p->dir_ent.start) | \
56    (fs->fat_bits == 32 ? (uint32_t)le16toh(p->dir_ent.starthi) << 16 : 0))
57
58 #define MODIFY(p,i,v)                                   \
59   do {                                                  \
60     if (p->offset) {                                    \
61         p->dir_ent.i = v;                               \
62         fs_write(p->offset+offsetof(DIR_ENT,i),         \
63                  sizeof(p->dir_ent.i),&p->dir_ent.i);   \
64     }                                                   \
65   } while(0)
66
67 #define MODIFY_START(p,v,fs)                                            \
68   do {                                                                  \
69     uint32_t __v = (v);                                                 \
70     if (!p->offset) {                                                   \
71         /* writing to fake entry for FAT32 root dir */                  \
72         if (!__v) die("Oops, deleting FAT32 root dir!");                \
73         fs->root_cluster = __v;                                         \
74         p->dir_ent.start = htole16(__v&0xffff);                         \
75         p->dir_ent.starthi = htole16(__v>>16);                          \
76         __v = htole32(__v);                                             \
77         fs_write(offsetof(struct boot_sector,root_cluster),             \
78                  sizeof(((struct boot_sector *)0)->root_cluster),       \
79                  &__v);                                                 \
80     }                                                                   \
81     else {                                                              \
82         MODIFY(p,start,htole16((__v)&0xffff));                          \
83         if (fs->fat_bits == 32)                                         \
84             MODIFY(p,starthi,htole16((__v)>>16));                       \
85     }                                                                   \
86   } while(0)
87
88 /**
89  * Construct a full path (starting with '/') for the specified dentry,
90  * relative to the partition. All components are "long" names where possible.
91  *
92  * @param[in]   file    Information about dentry (file or directory) of interest
93  *
94  * return       Pointer to static string containing file's full path
95  */
96 static char *path_name(DOS_FILE * file)
97 {
98     static char path[PATH_NAME_MAX * 2];
99
100     if (!file)
101         *path = 0;              /* Reached the root directory */
102     else {
103         if (strlen(path_name(file->parent)) > PATH_NAME_MAX)
104             die("Path name too long.");
105         if (strcmp(path, "/") != 0)
106             strcat(path, "/");
107
108         /* Append the long name to the path,
109          * or the short name if there isn't a long one
110          */
111         strcpy(strrchr(path, 0),
112                file->lfn ? file->lfn : file_name(file->dir_ent.name));
113     }
114     return path;
115 }
116
117 static const char *month_str[] =
118     { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
119
120 static char *file_stat(DOS_FILE * file)
121 {
122     static char temp[100];
123     unsigned int hours, minutes, secs, day, month, year;
124     unsigned short time, date;
125
126     time = le16toh(file->dir_ent.time);
127     date = le16toh(file->dir_ent.date);
128     year = 1980 + (date >> 9);
129     month = ((date >> 5) & 15);
130     if (month < 1) month = 1;
131     else if (month > 12) month = 12;
132     day = (date & 31);
133     if (day < 1) day = 1;
134     hours = (time >> 11);
135     if (hours > 23) hours = 23;
136     minutes = ((time >> 5) & 63);
137     if (minutes > 59) minutes = 59;
138     secs = (time & 31) * 2;
139     if (secs > 59) secs = 59;
140     sprintf(temp, "  Size %u bytes, date %02u:%02u:%02u %s %02u %4u",
141         le32toh(file->dir_ent.size), hours, minutes, secs, month_str[month-1], day, year);
142     return temp;
143 }
144
145 static int bad_name(DOS_FILE * file)
146 {
147     int i, spc, suspicious = 0;
148     const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:.";
149     const unsigned char *name = file->dir_ent.name;
150     const unsigned char *ext = name + 8;
151
152     /* do not check synthetic FAT32 root entry */
153     if (!file->offset)
154         return 0;
155
156     /* check if we have neither a long filename nor a short name */
157     if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) {
158         return 1;
159     }
160
161     /* don't complain about the dummy 11 bytes used by patched Linux
162        kernels */
163     if (file->dir_ent.lcase & FAT_NO_83NAME)
164         return 0;
165
166     for (i = 0; i < MSDOS_NAME; i++) {
167         if ((name[i] < ' ' && !(i == 0 && name[0] == 0x05)) || name[i] == 0x7f)
168             return 1;
169         if (name[i] > 0x7f)
170             ++suspicious;
171         if (strchr(bad_chars, name[i]))
172             return 1;
173     }
174
175     if (name[0] == ' ')
176         return 1;
177
178     if (no_spaces_in_sfns) {
179         spc = 0;
180         for (i = 0; i < 8; i++) {
181             if (name[i] == ' ')
182                 spc = 1;
183             else if (spc)
184                 /* non-space after a space not allowed, space terminates the name
185                  * part */
186                 return 1;
187         }
188
189         spc = 0;
190         for (i = 0; i < 3; i++) {
191             if (ext[i] == ' ')
192                 spc = 1;
193             else if (spc)
194                 /* non-space after a space not allowed, space terminates the ext
195                  * part */
196                 return 1;
197         }
198     }
199
200     /* Under GEMDOS, chars >= 128 are never allowed. */
201     if (atari_format && suspicious)
202         return 1;
203
204     /* Under MS-DOS and Windows, chars >= 128 in short names are valid
205      * (but these characters can be visualised differently depending on
206      * local codepage: CP437, CP866, etc). The chars are all basically ok,
207      * so we shouldn't auto-correct such names. */
208     return 0;
209 }
210
211 static void lfn_remove(off_t from, off_t to)
212 {
213     DIR_ENT empty;
214
215     /* New dir entry is zeroed except first byte, which is set to 0xe5.
216      * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
217      * a directory at the first zero entry...
218      */
219     memset(&empty, 0, sizeof(empty));
220     empty.name[0] = DELETED_FLAG;
221
222     for (; from < to; from += sizeof(empty)) {
223         fs_write(from, sizeof(DIR_ENT), &empty);
224     }
225 }
226
227 static void drop_file(DOS_FS * fs, DOS_FILE * file)
228 {
229     (void) fs;
230
231     MODIFY(file, name[0], DELETED_FLAG);
232     if (file->lfn)
233         lfn_remove(file->lfn_offset, file->offset);
234     --n_files;
235 }
236
237 static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters)
238 {
239     int deleting;
240     uint32_t walk, next;
241
242     walk = FSTART(file, fs);
243     if ((deleting = !clusters))
244         MODIFY_START(file, 0, fs);
245     while (walk > 0 && walk != -1) {
246         next = next_cluster(fs, walk);
247         if (deleting)
248             set_fat(fs, walk, 0);
249         else if ((deleting = !--clusters))
250             set_fat(fs, walk, -1);
251         walk = next;
252     }
253 }
254
255 static void auto_rename(DOS_FILE * file)
256 {
257     DOS_FILE *first, *walk;
258     uint32_t number;
259
260     if (!file->offset) {
261         printf("Cannot rename FAT32 root dir\n");
262         return;                 /* cannot rename FAT32 root dir */
263     }
264     first = file->parent ? file->parent->first : root;
265     number = 0;
266     while (1) {
267         char num[8];
268         sprintf(num, "%07lu", (unsigned long)number);
269         memcpy(file->dir_ent.name, "FSCK", 4);
270         memcpy(file->dir_ent.name + 4, num, 7);
271         for (walk = first; walk; walk = walk->next)
272             if (walk != file
273                 && !strncmp((const char *)walk->dir_ent.name,
274                             (const char *)file->dir_ent.name, MSDOS_NAME))
275                 break;
276         if (!walk) {
277             if (file->dir_ent.lcase & FAT_NO_83NAME) {
278                 /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
279                    present */
280                 file->dir_ent.lcase &= ~FAT_NO_83NAME;
281                 /* reset the attributes, only keep DIR and VOLUME */
282                 file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
283                 fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
284             } else {
285                 fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
286             }
287             if (file->lfn) {
288                 lfn_remove(file->lfn_offset, file->offset);
289                 file->lfn = NULL;
290             }
291             return;
292         }
293         number++;
294         if (number > 9999999) {
295             die("Too many files need repair.");
296         }
297     }
298     die("Can't generate a unique name.");
299 }
300
301 static void rename_file(DOS_FILE * file)
302 {
303     unsigned char name[46];
304     unsigned char *walk, *here;
305
306     if (!file->offset) {
307         printf("Cannot rename FAT32 root dir\n");
308         return;                 /* cannot rename FAT32 root dir */
309     }
310     while (1) {
311         if (get_line("New name", (char *)name, 45)) {
312             if ((here = (unsigned char *)strchr((const char *)name, '\n')))
313                 *here = 0;
314             for (walk = (unsigned char *)strrchr((const char *)name, 0);
315                  walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
316             walk[1] = 0;
317             for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
318             if (file_cvt(walk, file->dir_ent.name)) {
319                 if (file->dir_ent.lcase & FAT_NO_83NAME) {
320                     /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
321                        present */
322                     file->dir_ent.lcase &= ~FAT_NO_83NAME;
323                     /* reset the attributes, only keep DIR and VOLUME */
324                     file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
325                     fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
326                 } else {
327                     fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
328                 }
329                 if (file->lfn) {
330                     lfn_remove(file->lfn_offset, file->offset);
331                     file->lfn = NULL;
332                 }
333                 return;
334             }
335         }
336     }
337 }
338
339 static uint32_t scan_free_entry(DOS_FS * fs, DOS_FILE * this)
340 {
341     uint32_t clu_num, offset;
342     int i;
343     DIR_ENT de;
344
345     i = 2 * sizeof(DIR_ENT); /* Skip '.' and '..' slots */
346     clu_num = FSTART(this, fs);
347     while (clu_num > 0 && clu_num != -1) {
348         offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
349         fs_read(offset, sizeof(DIR_ENT), &de);
350         if (IS_FREE(de.name))
351             return offset;
352         i += sizeof(DIR_ENT);
353         if (!(i % fs->cluster_size))
354             if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
355                 break;
356     }
357
358     return 0;
359 }
360
361 static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dotdot)
362 {
363     const char *name, *ent;
364     uint32_t new_offset, start;
365
366     if (dotdot) {
367         name = "..";
368         ent = MSDOS_DOTDOT;
369         if (!file->parent->parent) {
370             start = 0;
371         } else {
372             start = FSTART(file->parent->parent, fs);
373             if (start == fs->root_cluster)
374                 start = 0;
375         }
376     } else {
377         name = ".";
378         ent = MSDOS_DOT;
379         start = FSTART(file->parent, fs);
380     }
381
382     if (!(file->dir_ent.attr & ATTR_DIR) || (FSTART(file, fs) != start) ||
383         strncmp((const char *)(file->dir_ent.name), ent, MSDOS_NAME)) {
384
385         if (IS_FREE(file->dir_ent.name)) {
386             printf("%s\n  Expected a valid '%s' entry in the %s slot, found free entry.\n",
387                 path_name(file->parent), name, dotdot ? "second" : "first");
388             switch (get_choice(1, "  Creating.",
389                                2,
390                                1, "Create entry",
391                                2, "Drop parent")) {
392             case 1:
393                 goto conjure;
394             case 2:
395                 drop_file(fs, file->parent);
396                 return 1;
397             }
398         }
399
400         if (!strncmp((const char *)(file->dir_ent.name), ent, MSDOS_NAME)) {
401             printf("%s\n  Invalid '%s' entry in the %s slot. Fixing.\n",
402                 path_name(file->parent), name, dotdot ? "second" : "first");
403             MODIFY_START(file, start, fs);
404             MODIFY(file, attr, ATTR_DIR);
405         } else {
406             printf("%s\n  Expected a valid '%s' entry in this slot.\n",
407                    path_name(file), name);
408             switch (get_choice(3, "  Moving entry down.",
409                                3,
410                                1, "Drop entry",
411                                2, "Drop parent",
412                                3, "Move entry down")) {
413             case 1:
414                 drop_file(fs, file);
415                 goto conjure;
416             case 2:
417                 drop_file(fs, file->parent);
418                 return 1;
419             case 3:
420                 new_offset = scan_free_entry(fs, file->parent);
421                 if (!new_offset) {
422                     printf("No free entry found.\n");
423                     return 0;
424                 }
425
426                 fs_write(new_offset, sizeof(file->dir_ent), &file->dir_ent);
427                 goto conjure;
428             }
429         }
430     }
431     if (file->dir_ent.lcase & FAT_NO_83NAME) {
432         /* Some versions of mtools write these directory entries with random data in
433            this field. */
434         printf("%s\n  Is a dot with no 8.3 name flag set, clearing.\n", path_name(file));
435         file->dir_ent.lcase &= ~FAT_NO_83NAME;
436         MODIFY(file, lcase, file->dir_ent.lcase);
437     }
438
439     return 0;
440
441 conjure:
442     memset(&file->dir_ent, 0, sizeof(DIR_ENT));
443     memcpy(file->dir_ent.name, ent, MSDOS_NAME);
444     fs_write(file->offset, sizeof(file->dir_ent), &file->dir_ent);
445     MODIFY_START(file, start, fs);
446     MODIFY(file, attr, ATTR_DIR);
447     return 0;
448 }
449
450 static int check_file(DOS_FS * fs, DOS_FILE * file)
451 {
452     DOS_FILE *owner;
453     int restart;
454     uint32_t parent, grandp, curr, this, clusters, prev, walk, clusters2;
455
456     if (IS_FREE(file->dir_ent.name))
457         return 0;
458
459     if (file->dir_ent.attr & ATTR_DIR) {
460         if (le32toh(file->dir_ent.size)) {
461             printf("%s\n  Directory has non-zero size. Fixing it.\n",
462                    path_name(file));
463             MODIFY(file, size, htole32(0));
464         }
465         if (FSTART(file, fs) == 0) {
466             printf("%s\n Start does point to root directory. Deleting dir. \n",
467                    path_name(file));
468             MODIFY(file, name[0], DELETED_FLAG);
469             return 0;
470         }
471         if (file->parent) {
472             parent = FSTART(file->parent, fs);
473             grandp = file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
474             if (fs->root_cluster && grandp == fs->root_cluster)
475                 grandp = 0;
476
477             if (FSTART(file, fs) == parent) {
478                 printf("%s\n Start does point to containing directory. Deleting entry.\n",
479                        path_name(file));
480                 MODIFY(file, name[0], DELETED_FLAG);
481                 MODIFY_START(file, 0, fs);
482                 return 0;
483             }
484             if (FSTART(file, fs) == grandp) {
485                 printf("%s\n Start does point to containing directory's parent. Deleting entry.\n",
486                        path_name(file));
487                 MODIFY(file, name[0], DELETED_FLAG);
488                 MODIFY_START(file, 0, fs);
489                 return 0;
490             }
491         }
492     }
493     if (FSTART(file, fs) == 1) {
494         printf("%s\n  Bad start cluster 1. Truncating file.\n",
495                path_name(file));
496         if (!file->offset)
497             die("Bad FAT32 root directory! (bad start cluster 1)\n");
498         MODIFY_START(file, 0, fs);
499     }
500     if (FSTART(file, fs) >= fs->data_clusters + 2) {
501         printf
502             ("%s\n  Start cluster beyond limit (%lu > %lu). Truncating file.\n",
503              path_name(file), (unsigned long)FSTART(file, fs),
504              (unsigned long)(fs->data_clusters + 1));
505         if (!file->offset)
506             die("Bad FAT32 root directory! (start cluster beyond limit: %lu > %lu)\n",
507                 (unsigned long)FSTART(file, fs),
508                 (unsigned long)(fs->data_clusters + 1));
509         MODIFY_START(file, 0, fs);
510     }
511     clusters = prev = 0;
512     for (curr = FSTART(file, fs) ? FSTART(file, fs) :
513          -1; curr != -1; curr = next_cluster(fs, curr)) {
514         FAT_ENTRY curEntry;
515         get_fat(&curEntry, fs->fat, curr, fs);
516
517         if (!curEntry.value || bad_cluster(fs, curr)) {
518             printf("%s\n  Contains a %s cluster (%lu). Assuming EOF.\n",
519                    path_name(file), curEntry.value ? "bad" : "free", (unsigned long)curr);
520             if (prev)
521                 set_fat(fs, prev, -1);
522             else if (!file->offset)
523                 die("FAT32 root dir starts with a bad cluster!");
524             else
525                 MODIFY_START(file, 0, fs);
526             break;
527         }
528         if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <=
529             clusters * fs->cluster_size) {
530             printf
531                 ("%s\n  File size is %u bytes, cluster chain length is > %u "
532                  "bytes.\n  Truncating file to %u bytes.\n", path_name(file),
533                  le32toh(file->dir_ent.size),
534                  (unsigned)clusters * fs->cluster_size,
535                  le32toh(file->dir_ent.size));
536             truncate_file(fs, file, clusters);
537             break;
538         }
539         if ((owner = get_owner(fs, curr))) {
540             int do_trunc = 0;
541             printf("%s  and\n", path_name(owner));
542             printf("%s\n  share clusters.\n", path_name(file));
543             clusters2 = 0;
544             for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
545                  next_cluster(fs, walk))
546                 if (walk == curr)
547                     break;
548                 else {
549                     if ((unsigned long long)clusters2 * fs->cluster_size >= UINT32_MAX)
550                         die("Internal error: File size is larger than 2^32-1");
551                     clusters2++;
552                 }
553             restart = file->dir_ent.attr & ATTR_DIR;
554             if (!owner->offset) {
555                 printf("  Truncating second to %u bytes because first "
556                        "is FAT32 root dir.\n",
557                        (unsigned)clusters * fs->cluster_size);
558                 do_trunc = 2;
559             } else if (!file->offset) {
560                 printf("  Truncating first to %u bytes because second "
561                        "is FAT32 root dir.\n",
562                        (unsigned)clusters2 * fs->cluster_size);
563                 do_trunc = 1;
564             } else {
565                 char *trunc_first_string;
566                 char *trunc_second_string;
567                 char *noninteractive_string;
568
569                 xasprintf(&trunc_first_string,
570                          "Truncate first to %u bytes%s",
571                          (unsigned)clusters2 * fs->cluster_size,
572                          restart ? " and restart" : "");
573                 xasprintf(&trunc_second_string,
574                           "Truncate second to %u bytes",
575                           (unsigned)clusters * fs->cluster_size);
576                 xasprintf(&noninteractive_string,
577                           "  Truncating second to %u bytes.",
578                           (unsigned)clusters * fs->cluster_size);
579
580                 do_trunc = get_choice(2, noninteractive_string,
581                                       2,
582                                       1, trunc_first_string,
583                                       2, trunc_second_string);
584
585                 free(trunc_first_string);
586                 free(trunc_second_string);
587                 free(noninteractive_string);
588             }
589
590             if (do_trunc == 1) {
591                 prev = 0;
592                 clusters = 0;
593                 for (this = FSTART(owner, fs); this > 0 && this != -1; this =
594                      next_cluster(fs, this)) {
595                     if (this == curr) {
596                         if (prev)
597                             set_fat(fs, prev, -1);
598                         else
599                             MODIFY_START(owner, 0, fs);
600                         MODIFY(owner, size, htole32(clusters * fs->cluster_size));
601                         if (restart)
602                             return 1;
603                         while (this > 0 && this != -1) {
604                             set_owner(fs, this, NULL);
605                             this = next_cluster(fs, this);
606                         }
607                         this = curr;
608                         break;
609                     }
610                     if ((unsigned long long)clusters * fs->cluster_size >= UINT32_MAX)
611                         die("Internal error: File size is larger than 2^32-1");
612                     clusters++;
613                     prev = this;
614                 }
615                 if (this != curr)
616                     die("Internal error: didn't find cluster %d in chain"
617                         " starting at %d", curr, FSTART(owner, fs));
618             } else {
619                 if (prev)
620                     set_fat(fs, prev, -1);
621                 else
622                     MODIFY_START(file, 0, fs);
623                 break;
624             }
625         }
626         set_owner(fs, curr, file);
627         if ((unsigned long long)clusters * fs->cluster_size >= UINT32_MAX)
628             die("Internal error: File size is larger than 2^32-1");
629         clusters++;
630         prev = curr;
631     }
632     if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) >
633         clusters * fs->cluster_size) {
634         printf
635             ("%s\n  File size is %u bytes, cluster chain length is %u bytes."
636              "\n  Truncating file to %u bytes.\n", path_name(file),
637              le32toh(file->dir_ent.size),
638              (unsigned)clusters * fs->cluster_size,
639              (unsigned)clusters * fs->cluster_size);
640         MODIFY(file, size,
641                htole32(clusters * fs->cluster_size));
642     }
643     return 0;
644 }
645
646 static int check_files(DOS_FS * fs, DOS_FILE * start)
647 {
648     while (start) {
649         if (check_file(fs, start))
650             return 1;
651         start = start->next;
652     }
653     return 0;
654 }
655
656 static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
657 {
658     DOS_FILE *parent, **walk, **scan;
659     int skip, redo;
660     int good, bad;
661
662     if (!*root)
663         return 0;
664     parent = (*root)->parent;
665     good = bad = 0;
666     for (walk = root; *walk; walk = &(*walk)->next)
667         if (bad_name(*walk))
668             bad++;
669         else
670             good++;
671     if (*root && parent && good + bad > 4 && bad > good / 2) {
672         printf("%s\n  Has a large number of bad entries. (%d/%d)\n",
673                path_name(parent), bad, good + bad);
674         if (!dots)
675             printf("  Not dropping root directory.\n");
676         else if (get_choice(2, "  Not dropping it in auto-mode.",
677                             2,
678                             1, "Drop directory",
679                             2, "Keep directory") == 1) {
680             truncate_file(fs, parent, 0);
681             MODIFY(parent, name[0], DELETED_FLAG);
682             /* buglet: deleted directory stays in the list. */
683             return 1;
684         }
685     }
686     redo = 0;
687     walk = root;
688     while (*walk) {
689         if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
690             puts(path_name(*walk));
691             printf("  Bad short file name (%s).\n",
692                    file_name((*walk)->dir_ent.name));
693             switch (get_choice(3, "  Auto-renaming it.",
694                                4,
695                                1, "Drop file",
696                                2, "Rename file",
697                                3, "Auto-rename",
698                                4, "Keep it")) {
699             case 1:
700                 drop_file(fs, *walk);
701                 walk = &(*walk)->next;
702                 continue;
703             case 2:
704                 rename_file(*walk);
705                 redo = 1;
706                 break;
707             case 3:
708                 auto_rename(*walk);
709                 printf("  Renamed to %s\n", file_name((*walk)->dir_ent.name));
710                 break;
711             case 4:
712                 break;
713             }
714         }
715         /* don't check for duplicates of the volume label */
716         if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
717             scan = &(*walk)->next;
718             skip = 0;
719             while (*scan && !skip) {
720                 if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
721                     !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
722                             MSDOS_NAME)) {
723                     printf("%s\n  Duplicate directory entry.\n  First  %s\n",
724                            path_name(*walk), file_stat(*walk));
725                     printf("  Second %s\n", file_stat(*scan));
726                     switch (get_choice(6, "  Auto-renaming second.",
727                                        6,
728                                        1, "Drop first",
729                                        2, "Drop second",
730                                        3, "Rename first",
731                                        4, "Rename second",
732                                        5, "Auto-rename first",
733                                        6, "Auto-rename second")) {
734                     case 1:
735                         drop_file(fs, *walk);
736                         *walk = (*walk)->next;
737                         skip = 1;
738                         break;
739                     case 2:
740                         drop_file(fs, *scan);
741                         *scan = (*scan)->next;
742                         continue;
743                     case 3:
744                         rename_file(*walk);
745                         printf("  Renamed to %s\n", path_name(*walk));
746                         redo = 1;
747                         break;
748                     case 4:
749                         rename_file(*scan);
750                         printf("  Renamed to %s\n", path_name(*walk));
751                         redo = 1;
752                         break;
753                     case 5:
754                         auto_rename(*walk);
755                         printf("  Renamed to %s\n",
756                                file_name((*walk)->dir_ent.name));
757                         break;
758                     case 6:
759                         auto_rename(*scan);
760                         printf("  Renamed to %s\n",
761                                file_name((*scan)->dir_ent.name));
762                         break;
763                     }
764                 }
765                 scan = &(*scan)->next;
766             }
767             if (skip)
768                 continue;
769         }
770         if (!redo)
771             walk = &(*walk)->next;
772         else {
773             walk = root;
774             redo = 0;
775         }
776     }
777     return 0;
778 }
779
780 /**
781  * Check a dentry's cluster chain for bad clusters.
782  * If requested, we verify readability and mark unreadable clusters as bad.
783  *
784  * @param[inout]    fs          Information about the filesystem
785  * @param[in]       file        dentry to check
786  * @param[in]       read_test   Nonzero == verify that dentry's clusters can
787  *                              be read
788  */
789 static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
790 {
791     DOS_FILE *owner;
792     uint32_t walk, prev, clusters, next_clu;
793
794     prev = clusters = 0;
795     for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
796          walk = next_clu) {
797         next_clu = next_cluster(fs, walk);
798
799         /* In this stage we are checking only for a loop within our own
800          * cluster chain.
801          * Cross-linking of clusters is handled in check_file()
802          */
803         if ((owner = get_owner(fs, walk))) {
804             if (owner == file) {
805                 printf("%s\n  Circular cluster chain. Truncating to %lu "
806                        "cluster%s.\n", path_name(file), (unsigned long)clusters,
807                        clusters == 1 ? "" : "s");
808                 if (prev)
809                     set_fat(fs, prev, -1);
810                 else if (!file->offset)
811                     die("Bad FAT32 root directory! (bad start cluster)\n");
812                 else
813                     MODIFY_START(file, 0, fs);
814             }
815             break;
816         }
817         if (bad_cluster(fs, walk))
818             break;
819         if (read_test) {
820             if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
821                 prev = walk;
822                 clusters++;
823             } else {
824                 printf("%s\n  Cluster %lu (%lu) is unreadable. Skipping it.\n",
825                        path_name(file), (unsigned long)clusters, (unsigned long)walk);
826                 if (prev)
827                     set_fat(fs, prev, next_cluster(fs, walk));
828                 else
829                     MODIFY_START(file, next_cluster(fs, walk), fs);
830                 set_fat(fs, walk, -2);
831             }
832         } else {
833             prev = walk;
834             clusters++;
835         }
836         set_owner(fs, walk, file);
837     }
838     /* Revert ownership (for now) */
839     for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
840          walk = next_cluster(fs, walk))
841         if (bad_cluster(fs, walk))
842             break;
843         else if (get_owner(fs, walk) == file)
844             set_owner(fs, walk, NULL);
845         else
846             break;
847 }
848
849 static void undelete(DOS_FS * fs, DOS_FILE * file)
850 {
851     uint32_t clusters, left, prev, walk;
852
853     clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) /
854         fs->cluster_size;
855     prev = 0;
856
857     walk = FSTART(file, fs);
858
859     while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) {
860
861         FAT_ENTRY curEntry;
862         get_fat(&curEntry, fs->fat, walk, fs);
863
864         if (!curEntry.value)
865             break;
866
867         left--;
868         if (prev)
869             set_fat(fs, prev, walk);
870         prev = walk;
871         walk++;
872     }
873     if (prev)
874         set_fat(fs, prev, -1);
875     else
876         MODIFY_START(file, 0, fs);
877     if (left)
878         printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
879                (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s");
880
881 }
882
883 static void new_dir(void)
884 {
885     lfn_reset();
886 }
887
888 /**
889  * Create a description for a referenced dentry and insert it in our dentry
890  * tree. Then, go check the dentry's cluster chain for bad clusters and
891  * cluster loops.
892  *
893  * @param[inout]    fs      Information about the filesystem
894  * @param[out]      chain
895  * @param[in]       parent  Information about parent directory of this file
896  *                          NULL == no parent ('file' is root directory)
897  * @param[in]       offset  Partition-relative byte offset of directory entry of interest
898  *                          0 == Root directory
899  * @param           cp
900  */
901 static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
902                      off_t offset, FDSC ** cp)
903 {
904     DOS_FILE *new;
905     DIR_ENT de;
906     FD_TYPE type;
907
908     if (offset)
909         fs_read(offset, sizeof(DIR_ENT), &de);
910     else {
911         /* Construct a DIR_ENT for the root directory */
912         memset(&de, 0, sizeof de);
913         memcpy(de.name, "           ", MSDOS_NAME);
914         de.attr = ATTR_DIR;
915         de.start = htole16(fs->root_cluster & 0xffff);
916         de.starthi = htole16((fs->root_cluster >> 16) & 0xffff);
917     }
918     if ((type = file_type(cp, (char *)de.name)) != fdt_none) {
919         if (type == fdt_undelete && (de.attr & ATTR_DIR))
920             die("Can't undelete directories.");
921         file_modify(cp, (char *)de.name);
922         fs_write(offset, 1, &de);
923     }
924     if (IS_FREE(de.name)) {
925         lfn_check_orphaned();
926         return;
927     }
928     if (de.attr == VFAT_LN_ATTR) {
929         lfn_add_slot(&de, offset);
930         return;
931     }
932     new = qalloc(&mem_queue, sizeof(DOS_FILE));
933     new->lfn = lfn_get(&de, &new->lfn_offset);
934     new->offset = offset;
935     memcpy(&new->dir_ent, &de, sizeof(de));
936     new->next = new->first = NULL;
937     new->parent = parent;
938     if (type == fdt_undelete)
939         undelete(fs, new);
940     **chain = new;
941     *chain = &new->next;
942     if (list) {
943         printf("Checking file %s", path_name(new));
944         if (new->lfn)
945             printf(" (%s)", file_name(new->dir_ent.name));      /* (8.3) */
946         printf("\n");
947     }
948     /* Don't include root directory in the total file count */
949     if (offset)
950         ++n_files;
951     test_file(fs, new, test);   /* Bad cluster check */
952 }
953
954 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
955
956 static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
957 {
958     DOS_FILE **chain;
959     int i;
960     uint32_t clu_num;
961
962     chain = &this->first;
963     i = 0;
964     clu_num = FSTART(this, fs);
965     new_dir();
966     if (clu_num != 0 && clu_num != -1 && this->offset) {
967         DOS_FILE file;
968
969         file.lfn = NULL;
970         file.lfn_offset = 0;
971         file.next = NULL;
972         file.parent = this;
973         file.first = NULL;
974
975         file.offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
976         fs_read(file.offset, sizeof(DIR_ENT), &file.dir_ent);
977         if (handle_dot(fs, &file, 0))
978             return 1;
979         i += sizeof(DIR_ENT);
980
981         file.offset = cluster_start(fs, clu_num) + (i % fs->cluster_size);
982         fs_read(file.offset, sizeof(DIR_ENT), &file.dir_ent);
983         if (handle_dot(fs, &file, 1))
984             return 1;
985         i += sizeof(DIR_ENT);
986     }
987     while (clu_num > 0 && clu_num != -1) {
988         add_file(fs, &chain, this,
989                  cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
990         i += sizeof(DIR_ENT);
991         if (!(i % fs->cluster_size))
992             if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
993                 break;
994     }
995     lfn_check_orphaned();
996     if (check_dir(fs, &this->first, this->offset))
997         return 0;
998     if (check_files(fs, this->first))
999         return 1;
1000     return subdirs(fs, this, cp);
1001 }
1002
1003 /**
1004  * Recursively scan subdirectories of the specified parent directory.
1005  *
1006  * @param[inout]    fs      Information about the filesystem
1007  * @param[in]       parent  Identifies the directory to scan
1008  * @param[in]       cp
1009  *
1010  * @return  0   Success
1011  * @return  1   Error
1012  */
1013 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
1014 {
1015     DOS_FILE *walk;
1016
1017     for (walk = parent ? parent->first : root; walk; walk = walk->next)
1018         if (!IS_FREE(walk->dir_ent.name) && (walk->dir_ent.attr & ATTR_DIR))
1019             if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
1020                 return 1;
1021     return 0;
1022 }
1023
1024 /**
1025  * Scan all directory and file information for errors.
1026  *
1027  * @param[inout]    fs      Information about the filesystem
1028  *
1029  * @return  0   Success
1030  * @return  1   Error
1031  */
1032 int scan_root(DOS_FS * fs)
1033 {
1034     DOS_FILE **chain;
1035     int i;
1036
1037     root = NULL;
1038     chain = &root;
1039     new_dir();
1040     if (fs->root_cluster) {
1041         add_file(fs, &chain, NULL, 0, &fp_root);
1042     } else {
1043         for (i = 0; i < fs->root_entries; i++)
1044             add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
1045                      &fp_root);
1046     }
1047     lfn_check_orphaned();
1048     (void)check_dir(fs, &root, 0);
1049     if (check_files(fs, root))
1050         return 1;
1051     return subdirs(fs, NULL, &fp_root);
1052 }
1053
1054 static char print_fat_dirty_state(void)
1055 {
1056     printf("Dirty bit is set. Fs was not properly unmounted and"
1057            " some data may be corrupt.\n");
1058
1059     return get_choice(1, " Automatically removing dirty bit.",
1060                       2,
1061                       1, "Remove dirty bit",
1062                       2, "No action");
1063 }
1064
1065 void check_dirty_bits(DOS_FS * fs)
1066 {
1067     if (fs->fat_bits == 32) {
1068         struct boot_sector b32;
1069         FAT_ENTRY fat32_flags;
1070
1071         get_fat(&fat32_flags, fs->fat, 1, fs);
1072         fs_read(0, sizeof(b32), &b32);
1073
1074         if ((b32.boot_flags & FAT_STATE_DIRTY) || !(fat32_flags.value & FAT32_FLAG_CLEAN_SHUTDOWN)) {
1075             if (print_fat_dirty_state() == 1) {
1076                 if (b32.boot_flags & FAT_STATE_DIRTY) {
1077                     b32.boot_flags &= ~FAT_STATE_DIRTY;
1078                     fs_write(0, sizeof(b32), &b32);
1079                 }
1080                 if (!(fat32_flags.value & FAT32_FLAG_CLEAN_SHUTDOWN)) {
1081                     uint32_t *new_flags_ptr = (uint32_t *)(fs->fat + 4);
1082                     *new_flags_ptr = htole32(fat32_flags.value | FAT32_FLAG_CLEAN_SHUTDOWN | (fat32_flags.reserved << 28));
1083                     fs_write(fs->fat_start + 4, 4, new_flags_ptr);
1084                     if (fs->nfats > 1)
1085                         fs_write(fs->fat_start + 4 + fs->fat_size, 4, new_flags_ptr);
1086                 }
1087             }
1088         }
1089     } else {
1090         struct boot_sector_16 b16;
1091         FAT_ENTRY fat16_flags;
1092         int fat16_is_dirty = 0;
1093
1094         fs_read(0, sizeof(b16), &b16);
1095
1096         if (fs->fat_bits == 16) {
1097             get_fat(&fat16_flags, fs->fat, 1, fs);
1098             fat16_is_dirty = !(fat16_flags.value & FAT16_FLAG_CLEAN_SHUTDOWN);
1099         }
1100
1101         if ((b16.boot_flags & FAT_STATE_DIRTY) || fat16_is_dirty) {
1102             if (print_fat_dirty_state() == 1) {
1103                 if (b16.boot_flags & FAT_STATE_DIRTY) {
1104                     b16.boot_flags &= ~FAT_STATE_DIRTY;
1105                     fs_write(0, sizeof(b16), &b16);
1106                 }
1107                 if (fat16_is_dirty) {
1108                     uint16_t *new_flags_ptr = (uint16_t *)(fs->fat + 2);
1109                     *new_flags_ptr = htole16(fat16_flags.value | FAT16_FLAG_CLEAN_SHUTDOWN);
1110                     fs_write(fs->fat_start + 2, 2, new_flags_ptr);
1111                     if (fs->nfats > 1)
1112                         fs_write(fs->fat_start + 2 + fs->fat_size, 2, new_flags_ptr);
1113                 }
1114             }
1115         }
1116     }
1117 }
1118
1119 static void get_new_label(char doslabel[12])
1120 {
1121     char newlabel[256];
1122     size_t len;
1123     char *p;
1124     int ret;
1125     int i;
1126
1127     while (1) {
1128         if (get_line("New label", newlabel, sizeof(newlabel))) {
1129             if ((p = strchr(newlabel, '\n')))
1130                 *p = 0;
1131
1132             len = mbstowcs(NULL, newlabel, 0);
1133             if (len != (size_t)-1 && len > 11) {
1134                 printf("Label can be no longer than 11 characters\n");
1135                 continue;
1136             }
1137
1138             if (!local_string_to_dos_string(doslabel, newlabel, 12)) {
1139                 printf("Error when processing label\n");
1140                 continue;
1141             }
1142
1143             for (i = strlen(doslabel); i < 11; ++i)
1144                 doslabel[i] = ' ';
1145             doslabel[11] = 0;
1146
1147             ret = validate_volume_label(doslabel);
1148             if ((ret && only_uppercase_label) || (ret & ~0x1)) {
1149                 printf("New label is invalid\n");
1150                 continue;
1151             } else if (ret & 0x1) {
1152                 printf("Warning: lowercase labels might not work properly on some systems\n");
1153             }
1154
1155             break;
1156         }
1157     }
1158 }
1159
1160 static int check_boot_label(DOS_FS *fs)
1161 {
1162     char doslabel[12];
1163     wchar_t wlabel[12];
1164     int ret;
1165     int i;
1166
1167     ret = validate_volume_label(fs->label);
1168     if (ret & ~0x1) {
1169         printf("Label '%s' stored in boot sector is not valid.\n", pretty_label(fs->label));
1170         switch (get_choice(1, "  Auto-removing label from boot sector.",
1171                            2,
1172                            1, "Remove invalid label from boot sector",
1173                            2, "Enter new label")) {
1174         case 1:
1175             write_boot_label(fs, "NO NAME    ");
1176             memcpy(fs->label, "NO NAME    ", 11);
1177             return 1;
1178         case 2:
1179             get_new_label(doslabel);
1180             write_boot_label(fs, doslabel);
1181             memcpy(fs->label, doslabel, 11);
1182             return 1;
1183         }
1184     } else if ((ret & 0x1) && only_uppercase_label) {
1185         printf("Label '%s' stored in boot sector contains lowercase characters.\n", pretty_label(fs->label));
1186         switch (get_choice(1, "  Auto-changing lowercase characters to uppercase",
1187                            3,
1188                            1, "Change lowercase characters to uppercase",
1189                            2, "Remove invalid label",
1190                            2, "Set new label")) {
1191         case 1:
1192             if (!dos_string_to_wchar_string(wlabel, fs->label, sizeof(wlabel)))
1193                 die("Cannot change lowercase characters to uppercase.");
1194             for (i = 0; i < 11; ++i)
1195                 wlabel[i] = towupper(wlabel[i]);
1196             if (!wchar_string_to_dos_string(doslabel, wlabel, sizeof(doslabel)))
1197                 die("Cannot change lowercase characters to uppercase.");
1198             write_boot_label(fs, doslabel);
1199             memcpy(fs->label, doslabel, 11);
1200             return 1;
1201         case 2:
1202             write_boot_label(fs, "NO NAME    ");
1203             memcpy(fs->label, "NO NAME    ", 11);
1204             return 1;
1205         case 3:
1206             get_new_label(doslabel);
1207             write_boot_label(fs, doslabel);
1208             memcpy(fs->label, doslabel, 11);
1209             return 1;
1210         }
1211     }
1212
1213     return 0;
1214 }
1215
1216 void check_label(DOS_FS *fs)
1217 {
1218     DIR_ENT de;
1219     off_t offset;
1220     char buffer[256];
1221     char doslabel[12];
1222     wchar_t wlabel[12];
1223     int ret;
1224     int i;
1225
1226     offset = find_volume_de(fs, &de);
1227
1228     if (offset == 0 && memcmp(fs->label, "NO NAME    ", 11) != 0)
1229         check_boot_label(fs);
1230
1231     if (offset == 0 && memcmp(fs->label, "NO NAME    ", 11) != 0) {
1232         printf("Label in boot sector is '%s', but there is no volume label in root directory.\n", pretty_label(fs->label));
1233         switch (get_choice(1, "  Auto-removing label from boot sector.",
1234                            2,
1235                            1, "Remove label from boot sector",
1236                            2, "Copy label from boot sector to root directory")) {
1237         case 1:
1238             write_boot_label(fs, "NO NAME    ");
1239             memcpy(fs->label, "NO NAME    ", 11);
1240             break;
1241         case 2:
1242             write_volume_label(fs, fs->label);
1243             offset = find_volume_de(fs, &de);
1244             break;
1245         }
1246     }
1247
1248     if (offset != 0) {
1249         memcpy(doslabel, de.name, 11);
1250         if (doslabel[0] == 0x05)
1251             doslabel[0] = 0xe5;
1252         ret = validate_volume_label(doslabel);
1253         if (ret & ~0x1) {
1254             printf("Volume label '%s' stored in root directory is not valid.\n", pretty_label(doslabel));
1255             switch (get_choice(1, "  Auto-removing label.",
1256                                2,
1257                                1, "Remove invalid label",
1258                                2, "Set new label")) {
1259             case 1:
1260                 remove_label(fs);
1261                 memcpy(fs->label, "NO NAME    ", 11);
1262                 offset = 0;
1263                 break;
1264             case 2:
1265                 get_new_label(doslabel);
1266                 write_label(fs, doslabel);
1267                 memcpy(fs->label, doslabel, 11);
1268                 break;
1269             }
1270         } else if ((ret & 0x1) && only_uppercase_label) {
1271             printf("Volume label '%s' stored in root directory contains lowercase characters.\n", pretty_label(doslabel));
1272             switch (get_choice(1, "  Auto-changing lowercase characters to uppercase",
1273                                3,
1274                                1, "Change lowercase characters to uppercase",
1275                                2, "Remove invalid label",
1276                                2, "Set new label")) {
1277             case 1:
1278                 if (!dos_string_to_wchar_string(wlabel, doslabel, sizeof(wlabel)))
1279                     die("Cannot change lowercase characters to uppercase.");
1280                 for (i = 0; i < 11; ++i)
1281                     wlabel[i] = towupper(wlabel[i]);
1282                 if (!wchar_string_to_dos_string(doslabel, wlabel, sizeof(doslabel)))
1283                     die("Cannot change lowercase characters to uppercase.");
1284                 write_label(fs, doslabel);
1285                 memcpy(fs->label, doslabel, 11);
1286                 break;
1287             case 2:
1288                 remove_label(fs);
1289                 memcpy(fs->label, "NO NAME    ", 11);
1290                 offset = 0;
1291                 break;
1292             case 3:
1293                 get_new_label(doslabel);
1294                 write_label(fs, doslabel);
1295                 memcpy(fs->label, doslabel, 11);
1296                 break;
1297             }
1298         }
1299     }
1300
1301 again:
1302
1303     if (offset != 0 && memcmp(fs->label, "NO NAME    ", 11) == 0 && memcmp(doslabel, "NO NAME    ", 11) != 0) {
1304         printf("There is no label in boot sector, but there is volume label '%s' stored in root directory\n", pretty_label(doslabel));
1305         switch (get_choice(1, "  Auto-copying volume label from root directory to boot sector.",
1306                            2,
1307                            1, "Copy volume label from root directory to boot sector",
1308                            2, "Remove volume label from root directory")) {
1309         case 1:
1310             write_boot_label(fs, doslabel);
1311             memcpy(fs->label, doslabel, 11);
1312             break;
1313         case 2:
1314             remove_label(fs);
1315             offset = 0;
1316             break;
1317         }
1318     }
1319
1320     if (offset != 0 && memcmp(fs->label, "NO NAME    ", 11) != 0 && memcmp(fs->label, doslabel, 11) != 0) {
1321         strncpy(buffer, pretty_label(doslabel), sizeof(buffer)-1);
1322         buffer[sizeof(buffer)-1] = 0;
1323         printf("Volume label '%s' stored in root directory and label '%s' stored in boot sector and different.\n", buffer, pretty_label(fs->label));
1324         switch (get_choice(1, "  Auto-copying volume label from root directory to boot sector.",
1325                            2,
1326                            1, "Copy volume label from root directory to boot sector",
1327                            2, "Copy label from boot sector to root directory")) {
1328         case 1:
1329             write_boot_label(fs, doslabel);
1330             memcpy(fs->label, doslabel, 11);
1331             break;
1332         case 2:
1333             ret = check_boot_label(fs);
1334             if (ret)
1335                 goto again;
1336             write_volume_label(fs, fs->label);
1337             offset = find_volume_de(fs, &de);
1338             /* NOTE: doslabel is not updated */
1339             break;
1340         }
1341     }
1342 }