"Fossies" - the Fresh Open Source Software Archive

Member "dosfstools-4.2/src/check.c" (31 Jan 2021, 39830 Bytes) of package /linux/misc/dosfstools-4.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "check.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1_vs_4.2.

    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 }