• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* check.c - Check and repair a PC/MS-DOS file system
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 
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.
10 
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.
15 
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/>.
18 
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.
21 */
22 
23 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
24  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
25 
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <limits.h>
31 #include <time.h>
32 
33 #include "common.h"
34 #include "dosfsck.h"
35 #include "io.h"
36 #include "fat.h"
37 #include "file.h"
38 #include "lfn.h"
39 #include "check.h"
40 
41 
42 static DOS_FILE *root;
43 
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))
48 
49 #define MODIFY(p,i,v)					\
50   do {							\
51     if (p->offset) {					\
52 	p->dir_ent.i = v;				\
53 	fs_write(p->offset+offsetof(DIR_ENT,i),		\
54 		 sizeof(p->dir_ent.i),&p->dir_ent.i);	\
55     }							\
56   } while(0)
57 
58 #define MODIFY_START(p,v,fs)						\
59   do {									\
60     unsigned long __v = (v);						\
61     if (!p->offset) {							\
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);				\
67 	__v = CT_LE_L(__v);						\
68 	fs_write((loff_t)offsetof(struct boot_sector,root_cluster),	\
69 	         sizeof(((struct boot_sector *)0)->root_cluster),	\
70 		 &__v);							\
71     }									\
72     else {								\
73 	MODIFY(p,start,CT_LE_W((__v)&0xffff));				\
74 	if (fs->fat_bits == 32)						\
75 	    MODIFY(p,starthi,CT_LE_W((__v)>>16));			\
76     }									\
77   } while(0)
78 
79 
alloc_rootdir_entry(DOS_FS * fs,DIR_ENT * de,const char * pattern)80 loff_t alloc_rootdir_entry(DOS_FS *fs, DIR_ENT *de, const char *pattern)
81 {
82     static int curr_num = 0;
83     loff_t offset;
84 
85     if (fs->root_cluster) {
86 	DIR_ENT d2;
87 	int i = 0, got = 0;
88 	unsigned long clu_num, prev = 0;
89 	loff_t offset2;
90 
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) {
96 		got = 1;
97 		break;
98 	    }
99 	    i += sizeof(DIR_ENT);
100 	    offset += sizeof(DIR_ENT);
101 	    if ((i % fs->cluster_size) == 0) {
102 		prev = clu_num;
103 		if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
104 		    break;
105 		offset = cluster_start(fs,clu_num);
106 	    }
107 	}
108 	if (!got) {
109 	    /* no free slot, need to extend root dir: alloc next free cluster
110 	     * after previous one */
111 	    if (!prev)
112 		die("Root directory has no cluster allocated!");
113 	    for (clu_num = prev+1; clu_num != prev; clu_num++) {
114 		if (clu_num >= fs->clusters+2) clu_num = 2;
115 		if (!fs->fat[clu_num].value)
116 		    break;
117 	    }
118 	    if (clu_num == prev)
119 		die("Root directory full and no free cluster");
120 	    set_fat(fs,prev,clu_num);
121 	    set_fat(fs,clu_num,-1);
122 	    set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
123 	    /* clear new cluster */
124 	    memset( &d2, 0, sizeof(d2) );
125 	    offset = cluster_start(fs,clu_num);
126 	    for( i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT) )
127 		fs_write( offset+i, sizeof(d2), &d2 );
128 	}
129 	memset(de,0,sizeof(DIR_ENT));
130 	while (1) {
131 	    sprintf(de->name,pattern,curr_num);
132 	    clu_num = fs->root_cluster;
133 	    i = 0;
134 	    offset2 = cluster_start(fs,clu_num);
135 	    while (clu_num > 0 && clu_num != -1) {
136 		fs_read(offset2,sizeof(DIR_ENT),&d2);
137 		if (offset2 != offset &&
138 		    !strncmp(d2.name,de->name,MSDOS_NAME))
139 		    break;
140 		i += sizeof(DIR_ENT);
141 		offset2 += sizeof(DIR_ENT);
142 		if ((i % fs->cluster_size) == 0) {
143 		    if ((clu_num = next_cluster(fs,clu_num)) == 0 ||
144 			clu_num == -1)
145 			break;
146 		    offset2 = cluster_start(fs,clu_num);
147 		}
148 	    }
149 	    if (clu_num == 0 || clu_num == -1)
150 		break;
151 	    if (++curr_num >= 10000) die("Unable to create unique name");
152 	}
153     }
154     else {
155 	DIR_ENT *root;
156 	int next_free = 0, scan;
157 
158 	root = alloc(fs->root_entries*sizeof(DIR_ENT));
159 	fs_read(fs->root_start,fs->root_entries*sizeof(DIR_ENT),root);
160 
161 	while (next_free < fs->root_entries)
162 	    if (IS_FREE(root[next_free].name) &&
163 		root[next_free].attr != VFAT_LN_ATTR)
164 		break;
165 	    else next_free++;
166 	if (next_free == fs->root_entries)
167 	    die("Root directory is full.");
168 	offset = fs->root_start+next_free*sizeof(DIR_ENT);
169 	memset(de,0,sizeof(DIR_ENT));
170 	while (1) {
171 	    sprintf(de->name,pattern,curr_num);
172 	    for (scan = 0; scan < fs->root_entries; scan++)
173 		if (scan != next_free &&
174 		    !strncmp(root[scan].name,de->name,MSDOS_NAME))
175 		    break;
176 	    if (scan == fs->root_entries) break;
177 	    if (++curr_num >= 10000) die("Unable to create unique name");
178 	}
179 	free(root);
180     }
181     ++n_files;
182     return offset;
183 }
184 
185 
path_name(DOS_FILE * file)186 static char *path_name(DOS_FILE *file)
187 {
188     static char path[PATH_MAX*2];
189 
190     if (!file) *path = 0;
191     else {
192 	if (strlen(path_name(file->parent)) > PATH_MAX)
193 	    die("Path name too long.");
194 	if (strcmp(path,"/") != 0) strcat(path,"/");
195 	strcpy(strrchr(path,0),file->lfn?file->lfn:file_name(file->dir_ent.name));
196     }
197     return path;
198 }
199 
200 
201 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
202 		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
203 
204 
205 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
206 
date_dos2unix(unsigned short time,unsigned short date)207 time_t date_dos2unix(unsigned short time,unsigned short date)
208 {
209     int month,year;
210     time_t secs;
211 
212     month = ((date >> 5) & 15)-1;
213     year = date >> 9;
214     secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
215       ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
216       month < 2 ? 1 : 0)+3653);
217                        /* days since 1.1.70 plus 80's leap day */
218     return secs;
219 }
220 
221 
file_stat(DOS_FILE * file)222 static char *file_stat(DOS_FILE *file)
223 {
224     static char temp[100];
225     struct tm *tm;
226     char tmp[100];
227     time_t date;
228 
229     date = date_dos2unix(CF_LE_W(file->dir_ent.time),CF_LE_W(file->
230       dir_ent.date));
231     tm = localtime(&date);
232     strftime(tmp,99,"%H:%M:%S %b %d %Y",tm);
233     sprintf(temp,"  Size %u bytes, date %s",CF_LE_L(file->dir_ent.size),tmp);
234     return temp;
235 }
236 
237 
bad_name(unsigned char * name)238 static int bad_name(unsigned char *name)
239 {
240     int i, spc, suspicious = 0;
241     char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
242 
243     /* Do not complain about (and auto-correct) the extended attribute files
244      * of OS/2. */
245     if (strncmp(name,"EA DATA  SF",11) == 0 ||
246         strncmp(name,"WP ROOT  SF",11) == 0) return 0;
247 
248     for (i = 0; i < 8; i++) {
249 	if (name[i] < ' ' || name[i] == 0x7f) return 1;
250 	if (name[i] > 0x7f) ++suspicious;
251 	if (strchr(bad_chars,name[i])) return 1;
252     }
253 
254     for (i = 8; i < 11; i++) {
255 	if (name[i] < ' ' || name[i] == 0x7f) return 1;
256 	if (name[i] > 0x7f) ++suspicious;
257 	if (strchr(bad_chars,name[i])) return 1;
258     }
259 
260     spc = 0;
261     for (i = 0; i < 8; i++) {
262 	if (name[i] == ' ')
263 	    spc = 1;
264 	else if (spc)
265 	    /* non-space after a space not allowed, space terminates the name
266 	     * part */
267 	    return 1;
268     }
269 
270     spc = 0;
271     for (i = 8; i < 11; i++) {
272 	if (name[i] == ' ')
273 	    spc = 1;
274 	else if (spc)
275 	    /* non-space after a space not allowed, space terminates the name
276 	     * part */
277 	    return 1;
278     }
279 
280     /* Under GEMDOS, chars >= 128 are never allowed. */
281     if (atari_format && suspicious)
282 	return 1;
283 
284     /* Only complain about too much suspicious chars in interactive mode,
285      * never correct them automatically. The chars are all basically ok, so we
286      * shouldn't auto-correct such names. */
287     if (interactive && suspicious > 6)
288 	return 1;
289     return 0;
290 }
291 
292 
drop_file(DOS_FS * fs,DOS_FILE * file)293 static void drop_file(DOS_FS *fs,DOS_FILE *file)
294 {
295     unsigned long cluster;
296 
297     MODIFY(file,name[0],DELETED_FLAG);
298     for (cluster = FSTART(file,fs); cluster > 0 && cluster <
299       fs->clusters+2; cluster = next_cluster(fs,cluster))
300 	set_owner(fs,cluster,NULL);
301     --n_files;
302 }
303 
304 
truncate_file(DOS_FS * fs,DOS_FILE * file,unsigned long clusters)305 static void truncate_file(DOS_FS *fs,DOS_FILE *file,unsigned long clusters)
306 {
307     int deleting;
308     unsigned long walk,next,prev;
309 
310     walk = FSTART(file,fs);
311     prev = 0;
312     if ((deleting = !clusters)) MODIFY_START(file,0,fs);
313     while (walk > 0 && walk != -1) {
314 	next = next_cluster(fs,walk);
315 	if (deleting) set_fat(fs,walk,0);
316 	else if ((deleting = !--clusters)) set_fat(fs,walk,-1);
317 	prev = walk;
318 	walk = next;
319     }
320 }
321 
322 
auto_rename(DOS_FILE * file)323 static void auto_rename(DOS_FILE *file)
324 {
325     DOS_FILE *first,*walk;
326     unsigned long int number;
327 
328     if (!file->offset) return;	/* cannot rename FAT32 root dir */
329     first = file->parent ? file->parent->first : root;
330     number = 0;
331     while (1) {
332 	sprintf(file->dir_ent.name, "FSCK%04d", number / 1000);
333 	sprintf(file->dir_ent.name, "%03d", number % 1000);
334 	for (walk = first; walk; walk = walk->next)
335 	    if (walk != file && !strncmp(walk->dir_ent.name,file->dir_ent.
336 	      name,MSDOS_NAME)) break;
337 	if (!walk) {
338 	    fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
339 	    return;
340 	}
341 	number++;
342 	if (number > 9999999) {
343 		die("Too many files need repair.");
344 	}
345     }
346     die("Can't generate a unique name.");
347 }
348 
349 
rename_file(DOS_FILE * file)350 static void rename_file(DOS_FILE *file)
351 {
352     unsigned char name[46];
353     unsigned char *walk,*here;
354 
355     if (!file->offset) {
356 	printf( "Cannot rename FAT32 root dir\n" );
357 	return;	/* cannot rename FAT32 root dir */
358     }
359     while (1) {
360 	printf("New name: ");
361 	fflush(stdout);
362 	if (fgets(name,45,stdin)) {
363 	    if ((here = strchr(name,'\n'))) *here = 0;
364 	    for (walk = strrchr(name,0); walk >= name && (*walk == ' ' ||
365 	      *walk == '\t'); walk--);
366 	    walk[1] = 0;
367 	    for (walk = name; *walk == ' ' || *walk == '\t'; walk++);
368 	    if (file_cvt(walk,file->dir_ent.name)) {
369 		fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
370 		return;
371 	    }
372 	}
373     }
374 }
375 
376 
handle_dot(DOS_FS * fs,DOS_FILE * file,int dots)377 static int handle_dot(DOS_FS *fs,DOS_FILE *file,int dots)
378 {
379     char *name;
380 
381     name = strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ? ".." : ".";
382     if (!(file->dir_ent.attr & ATTR_DIR)) {
383 	printf("%s\n  Is a non-directory.\n",path_name(file));
384 	if (interactive)
385 	    printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
386 	      "4) Convert to directory\n");
387 	else printf("  Auto-renaming it.\n");
388 	switch (interactive ? get_key("1234","?") : '2') {
389 	    case '1':
390 		drop_file(fs,file);
391 		return 1;
392 	    case '2':
393 		auto_rename(file);
394 		printf("  Renamed to %s\n",file_name(file->dir_ent.name));
395 		return 0;
396 	    case '3':
397 		rename_file(file);
398 		return 0;
399 	    case '4':
400 		MODIFY(file,size,CT_LE_L(0));
401 		MODIFY(file,attr,file->dir_ent.attr | ATTR_DIR);
402 		break;
403 	}
404     }
405     if (!dots) {
406 	printf("Root contains directory \"%s\". Dropping it.\n",name);
407 	drop_file(fs,file);
408 	return 1;
409     }
410     return 0;
411 }
412 
413 
check_file(DOS_FS * fs,DOS_FILE * file)414 static int check_file(DOS_FS *fs,DOS_FILE *file)
415 {
416     DOS_FILE *owner;
417     int restart;
418     unsigned long expect,curr,this,clusters,prev,walk,clusters2;
419 
420     if (file->dir_ent.attr & ATTR_DIR) {
421 	if (CF_LE_L(file->dir_ent.size)) {
422 	    printf("%s\n  Directory has non-zero size. Fixing it.\n",
423 	      path_name(file));
424 	    MODIFY(file,size,CT_LE_L(0));
425 	}
426 	if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) {
427 	    expect = FSTART(file->parent,fs);
428 	    if (FSTART(file,fs) != expect) {
429 		printf("%s\n  Start (%ld) does not point to parent (%ld)\n",
430 		  path_name(file),FSTART(file,fs),expect);
431 		MODIFY_START(file,expect,fs);
432 	    }
433 	    return 0;
434 	}
435 	if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOTDOT,
436 	  MSDOS_NAME)) {
437 	    expect = file->parent->parent ? FSTART(file->parent->parent,fs):0;
438 	    if (fs->root_cluster && expect == fs->root_cluster)
439 		expect = 0;
440 	    if (FSTART(file,fs) != expect) {
441 		printf("%s\n  Start (%lu) does not point to .. (%lu)\n",
442 		  path_name(file),FSTART(file,fs),expect);
443 		MODIFY_START(file,expect,fs);
444 	    }
445 	    return 0;
446 	}
447 	if (FSTART(file,fs)==0){
448 		printf ("%s\n Start does point to root directory. Deleting dir. \n",
449 				path_name(file));
450     		MODIFY(file,name[0],DELETED_FLAG);
451 		return 0;
452 	}
453     }
454     if (FSTART(file,fs) >= fs->clusters+2) {
455 	printf("%s\n  Start cluster beyond limit (%lu > %lu). Truncating file.\n",
456 	  path_name(file),FSTART(file,fs),fs->clusters+1);
457 	if (!file->offset)
458 	    die( "Bad FAT32 root directory! (bad start cluster)\n" );
459 	MODIFY_START(file,0,fs);
460     }
461     clusters = prev = 0;
462     for (curr = FSTART(file,fs) ? FSTART(file,fs) :
463       -1; curr != -1; curr = next_cluster(fs,curr)) {
464 	if (!fs->fat[curr].value || bad_cluster(fs,curr)) {
465 	    printf("%s\n  Contains a %s cluster (%lu). Assuming EOF.\n",
466 	      path_name(file),fs->fat[curr].value ? "bad" : "free",curr);
467 	    if (prev) set_fat(fs,prev,-1);
468 	    else if (!file->offset)
469 		die( "FAT32 root dir starts with a bad cluster!" );
470 	    else MODIFY_START(file,0,fs);
471 	    break;
472 	}
473 	if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
474 	  (unsigned long long)clusters*fs->cluster_size) {
475 	    printf("%s\n  File size is %u bytes, cluster chain length is > %llu "
476 	      "bytes.\n  Truncating file to %u bytes.\n",path_name(file),
477 	      CF_LE_L(file->dir_ent.size),(unsigned long long)clusters*fs->cluster_size,
478 	      CF_LE_L(file->dir_ent.size));
479 	    truncate_file(fs,file,clusters);
480 	    break;
481 	}
482 	if ((owner = get_owner(fs,curr))) {
483 	    int do_trunc = 0;
484 	    printf("%s  and\n",path_name(owner));
485 	    printf("%s\n  share clusters.\n",path_name(file));
486 	    clusters2 = 0;
487 	    for (walk = FSTART(owner,fs); walk > 0 && walk != -1; walk =
488 	      next_cluster(fs,walk))
489 		if (walk == curr) break;
490 		else clusters2++;
491 	    restart = file->dir_ent.attr & ATTR_DIR;
492 	    if (!owner->offset) {
493 		printf( "  Truncating second to %llu bytes because first "
494 			"is FAT32 root dir.\n", (unsigned long long)clusters2*fs->cluster_size );
495 		do_trunc = 2;
496 	    }
497 	    else if (!file->offset) {
498 		printf( "  Truncating first to %llu bytes because second "
499 			"is FAT32 root dir.\n", (unsigned long long)clusters*fs->cluster_size );
500 		do_trunc = 1;
501 	    }
502 	    else if (interactive)
503 		printf("1) Truncate first to %llu bytes%s\n"
504 		  "2) Truncate second to %llu bytes\n",(unsigned long long)clusters*fs->cluster_size,
505 		  restart ? " and restart" : "",(unsigned long long)clusters2*fs->cluster_size);
506 	    else printf("  Truncating second to %llu bytes.\n",(unsigned long long)clusters2*
507 		  fs->cluster_size);
508 	    if (do_trunc != 2 &&
509 		(do_trunc == 1 ||
510 		 (interactive && get_key("12","?") == '1'))) {
511 		prev = 0;
512 		clusters = 0;
513 		for (this = FSTART(owner,fs); this > 0 && this != -1; this =
514 		  next_cluster(fs,this)) {
515 		    if (this == curr) {
516 			if (prev) set_fat(fs,prev,-1);
517 			else MODIFY_START(owner,0,fs);
518 			MODIFY(owner,size,CT_LE_L((unsigned long long)clusters*fs->cluster_size));
519 			if (restart) return 1;
520 			while (this > 0 && this != -1) {
521 			    set_owner(fs,this,NULL);
522 			    this = next_cluster(fs,this);
523 			}
524 			this = curr;
525 			break;
526 		    }
527 		    clusters++;
528 		    prev = this;
529 		}
530 		if (this != curr)
531 		    die("Internal error: didn't find cluster %d in chain"
532 		      " starting at %d",curr,FSTART(owner,fs));
533 	    }
534 	    else {
535 		if (prev) set_fat(fs,prev,-1);
536 		else MODIFY_START(file,0,fs);
537 		break;
538 	    }
539 	}
540 	set_owner(fs,curr,file);
541 	clusters++;
542 	prev = curr;
543     }
544     if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
545       (unsigned long long)clusters*fs->cluster_size) {
546 	printf("%s\n  File size is %u bytes, cluster chain length is %llu bytes."
547 	  "\n  Truncating file to %lu bytes.\n",path_name(file),CF_LE_L(file->
548 	  dir_ent.size),(unsigned long long)clusters*fs->cluster_size,(unsigned long long)clusters*fs->cluster_size);
549 	MODIFY(file,size,CT_LE_L((unsigned long long)clusters*fs->cluster_size));
550     }
551     return 0;
552 }
553 
554 
check_files(DOS_FS * fs,DOS_FILE * start)555 static int check_files(DOS_FS *fs,DOS_FILE *start)
556 {
557     while (start) {
558 	if (check_file(fs,start)) return 1;
559 	start = start->next;
560     }
561     return 0;
562 }
563 
564 
check_dir(DOS_FS * fs,DOS_FILE ** root,int dots)565 static int check_dir(DOS_FS *fs,DOS_FILE **root,int dots)
566 {
567     DOS_FILE *parent,**walk,**scan;
568     int dot,dotdot,skip,redo;
569     int good,bad;
570 
571     if (!*root) return 0;
572     parent = (*root)->parent;
573     good = bad = 0;
574     for (walk = root; *walk; walk = &(*walk)->next)
575 	if (bad_name((*walk)->dir_ent.name)) bad++;
576 	else good++;
577     if (*root && parent && good+bad > 4 && bad > good/2) {
578 	printf("%s\n  Has a large number of bad entries. (%d/%d)\n",
579 	  path_name(parent),bad,good+bad);
580 	if (!dots) printf( "  Not dropping root directory.\n" );
581 	else if (!interactive) printf("  Not dropping it in auto-mode.\n");
582 	else if (get_key("yn","Drop directory ? (y/n)") == 'y') {
583 	    truncate_file(fs,parent,0);
584 	    MODIFY(parent,name[0],DELETED_FLAG);
585 	    /* buglet: deleted directory stays in the list. */
586 	    return 1;
587 	}
588     }
589     dot = dotdot = redo = 0;
590     walk = root;
591     while (*walk) {
592 	if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ||
593 	  !strncmp((*walk)->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) {
594 	    if (handle_dot(fs,*walk,dots)) {
595 		*walk = (*walk)->next;
596 		continue;
597 	    }
598 	    if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) dot++;
599 	    else dotdot++;
600 	}
601 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME) &&
602 	    bad_name((*walk)->dir_ent.name)) {
603 	    printf("%s\n  Bad file name.\n",path_name(*walk));
604 	    if (interactive)
605 		printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
606 		  "4) Keep it\n");
607 	    else printf("  Auto-renaming it.\n");
608 	    switch (interactive ? get_key("1234","?") : '3') {
609 		case '1':
610 		    drop_file(fs,*walk);
611 		    walk = &(*walk)->next;
612 		    continue;
613 		case '2':
614 		    rename_file(*walk);
615 		    redo = 1;
616 		    break;
617 		case '3':
618 		    auto_rename(*walk);
619 		    printf("  Renamed to %s\n",file_name((*walk)->dir_ent.
620 		      name));
621 		    break;
622 		case '4':
623 		    break;
624 	    }
625 	}
626 	/* don't check for duplicates of the volume label */
627 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
628 	    scan = &(*walk)->next;
629 	    skip = 0;
630 	    while (*scan && !skip) {
631 		if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
632 		    !strncmp((*walk)->dir_ent.name,(*scan)->dir_ent.name,MSDOS_NAME)) {
633 		    printf("%s\n  Duplicate directory entry.\n  First  %s\n",
634 			   path_name(*walk),file_stat(*walk));
635 		    printf("  Second %s\n",file_stat(*scan));
636 		    if (interactive)
637 			printf("1) Drop first\n2) Drop second\n3) Rename first\n"
638 			       "4) Rename second\n5) Auto-rename first\n"
639 			       "6) Auto-rename second\n");
640 		    else printf("  Auto-renaming second.\n");
641 		    switch (interactive ? get_key("123456","?") : '6') {
642 		      case '1':
643 			drop_file(fs,*walk);
644 			*walk = (*walk)->next;
645 			skip = 1;
646 			break;
647 		      case '2':
648 			drop_file(fs,*scan);
649 			*scan = (*scan)->next;
650 			continue;
651 		      case '3':
652 			rename_file(*walk);
653 			printf("  Renamed to %s\n",path_name(*walk));
654 			redo = 1;
655 			break;
656 		      case '4':
657 			rename_file(*scan);
658 			printf("  Renamed to %s\n",path_name(*walk));
659 			redo = 1;
660 			break;
661 		      case '5':
662 			auto_rename(*walk);
663 			printf("  Renamed to %s\n",file_name((*walk)->dir_ent.
664 			  name));
665 			break;
666 		      case '6':
667 			auto_rename(*scan);
668 			printf("  Renamed to %s\n",file_name((*scan)->dir_ent.
669 			  name));
670 			break;
671 		    }
672 		}
673 		scan = &(*scan)->next;
674 	    }
675 	    if (skip) continue;
676 	}
677 	if (!redo) walk = &(*walk)->next;
678 	else {
679 	    walk = root;
680 	    dot = dotdot = redo = 0;
681 	}
682     }
683     if (dots && !dot)
684 	printf("%s\n  \".\" is missing. Can't fix this yet.\n",
685 	  path_name(parent));
686     if (dots && !dotdot)
687 	printf("%s\n  \"..\" is missing. Can't fix this yet.\n",
688 	  path_name(parent));
689     return 0;
690 }
691 
692 
test_file(DOS_FS * fs,DOS_FILE * file,int read_test)693 static void test_file(DOS_FS *fs,DOS_FILE *file,int read_test)
694 {
695     DOS_FILE *owner;
696     unsigned long walk,prev,clusters,next_clu;
697 
698     prev = clusters = 0;
699     for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
700       walk = next_clu) {
701 	next_clu = next_cluster(fs,walk);
702 	if ((owner = get_owner(fs,walk))) {
703 	    if (owner == file) {
704 		printf("%s\n  Circular cluster chain. Truncating to %lu "
705 		  "cluster%s.\n",path_name(file),clusters,clusters == 1 ? "" :
706 		  "s");
707 		if (prev) set_fat(fs,prev,-1);
708 		else if (!file->offset)
709 		    die( "Bad FAT32 root directory! (bad start cluster)\n" );
710 		else MODIFY_START(file,0,fs);
711 	    }
712 	    break;
713 	}
714 	if (bad_cluster(fs,walk)) break;
715 	if (read_test) {
716 	    if (fs_test(cluster_start(fs,walk),fs->cluster_size)) {
717 		prev = walk;
718 		clusters++;
719 	    }
720 	    else {
721 		printf("%s\n  Cluster %lu (%lu) is unreadable. Skipping it.\n",
722 		  path_name(file),clusters,walk);
723 		if (prev) set_fat(fs,prev,next_cluster(fs,walk));
724 		else MODIFY_START(file,next_cluster(fs,walk),fs);
725 		set_fat(fs,walk,-2);
726 	    }
727 	}
728 	set_owner(fs,walk,file);
729     }
730     for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
731       walk = next_cluster(fs,walk))
732 	if (bad_cluster(fs,walk)) break;
733 	else if (get_owner(fs,walk) == file) set_owner(fs,walk,NULL);
734 	    else break;
735 }
736 
737 
undelete(DOS_FS * fs,DOS_FILE * file)738 static void undelete(DOS_FS *fs,DOS_FILE *file)
739 {
740     unsigned long clusters,left,prev,walk;
741 
742     clusters = left = (CF_LE_L(file->dir_ent.size)+fs->cluster_size-1)/
743       fs->cluster_size;
744     prev = 0;
745     for (walk = FSTART(file,fs); left && walk >= 2 && walk <
746        fs->clusters+2 && !fs->fat[walk].value; walk++) {
747 	left--;
748 	if (prev) set_fat(fs,prev,walk);
749 	prev = walk;
750     }
751     if (prev) set_fat(fs,prev,-1);
752     else MODIFY_START(file,0,fs);
753     if (left)
754 	printf("Warning: Did only undelete %lu of %lu cluster%s.\n",clusters-left,
755 	  clusters,clusters == 1 ? "" : "s");
756 
757 }
758 
759 
new_dir(void)760 static void new_dir( void )
761 {
762     lfn_reset();
763 }
764 
765 
add_file(DOS_FS * fs,DOS_FILE *** chain,DOS_FILE * parent,loff_t offset,FDSC ** cp)766 static void add_file(DOS_FS *fs,DOS_FILE ***chain,DOS_FILE *parent,
767 					 loff_t offset,FDSC **cp)
768 {
769     DOS_FILE *new;
770     DIR_ENT de;
771     FD_TYPE type;
772 
773     if (offset)
774 	fs_read(offset,sizeof(DIR_ENT),&de);
775     else {
776 	memcpy(de.name,"           ",MSDOS_NAME);
777 	de.attr = ATTR_DIR;
778 	de.size = de.time = de.date = 0;
779 	de.start = CT_LE_W(fs->root_cluster & 0xffff);
780 	de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff);
781     }
782     if ((type = file_type(cp,de.name)) != fdt_none) {
783 	if (type == fdt_undelete && (de.attr & ATTR_DIR))
784 	    die("Can't undelete directories.");
785 	file_modify(cp,de.name);
786 	fs_write(offset,1,&de);
787     }
788     if (IS_FREE(de.name)) {
789 	lfn_check_orphaned();
790 	return;
791     }
792     if (de.attr == VFAT_LN_ATTR) {
793 	lfn_add_slot(&de,offset);
794 	return;
795     }
796     new = qalloc(&mem_queue,sizeof(DOS_FILE));
797     new->lfn = lfn_get(&de);
798     new->offset = offset;
799     memcpy(&new->dir_ent,&de,sizeof(de));
800     new->next = new->first = NULL;
801     new->parent = parent;
802     if (type == fdt_undelete) undelete(fs,new);
803     **chain = new;
804     *chain = &new->next;
805     if (list) {
806 	printf("Checking file %s",path_name(new));
807 	if (new->lfn)
808 	    printf(" (%s)", file_name(new->dir_ent.name) );
809 	printf("\n");
810     }
811     if (offset &&
812 	strncmp(de.name,MSDOS_DOT,MSDOS_NAME) != 0 &&
813 	strncmp(de.name,MSDOS_DOTDOT,MSDOS_NAME) != 0)
814 	++n_files;
815     test_file(fs,new,test);
816 }
817 
818 
819 static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp);
820 
821 
scan_dir(DOS_FS * fs,DOS_FILE * this,FDSC ** cp)822 static int scan_dir(DOS_FS *fs,DOS_FILE *this,FDSC **cp)
823 {
824     DOS_FILE **chain;
825     int i;
826     unsigned long clu_num;
827 
828     chain = &this->first;
829     i = 0;
830     clu_num = FSTART(this,fs);
831     new_dir();
832     while (clu_num > 0 && clu_num != -1) {
833 	add_file(fs,&chain,this,cluster_start(fs,clu_num)+(i % fs->
834 	  cluster_size),cp);
835 	i += sizeof(DIR_ENT);
836 	if (!(i % fs->cluster_size))
837 	    if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
838 		break;
839     }
840     lfn_check_orphaned();
841     if (check_dir(fs,&this->first,this->offset)) return 0;
842     if (check_files(fs,this->first)) return 1;
843     return subdirs(fs,this,cp);
844 }
845 
846 
subdirs(DOS_FS * fs,DOS_FILE * parent,FDSC ** cp)847 static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp)
848 {
849     DOS_FILE *walk;
850 
851     for (walk = parent ? parent->first : root; walk; walk = walk->next)
852 	if (walk->dir_ent.attr & ATTR_DIR)
853 	    if (strncmp(walk->dir_ent.name,MSDOS_DOT,MSDOS_NAME) &&
854 	      strncmp(walk->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME))
855 		if (scan_dir(fs,walk,file_cd(cp,walk->dir_ent.name))) return 1;
856     return 0;
857 }
858 
859 
scan_root(DOS_FS * fs)860 int scan_root(DOS_FS *fs)
861 {
862     DOS_FILE **chain;
863     int i;
864 
865     root = NULL;
866     chain = &root;
867     new_dir();
868     if (fs->root_cluster) {
869 	add_file(fs,&chain,NULL,0,&fp_root);
870     }
871     else {
872 	for (i = 0; i < fs->root_entries; i++)
873 	    add_file(fs,&chain,NULL,fs->root_start+i*sizeof(DIR_ENT),&fp_root);
874     }
875     lfn_check_orphaned();
876     (void) check_dir(fs,&root,0);
877     if (check_files(fs,root)) return 1;
878     return subdirs(fs,NULL,&fp_root);
879 }
880 
881 /* Local Variables: */
882 /* tab-width: 8     */
883 /* End:             */
884