"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "dosfstools-2.11/dosfsck/check.c" of archive dosfstools-2.11.src.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /* check.c  -  Check and repair a PC/MS-DOS file system */
    2 
    3 /* Written 1993 by Werner Almesberger */
    4 
    5 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
    6  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
    7 
    8 
    9 #include <stdio.h>
   10 #include <stdlib.h>
   11 #include <string.h>
   12 #include <limits.h>
   13 #include <time.h>
   14 
   15 #include "common.h"
   16 #include "dosfsck.h"
   17 #include "io.h"
   18 #include "fat.h"
   19 #include "file.h"
   20 #include "lfn.h"
   21 #include "check.h"
   22 
   23 
   24 static DOS_FILE *root;
   25 
   26 /* get start field of a dir entry */
   27 #define FSTART(p,fs) \
   28   ((unsigned long)CF_LE_W(p->dir_ent.start) | \
   29    (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0))
   30 
   31 #define MODIFY(p,i,v)					\
   32   do {							\
   33     if (p->offset) {					\
   34 	p->dir_ent.i = v;				\
   35 	fs_write(p->offset+offsetof(DIR_ENT,i),		\
   36 		 sizeof(p->dir_ent.i),&p->dir_ent.i);	\
   37     }							\
   38   } while(0)
   39 
   40 #define MODIFY_START(p,v,fs)						\
   41   do {									\
   42     unsigned long __v = (v);						\
   43     if (!p->offset) {							\
   44 	/* writing to fake entry for FAT32 root dir */			\
   45 	if (!__v) die("Oops, deleting FAT32 root dir!");		\
   46 	fs->root_cluster = __v;						\
   47 	p->dir_ent.start = CT_LE_W(__v&0xffff);				\
   48 	p->dir_ent.starthi = CT_LE_W(__v>>16);				\
   49 	__v = CT_LE_L(__v);						\
   50 	fs_write((loff_t)offsetof(struct boot_sector,root_cluster),	\
   51 	         sizeof(((struct boot_sector *)0)->root_cluster),	\
   52 		 &__v);							\
   53     }									\
   54     else {								\
   55 	MODIFY(p,start,CT_LE_W((__v)&0xffff));				\
   56 	if (fs->fat_bits == 32)						\
   57 	    MODIFY(p,starthi,CT_LE_W((__v)>>16));			\
   58     }									\
   59   } while(0)
   60 
   61 
   62 loff_t alloc_rootdir_entry(DOS_FS *fs, DIR_ENT *de, const char *pattern)
   63 {
   64     static int curr_num = 0;
   65     loff_t offset;
   66 
   67     if (fs->root_cluster) {
   68 	DIR_ENT d2;
   69 	int i = 0, got = 0;
   70 	unsigned long clu_num, prev = 0;
   71 	loff_t offset2;
   72 
   73 	clu_num = fs->root_cluster;
   74 	offset = cluster_start(fs,clu_num);
   75 	while (clu_num > 0 && clu_num != -1) {
   76 	    fs_read(offset,sizeof(DIR_ENT),&d2);
   77 	    if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
   78 		got = 1;
   79 		break;
   80 	    }
   81 	    i += sizeof(DIR_ENT);
   82 	    offset += sizeof(DIR_ENT);
   83 	    if ((i % fs->cluster_size) == 0) {
   84 		prev = clu_num;
   85 		if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
   86 		    break;
   87 		offset = cluster_start(fs,clu_num);
   88 	    }
   89 	}
   90 	if (!got) {
   91 	    /* no free slot, need to extend root dir: alloc next free cluster
   92 	     * after previous one */
   93 	    if (!prev)
   94 		die("Root directory has no cluster allocated!");
   95 	    for (clu_num = prev+1; clu_num != prev; clu_num++) {
   96 		if (clu_num >= fs->clusters+2) clu_num = 2;
   97 		if (!fs->fat[clu_num].value)
   98 		    break;
   99 	    }
  100 	    if (clu_num == prev)
  101 		die("Root directory full and no free cluster");
  102 	    set_fat(fs,prev,clu_num);
  103 	    set_fat(fs,clu_num,-1);
  104 	    set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
  105 	    /* clear new cluster */
  106 	    memset( &d2, 0, sizeof(d2) );
  107 	    offset = cluster_start(fs,clu_num);
  108 	    for( i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT) )
  109 		fs_write( offset+i, sizeof(d2), &d2 );
  110 	}
  111 	memset(de,0,sizeof(DIR_ENT));
  112 	while (1) {
  113 	    sprintf(de->name,pattern,curr_num);
  114 	    clu_num = fs->root_cluster;
  115 	    i = 0;
  116 	    offset2 = cluster_start(fs,clu_num);
  117 	    while (clu_num > 0 && clu_num != -1) {
  118 		fs_read(offset2,sizeof(DIR_ENT),&d2);
  119 		if (offset2 != offset &&
  120 		    !strncmp(d2.name,de->name,MSDOS_NAME))
  121 		    break;
  122 		i += sizeof(DIR_ENT);
  123 		offset2 += sizeof(DIR_ENT);
  124 		if ((i % fs->cluster_size) == 0) {
  125 		    if ((clu_num = next_cluster(fs,clu_num)) == 0 ||
  126 			clu_num == -1)
  127 			break;
  128 		    offset2 = cluster_start(fs,clu_num);
  129 		}
  130 	    }
  131 	    if (clu_num == 0 || clu_num == -1)
  132 		break;
  133 	    if (++curr_num >= 10000) die("Unable to create unique name");
  134 	}
  135     }
  136     else {
  137 	DIR_ENT *root;
  138 	int next_free = 0, scan;
  139 
  140 	root = alloc(fs->root_entries*sizeof(DIR_ENT));
  141 	fs_read(fs->root_start,fs->root_entries*sizeof(DIR_ENT),root);
  142 
  143 	while (next_free < fs->root_entries)
  144 	    if (IS_FREE(root[next_free].name) &&
  145 		root[next_free].attr != VFAT_LN_ATTR)
  146 		break;
  147 	    else next_free++;
  148 	if (next_free == fs->root_entries)
  149 	    die("Root directory is full.");
  150 	offset = fs->root_start+next_free*sizeof(DIR_ENT);
  151 	memset(de,0,sizeof(DIR_ENT));
  152 	while (1) {
  153 	    sprintf(de->name,pattern,curr_num);
  154 	    for (scan = 0; scan < fs->root_entries; scan++)
  155 		if (scan != next_free &&
  156 		    !strncmp(root[scan].name,de->name,MSDOS_NAME))
  157 		    break;
  158 	    if (scan == fs->root_entries) break;
  159 	    if (++curr_num >= 10000) die("Unable to create unique name");
  160 	}
  161 	free(root);
  162     }
  163     ++n_files;
  164     return offset;
  165 }
  166 
  167 
  168 static char *path_name(DOS_FILE *file)
  169 {
  170     static char path[PATH_MAX*2];
  171 
  172     if (!file) *path = 0;
  173     else {
  174 	if (strlen(path_name(file->parent)) > PATH_MAX)
  175 	    die("Path name too long.");
  176 	if (strcmp(path,"/") != 0) strcat(path,"/");
  177 	strcpy(strrchr(path,0),file->lfn?file->lfn:file_name(file->dir_ent.name));
  178     }
  179     return path;
  180 }
  181 
  182 
  183 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
  184 		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
  185 
  186 
  187 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
  188 
  189 time_t date_dos2unix(unsigned short time,unsigned short date)
  190 {
  191     int month,year;
  192     time_t secs;
  193 
  194     month = ((date >> 5) & 15)-1;
  195     year = date >> 9;
  196     secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
  197       ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
  198       month < 2 ? 1 : 0)+3653);
  199                        /* days since 1.1.70 plus 80's leap day */
  200     return secs;
  201 }
  202 
  203 
  204 static char *file_stat(DOS_FILE *file)
  205 {
  206     static char temp[100];
  207     struct tm *tm;
  208     char tmp[100];
  209     time_t date;
  210 
  211     date = date_dos2unix(CF_LE_W(file->dir_ent.time),CF_LE_W(file->
  212       dir_ent.date));
  213     tm = localtime(&date);
  214     strftime(tmp,99,"%H:%M:%S %b %d %Y",tm);
  215     sprintf(temp,"  Size %u bytes, date %s",CF_LE_L(file->dir_ent.size),tmp);
  216     return temp;
  217 }
  218 
  219 
  220 static int bad_name(unsigned char *name)
  221 {
  222     int i, spc, suspicious = 0;
  223     char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
  224 
  225     /* Do not complain about (and auto-correct) the extended attribute files
  226      * of OS/2. */
  227     if (strncmp(name,"EA DATA  SF",11) == 0 ||
  228         strncmp(name,"WP ROOT  SF",11) == 0) return 0;
  229 
  230     for (i = 0; i < 8; i++) {
  231 	if (name[i] < ' ' || name[i] == 0x7f) return 1;
  232 	if (name[i] > 0x7f) ++suspicious;
  233 	if (strchr(bad_chars,name[i])) return 1;
  234     }
  235 
  236     for (i = 8; i < 11; i++) {
  237 	if (name[i] < ' ' || name[i] == 0x7f) return 1;
  238 	if (name[i] > 0x7f) ++suspicious;
  239 	if (strchr(bad_chars,name[i])) return 1;
  240     }
  241 
  242     spc = 0;
  243     for (i = 0; i < 8; i++) {
  244 	if (name[i] == ' ')
  245 	    spc = 1;
  246 	else if (spc)
  247 	    /* non-space after a space not allowed, space terminates the name
  248 	     * part */
  249 	    return 1;
  250     }
  251 
  252     spc = 0;
  253     for (i = 8; i < 11; i++) {
  254 	if (name[i] == ' ')
  255 	    spc = 1;
  256 	else if (spc)
  257 	    /* non-space after a space not allowed, space terminates the name
  258 	     * part */
  259 	    return 1;
  260     }
  261 
  262     /* Under GEMDOS, chars >= 128 are never allowed. */
  263     if (atari_format && suspicious)
  264 	return 1;
  265 
  266     /* Only complain about too much suspicious chars in interactive mode,
  267      * never correct them automatically. The chars are all basically ok, so we
  268      * shouldn't auto-correct such names. */
  269     if (interactive && suspicious > 6)
  270 	return 1;
  271     return 0;
  272 }
  273 
  274 
  275 static void drop_file(DOS_FS *fs,DOS_FILE *file)
  276 {
  277     unsigned long cluster;
  278 
  279     MODIFY(file,name[0],DELETED_FLAG);
  280     for (cluster = FSTART(file,fs); cluster > 0 && cluster <
  281       fs->clusters+2; cluster = next_cluster(fs,cluster))
  282 	set_owner(fs,cluster,NULL);
  283     --n_files;
  284 }
  285 
  286 
  287 static void truncate_file(DOS_FS *fs,DOS_FILE *file,unsigned long clusters)
  288 {
  289     int deleting;
  290     unsigned long walk,next,prev;
  291 
  292     walk = FSTART(file,fs);
  293     prev = 0;
  294     if ((deleting = !clusters)) MODIFY_START(file,0,fs);
  295     while (walk > 0 && walk != -1) {
  296 	next = next_cluster(fs,walk);
  297 	if (deleting) set_fat(fs,walk,0);
  298 	else if ((deleting = !--clusters)) set_fat(fs,walk,-1);
  299 	prev = walk;
  300 	walk = next;
  301     }
  302 }
  303 
  304 
  305 static void auto_rename(DOS_FILE *file)
  306 {
  307     DOS_FILE *first,*walk;
  308     int number;
  309 
  310     if (!file->offset) return;	/* cannot rename FAT32 root dir */
  311     first = file->parent ? file->parent->first : root;
  312     number = 0;
  313     while (1) {
  314 	sprintf(file->dir_ent.name,"FSCK%04d",number);
  315 	strncpy(file->dir_ent.ext,"REN",3);
  316 	for (walk = first; walk; walk = walk->next)
  317 	    if (walk != file && !strncmp(walk->dir_ent.name,file->dir_ent.
  318 	      name,MSDOS_NAME)) break;
  319 	if (!walk) {
  320 	    fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
  321 	    return;
  322 	}
  323 	number++;
  324     }
  325     die("Can't generate a unique name.");
  326 }
  327 
  328 
  329 static void rename_file(DOS_FILE *file)
  330 {
  331     unsigned char name[46];
  332     unsigned char *walk,*here;
  333 
  334     if (!file->offset) {
  335 	printf( "Cannot rename FAT32 root dir\n" );
  336 	return;	/* cannot rename FAT32 root dir */
  337     }
  338     while (1) {
  339 	printf("New name: ");
  340 	fflush(stdout);
  341 	if (fgets(name,45,stdin)) {
  342 	    if ((here = strchr(name,'\n'))) *here = 0;
  343 	    for (walk = strrchr(name,0); walk >= name && (*walk == ' ' ||
  344 	      *walk == '\t'); walk--);
  345 	    walk[1] = 0;
  346 	    for (walk = name; *walk == ' ' || *walk == '\t'; walk++);
  347 	    if (file_cvt(walk,file->dir_ent.name)) {
  348 		fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
  349 		return;
  350 	    }
  351 	}
  352     }
  353 }
  354 
  355 
  356 static int handle_dot(DOS_FS *fs,DOS_FILE *file,int dots)
  357 {
  358     char *name;
  359 
  360     name = strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ? ".." : ".";
  361     if (!(file->dir_ent.attr & ATTR_DIR)) {
  362 	printf("%s\n  Is a non-directory.\n",path_name(file));
  363 	if (interactive)
  364 	    printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
  365 	      "4) Convert to directory\n");
  366 	else printf("  Auto-renaming it.\n");
  367 	switch (interactive ? get_key("1234","?") : '2') {
  368 	    case '1':
  369 		drop_file(fs,file);
  370 		return 1;
  371 	    case '2':
  372 		auto_rename(file);
  373 		printf("  Renamed to %s\n",file_name(file->dir_ent.name));
  374 		return 0;
  375 	    case '3':
  376 		rename_file(file);
  377 		return 0;
  378 	    case '4':
  379 		MODIFY(file,size,CT_LE_L(0));
  380 		MODIFY(file,attr,file->dir_ent.attr | ATTR_DIR);
  381 		break;
  382 	}
  383     }
  384     if (!dots) {
  385 	printf("Root contains directory \"%s\". Dropping it.\n",name);
  386 	drop_file(fs,file);
  387 	return 1;
  388     }
  389     return 0;
  390 }
  391 
  392 
  393 static int check_file(DOS_FS *fs,DOS_FILE *file)
  394 {
  395     DOS_FILE *owner;
  396     int restart;
  397     unsigned long expect,curr,this,clusters,prev,walk,clusters2;
  398 
  399     if (file->dir_ent.attr & ATTR_DIR) {
  400 	if (CF_LE_L(file->dir_ent.size)) {
  401 	    printf("%s\n  Directory has non-zero size. Fixing it.\n",
  402 	      path_name(file));
  403 	    MODIFY(file,size,CT_LE_L(0));
  404 	}
  405 	if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) {
  406 	    expect = FSTART(file->parent,fs);
  407 	    if (FSTART(file,fs) != expect) {
  408 		printf("%s\n  Start (%ld) does not point to parent (%ld)\n",
  409 		  path_name(file),FSTART(file,fs),expect);
  410 		MODIFY_START(file,expect,fs);
  411 	    }
  412 	    return 0;
  413 	}
  414 	if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOTDOT,
  415 	  MSDOS_NAME)) {
  416 	    expect = file->parent->parent ? FSTART(file->parent->parent,fs):0;
  417 	    if (fs->root_cluster && expect == fs->root_cluster)
  418 		expect = 0;
  419 	    if (FSTART(file,fs) != expect) {
  420 		printf("%s\n  Start (%lu) does not point to .. (%lu)\n",
  421 		  path_name(file),FSTART(file,fs),expect);
  422 		MODIFY_START(file,expect,fs);
  423 	    }
  424 	    return 0;
  425 	}
  426 	if (FSTART(file,fs)==0){
  427 		printf ("%s\n Start does point to root directory. Deleting dir. \n",
  428 				path_name(file));
  429     		MODIFY(file,name[0],DELETED_FLAG);
  430 		return 0;
  431 	}
  432     }
  433     if (FSTART(file,fs) >= fs->clusters+2) {
  434 	printf("%s\n  Start cluster beyond limit (%lu > %lu). Truncating file.\n",
  435 	  path_name(file),FSTART(file,fs),fs->clusters+1);
  436 	if (!file->offset)
  437 	    die( "Bad FAT32 root directory! (bad start cluster)\n" );
  438 	MODIFY_START(file,0,fs);
  439     }
  440     clusters = prev = 0;
  441     for (curr = FSTART(file,fs) ? FSTART(file,fs) :
  442       -1; curr != -1; curr = next_cluster(fs,curr)) {
  443 	if (!fs->fat[curr].value || bad_cluster(fs,curr)) {
  444 	    printf("%s\n  Contains a %s cluster (%lu). Assuming EOF.\n",
  445 	      path_name(file),fs->fat[curr].value ? "bad" : "free",curr);
  446 	    if (prev) set_fat(fs,prev,-1);
  447 	    else if (!file->offset)
  448 		die( "FAT32 root dir starts with a bad cluster!" );
  449 	    else MODIFY_START(file,0,fs);
  450 	    break;
  451 	}
  452 	if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
  453 	  clusters*fs->cluster_size) {
  454 	    printf("%s\n  File size is %u bytes, cluster chain length is > %lu "
  455 	      "bytes.\n  Truncating file to %u bytes.\n",path_name(file),
  456 	      CF_LE_L(file->dir_ent.size),clusters*fs->cluster_size,
  457 	      CF_LE_L(file->dir_ent.size));
  458 	    truncate_file(fs,file,clusters);
  459 	    break;
  460 	}
  461 	if ((owner = get_owner(fs,curr))) {
  462 	    int do_trunc = 0;
  463 	    printf("%s  and\n",path_name(owner));
  464 	    printf("%s\n  share clusters.\n",path_name(file));
  465 	    clusters2 = 0;
  466 	    for (walk = FSTART(owner,fs); walk > 0 && walk != -1; walk =
  467 	      next_cluster(fs,walk))
  468 		if (walk == curr) break;
  469 		else clusters2++;
  470 	    restart = file->dir_ent.attr & ATTR_DIR;
  471 	    if (!owner->offset) {
  472 		printf( "  Truncating second to %lu bytes because first "
  473 			"is FAT32 root dir.\n", clusters2*fs->cluster_size );
  474 		do_trunc = 2;
  475 	    }
  476 	    else if (!file->offset) {
  477 		printf( "  Truncating first to %lu bytes because second "
  478 			"is FAT32 root dir.\n", clusters*fs->cluster_size );
  479 		do_trunc = 1;
  480 	    }
  481 	    else if (interactive)
  482 		printf("1) Truncate first to %lu bytes%s\n"
  483 		  "2) Truncate second to %lu bytes\n",clusters*fs->cluster_size,
  484 		  restart ? " and restart" : "",clusters2*fs->cluster_size);
  485 	    else printf("  Truncating second to %lu bytes.\n",clusters2*
  486 		  fs->cluster_size);
  487 	    if (do_trunc != 2 &&
  488 		(do_trunc == 1 ||
  489 		 (interactive && get_key("12","?") == '1'))) {
  490 		prev = 0;
  491 		clusters = 0;
  492 		for (this = FSTART(owner,fs); this > 0 && this != -1; this =
  493 		  next_cluster(fs,this)) {
  494 		    if (this == curr) {
  495 			if (prev) set_fat(fs,prev,-1);
  496 			else MODIFY_START(owner,0,fs);
  497 			MODIFY(owner,size,CT_LE_L(clusters*fs->cluster_size));
  498 			if (restart) return 1;
  499 			while (this > 0 && this != -1) {
  500 			    set_owner(fs,this,NULL);
  501 			    this = next_cluster(fs,this);
  502 			}
  503 			break;
  504 		    }
  505 		    clusters++;
  506 		    prev = this;
  507 		}
  508 		if (this != curr)
  509 		    die("Internal error: didn't find cluster %d in chain"
  510 		      " starting at %d",curr,FSTART(owner,fs));
  511 	    }
  512 	    else {
  513 		if (prev) set_fat(fs,prev,-1);
  514 		else MODIFY_START(file,0,fs);
  515 		break;
  516 	    }
  517 	}
  518 	set_owner(fs,curr,file);
  519 	clusters++;
  520 	prev = curr;
  521     }
  522     if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
  523       clusters*fs->cluster_size) {
  524 	printf("%s\n  File size is %u bytes, cluster chain length is %lu bytes."
  525 	  "\n  Truncating file to %lu bytes.\n",path_name(file),CF_LE_L(file->
  526 	  dir_ent.size),clusters*fs->cluster_size,clusters*fs->cluster_size);
  527 	MODIFY(file,size,CT_LE_L(clusters*fs->cluster_size));
  528     }
  529     return 0;
  530 }
  531 
  532 
  533 static int check_files(DOS_FS *fs,DOS_FILE *start)
  534 {
  535     while (start) {
  536 	if (check_file(fs,start)) return 1;
  537 	start = start->next;
  538     }
  539     return 0;
  540 }
  541 
  542 
  543 static int check_dir(DOS_FS *fs,DOS_FILE **root,int dots)
  544 {
  545     DOS_FILE *parent,**walk,**scan;
  546     int dot,dotdot,skip,redo;
  547     int good,bad;
  548 
  549     if (!*root) return 0;
  550     parent = (*root)->parent;
  551     good = bad = 0;
  552     for (walk = root; *walk; walk = &(*walk)->next)
  553 	if (bad_name((*walk)->dir_ent.name)) bad++;
  554 	else good++;
  555     if (*root && parent && good+bad > 4 && bad > good/2) {
  556 	printf("%s\n  Has a large number of bad entries. (%d/%d)\n",
  557 	  path_name(parent),bad,good+bad);
  558 	if (!dots) printf( "  Not dropping root directory.\n" );
  559 	else if (!interactive) printf("  Not dropping it in auto-mode.\n");
  560 	else if (get_key("yn","Drop directory ? (y/n)") == 'y') {
  561 	    truncate_file(fs,parent,0);
  562 	    MODIFY(parent,name[0],DELETED_FLAG);
  563 	    /* buglet: deleted directory stays in the list. */
  564 	    return 1;
  565 	}
  566     }
  567     dot = dotdot = redo = 0;
  568     walk = root;
  569     while (*walk) {
  570 	if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ||
  571 	  !strncmp((*walk)->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) {
  572 	    if (handle_dot(fs,*walk,dots)) {
  573 		*walk = (*walk)->next;
  574 		continue;
  575 	    }
  576 	    if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) dot++;
  577 	    else dotdot++;
  578 	}
  579 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME) &&
  580 	    bad_name((*walk)->dir_ent.name)) {
  581 	    printf("%s\n  Bad file name.\n",path_name(*walk));
  582 	    if (interactive)
  583 		printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
  584 		  "4) Keep it\n");
  585 	    else printf("  Auto-renaming it.\n");
  586 	    switch (interactive ? get_key("1234","?") : '3') {
  587 		case '1':
  588 		    drop_file(fs,*walk);
  589 		    walk = &(*walk)->next;
  590 		    continue;
  591 		case '2':
  592 		    rename_file(*walk);
  593 		    redo = 1;
  594 		    break;
  595 		case '3':
  596 		    auto_rename(*walk);
  597 		    printf("  Renamed to %s\n",file_name((*walk)->dir_ent.
  598 		      name));
  599 		    break;
  600 		case '4':
  601 		    break;
  602 	    }
  603 	}
  604 	/* don't check for duplicates of the volume label */
  605 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
  606 	    scan = &(*walk)->next;
  607 	    skip = 0;
  608 	    while (*scan && !skip) {
  609 		if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
  610 		    !strncmp((*walk)->dir_ent.name,(*scan)->dir_ent.name,MSDOS_NAME)) {
  611 		    printf("%s\n  Duplicate directory entry.\n  First  %s\n",
  612 			   path_name(*walk),file_stat(*walk));
  613 		    printf("  Second %s\n",file_stat(*scan));
  614 		    if (interactive)
  615 			printf("1) Drop first\n2) Drop second\n3) Rename first\n"
  616 			       "4) Rename second\n5) Auto-rename first\n"
  617 			       "6) Auto-rename second\n");
  618 		    else printf("  Auto-renaming second.\n");
  619 		    switch (interactive ? get_key("123456","?") : '6') {
  620 		      case '1':
  621 			drop_file(fs,*walk);
  622 			*walk = (*walk)->next;
  623 			skip = 1;
  624 			break;
  625 		      case '2':
  626 			drop_file(fs,*scan);
  627 			*scan = (*scan)->next;
  628 			continue;
  629 		      case '3':
  630 			rename_file(*walk);
  631 			printf("  Renamed to %s\n",path_name(*walk));
  632 			redo = 1;
  633 			break;
  634 		      case '4':
  635 			rename_file(*scan);
  636 			printf("  Renamed to %s\n",path_name(*walk));
  637 			redo = 1;
  638 			break;
  639 		      case '5':
  640 			auto_rename(*walk);
  641 			printf("  Renamed to %s\n",file_name((*walk)->dir_ent.
  642 			  name));
  643 			break;
  644 		      case '6':
  645 			auto_rename(*scan);
  646 			printf("  Renamed to %s\n",file_name((*scan)->dir_ent.
  647 			  name));
  648 			break;
  649 		    }
  650 		}
  651 		scan = &(*scan)->next;
  652 	    }
  653 	    if (skip) continue;
  654 	}
  655 	if (!redo) walk = &(*walk)->next;
  656 	else {
  657 	    walk = root;
  658 	    dot = dotdot = redo = 0;
  659 	}
  660     }
  661     if (dots && !dot)
  662 	printf("%s\n  \".\" is missing. Can't fix this yet.\n",
  663 	  path_name(parent));
  664     if (dots && !dotdot)
  665 	printf("%s\n  \"..\" is missing. Can't fix this yet.\n",
  666 	  path_name(parent));
  667     return 0;
  668 }
  669 
  670 
  671 static void test_file(DOS_FS *fs,DOS_FILE *file,int read_test)
  672 {
  673     DOS_FILE *owner;
  674     unsigned long walk,prev,clusters,next_clu;
  675 
  676     prev = clusters = 0;
  677     for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
  678       walk = next_clu) {
  679 	next_clu = next_cluster(fs,walk);
  680 	if ((owner = get_owner(fs,walk))) {
  681 	    if (owner == file) {
  682 		printf("%s\n  Circular cluster chain. Truncating to %lu "
  683 		  "cluster%s.\n",path_name(file),clusters,clusters == 1 ? "" :
  684 		  "s");
  685 		if (prev) set_fat(fs,prev,-1);
  686 		else if (!file->offset)
  687 		    die( "Bad FAT32 root directory! (bad start cluster)\n" );
  688 		else MODIFY_START(file,0,fs);
  689 	    }
  690 	    break;
  691 	}
  692 	if (bad_cluster(fs,walk)) break;
  693 	if (read_test) {
  694 	    if (fs_test(cluster_start(fs,walk),fs->cluster_size)) {
  695 		prev = walk;
  696 		clusters++;
  697 	    }
  698 	    else {
  699 		printf("%s\n  Cluster %lu (%lu) is unreadable. Skipping it.\n",
  700 		  path_name(file),clusters,walk);
  701 		if (prev) set_fat(fs,prev,next_cluster(fs,walk));
  702 		else MODIFY_START(file,next_cluster(fs,walk),fs);
  703 		set_fat(fs,walk,-2);
  704 	    }
  705 	}
  706 	set_owner(fs,walk,file);
  707     }
  708     for (walk = FSTART(file,fs); walk