1 /* check.c - Check and repair a PC/MS-DOS file system
3 Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4 Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 On Debian systems, the complete text of the GNU General Public License
20 can be found in /usr/share/common-licenses/GPL-3 file.
23 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
24 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
42 static DOS_FILE *root;
44 /* get start field of a dir entry */
45 #define FSTART(p,fs) \
46 ((unsigned long)CF_LE_W(p->dir_ent.start) | \
47 (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0))
49 #define MODIFY(p,i,v) \
53 fs_write(p->offset+offsetof(DIR_ENT,i), \
54 sizeof(p->dir_ent.i),&p->dir_ent.i); \
58 #define MODIFY_START(p,v,fs) \
60 unsigned long __v = (v); \
62 /* writing to fake entry for FAT32 root dir */ \
63 if (!__v) die("Oops, deleting FAT32 root dir!"); \
64 fs->root_cluster = __v; \
65 p->dir_ent.start = CT_LE_W(__v&0xffff); \
66 p->dir_ent.starthi = CT_LE_W(__v>>16); \
68 fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \
69 sizeof(((struct boot_sector *)0)->root_cluster), \
73 MODIFY(p,start,CT_LE_W((__v)&0xffff)); \
74 if (fs->fat_bits == 32) \
75 MODIFY(p,starthi,CT_LE_W((__v)>>16)); \
80 loff_t alloc_rootdir_entry(DOS_FS *fs, DIR_ENT *de, const char *pattern)
82 static int curr_num = 0;
85 if (fs->root_cluster) {
88 unsigned long clu_num, prev = 0;
91 clu_num = fs->root_cluster;
92 offset = cluster_start(fs,clu_num);
93 while (clu_num > 0 && clu_num != -1) {
94 fs_read(offset,sizeof(DIR_ENT),&d2);
95 if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
100 offset += sizeof(DIR_ENT);
101 if ((i % fs->cluster_size) == 0) {
103 if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
105 offset = cluster_start(fs,clu_num);
109 /* no free slot, need to extend root dir: alloc next free cluster
110 * after previous one */
112 die("Root directory has no cluster allocated!");
113 for (clu_num = prev+1; clu_num != prev; clu_num++) {
116 if (clu_num >= fs->clusters+2) clu_num = 2;
117 get_fat(&entry, fs->fat, clu_num, fs);
122 die("Root directory full and no free cluster");
123 set_fat(fs,prev,clu_num);
124 set_fat(fs,clu_num,-1);
125 set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
126 /* clear new cluster */
127 memset( &d2, 0, sizeof(d2) );
128 offset = cluster_start(fs,clu_num);
129 for( i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT) )
130 fs_write( offset+i, sizeof(d2), &d2 );
132 memset(de,0,sizeof(DIR_ENT));
135 sprintf(expanded, pattern, curr_num);
136 memcpy(de->name+4, expanded, 4);
137 memcpy(de->ext, expanded+4, 3);
138 clu_num = fs->root_cluster;
140 offset2 = cluster_start(fs,clu_num);
141 while (clu_num > 0 && clu_num != -1) {
142 fs_read(offset2,sizeof(DIR_ENT),&d2);
143 if (offset2 != offset &&
144 !strncmp(d2.name,de->name,MSDOS_NAME))
146 i += sizeof(DIR_ENT);
147 offset2 += sizeof(DIR_ENT);
148 if ((i % fs->cluster_size) == 0) {
149 if ((clu_num = next_cluster(fs,clu_num)) == 0 ||
152 offset2 = cluster_start(fs,clu_num);
155 if (clu_num == 0 || clu_num == -1)
157 if (++curr_num >= 10000) die("Unable to create unique name");
162 int next_free = 0, scan;
164 root = alloc(fs->root_entries*sizeof(DIR_ENT));
165 fs_read(fs->root_start,fs->root_entries*sizeof(DIR_ENT),root);
167 while (next_free < fs->root_entries)
168 if (IS_FREE(root[next_free].name) &&
169 root[next_free].attr != VFAT_LN_ATTR)
172 if (next_free == fs->root_entries)
173 die("Root directory is full.");
174 offset = fs->root_start+next_free*sizeof(DIR_ENT);
175 memset(de,0,sizeof(DIR_ENT));
177 sprintf(de->name,pattern,curr_num);
178 for (scan = 0; scan < fs->root_entries; scan++)
179 if (scan != next_free &&
180 !strncmp(root[scan].name,de->name,MSDOS_NAME))
182 if (scan == fs->root_entries) break;
183 if (++curr_num >= 10000) die("Unable to create unique name");
193 * Construct a full path (starting with '/') for the specified dentry,
194 * relative to the partition. All components are "long" names where possible.
196 * @param[in] file Information about dentry (file or directory) of interest
198 * return Pointer to static string containing file's full path
200 static char *path_name(DOS_FILE *file)
202 static char path[PATH_MAX*2];
204 if (!file) *path = 0; /* Reached the root directory */
206 if (strlen(path_name(file->parent)) > PATH_MAX)
207 die("Path name too long.");
208 if (strcmp(path,"/") != 0) strcat(path,"/");
210 /* Append the long name to the path,
211 * or the short name if there isn't a long one
213 strcpy(strrchr(path,0),file->lfn?file->lfn:file_name(file->dir_ent.name));
219 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
220 /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
223 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
225 time_t date_dos2unix(unsigned short time,unsigned short date)
230 month = ((date >> 5) & 15)-1;
232 secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
233 ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
234 month < 2 ? 1 : 0)+3653);
235 /* days since 1.1.70 plus 80's leap day */
240 static char *file_stat(DOS_FILE *file)
242 static char temp[100];
247 date = date_dos2unix(CF_LE_W(file->dir_ent.time),CF_LE_W(file->
249 tm = localtime(&date);
250 strftime(tmp,99,"%H:%M:%S %b %d %Y",tm);
251 sprintf(temp," Size %u bytes, date %s",CF_LE_L(file->dir_ent.size),tmp);
256 static int bad_name(DOS_FILE *file)
258 int i, spc, suspicious = 0;
259 char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
260 unsigned char *name = file->dir_ent.name;
262 /* Do not complain about (and auto-correct) the extended attribute files
264 if (strncmp(name,"EA DATA SF",11) == 0 ||
265 strncmp(name,"WP ROOT SF",11) == 0) return 0;
267 /* don't complain about the dummy 11 bytes used by patched Linux
269 if (file->dir_ent.lcase & FAT_NO_83NAME) return 0;
271 for (i = 0; i < 8; i++) {
272 if (name[i] < ' ' || name[i] == 0x7f) return 1;
273 if (name[i] > 0x7f) ++suspicious;
274 if (strchr(bad_chars,name[i])) return 1;
277 for (i = 8; i < 11; i++) {
278 if (name[i] < ' ' || name[i] == 0x7f) return 1;
279 if (name[i] > 0x7f) ++suspicious;
280 if (strchr(bad_chars,name[i])) return 1;
284 for (i = 0; i < 8; i++) {
288 /* non-space after a space not allowed, space terminates the name
294 for (i = 8; i < 11; i++) {
298 /* non-space after a space not allowed, space terminates the name
303 /* Under GEMDOS, chars >= 128 are never allowed. */
304 if (atari_format && suspicious)
307 /* Under MS-DOS and Windows, chars >= 128 in short names are valid
308 * (but these characters can be visualised differently depending on
309 * local codepage: CP437, CP866, etc). The chars are all basically ok,
310 * so we shouldn't auto-correct such names. */
314 static void lfn_remove(loff_t from, loff_t to)
319 /* New dir entry is zeroed except first byte, which is set to 0xe5.
320 * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
321 * a directory at the first zero entry...
323 memset( &empty, 0, sizeof(empty) );
324 empty.name[0] = DELETED_FLAG;
326 for( ; from < to; from += sizeof(empty) ) {
327 fs_write( from, sizeof(DIR_ENT), &empty );
331 static void drop_file(DOS_FS *fs,DOS_FILE *file)
333 unsigned long cluster;
335 MODIFY(file,name[0],DELETED_FLAG);
336 if (file->lfn) lfn_remove(file->lfn_offset, file->offset);
337 for (cluster = FSTART(file,fs); cluster > 0 && cluster <
338 fs->clusters+2; cluster = next_cluster(fs,cluster))
339 set_owner(fs,cluster,NULL);
344 static void truncate_file(DOS_FS *fs,DOS_FILE *file,unsigned long clusters)
347 unsigned long walk,next,prev;
349 walk = FSTART(file,fs);
351 if ((deleting = !clusters)) MODIFY_START(file,0,fs);
352 while (walk > 0 && walk != -1) {
353 next = next_cluster(fs,walk);
354 if (deleting) set_fat(fs,walk,0);
355 else if ((deleting = !--clusters)) set_fat(fs,walk,-1);
362 static void auto_rename(DOS_FILE *file)
364 DOS_FILE *first,*walk;
365 unsigned long int number;
367 if (!file->offset) return; /* cannot rename FAT32 root dir */
368 first = file->parent ? file->parent->first : root;
372 sprintf(num, "%07d", number);
373 memcpy(file->dir_ent.name, "FSCK", 4);
374 memcpy(file->dir_ent.name+4, num, 4);
375 memcpy(file->dir_ent.ext, num+4, 3);
376 for (walk = first; walk; walk = walk->next)
377 if (walk != file && !strncmp(walk->dir_ent.name,file->dir_ent.
378 name,MSDOS_NAME)) break;
380 fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
381 if (file->lfn) lfn_fix_checksum(file->lfn_offset, file->offset, file->dir_ent.name);
385 if (number > 9999999) {
386 die("Too many files need repair.");
389 die("Can't generate a unique name.");
393 static void rename_file(DOS_FILE *file)
395 unsigned char name[46];
396 unsigned char *walk,*here;
399 printf( "Cannot rename FAT32 root dir\n" );
400 return; /* cannot rename FAT32 root dir */
403 printf("New name: ");
405 if (fgets(name,45,stdin)) {
406 if ((here = strchr(name,'\n'))) *here = 0;
407 for (walk = strrchr(name,0); walk >= name && (*walk == ' ' ||
408 *walk == '\t'); walk--);
410 for (walk = name; *walk == ' ' || *walk == '\t'; walk++);
411 if (file_cvt(walk,file->dir_ent.name)) {
412 fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
413 if (file->lfn) lfn_fix_checksum(file->lfn_offset, file->offset, file->dir_ent.name);
421 static int handle_dot(DOS_FS *fs,DOS_FILE *file,int dots)
425 name = strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ? ".." : ".";
426 if (!(file->dir_ent.attr & ATTR_DIR)) {
427 printf("%s\n Is a non-directory.\n",path_name(file));
429 printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
430 "4) Convert to directory\n");
431 else printf(" Auto-renaming it.\n");
432 switch (interactive ? get_key("1234","?") : '2') {
438 printf(" Renamed to %s\n",file_name(file->dir_ent.name));
444 MODIFY(file,size,CT_LE_L(0));
445 MODIFY(file,attr,file->dir_ent.attr | ATTR_DIR);
450 printf("Root contains directory \"%s\". Dropping it.\n",name);
458 static int check_file(DOS_FS *fs,DOS_FILE *file)
462 unsigned long expect,curr,this,clusters,prev,walk,clusters2;
464 if (file->dir_ent.attr & ATTR_DIR) {
465 if (CF_LE_L(file->dir_ent.size)) {
466 printf("%s\n Directory has non-zero size. Fixing it.\n",
468 MODIFY(file,size,CT_LE_L(0));
470 if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) {
471 expect = FSTART(file->parent,fs);
472 if (FSTART(file,fs) != expect) {
473 printf("%s\n Start (%ld) does not point to parent (%ld)\n",
474 path_name(file),FSTART(file,fs),expect);
475 MODIFY_START(file,expect,fs);
479 if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOTDOT,
481 expect = file->parent->parent ? FSTART(file->parent->parent,fs):0;
482 if (fs->root_cluster && expect == fs->root_cluster)
484 if (FSTART(file,fs) != expect) {
485 printf("%s\n Start (%lu) does not point to .. (%lu)\n",
486 path_name(file),FSTART(file,fs),expect);
487 MODIFY_START(file,expect,fs);
491 if (FSTART(file,fs)==0){
492 printf ("%s\n Start does point to root directory. Deleting dir. \n",
494 MODIFY(file,name[0],DELETED_FLAG);
498 if (FSTART(file,fs) >= fs->clusters+2) {
499 printf("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n",
500 path_name(file),FSTART(file,fs),fs->clusters+1);
502 die( "Bad FAT32 root directory! (bad start cluster)\n" );
503 MODIFY_START(file,0,fs);
506 for (curr = FSTART(file,fs) ? FSTART(file,fs) :
507 -1; curr != -1; curr = next_cluster(fs,curr)) {
509 get_fat(&curEntry, fs->fat, curr, fs);
511 if (!curEntry.value || bad_cluster(fs,curr)) {
512 printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n",
513 path_name(file), curEntry.value ? "bad" : "free",curr);
514 if (prev) set_fat(fs,prev,-1);
515 else if (!file->offset)
516 die( "FAT32 root dir starts with a bad cluster!" );
517 else MODIFY_START(file,0,fs);
520 if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
521 (unsigned long long)clusters*fs->cluster_size) {
522 printf("%s\n File size is %u bytes, cluster chain length is > %llu "
523 "bytes.\n Truncating file to %u bytes.\n",path_name(file),
524 CF_LE_L(file->dir_ent.size),(unsigned long long)clusters*fs->cluster_size,
525 CF_LE_L(file->dir_ent.size));
526 truncate_file(fs,file,clusters);
529 if ((owner = get_owner(fs,curr))) {
531 printf("%s and\n",path_name(owner));
532 printf("%s\n share clusters.\n",path_name(file));
534 for (walk = FSTART(owner,fs); walk > 0 && walk != -1; walk =
535 next_cluster(fs,walk))
536 if (walk == curr) break;
538 restart = file->dir_ent.attr & ATTR_DIR;
539 if (!owner->offset) {
540 printf( " Truncating second to %llu bytes because first "
541 "is FAT32 root dir.\n", (unsigned long long)clusters2*fs->cluster_size );
544 else if (!file->offset) {
545 printf( " Truncating first to %llu bytes because second "
546 "is FAT32 root dir.\n", (unsigned long long)clusters*fs->cluster_size );
549 else if (interactive)
550 printf("1) Truncate first to %llu bytes%s\n"
551 "2) Truncate second to %llu bytes\n",(unsigned long long)clusters*fs->cluster_size,
552 restart ? " and restart" : "",(unsigned long long)clusters2*fs->cluster_size);
553 else printf(" Truncating second to %llu bytes.\n",(unsigned long long)clusters2*
557 (interactive && get_key("12","?") == '1'))) {
560 for (this = FSTART(owner,fs); this > 0 && this != -1; this =
561 next_cluster(fs,this)) {
563 if (prev) set_fat(fs,prev,-1);
564 else MODIFY_START(owner,0,fs);
565 MODIFY(owner,size,CT_LE_L((unsigned long long)clusters*fs->cluster_size));
566 if (restart) return 1;
567 while (this > 0 && this != -1) {
568 set_owner(fs,this,NULL);
569 this = next_cluster(fs,this);
578 die("Internal error: didn't find cluster %d in chain"
579 " starting at %d",curr,FSTART(owner,fs));
582 if (prev) set_fat(fs,prev,-1);
583 else MODIFY_START(file,0,fs);
587 set_owner(fs,curr,file);
591 if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
592 (unsigned long long)clusters*fs->cluster_size) {
593 printf("%s\n File size is %u bytes, cluster chain length is %llu bytes."
594 "\n Truncating file to %lu bytes.\n",path_name(file),CF_LE_L(file->
595 dir_ent.size),(unsigned long long)clusters*fs->cluster_size,(unsigned long long)clusters*fs->cluster_size);
596 MODIFY(file,size,CT_LE_L((unsigned long long)clusters*fs->cluster_size));
602 static int check_files(DOS_FS *fs,DOS_FILE *start)
605 if (check_file(fs,start)) return 1;
612 static int check_dir(DOS_FS *fs,DOS_FILE **root,int dots)
614 DOS_FILE *parent,**walk,**scan;
615 int dot,dotdot,skip,redo;
618 if (!*root) return 0;
619 parent = (*root)->parent;
621 for (walk = root; *walk; walk = &(*walk)->next)
622 if (bad_name(*walk)) bad++;
624 if (*root && parent && good+bad > 4 && bad > good/2) {
625 printf("%s\n Has a large number of bad entries. (%d/%d)\n",
626 path_name(parent),bad,good+bad);
627 if (!dots) printf( " Not dropping root directory.\n" );
628 else if (!interactive) printf(" Not dropping it in auto-mode.\n");
629 else if (get_key("yn","Drop directory ? (y/n)") == 'y') {
630 truncate_file(fs,parent,0);
631 MODIFY(parent,name[0],DELETED_FLAG);
632 /* buglet: deleted directory stays in the list. */
636 dot = dotdot = redo = 0;
639 if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ||
640 !strncmp((*walk)->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) {
641 if (handle_dot(fs,*walk,dots)) {
642 *walk = (*walk)->next;
645 if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) dot++;
648 if (!((*walk)->dir_ent.attr & ATTR_VOLUME) &&
650 puts(path_name(*walk));
651 printf(" Bad short file name (%s).\n", file_name((*walk)->dir_ent.name));
653 printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
655 else printf(" Auto-renaming it.\n");
656 switch (interactive ? get_key("1234","?") : '3') {
659 walk = &(*walk)->next;
667 printf(" Renamed to %s\n",file_name((*walk)->dir_ent.
674 /* don't check for duplicates of the volume label */
675 if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
676 scan = &(*walk)->next;
678 while (*scan && !skip) {
679 if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
680 !memcmp((*walk)->dir_ent.name,(*scan)->dir_ent.name,MSDOS_NAME)) {
681 printf("%s\n Duplicate directory entry.\n First %s\n",
682 path_name(*walk),file_stat(*walk));
683 printf(" Second %s\n",file_stat(*scan));
685 printf("1) Drop first\n2) Drop second\n3) Rename first\n"
686 "4) Rename second\n5) Auto-rename first\n"
687 "6) Auto-rename second\n");
688 else printf(" Auto-renaming second.\n");
689 switch (interactive ? get_key("123456","?") : '6') {
692 *walk = (*walk)->next;
697 *scan = (*scan)->next;
701 printf(" Renamed to %s\n",path_name(*walk));
706 printf(" Renamed to %s\n",path_name(*walk));
711 printf(" Renamed to %s\n",file_name((*walk)->dir_ent.
716 printf(" Renamed to %s\n",file_name((*scan)->dir_ent.
721 scan = &(*scan)->next;
725 if (!redo) walk = &(*walk)->next;
728 dot = dotdot = redo = 0;
732 printf("%s\n \".\" is missing. Can't fix this yet.\n",
735 printf("%s\n \"..\" is missing. Can't fix this yet.\n",
742 * Check a dentry's cluster chain for bad clusters.
743 * If requested, we verify readability and mark unreadable clusters as bad.
745 * @param[inout] fs Information about the filesystem
746 * @param[in] file dentry to check
747 * @param[in] read_test Nonzero == verify that dentry's clusters can
750 static void test_file(DOS_FS *fs,DOS_FILE *file,int read_test)
753 unsigned long walk,prev,clusters,next_clu;
756 for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
758 next_clu = next_cluster(fs,walk);
760 /* In this stage we are checking only for a loop within our own
762 * Cross-linking of clusters is handled in check_file()
764 if ((owner = get_owner(fs,walk))) {
766 printf("%s\n Circular cluster chain. Truncating to %lu "
767 "cluster%s.\n",path_name(file),clusters,clusters == 1 ? "" :
769 if (prev) set_fat(fs,prev,-1);
770 else if (!file->offset)
771 die( "Bad FAT32 root directory! (bad start cluster)\n" );
772 else MODIFY_START(file,0,fs);
776 if (bad_cluster(fs,walk)) break;
778 if (fs_test(cluster_start(fs,walk),fs->cluster_size)) {
783 printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n",
784 path_name(file),clusters,walk);
785 if (prev) set_fat(fs,prev,next_cluster(fs,walk));
786 else MODIFY_START(file,next_cluster(fs,walk),fs);
790 set_owner(fs,walk,file);
792 /* Revert ownership (for now) */
793 for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
794 walk = next_cluster(fs,walk))
795 if (bad_cluster(fs,walk)) break;
796 else if (get_owner(fs,walk) == file) set_owner(fs,walk,NULL);
801 static void undelete(DOS_FS *fs,DOS_FILE *file)
803 unsigned long clusters,left,prev,walk;
805 clusters = left = (CF_LE_L(file->dir_ent.size)+fs->cluster_size-1)/
809 walk = FSTART(file,fs);
811 while (left && (walk >= 2) && (walk < fs->clusters+2)) {
814 get_fat(&curEntry, fs->fat, walk, fs);
820 if (prev) set_fat(fs,prev,walk);
824 if (prev) set_fat(fs,prev,-1);
825 else MODIFY_START(file,0,fs);
827 printf("Warning: Did only undelete %lu of %lu cluster%s.\n",clusters-left,
828 clusters,clusters == 1 ? "" : "s");
833 static void new_dir( void )
840 * Create a description for a referenced dentry and insert it in our dentry
841 * tree. Then, go check the dentry's cluster chain for bad clusters and
844 * @param[inout] fs Information about the filesystem
846 * @param[in] parent Information about parent directory of this file
847 * NULL == no parent ('file' is root directory)
848 * @param[in] offset Partition-relative byte offset of directory entry of interest
849 * 0 == Root directory
852 static void add_file(DOS_FS *fs,DOS_FILE ***chain,DOS_FILE *parent,
853 loff_t offset,FDSC **cp)
860 fs_read(offset,sizeof(DIR_ENT),&de);
862 /* Construct a DIR_ENT for the root directory */
863 memcpy(de.name," ",MSDOS_NAME);
865 de.size = de.time = de.date = 0;
866 de.start = CT_LE_W(fs->root_cluster & 0xffff);
867 de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff);
869 if ((type = file_type(cp,de.name)) != fdt_none) {
870 if (type == fdt_undelete && (de.attr & ATTR_DIR))
871 die("Can't undelete directories.");
872 file_modify(cp,de.name);
873 fs_write(offset,1,&de);
875 if (IS_FREE(de.name)) {
876 lfn_check_orphaned();
879 if (de.attr == VFAT_LN_ATTR) {
880 lfn_add_slot(&de,offset);
883 new = qalloc(&mem_queue,sizeof(DOS_FILE));
884 new->lfn = lfn_get(&de, &new->lfn_offset);
885 new->offset = offset;
886 memcpy(&new->dir_ent,&de,sizeof(de));
887 new->next = new->first = NULL;
888 new->parent = parent;
889 if (type == fdt_undelete) undelete(fs,new);
893 printf("Checking file %s",path_name(new));
895 printf(" (%s)", file_name(new->dir_ent.name) ); /* (8.3) */
898 /* Don't include root directory, '.', or '..' in the total file count */
900 strncmp(de.name,MSDOS_DOT,MSDOS_NAME) != 0 &&
901 strncmp(de.name,MSDOS_DOTDOT,MSDOS_NAME) != 0)
903 test_file(fs,new,test); /* Bad cluster check */
907 static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp);
910 static int scan_dir(DOS_FS *fs,DOS_FILE *this,FDSC **cp)
914 unsigned long clu_num;
916 chain = &this->first;
918 clu_num = FSTART(this,fs);
920 while (clu_num > 0 && clu_num != -1) {
921 add_file(fs,&chain,this,cluster_start(fs,clu_num)+(i % fs->
923 i += sizeof(DIR_ENT);
924 if (!(i % fs->cluster_size))
925 if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
928 lfn_check_orphaned();
929 if (check_dir(fs,&this->first,this->offset)) return 0;
930 if (check_files(fs,this->first)) return 1;
931 return subdirs(fs,this,cp);
936 * Recursively scan subdirectories of the specified parent directory.
938 * @param[inout] fs Information about the filesystem
939 * @param[in] parent Identifies the directory to scan
945 static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp)
949 for (walk = parent ? parent->first : root; walk; walk = walk->next)
950 if (walk->dir_ent.attr & ATTR_DIR)
951 if (strncmp(walk->dir_ent.name,MSDOS_DOT,MSDOS_NAME) &&
952 strncmp(walk->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME))
953 if (scan_dir(fs,walk,file_cd(cp,walk->dir_ent.name))) return 1;
959 * Scan all directory and file information for errors.
961 * @param[inout] fs Information about the filesystem
966 int scan_root(DOS_FS *fs)
974 if (fs->root_cluster) {
975 add_file(fs,&chain,NULL,0,&fp_root);
978 for (i = 0; i < fs->root_entries; i++)
979 add_file(fs,&chain,NULL,fs->root_start+i*sizeof(DIR_ENT),&fp_root);
981 lfn_check_orphaned();
982 (void) check_dir(fs,&root,0);
983 if (check_files(fs,root)) return 1;
984 return subdirs(fs,NULL,&fp_root);