1 /* check.c - Check and repair a PC/MS-DOS filesystem
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>
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.
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.
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/>.
22 The complete text of the GNU General Public License
23 can be found in /usr/share/common-licenses/GPL-3 file.
26 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
27 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
48 /* the longest path on the filesystem that can be handled by path_name() */
49 #define PATH_NAME_MAX 1023
51 static DOS_FILE *root;
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))
58 #define MODIFY(p,i,v) \
62 fs_write(p->offset+offsetof(DIR_ENT,i), \
63 sizeof(p->dir_ent.i),&p->dir_ent.i); \
67 #define MODIFY_START(p,v,fs) \
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); \
77 fs_write(offsetof(struct boot_sector,root_cluster), \
78 sizeof(((struct boot_sector *)0)->root_cluster), \
82 MODIFY(p,start,htole16((__v)&0xffff)); \
83 if (fs->fat_bits == 32) \
84 MODIFY(p,starthi,htole16((__v)>>16)); \
89 * Construct a full path (starting with '/') for the specified dentry,
90 * relative to the partition. All components are "long" names where possible.
92 * @param[in] file Information about dentry (file or directory) of interest
94 * return Pointer to static string containing file's full path
96 static char *path_name(DOS_FILE * file)
98 static char path[PATH_NAME_MAX * 2];
101 *path = 0; /* Reached the root directory */
103 if (strlen(path_name(file->parent)) > PATH_NAME_MAX)
104 die("Path name too long.");
105 if (strcmp(path, "/") != 0)
108 /* Append the long name to the path,
109 * or the short name if there isn't a long one
111 strcpy(strrchr(path, 0),
112 file->lfn ? file->lfn : file_name(file->dir_ent.name));
117 static const char *month_str[] =
118 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
120 static char *file_stat(DOS_FILE * file)
122 static char temp[100];
123 unsigned int hours, minutes, secs, day, month, year;
124 unsigned short time, date;
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;
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);
145 static int bad_name(DOS_FILE * file)
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;
152 /* do not check synthetic FAT32 root entry */
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)) {
161 /* don't complain about the dummy 11 bytes used by patched Linux
163 if (file->dir_ent.lcase & FAT_NO_83NAME)
166 for (i = 0; i < MSDOS_NAME; i++) {
167 if ((name[i] < ' ' && !(i == 0 && name[0] == 0x05)) || name[i] == 0x7f)
171 if (strchr(bad_chars, name[i]))
178 if (no_spaces_in_sfns) {
180 for (i = 0; i < 8; i++) {
184 /* non-space after a space not allowed, space terminates the name
190 for (i = 0; i < 3; i++) {
194 /* non-space after a space not allowed, space terminates the ext
200 /* Under GEMDOS, chars >= 128 are never allowed. */
201 if (atari_format && suspicious)
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. */
211 static void lfn_remove(off_t from, off_t to)
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...
219 memset(&empty, 0, sizeof(empty));
220 empty.name[0] = DELETED_FLAG;
222 for (; from < to; from += sizeof(empty)) {
223 fs_write(from, sizeof(DIR_ENT), &empty);
227 static void drop_file(DOS_FS * fs, DOS_FILE * file)
231 MODIFY(file, name[0], DELETED_FLAG);
233 lfn_remove(file->lfn_offset, file->offset);
237 static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters)
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);
248 set_fat(fs, walk, 0);
249 else if ((deleting = !--clusters))
250 set_fat(fs, walk, -1);
255 static void auto_rename(DOS_FILE * file)
257 DOS_FILE *first, *walk;
261 printf("Cannot rename FAT32 root dir\n");
262 return; /* cannot rename FAT32 root dir */
264 first = file->parent ? file->parent->first : root;
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)
273 && !strncmp((const char *)walk->dir_ent.name,
274 (const char *)file->dir_ent.name, MSDOS_NAME))
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
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);
285 fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
288 lfn_remove(file->lfn_offset, file->offset);
294 if (number > 9999999) {
295 die("Too many files need repair.");
298 die("Can't generate a unique name.");
301 static void rename_file(DOS_FILE * file)
303 unsigned char name[46];
304 unsigned char *walk, *here;
307 printf("Cannot rename FAT32 root dir\n");
308 return; /* cannot rename FAT32 root dir */
311 if (get_line("New name", (char *)name, 45)) {
312 if ((here = (unsigned char *)strchr((const char *)name, '\n')))
314 for (walk = (unsigned char *)strrchr((const char *)name, 0);
315 walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
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
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);
327 fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
330 lfn_remove(file->lfn_offset, file->offset);
339 static uint32_t scan_free_entry(DOS_FS * fs, DOS_FILE * this)
341 uint32_t clu_num, offset;
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))
352 i += sizeof(DIR_ENT);
353 if (!(i % fs->cluster_size))
354 if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
361 static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dotdot)
363 const char *name, *ent;
364 uint32_t new_offset, start;
369 if (!file->parent->parent) {
372 start = FSTART(file->parent->parent, fs);
373 if (start == fs->root_cluster)
379 start = FSTART(file->parent, fs);
382 if (!(file->dir_ent.attr & ATTR_DIR) || (FSTART(file, fs) != start) ||
383 strncmp((const char *)(file->dir_ent.name), ent, MSDOS_NAME)) {
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.",
395 drop_file(fs, file->parent);
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);
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.",
412 3, "Move entry down")) {
417 drop_file(fs, file->parent);
420 new_offset = scan_free_entry(fs, file->parent);
422 printf("No free entry found.\n");
426 fs_write(new_offset, sizeof(file->dir_ent), &file->dir_ent);
431 if (file->dir_ent.lcase & FAT_NO_83NAME) {
432 /* Some versions of mtools write these directory entries with random data in
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);
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);
450 static int check_file(DOS_FS * fs, DOS_FILE * file)
454 uint32_t parent, grandp, curr, this, clusters, prev, walk, clusters2;
456 if (IS_FREE(file->dir_ent.name))
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",
463 MODIFY(file, size, htole32(0));
465 if (FSTART(file, fs) == 0) {
466 printf("%s\n Start does point to root directory. Deleting dir. \n",
468 MODIFY(file, name[0], DELETED_FLAG);
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)
477 if (FSTART(file, fs) == parent) {
478 printf("%s\n Start does point to containing directory. Deleting entry.\n",
480 MODIFY(file, name[0], DELETED_FLAG);
481 MODIFY_START(file, 0, fs);
484 if (FSTART(file, fs) == grandp) {
485 printf("%s\n Start does point to containing directory's parent. Deleting entry.\n",
487 MODIFY(file, name[0], DELETED_FLAG);
488 MODIFY_START(file, 0, fs);
493 if (FSTART(file, fs) == 1) {
494 printf("%s\n Bad start cluster 1. Truncating file.\n",
497 die("Bad FAT32 root directory! (bad start cluster 1)\n");
498 MODIFY_START(file, 0, fs);
500 if (FSTART(file, fs) >= fs->data_clusters + 2) {
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));
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);
512 for (curr = FSTART(file, fs) ? FSTART(file, fs) :
513 -1; curr != -1; curr = next_cluster(fs, curr)) {
515 get_fat(&curEntry, fs->fat, curr, fs);
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);
521 set_fat(fs, prev, -1);
522 else if (!file->offset)
523 die("FAT32 root dir starts with a bad cluster!");
525 MODIFY_START(file, 0, fs);
528 if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <=
529 clusters * fs->cluster_size) {
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);
539 if ((owner = get_owner(fs, curr))) {
541 printf("%s and\n", path_name(owner));
542 printf("%s\n share clusters.\n", path_name(file));
544 for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
545 next_cluster(fs, walk))
549 if ((unsigned long long)clusters2 * fs->cluster_size >= UINT32_MAX)
550 die("Internal error: File size is larger than 2^32-1");
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);
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);
565 char *trunc_first_string;
566 char *trunc_second_string;
567 char *noninteractive_string;
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);
580 do_trunc = get_choice(2, noninteractive_string,
582 1, trunc_first_string,
583 2, trunc_second_string);
585 free(trunc_first_string);
586 free(trunc_second_string);
587 free(noninteractive_string);
593 for (this = FSTART(owner, fs); this > 0 && this != -1; this =
594 next_cluster(fs, this)) {
597 set_fat(fs, prev, -1);
599 MODIFY_START(owner, 0, fs);
600 MODIFY(owner, size, htole32(clusters * fs->cluster_size));
603 while (this > 0 && this != -1) {
604 set_owner(fs, this, NULL);
605 this = next_cluster(fs, this);
610 if ((unsigned long long)clusters * fs->cluster_size >= UINT32_MAX)
611 die("Internal error: File size is larger than 2^32-1");
616 die("Internal error: didn't find cluster %d in chain"
617 " starting at %d", curr, FSTART(owner, fs));
620 set_fat(fs, prev, -1);
622 MODIFY_START(file, 0, fs);
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");
632 if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) >
633 clusters * fs->cluster_size) {
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);
641 htole32(clusters * fs->cluster_size));
646 static int check_files(DOS_FS * fs, DOS_FILE * start)
649 if (check_file(fs, start))
656 static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
658 DOS_FILE *parent, **walk, **scan;
664 parent = (*root)->parent;
666 for (walk = root; *walk; walk = &(*walk)->next)
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);
675 printf(" Not dropping root directory.\n");
676 else if (get_choice(2, " Not dropping it in auto-mode.",
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. */
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.",
700 drop_file(fs, *walk);
701 walk = &(*walk)->next;
709 printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name));
715 /* don't check for duplicates of the volume label */
716 if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
717 scan = &(*walk)->next;
719 while (*scan && !skip) {
720 if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
721 !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.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.",
732 5, "Auto-rename first",
733 6, "Auto-rename second")) {
735 drop_file(fs, *walk);
736 *walk = (*walk)->next;
740 drop_file(fs, *scan);
741 *scan = (*scan)->next;
745 printf(" Renamed to %s\n", path_name(*walk));
750 printf(" Renamed to %s\n", path_name(*walk));
755 printf(" Renamed to %s\n",
756 file_name((*walk)->dir_ent.name));
760 printf(" Renamed to %s\n",
761 file_name((*scan)->dir_ent.name));
765 scan = &(*scan)->next;
771 walk = &(*walk)->next;
781 * Check a dentry's cluster chain for bad clusters.
782 * If requested, we verify readability and mark unreadable clusters as bad.
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
789 static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
792 uint32_t walk, prev, clusters, next_clu;
795 for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
797 next_clu = next_cluster(fs, walk);
799 /* In this stage we are checking only for a loop within our own
801 * Cross-linking of clusters is handled in check_file()
803 if ((owner = get_owner(fs, walk))) {
805 printf("%s\n Circular cluster chain. Truncating to %lu "
806 "cluster%s.\n", path_name(file), (unsigned long)clusters,
807 clusters == 1 ? "" : "s");
809 set_fat(fs, prev, -1);
810 else if (!file->offset)
811 die("Bad FAT32 root directory! (bad start cluster)\n");
813 MODIFY_START(file, 0, fs);
817 if (bad_cluster(fs, walk))
820 if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
824 printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n",
825 path_name(file), (unsigned long)clusters, (unsigned long)walk);
827 set_fat(fs, prev, next_cluster(fs, walk));
829 MODIFY_START(file, next_cluster(fs, walk), fs);
830 set_fat(fs, walk, -2);
836 set_owner(fs, walk, file);
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))
843 else if (get_owner(fs, walk) == file)
844 set_owner(fs, walk, NULL);
849 static void undelete(DOS_FS * fs, DOS_FILE * file)
851 uint32_t clusters, left, prev, walk;
853 clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) /
857 walk = FSTART(file, fs);
859 while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) {
862 get_fat(&curEntry, fs->fat, walk, fs);
869 set_fat(fs, prev, walk);
874 set_fat(fs, prev, -1);
876 MODIFY_START(file, 0, fs);
878 printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
879 (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s");
883 static void new_dir(void)
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
893 * @param[inout] fs Information about the filesystem
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
901 static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
902 off_t offset, FDSC ** cp)
909 fs_read(offset, sizeof(DIR_ENT), &de);
911 /* Construct a DIR_ENT for the root directory */
912 memset(&de, 0, sizeof de);
913 memcpy(de.name, " ", MSDOS_NAME);
915 de.start = htole16(fs->root_cluster & 0xffff);
916 de.starthi = htole16((fs->root_cluster >> 16) & 0xffff);
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);
924 if (IS_FREE(de.name)) {
925 lfn_check_orphaned();
928 if (de.attr == VFAT_LN_ATTR) {
929 lfn_add_slot(&de, offset);
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)
943 printf("Checking file %s", path_name(new));
945 printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */
948 /* Don't include root directory in the total file count */
951 test_file(fs, new, test); /* Bad cluster check */
954 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
956 static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
962 chain = &this->first;
964 clu_num = FSTART(this, fs);
966 if (clu_num != 0 && clu_num != -1 && this->offset) {
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))
979 i += sizeof(DIR_ENT);
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))
985 i += sizeof(DIR_ENT);
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)
995 lfn_check_orphaned();
996 if (check_dir(fs, &this->first, this->offset))
998 if (check_files(fs, this->first))
1000 return subdirs(fs, this, cp);
1004 * Recursively scan subdirectories of the specified parent directory.
1006 * @param[inout] fs Information about the filesystem
1007 * @param[in] parent Identifies the directory to scan
1013 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
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)))
1025 * Scan all directory and file information for errors.
1027 * @param[inout] fs Information about the filesystem
1032 int scan_root(DOS_FS * fs)
1040 if (fs->root_cluster) {
1041 add_file(fs, &chain, NULL, 0, &fp_root);
1043 for (i = 0; i < fs->root_entries; i++)
1044 add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
1047 lfn_check_orphaned();
1048 (void)check_dir(fs, &root, 0);
1049 if (check_files(fs, root))
1051 return subdirs(fs, NULL, &fp_root);
1054 static char print_fat_dirty_state(void)
1056 printf("Dirty bit is set. Fs was not properly unmounted and"
1057 " some data may be corrupt.\n");
1059 return get_choice(1, " Automatically removing dirty bit.",
1061 1, "Remove dirty bit",
1065 void check_dirty_bits(DOS_FS * fs)
1067 if (fs->fat_bits == 32) {
1068 struct boot_sector b32;
1069 FAT_ENTRY fat32_flags;
1071 get_fat(&fat32_flags, fs->fat, 1, fs);
1072 fs_read(0, sizeof(b32), &b32);
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);
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);
1085 fs_write(fs->fat_start + 4 + fs->fat_size, 4, new_flags_ptr);
1090 struct boot_sector_16 b16;
1091 FAT_ENTRY fat16_flags;
1092 int fat16_is_dirty = 0;
1094 fs_read(0, sizeof(b16), &b16);
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);
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);
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);
1112 fs_write(fs->fat_start + 2 + fs->fat_size, 2, new_flags_ptr);
1119 static void get_new_label(char doslabel[12])
1128 if (get_line("New label", newlabel, sizeof(newlabel))) {
1129 if ((p = strchr(newlabel, '\n')))
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");
1138 if (!local_string_to_dos_string(doslabel, newlabel, 12)) {
1139 printf("Error when processing label\n");
1143 for (i = strlen(doslabel); i < 11; ++i)
1147 ret = validate_volume_label(doslabel);
1148 if ((ret && only_uppercase_label) || (ret & ~0x1)) {
1149 printf("New label is invalid\n");
1151 } else if (ret & 0x1) {
1152 printf("Warning: lowercase labels might not work properly on some systems\n");
1160 static int check_boot_label(DOS_FS *fs)
1167 ret = validate_volume_label(fs->label);
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.",
1172 1, "Remove invalid label from boot sector",
1173 2, "Enter new label")) {
1175 write_boot_label(fs, "NO NAME ");
1176 memcpy(fs->label, "NO NAME ", 11);
1179 get_new_label(doslabel);
1180 write_boot_label(fs, doslabel);
1181 memcpy(fs->label, doslabel, 11);
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",
1188 1, "Change lowercase characters to uppercase",
1189 2, "Remove invalid label",
1190 2, "Set new label")) {
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);
1202 write_boot_label(fs, "NO NAME ");
1203 memcpy(fs->label, "NO NAME ", 11);
1206 get_new_label(doslabel);
1207 write_boot_label(fs, doslabel);
1208 memcpy(fs->label, doslabel, 11);
1216 void check_label(DOS_FS *fs)
1226 offset = find_volume_de(fs, &de);
1228 if (offset == 0 && memcmp(fs->label, "NO NAME ", 11) != 0)
1229 check_boot_label(fs);
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.",
1235 1, "Remove label from boot sector",
1236 2, "Copy label from boot sector to root directory")) {
1238 write_boot_label(fs, "NO NAME ");
1239 memcpy(fs->label, "NO NAME ", 11);
1242 write_volume_label(fs, fs->label);
1243 offset = find_volume_de(fs, &de);
1249 memcpy(doslabel, de.name, 11);
1250 if (doslabel[0] == 0x05)
1252 ret = validate_volume_label(doslabel);
1254 printf("Volume label '%s' stored in root directory is not valid.\n", pretty_label(doslabel));
1255 switch (get_choice(1, " Auto-removing label.",
1257 1, "Remove invalid label",
1258 2, "Set new label")) {
1261 memcpy(fs->label, "NO NAME ", 11);
1265 get_new_label(doslabel);
1266 write_label(fs, doslabel);
1267 memcpy(fs->label, doslabel, 11);
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",
1274 1, "Change lowercase characters to uppercase",
1275 2, "Remove invalid label",
1276 2, "Set new label")) {
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);
1289 memcpy(fs->label, "NO NAME ", 11);
1293 get_new_label(doslabel);
1294 write_label(fs, doslabel);
1295 memcpy(fs->label, doslabel, 11);
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.",
1307 1, "Copy volume label from root directory to boot sector",
1308 2, "Remove volume label from root directory")) {
1310 write_boot_label(fs, doslabel);
1311 memcpy(fs->label, doslabel, 11);
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.",
1326 1, "Copy volume label from root directory to boot sector",
1327 2, "Copy label from boot sector to root directory")) {
1329 write_boot_label(fs, doslabel);
1330 memcpy(fs->label, doslabel, 11);
1333 ret = check_boot_label(fs);
1336 write_volume_label(fs, fs->label);
1337 offset = find_volume_de(fs, &de);
1338 /* NOTE: doslabel is not updated */