xref: /reactos/sdk/lib/fslib/vfatlib/check/check.c (revision aad80191)
1 /* check.c - Check and repair a PC/MS-DOS filesystem
2 
3    Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4    Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5    Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
6    Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
7 
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21    The complete text of the GNU General Public License
22    can be found in /usr/share/common-licenses/GPL-3 file.
23 */
24 
25 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
26  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
27 
28 #include "vfatlib.h"
29 
30 #define NDEBUG
31 #include <debug.h>
32 
33 
34 /* the longest path on the filesystem that can be handled by path_name() */
35 #define PATH_NAME_MAX 1023
36 
37 static DOS_FILE *root;
38 
39 /* get start field of a dir entry */
40 #define FSTART(p,fs) \
41   ((uint32_t)le16toh(p->dir_ent.start) | \
42    (fs->fat_bits == 32 ? le16toh(p->dir_ent.starthi) << 16 : 0))
43 
44 #define MODIFY(p,i,v)					\
45   do {							\
46     if (p->offset) {					\
47 	p->dir_ent.i = v;				\
48 	fs_write(p->offset+offsetof(DIR_ENT,i),		\
49 		 sizeof(p->dir_ent.i),&p->dir_ent.i);	\
50     }							\
51   } while(0)
52 
53 #define MODIFY_START(p,v,fs)						\
54   do {									\
55     uint32_t __v = (v);							\
56     if (!p->offset) {							\
57 	/* writing to fake entry for FAT32 root dir */			\
58 	if (!__v) die("Oops, deleting FAT32 root dir!");		\
59 	fs->root_cluster = __v;						\
60 	p->dir_ent.start = htole16(__v&0xffff);				\
61 	p->dir_ent.starthi = htole16(__v>>16);				\
62 	__v = htole32(__v);						\
63 	fs_write(offsetof(struct boot_sector,root_cluster),		\
64 	         sizeof(((struct boot_sector *)0)->root_cluster),	\
65 		 &__v);							\
66     }									\
67     else {								\
68 	MODIFY(p,start,htole16((__v)&0xffff));				\
69 	if (fs->fat_bits == 32)						\
70 	    MODIFY(p,starthi,htole16((__v)>>16));			\
71     }									\
72   } while(0)
73 
74 off_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern, int gen_name)
75 {
76     static int curr_num = 0;
77     off_t offset;
78 
79     if (fs->root_cluster) {
80 	DIR_ENT d2;
81 	int i = 0, got = 0;
82 	uint32_t clu_num, prev = 0;
83 	off_t offset2;
84 
85 	clu_num = fs->root_cluster;
86 	offset = cluster_start(fs, clu_num);
87 	while (clu_num > 0 && clu_num != -1) {
88 	    fs_read(offset, sizeof(DIR_ENT), &d2);
89 	    if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
90 		got = 1;
91 		break;
92 	    }
93 	    i += sizeof(DIR_ENT);
94 	    offset += sizeof(DIR_ENT);
95 	    if ((i % fs->cluster_size) == 0) {
96 		prev = clu_num;
97 		if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
98 		    break;
99 		offset = cluster_start(fs, clu_num);
100 	    }
101 	}
102 	if (!got) {
103 	    /* no free slot, need to extend root dir: alloc next free cluster
104 	     * after previous one */
105 	    if (!prev)
106 		die("Root directory has no cluster allocated!");
107 	    for (clu_num = prev + 1; clu_num != prev; clu_num++) {
108 		FAT_ENTRY entry;
109 
110 		if (clu_num >= fs->data_clusters + 2)
111 		    clu_num = 2;
112 		get_fat(&entry, fs->fat, clu_num, fs);
113 		if (!entry.value)
114 		    break;
115 	    }
116 	    if (clu_num == prev)
117 		die("Root directory full and no free cluster");
118 	    set_fat(fs, prev, clu_num);
119 	    set_fat(fs, clu_num, -1);
120 	    set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
121 	    /* clear new cluster */
122 	    memset(&d2, 0, sizeof(d2));
123 	    offset = cluster_start(fs, clu_num);
124 	    for (i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT))
125 		fs_write(offset + i, sizeof(d2), &d2);
126 	}
127 	memset(de, 0, sizeof(DIR_ENT));
128 	if (gen_name) {
129 	    while (1) {
130 		char expanded[12];
131 		sprintf(expanded, pattern, curr_num);
132 		memcpy(de->name, expanded, MSDOS_NAME);
133 		clu_num = fs->root_cluster;
134 		i = 0;
135 		offset2 = cluster_start(fs, clu_num);
136 		while (clu_num > 0 && clu_num != -1) {
137 		    fs_read(offset2, sizeof(DIR_ENT), &d2);
138 		    if (offset2 != offset &&
139 			!strncmp((const char *)d2.name, (const char *)de->name,
140 				 MSDOS_NAME))
141 			break;
142 		    i += sizeof(DIR_ENT);
143 		    offset2 += sizeof(DIR_ENT);
144 		    if ((i % fs->cluster_size) == 0) {
145 			if ((clu_num = next_cluster(fs, clu_num)) == 0 ||
146 			    clu_num == -1)
147 			    break;
148 			offset2 = cluster_start(fs, clu_num);
149 		    }
150 		}
151 		if (clu_num == 0 || clu_num == -1)
152 		    break;
153 		if (++curr_num >= 10000)
154 		    die("Unable to create unique name");
155 	    }
156 	} else {
157 	    memcpy(de->name, pattern, MSDOS_NAME);
158 	}
159     } else {
160 	DIR_ENT *root;
161 	int next_free = 0, scan;
162 
163 	root = alloc(fs->root_entries * sizeof(DIR_ENT));
164 	fs_read(fs->root_start, fs->root_entries * sizeof(DIR_ENT), root);
165 
166 	while (next_free < fs->root_entries)
167 	    if (IS_FREE(root[next_free].name) &&
168 		root[next_free].attr != VFAT_LN_ATTR)
169 		break;
170 	    else
171 		next_free++;
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));
176 	if (gen_name) {
177 	    while (1) {
178 		char expanded[12];
179 		sprintf(expanded, pattern, curr_num);
180 		memcpy(de->name, expanded, MSDOS_NAME);
181 		for (scan = 0; scan < fs->root_entries; scan++)
182 		    if (scan != next_free &&
183 			!strncmp((const char *)root[scan].name,
184 				 (const char *)de->name, MSDOS_NAME))
185 			break;
186 		if (scan == fs->root_entries)
187 		    break;
188 		if (++curr_num >= 10000)
189 		    die("Unable to create unique name");
190 	    }
191 	} else {
192 	    memcpy(de->name, pattern, MSDOS_NAME);
193 	}
194 	free(root);
195     }
196     ++n_files;
197     return offset;
198 }
199 
200 /**
201  * Construct a full path (starting with '/') for the specified dentry,
202  * relative to the partition. All components are "long" names where possible.
203  *
204  * @param[in]   file    Information about dentry (file or directory) of interest
205  *
206  * return       Pointer to static string containing file's full path
207  */
208 static char *path_name(DOS_FILE * file)
209 {
210     static char path[PATH_NAME_MAX * 2];
211 
212     if (!file)
213 	*path = 0;		/* Reached the root directory */
214     else {
215 	if (strlen(path_name(file->parent)) > PATH_NAME_MAX)
216 	    die("Path name too long.");
217 	if (strcmp(path, "/") != 0)
218 	    strcat(path, "/");
219 
220 	/* Append the long name to the path,
221 	 * or the short name if there isn't a long one
222 	 */
223 	strcpy(strrchr(path, 0),
224 	       file->lfn ? file->lfn : file_name(file->dir_ent.name));
225     }
226     return path;
227 }
228 
229 static const int day_n[] =
230     {   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 };
231 /*    Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec              */
232 
233 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
234 
235 static time_t date_dos2unix(unsigned short time, unsigned short date)
236 {
237     int month, year;
238     time_t secs;
239 
240     month = ((date >> 5) & 15) - 1;
241     if (month < 0) {
242 	/* make sure that nothing bad happens if the month bits were zero */
243 	month = 0;
244     }
245     year = date >> 9;
246     secs =
247 	(time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
248 	86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 -
249 		 ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
250     /* days since 1.1.70 plus 80's leap day */
251     return secs;
252 }
253 
254 #ifdef __REACTOS__ // Old version!
255 
256 static char *file_stat(DOS_FILE * file)
257 {
258     static char temp[100];
259     char tmp[100];
260     time_t date;
261     LARGE_INTEGER time;
262     TIME_FIELDS time_fields;
263 
264     date = date_dos2unix(le16toh(file->dir_ent.time), le16toh(file->dir_ent.date));
265 
266     RtlSecondsSince1970ToTime(date, &time);
267     RtlTimeToTimeFields(&time, &time_fields);
268 
269     _snprintf(tmp, sizeof(tmp), "%d:%d:%d %d.%d.%d",
270         time_fields.Hour, time_fields.Minute, time_fields.Second,
271         time_fields.Day, time_fields.Month, time_fields.Year);
272 
273     _snprintf(temp, sizeof(temp), "  Size %u bytes, date %s", le32toh(file->dir_ent.size), tmp);
274     return temp;
275 }
276 
277 #else
278 
279 static char *file_stat(DOS_FILE * file)
280 {
281     static char temp[100];
282     struct tm *tm;
283     char tmp[100];
284     time_t date;
285 
286     date =
287 	date_dos2unix(le16toh(file->dir_ent.time), le16toh(file->dir_ent.date));
288     tm = localtime(&date);
289     strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm);
290     sprintf(temp, "  Size %u bytes, date %s", le32toh(file->dir_ent.size), tmp);
291     return temp;
292 }
293 
294 #endif
295 
296 static int bad_name(DOS_FILE * file)
297 {
298     int i, spc, suspicious = 0;
299     const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
300     const unsigned char *name = file->dir_ent.name;
301     const unsigned char *ext = name + 8;
302 
303     /* Do not complain about (and auto-correct) the extended attribute files
304      * of OS/2. */
305     if (strncmp((const char *)name, "EA DATA  SF", 11) == 0 ||
306 	strncmp((const char *)name, "WP ROOT  SF", 11) == 0)
307 	return 0;
308 
309     /* check if we have neither a long filename nor a short name */
310     if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) {
311 	return 1;
312     }
313 
314     /* don't complain about the dummy 11 bytes used by patched Linux
315        kernels */
316     if (file->dir_ent.lcase & FAT_NO_83NAME)
317 	return 0;
318 
319     for (i = 0; i < MSDOS_NAME; i++) {
320 	if (name[i] < ' ' || name[i] == 0x7f)
321 	    return 1;
322 	if (name[i] > 0x7f)
323 	    ++suspicious;
324 	if (strchr(bad_chars, name[i]))
325 	    return 1;
326     }
327 
328     spc = 0;
329     for (i = 0; i < 8; i++) {
330 	if (name[i] == ' ')
331 	    spc = 1;
332 	else if (spc)
333 	    /* non-space after a space not allowed, space terminates the name
334 	     * part */
335 	    return 1;
336     }
337 
338     spc = 0;
339     for (i = 0; i < 3; i++) {
340 	if (ext[i] == ' ')
341 	    spc = 1;
342 	else if (spc)
343 	    /* non-space after a space not allowed, space terminates the ext
344 	     * part */
345 	    return 1;
346     }
347 
348     /* Under GEMDOS, chars >= 128 are never allowed. */
349     if (atari_format && suspicious)
350 	return 1;
351 
352 #ifdef __REACTOS__ // Old !!!!!!!!!!!!!!!
353 
354     /* Only complain about too much suspicious chars in interactive mode,
355      * never correct them automatically. The chars are all basically ok, so we
356      * shouldn't auto-correct such names. */
357     if (interactive && suspicious > 6)
358 	return 1;
359     return 0;
360 
361 #else
362 
363     /* Under MS-DOS and Windows, chars >= 128 in short names are valid
364      * (but these characters can be visualised differently depending on
365      * local codepage: CP437, CP866, etc). The chars are all basically ok,
366      * so we shouldn't auto-correct such names. */
367     return 0;
368 
369 #endif
370 }
371 
372 static void lfn_remove(off_t from, off_t to)
373 {
374     DIR_ENT empty;
375 
376     /* New dir entry is zeroed except first byte, which is set to 0xe5.
377      * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
378      * a directory at the first zero entry...
379      */
380     memset(&empty, 0, sizeof(empty));
381     empty.name[0] = DELETED_FLAG;
382 
383     for (; from < to; from += sizeof(empty)) {
384 	fs_write(from, sizeof(DIR_ENT), &empty);
385     }
386 }
387 
388 static void drop_file(DOS_FS * fs, DOS_FILE * file)
389 {
390     uint32_t cluster;
391 
392     MODIFY(file, name[0], DELETED_FLAG);
393     if (file->lfn)
394 	lfn_remove(file->lfn_offset, file->offset);
395     for (cluster = FSTART(file, fs); cluster > 0 && cluster <
396 	 fs->data_clusters + 2; cluster = next_cluster(fs, cluster))
397 	set_owner(fs, cluster, NULL);
398     --n_files;
399 }
400 
401 static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters)
402 {
403     int deleting;
404     uint32_t walk, next;
405 
406     walk = FSTART(file, fs);
407     if ((deleting = !clusters))
408 	MODIFY_START(file, 0, fs);
409     while (walk > 0 && walk != -1) {
410 	next = next_cluster(fs, walk);
411 	if (deleting)
412 	    set_fat(fs, walk, 0);
413 	else if ((deleting = !--clusters))
414 	    set_fat(fs, walk, -1);
415 	walk = next;
416     }
417 }
418 
419 static void auto_rename(DOS_FILE * file)
420 {
421     DOS_FILE *first, *walk;
422     uint32_t number;
423 
424     if (!file->offset)
425 	return;			/* cannot rename FAT32 root dir */
426     first = file->parent ? file->parent->first : root;
427     number = 0;
428     while (1) {
429 	char num[8];
430 	sprintf(num, "%07lu", (unsigned long)number);
431 	memcpy(file->dir_ent.name, "FSCK", 4);
432 	memcpy(file->dir_ent.name + 4, num, 7);
433 	for (walk = first; walk; walk = walk->next)
434 	    if (walk != file
435 		&& !strncmp((const char *)walk->dir_ent.name,
436 			    (const char *)file->dir_ent.name, MSDOS_NAME))
437 		break;
438 	if (!walk) {
439 	    if (file->dir_ent.lcase & FAT_NO_83NAME) {
440 		/* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
441 		   present */
442 		file->dir_ent.lcase &= ~FAT_NO_83NAME;
443 		/* reset the attributes, only keep DIR and VOLUME */
444 		file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
445 		fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
446 	    } else {
447 		fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
448 	    }
449 	    if (file->lfn)
450 		lfn_fix_checksum(file->lfn_offset, file->offset,
451 				 (const char *)file->dir_ent.name);
452 	    return;
453 	}
454 	number++;
455 	if (number > 9999999) {
456 	    die("Too many files need repair.");
457 	}
458     }
459     die("Can't generate a unique name.");
460 }
461 
462 static void rename_file(DOS_FILE * file)
463 {
464 #ifndef __REACTOS__
465     unsigned char name[46];
466     unsigned char *walk, *here;
467 #endif
468 
469     if (!file->offset) {
470 #ifndef __REACTOS__
471 	printf("Cannot rename FAT32 root dir\n");
472 #else
473 	VfatPrint( "Cannot rename FAT32 root dir\n" );
474 #endif
475 	return;			/* cannot rename FAT32 root dir */
476     }
477     while (1) {
478 #ifndef __REACTOS__
479 	printf("New name: ");
480 	fflush(stdout);
481 	if (fgets((char *)name, 45, stdin)) {
482 	    if ((here = (unsigned char *)strchr((const char *)name, '\n')))
483 		*here = 0;
484 	    for (walk = (unsigned char *)strrchr((const char *)name, 0);
485 		 walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
486 	    walk[1] = 0;
487 	    for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
488 	    if (file_cvt(walk, file->dir_ent.name)) {
489 		if (file->dir_ent.lcase & FAT_NO_83NAME) {
490 		    /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not
491 		       present */
492 		    file->dir_ent.lcase &= ~FAT_NO_83NAME;
493 		    /* reset the attributes, only keep DIR and VOLUME */
494 		    file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME);
495 		    fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent);
496 		} else {
497 		    fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
498 		}
499 		if (file->lfn)
500 		    lfn_fix_checksum(file->lfn_offset, file->offset,
501 				     (const char *)file->dir_ent.name);
502 		return;
503 	    }
504 	}
505 #else
506     return;
507 #endif
508     }
509 }
510 
511 static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots)
512 {
513     const char *name;
514 
515     name =
516 	strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
517 		MSDOS_NAME) ? ".." : ".";
518     if (!(file->dir_ent.attr & ATTR_DIR)) {
519 	printf("%s\n  Is a non-directory.\n", path_name(file));
520 	if (interactive)
521 	    printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
522 		   "4) Convert to directory\n");
523 #ifndef __REACTOS__
524 	else
525 #else
526 	else if (rw)
527 #endif
528 	    printf("  Auto-renaming it.\n");
529 #ifdef __REACTOS__
530 	if (rw || interactive) {
531 #endif
532 	switch (interactive ? get_key("1234", "?") : '2') {
533 	case '1':
534 	    drop_file(fs, file);
535 	    return 1;
536 	case '2':
537 	    auto_rename(file);
538 	    printf("  Renamed to %s\n", file_name(file->dir_ent.name));
539 	    return 0;
540 	case '3':
541 	    rename_file(file);
542 	    return 0;
543 	case '4':
544 	    MODIFY(file, size, htole32(0));
545 	    MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR);
546 	    break;
547 #ifdef __REACTOS__
548 		}
549 #endif
550 	}
551     }
552     if (!dots) {
553 	printf("Root contains directory \"%s\". Dropping it.\n", name);
554 	drop_file(fs, file);
555 	return 1;
556     }
557     return 0;
558 }
559 
560 static int check_file(DOS_FS * fs, DOS_FILE * file)
561 {
562     DOS_FILE *owner;
563     int restart;
564     uint32_t expect, curr, this, clusters, prev, walk, clusters2;
565 
566     if (file->dir_ent.attr & ATTR_DIR) {
567 	if (le32toh(file->dir_ent.size)) {
568 #ifndef __REACTOS__
569 	    printf("%s\n  Directory has non-zero size. Fixing it.\n",
570 		   path_name(file));
571 #else
572 	    printf("%s\n  Directory has non-zero size.%s\n",
573 		   path_name(file), (rw) ? " Fixing it." : "");
574 	    if (rw)
575 #endif
576 	    MODIFY(file, size, htole32(0));
577 	}
578 	if (file->parent
579 	    && !strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
580 			MSDOS_NAME)) {
581 	    expect = FSTART(file->parent, fs);
582 	    if (FSTART(file, fs) != expect) {
583 		printf("%s\n  Start (%lu) does not point to parent (%lu)\n",
584 		       path_name(file), (unsigned long)FSTART(file, fs), (long)expect);
585 #ifdef __REACTOS__
586 		if (rw)
587 #endif
588 		MODIFY_START(file, expect, fs);
589 	    }
590 	    return 0;
591 	}
592 	if (file->parent
593 	    && !strncmp((const char *)file->dir_ent.name, MSDOS_DOTDOT,
594 			MSDOS_NAME)) {
595 	    expect =
596 		file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
597 	    if (fs->root_cluster && expect == fs->root_cluster)
598 		expect = 0;
599 	    if (FSTART(file, fs) != expect) {
600 		printf("%s\n  Start (%lu) does not point to .. (%lu)\n",
601 		       path_name(file), (unsigned long)FSTART(file, fs), (unsigned long)expect);
602 #ifdef __REACTOS__
603 		if (rw)
604 #endif
605 		MODIFY_START(file, expect, fs);
606 	    }
607 	    return 0;
608 	}
609 	if (FSTART(file, fs) == 0) {
610 #ifndef __REACTOS__
611 	    printf("%s\n Start does point to root directory. Deleting dir. \n",
612 		   path_name(file));
613 #else
614 	    printf("%s\n Start does point to root directory.%s\n",
615 		   path_name(file), (rw) ? " Deleting dir. " : "");
616 	    if (rw)
617 #endif
618 	    MODIFY(file, name[0], DELETED_FLAG);
619 	    return 0;
620 	}
621     }
622     if (FSTART(file, fs) == 1) {
623 	printf("%s\n  Bad start cluster 1. Truncating file.\n",
624 	       path_name(file));
625 	if (!file->offset)
626 	    die("Bad FAT32 root directory! (bad start cluster 1)\n");
627 #ifdef __REACTOS__
628 	if (rw)
629 #endif
630 	MODIFY_START(file, 0, fs);
631     }
632     if (FSTART(file, fs) >= fs->data_clusters + 2) {
633 	printf
634 #ifndef __REACTOS__
635 	    ("%s\n  Start cluster beyond limit (%lu > %lu). Truncating file.\n",
636 	     path_name(file), (unsigned long)FSTART(file, fs),
637 	     (unsigned long)(fs->data_clusters + 1));
638 #else
639 	    ("%s\n  Start cluster beyond limit (%lu > %lu).%s\n",
640 	     path_name(file), (unsigned long)FSTART(file, fs),
641 	     (unsigned long)(fs->data_clusters + 1),
642 	     (rw) ? " Truncating file." : "");
643 #endif
644 	if (!file->offset)
645 	    die("Bad FAT32 root directory! (start cluster beyond limit: %lu > %lu)\n",
646 		(unsigned long)FSTART(file, fs),
647 		(unsigned long)(fs->data_clusters + 1));
648 #ifdef __REACTOS__
649 	if (rw)
650 #endif
651 	MODIFY_START(file, 0, fs);
652     }
653     clusters = prev = 0;
654     for (curr = FSTART(file, fs) ? FSTART(file, fs) :
655 	 -1; curr != -1; curr = next_cluster(fs, curr)) {
656 	FAT_ENTRY curEntry;
657 	get_fat(&curEntry, fs->fat, curr, fs);
658 
659 	if (!curEntry.value || bad_cluster(fs, curr)) {
660 #ifndef __REACTOS__
661 	    printf("%s\n  Contains a %s cluster (%lu). Assuming EOF.\n",
662 		   path_name(file), curEntry.value ? "bad" : "free", (unsigned long)curr);
663 #else
664 	    printf("%s\n  Contains a %s cluster (%lu).%s\n",
665 		   path_name(file), curEntry.value ? "bad" : "free", (unsigned long)curr,
666 		   (rw) ? " Assuming EOF." : "");
667 #endif
668 	    if (prev)
669 		set_fat(fs, prev, -1);
670 	    else if (!file->offset)
671 		die("FAT32 root dir starts with a bad cluster!");
672 	    else
673 #ifdef __REACTOS__
674 		if (rw)
675 #endif
676 		MODIFY_START(file, 0, fs);
677 	    break;
678 	}
679 	if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <=
680 	    (uint64_t)clusters * fs->cluster_size) {
681 #ifdef __REACTOS__
682 	    if (rw) {
683 #endif
684 	    printf
685 		("%s\n  File size is %u bytes, cluster chain length is > %llu "
686 		 "bytes.\n  Truncating file to %u bytes.\n", path_name(file),
687 		 le32toh(file->dir_ent.size),
688 		 (unsigned long long)clusters * fs->cluster_size,
689 		 le32toh(file->dir_ent.size));
690 	    truncate_file(fs, file, clusters);
691 #ifdef __REACTOS__
692 	    } else {
693 	    printf
694 		("%s\n  File size is %u bytes, cluster chain length is > %llu "
695 		 "bytes.\n", path_name(file),
696 		 le32toh(file->dir_ent.size),
697 		 (unsigned long long)clusters * fs->cluster_size);
698 	    }
699 #endif
700 	    break;
701 	}
702 	if ((owner = get_owner(fs, curr))) {
703 	    int do_trunc = 0;
704 	    printf("%s  and\n", path_name(owner));
705 	    printf("%s\n  share clusters.\n", path_name(file));
706 	    clusters2 = 0;
707 	    for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
708 		 next_cluster(fs, walk))
709 		if (walk == curr)
710 		    break;
711 		else
712 		    clusters2++;
713 	    restart = file->dir_ent.attr & ATTR_DIR;
714 #ifndef __REACTOS__
715 	    if (!owner->offset) {
716 #else
717 	    if (!owner->offset && rw) {
718 #endif
719 		printf("  Truncating second to %llu bytes because first "
720 		       "is FAT32 root dir.\n",
721 		       (unsigned long long)clusters * fs->cluster_size);
722 		do_trunc = 2;
723 #ifndef __REACTOS__
724 	    } else if (!file->offset) {
725 #else
726 	    } else if (!file->offset && rw) {
727 #endif
728 		printf("  Truncating first to %llu bytes because second "
729 		       "is FAT32 root dir.\n",
730 		       (unsigned long long)clusters2 * fs->cluster_size);
731 		do_trunc = 1;
732 	    } else if (interactive)
733 		printf("1) Truncate first to %llu bytes%s\n"
734 		       "2) Truncate second to %llu bytes\n",
735 		       (unsigned long long)clusters2 * fs->cluster_size,
736 		       restart ? " and restart" : "",
737 		       (unsigned long long)clusters * fs->cluster_size);
738 	    else
739 #ifdef __REACTOS__
740 	    if (rw)
741 #endif
742 		printf("  Truncating second to %llu bytes.\n",
743 		       (unsigned long long)clusters * fs->cluster_size);
744 #ifndef __REACTOS__
745 	    if (do_trunc != 2
746 		&& (do_trunc == 1
747 #else
748 	    if ((do_trunc != 2 && rw)
749 		&& ((do_trunc == 1 && rw)
750 #endif
751 		    || (interactive && get_key("12", "?") == '1'))) {
752 		prev = 0;
753 		clusters = 0;
754 		for (this = FSTART(owner, fs); this > 0 && this != -1; this =
755 		     next_cluster(fs, this)) {
756 		    if (this == curr) {
757 			if (prev)
758 			    set_fat(fs, prev, -1);
759 			else
760 			    MODIFY_START(owner, 0, fs);
761 			MODIFY(owner, size,
762 			       htole32((uint64_t)clusters *
763 				       fs->cluster_size));
764 			if (restart)
765 			    return 1;
766 			while (this > 0 && this != -1) {
767 			    set_owner(fs, this, NULL);
768 			    this = next_cluster(fs, this);
769 			}
770 			this = curr;
771 			break;
772 		    }
773 		    clusters++;
774 		    prev = this;
775 		}
776 		if (this != curr)
777 		    die("Internal error: didn't find cluster %d in chain"
778 			" starting at %d", curr, FSTART(owner, fs));
779 	    } else {
780 		if (prev)
781 		    set_fat(fs, prev, -1);
782 		else
783 		    MODIFY_START(file, 0, fs);
784 		break;
785 	    }
786 	}
787 	set_owner(fs, curr, file);
788 	clusters++;
789 	prev = curr;
790     }
791     if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) >
792 #ifndef __REACTOS__
793 	(uint64_t)clusters * fs->cluster_size) {
794 #else
795 	(uint64_t)clusters * fs->cluster_size && rw) {
796 #endif
797 	printf
798 	    ("%s\n  File size is %u bytes, cluster chain length is %llu bytes."
799 	     "\n  Truncating file to %llu bytes.\n", path_name(file),
800 	     le32toh(file->dir_ent.size),
801 	     (unsigned long long)clusters * fs->cluster_size,
802 	     (unsigned long long)clusters * fs->cluster_size);
803 	MODIFY(file, size,
804 	       htole32((uint64_t)clusters * fs->cluster_size));
805     }
806     return 0;
807 }
808 
809 static int check_files(DOS_FS * fs, DOS_FILE * start)
810 {
811     while (start) {
812 	if (check_file(fs, start))
813 	    return 1;
814 	start = start->next;
815     }
816     return 0;
817 }
818 
819 static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
820 {
821     DOS_FILE *parent, **walk, **scan;
822     int dot, dotdot, skip, redo;
823     int good, bad;
824 
825     if (!*root)
826 	return 0;
827     parent = (*root)->parent;
828     good = bad = 0;
829     for (walk = root; *walk; walk = &(*walk)->next)
830 	if (bad_name(*walk))
831 	    bad++;
832 	else
833 	    good++;
834     if (*root && parent && good + bad > 4 && bad > good / 2) {
835 	printf("%s\n  Has a large number of bad entries. (%d/%d)\n",
836 	       path_name(parent), bad, good + bad);
837 	if (!dots)
838 	    printf("  Not dropping root directory.\n");
839 #ifndef __REACTOS__
840 	else if (!interactive)
841 #else
842 	else if (!interactive || !rw)
843 #endif
844 	    printf("  Not dropping it in auto-mode.\n");
845 	else if (get_key("yn", "Drop directory ? (y/n)") == 'y') {
846 	    truncate_file(fs, parent, 0);
847 	    MODIFY(parent, name[0], DELETED_FLAG);
848 	    /* buglet: deleted directory stays in the list. */
849 	    return 1;
850 	}
851     }
852     dot = dotdot = redo = 0;
853     walk = root;
854     while (*walk) {
855 	if (!strncmp
856 	    ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)
857 	    || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT,
858 			MSDOS_NAME)) {
859 	    if (handle_dot(fs, *walk, dots)) {
860 		*walk = (*walk)->next;
861 		continue;
862 	    }
863 	    if (!strncmp
864 		((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME))
865 		dot++;
866 	    else
867 		dotdot++;
868 	}
869 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
870 #ifndef __REACTOS__
871 	    puts(path_name(*walk));
872 	    printf("  Bad short file name (%s).\n",
873 		   file_name((*walk)->dir_ent.name));
874 #else
875 	    printf("%s\n  Bad short file name (%s).\n",
876 		   path_name(*walk), file_name((*walk)->dir_ent.name));
877 #endif
878 	    if (interactive)
879 		printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
880 		       "4) Keep it\n");
881 	    else
882 #ifdef __REACTOS__
883 	    if (rw)
884 #endif
885 		printf("  Auto-renaming it.\n");
886 #ifdef __REACTOS__
887 	    if (rw || interactive) {
888 #endif
889 	    switch (interactive ? get_key("1234", "?") : '3') {
890 	    case '1':
891 		drop_file(fs, *walk);
892 		walk = &(*walk)->next;
893 		continue;
894 	    case '2':
895 		rename_file(*walk);
896 		redo = 1;
897 		break;
898 	    case '3':
899 		auto_rename(*walk);
900 		printf("  Renamed to %s\n", file_name((*walk)->dir_ent.name));
901 		break;
902 	    case '4':
903 		break;
904 #ifdef __REACTOS__
905 		    }
906 #endif
907 	    }
908 	}
909 	/* don't check for duplicates of the volume label */
910 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
911 	    scan = &(*walk)->next;
912 	    skip = 0;
913 	    while (*scan && !skip) {
914 		if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
915 		    !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
916 			    MSDOS_NAME)) {
917 		    printf("%s\n  Duplicate directory entry.\n  First  %s\n",
918 			   path_name(*walk), file_stat(*walk));
919 		    printf("  Second %s\n", file_stat(*scan));
920 		    if (interactive)
921 			printf
922 			    ("1) Drop first\n2) Drop second\n3) Rename first\n"
923 			     "4) Rename second\n5) Auto-rename first\n"
924 			     "6) Auto-rename second\n");
925 		    else
926 #ifdef __REACTOS__
927 		    if (rw)
928 #endif
929 			printf("  Auto-renaming second.\n");
930 #ifdef __REACTOS__
931 		    if (rw || interactive) {
932 #endif
933 		    switch (interactive ? get_key("123456", "?") : '6') {
934 		    case '1':
935 			drop_file(fs, *walk);
936 			*walk = (*walk)->next;
937 			skip = 1;
938 			break;
939 		    case '2':
940 			drop_file(fs, *scan);
941 			*scan = (*scan)->next;
942 			continue;
943 		    case '3':
944 			rename_file(*walk);
945 			printf("  Renamed to %s\n", path_name(*walk));
946 			redo = 1;
947 			break;
948 		    case '4':
949 			rename_file(*scan);
950 			printf("  Renamed to %s\n", path_name(*walk));
951 			redo = 1;
952 			break;
953 		    case '5':
954 			auto_rename(*walk);
955 			printf("  Renamed to %s\n",
956 			       file_name((*walk)->dir_ent.name));
957 			break;
958 		    case '6':
959 			auto_rename(*scan);
960 			printf("  Renamed to %s\n",
961 			       file_name((*scan)->dir_ent.name));
962 			break;
963 #ifdef __REACTOS__
964 			    }
965 #endif
966 		    }
967 		}
968 		scan = &(*scan)->next;
969 	    }
970 	    if (skip)
971 		continue;
972 	}
973 	if (!redo)
974 	    walk = &(*walk)->next;
975 	else {
976 	    walk = root;
977 	    dot = dotdot = redo = 0;
978 	}
979     }
980     if (dots && !dot)
981 	printf("%s\n  \".\" is missing. Can't fix this yet.\n",
982 	       path_name(parent));
983     if (dots && !dotdot)
984 	printf("%s\n  \"..\" is missing. Can't fix this yet.\n",
985 	       path_name(parent));
986     return 0;
987 }
988 
989 /**
990  * Check a dentry's cluster chain for bad clusters.
991  * If requested, we verify readability and mark unreadable clusters as bad.
992  *
993  * @param[inout]    fs          Information about the filesystem
994  * @param[in]       file        dentry to check
995  * @param[in]       read_test   Nonzero == verify that dentry's clusters can
996  *                              be read
997  */
998 static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
999 {
1000     DOS_FILE *owner;
1001     uint32_t walk, prev, clusters, next_clu;
1002 
1003     prev = clusters = 0;
1004     for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
1005 	 walk = next_clu) {
1006 	next_clu = next_cluster(fs, walk);
1007 
1008 	/* In this stage we are checking only for a loop within our own
1009 	 * cluster chain.
1010 	 * Cross-linking of clusters is handled in check_file()
1011 	 */
1012 	if ((owner = get_owner(fs, walk))) {
1013 	    if (owner == file) {
1014 		printf("%s\n  Circular cluster chain. Truncating to %lu "
1015 		       "cluster%s.\n", path_name(file), (unsigned long)clusters,
1016 		       clusters == 1 ? "" : "s");
1017 		if (prev)
1018 		    set_fat(fs, prev, -1);
1019 		else if (!file->offset)
1020 		    die("Bad FAT32 root directory! (bad start cluster)\n");
1021 		else
1022 		    MODIFY_START(file, 0, fs);
1023 	    }
1024 	    break;
1025 	}
1026 	if (bad_cluster(fs, walk))
1027 	    break;
1028 	if (read_test) {
1029 	    if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
1030 		prev = walk;
1031 		clusters++;
1032 	    } else {
1033 		printf("%s\n  Cluster %lu (%lu) is unreadable. Skipping it.\n",
1034 		       path_name(file), (unsigned long)clusters, (unsigned long)walk);
1035 		if (prev)
1036 		    set_fat(fs, prev, next_cluster(fs, walk));
1037 		else
1038 		    MODIFY_START(file, next_cluster(fs, walk), fs);
1039 		set_fat(fs, walk, -2);
1040 	    }
1041 	} else {
1042 	    prev = walk;
1043 	    clusters++;
1044 	}
1045 	set_owner(fs, walk, file);
1046     }
1047     /* Revert ownership (for now) */
1048     for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
1049 	 walk = next_cluster(fs, walk))
1050 	if (bad_cluster(fs, walk))
1051 	    break;
1052 	else if (get_owner(fs, walk) == file)
1053 	    set_owner(fs, walk, NULL);
1054 	else
1055 	    break;
1056 }
1057 
1058 static void undelete(DOS_FS * fs, DOS_FILE * file)
1059 {
1060     uint32_t clusters, left, prev, walk;
1061 
1062     clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) /
1063 	fs->cluster_size;
1064     prev = 0;
1065 
1066     walk = FSTART(file, fs);
1067 
1068     while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) {
1069 
1070 	FAT_ENTRY curEntry;
1071 	get_fat(&curEntry, fs->fat, walk, fs);
1072 
1073 	if (!curEntry.value)
1074 	    break;
1075 
1076 	left--;
1077 	if (prev)
1078 	    set_fat(fs, prev, walk);
1079 	prev = walk;
1080 	walk++;
1081     }
1082     if (prev)
1083 	set_fat(fs, prev, -1);
1084     else
1085 	MODIFY_START(file, 0, fs);
1086     if (left)
1087 	printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
1088 	       (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s");
1089 
1090 }
1091 
1092 static void new_dir(void)
1093 {
1094     lfn_reset();
1095 }
1096 
1097 /**
1098  * Create a description for a referenced dentry and insert it in our dentry
1099  * tree. Then, go check the dentry's cluster chain for bad clusters and
1100  * cluster loops.
1101  *
1102  * @param[inout]    fs      Information about the filesystem
1103  * @param[out]      chain
1104  * @param[in]       parent  Information about parent directory of this file
1105  *                          NULL == no parent ('file' is root directory)
1106  * @param[in]       offset  Partition-relative byte offset of directory entry of interest
1107  *                          0 == Root directory
1108  * @param           cp
1109  */
1110 static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
1111 		     off_t offset, FDSC ** cp)
1112 {
1113     DOS_FILE *new;
1114     DIR_ENT de;
1115     FD_TYPE type;
1116 
1117     if (offset)
1118 	fs_read(offset, sizeof(DIR_ENT), &de);
1119     else {
1120 	/* Construct a DIR_ENT for the root directory */
1121 	memset(&de, 0, sizeof de);
1122 	memcpy(de.name, "           ", MSDOS_NAME);
1123 	de.attr = ATTR_DIR;
1124 	de.start = htole16(fs->root_cluster & 0xffff);
1125 	de.starthi = htole16((fs->root_cluster >> 16) & 0xffff);
1126     }
1127     if ((type = file_type(cp, (char *)de.name)) != fdt_none) {
1128 	if (type == fdt_undelete && (de.attr & ATTR_DIR))
1129 	    die("Can't undelete directories.");
1130 	file_modify(cp, (char *)de.name);
1131 	fs_write(offset, 1, &de);
1132     }
1133     if (IS_FREE(de.name)) {
1134 	lfn_check_orphaned();
1135 	return;
1136     }
1137     if (de.attr == VFAT_LN_ATTR) {
1138 	lfn_add_slot(&de, offset);
1139 	return;
1140     }
1141     new = qalloc(&mem_queue, sizeof(DOS_FILE));
1142     new->lfn = lfn_get(&de, &new->lfn_offset);
1143     new->offset = offset;
1144     memcpy(&new->dir_ent, &de, sizeof(de));
1145     new->next = new->first = NULL;
1146     new->parent = parent;
1147     if (type == fdt_undelete)
1148 	undelete(fs, new);
1149     **chain = new;
1150     *chain = &new->next;
1151     if (list) {
1152 	printf("Checking file %s", path_name(new));
1153 	if (new->lfn)
1154 	    printf(" (%s)", file_name(new->dir_ent.name));	/* (8.3) */
1155 	printf("\n");
1156     }
1157     /* Don't include root directory, '.', or '..' in the total file count */
1158     if (offset &&
1159 	strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 &&
1160 	strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0)
1161 	++n_files;
1162     test_file(fs, new, test);	/* Bad cluster check */
1163 }
1164 
1165 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
1166 
1167 static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
1168 {
1169     DOS_FILE **chain;
1170     int i;
1171     uint32_t clu_num;
1172 
1173     chain = &this->first;
1174     i = 0;
1175     clu_num = FSTART(this, fs);
1176     new_dir();
1177     while (clu_num > 0 && clu_num != -1) {
1178 	add_file(fs, &chain, this,
1179 		 cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
1180 	i += sizeof(DIR_ENT);
1181 	if (!(i % fs->cluster_size))
1182 	    if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
1183 		break;
1184     }
1185     lfn_check_orphaned();
1186     if (check_dir(fs, &this->first, this->offset))
1187 	return 0;
1188     if (check_files(fs, this->first))
1189 	return 1;
1190     return subdirs(fs, this, cp);
1191 }
1192 
1193 /**
1194  * Recursively scan subdirectories of the specified parent directory.
1195  *
1196  * @param[inout]    fs      Information about the filesystem
1197  * @param[in]       parent  Identifies the directory to scan
1198  * @param[in]       cp
1199  *
1200  * @return  0   Success
1201  * @return  1   Error
1202  */
1203 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
1204 {
1205     DOS_FILE *walk;
1206 
1207     for (walk = parent ? parent->first : root; walk; walk = walk->next)
1208 	if (walk->dir_ent.attr & ATTR_DIR)
1209 	    if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME)
1210 		&& strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT,
1211 			   MSDOS_NAME))
1212 		if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
1213 		    return 1;
1214     return 0;
1215 }
1216 
1217 /**
1218  * Scan all directory and file information for errors.
1219  *
1220  * @param[inout]    fs      Information about the filesystem
1221  *
1222  * @return  0   Success
1223  * @return  1   Error
1224  */
1225 int scan_root(DOS_FS * fs)
1226 {
1227     DOS_FILE **chain;
1228     int i;
1229 
1230     root = NULL;
1231     chain = &root;
1232     new_dir();
1233     if (fs->root_cluster) {
1234 	add_file(fs, &chain, NULL, 0, &fp_root);
1235     } else {
1236 	for (i = 0; i < fs->root_entries; i++)
1237 	    add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
1238 		     &fp_root);
1239     }
1240     lfn_check_orphaned();
1241     (void)check_dir(fs, &root, 0);
1242     if (check_files(fs, root))
1243 	return 1;
1244     return subdirs(fs, NULL, &fp_root);
1245 }
1246