xref: /reactos/sdk/lib/fslib/vfatlib/check/check.c (revision cc439606)
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 #ifdef __REACTOS__
670 	    {
671 	    if (rw)
672 #endif
673 		set_fat(fs, prev, -1);
674 #ifdef __REACTOS__
675 	    }
676 #endif
677 	    else if (!file->offset)
678 		die("FAT32 root dir starts with a bad cluster!");
679 	    else
680 #ifdef __REACTOS__
681 		if (rw)
682 #endif
683 		MODIFY_START(file, 0, fs);
684 	    break;
685 	}
686 	if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <=
687 	    (uint64_t)clusters * fs->cluster_size) {
688 #ifdef __REACTOS__
689 	    if (rw) {
690 #endif
691 	    printf
692 		("%s\n  File size is %u bytes, cluster chain length is > %llu "
693 		 "bytes.\n  Truncating file to %u bytes.\n", path_name(file),
694 		 le32toh(file->dir_ent.size),
695 		 (unsigned long long)clusters * fs->cluster_size,
696 		 le32toh(file->dir_ent.size));
697 	    truncate_file(fs, file, clusters);
698 #ifdef __REACTOS__
699 	    } else {
700 	    printf
701 		("%s\n  File size is %u bytes, cluster chain length is > %llu "
702 		 "bytes.\n", path_name(file),
703 		 le32toh(file->dir_ent.size),
704 		 (unsigned long long)clusters * fs->cluster_size);
705 	    }
706 #endif
707 	    break;
708 	}
709 	if ((owner = get_owner(fs, curr))) {
710 	    int do_trunc = 0;
711 	    printf("%s  and\n", path_name(owner));
712 	    printf("%s\n  share clusters.\n", path_name(file));
713 	    clusters2 = 0;
714 	    for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
715 		 next_cluster(fs, walk))
716 		if (walk == curr)
717 		    break;
718 		else
719 		    clusters2++;
720 	    restart = file->dir_ent.attr & ATTR_DIR;
721 #ifndef __REACTOS__
722 	    if (!owner->offset) {
723 #else
724 	    if (!owner->offset && rw) {
725 #endif
726 		printf("  Truncating second to %llu bytes because first "
727 		       "is FAT32 root dir.\n",
728 		       (unsigned long long)clusters * fs->cluster_size);
729 		do_trunc = 2;
730 #ifndef __REACTOS__
731 	    } else if (!file->offset) {
732 #else
733 	    } else if (!file->offset && rw) {
734 #endif
735 		printf("  Truncating first to %llu bytes because second "
736 		       "is FAT32 root dir.\n",
737 		       (unsigned long long)clusters2 * fs->cluster_size);
738 		do_trunc = 1;
739 	    } else if (interactive)
740 		printf("1) Truncate first to %llu bytes%s\n"
741 		       "2) Truncate second to %llu bytes\n",
742 		       (unsigned long long)clusters2 * fs->cluster_size,
743 		       restart ? " and restart" : "",
744 		       (unsigned long long)clusters * fs->cluster_size);
745 	    else
746 #ifdef __REACTOS__
747 	    if (rw)
748 #endif
749 		printf("  Truncating second to %llu bytes.\n",
750 		       (unsigned long long)clusters * fs->cluster_size);
751 #ifndef __REACTOS__
752 	    if (do_trunc != 2
753 		&& (do_trunc == 1
754 #else
755 	    if ((do_trunc != 2 && rw)
756 		&& ((do_trunc == 1 && rw)
757 #endif
758 		    || (interactive && get_key("12", "?") == '1'))) {
759 		prev = 0;
760 		clusters = 0;
761 		for (this = FSTART(owner, fs); this > 0 && this != -1; this =
762 		     next_cluster(fs, this)) {
763 		    if (this == curr) {
764 			if (prev)
765 			    set_fat(fs, prev, -1);
766 			else
767 			    MODIFY_START(owner, 0, fs);
768 			MODIFY(owner, size,
769 			       htole32((uint64_t)clusters *
770 				       fs->cluster_size));
771 			if (restart)
772 			    return 1;
773 			while (this > 0 && this != -1) {
774 			    set_owner(fs, this, NULL);
775 			    this = next_cluster(fs, this);
776 			}
777 			this = curr;
778 			break;
779 		    }
780 		    clusters++;
781 		    prev = this;
782 		}
783 		if (this != curr)
784 		    die("Internal error: didn't find cluster %d in chain"
785 			" starting at %d", curr, FSTART(owner, fs));
786 	    } else {
787 		if (prev)
788 		    set_fat(fs, prev, -1);
789 		else
790 		    MODIFY_START(file, 0, fs);
791 		break;
792 	    }
793 	}
794 	set_owner(fs, curr, file);
795 	clusters++;
796 	prev = curr;
797     }
798     if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) >
799 #ifndef __REACTOS__
800 	(uint64_t)clusters * fs->cluster_size) {
801 #else
802 	(uint64_t)clusters * fs->cluster_size && rw) {
803 #endif
804 	printf
805 	    ("%s\n  File size is %u bytes, cluster chain length is %llu bytes."
806 	     "\n  Truncating file to %llu bytes.\n", path_name(file),
807 	     le32toh(file->dir_ent.size),
808 	     (unsigned long long)clusters * fs->cluster_size,
809 	     (unsigned long long)clusters * fs->cluster_size);
810 	MODIFY(file, size,
811 	       htole32((uint64_t)clusters * fs->cluster_size));
812     }
813     return 0;
814 }
815 
816 static int check_files(DOS_FS * fs, DOS_FILE * start)
817 {
818     while (start) {
819 	if (check_file(fs, start))
820 	    return 1;
821 	start = start->next;
822     }
823     return 0;
824 }
825 
826 static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
827 {
828     DOS_FILE *parent, **walk, **scan;
829     int dot, dotdot, skip, redo;
830     int good, bad;
831 
832     if (!*root)
833 	return 0;
834     parent = (*root)->parent;
835     good = bad = 0;
836     for (walk = root; *walk; walk = &(*walk)->next)
837 	if (bad_name(*walk))
838 	    bad++;
839 	else
840 	    good++;
841     if (*root && parent && good + bad > 4 && bad > good / 2) {
842 	printf("%s\n  Has a large number of bad entries. (%d/%d)\n",
843 	       path_name(parent), bad, good + bad);
844 	if (!dots)
845 	    printf("  Not dropping root directory.\n");
846 #ifndef __REACTOS__
847 	else if (!interactive)
848 #else
849 	else if (!interactive || !rw)
850 #endif
851 	    printf("  Not dropping it in auto-mode.\n");
852 	else if (get_key("yn", "Drop directory ? (y/n)") == 'y') {
853 	    truncate_file(fs, parent, 0);
854 	    MODIFY(parent, name[0], DELETED_FLAG);
855 	    /* buglet: deleted directory stays in the list. */
856 	    return 1;
857 	}
858     }
859     dot = dotdot = redo = 0;
860     walk = root;
861     while (*walk) {
862 	if (!strncmp
863 	    ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)
864 	    || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT,
865 			MSDOS_NAME)) {
866 	    if (handle_dot(fs, *walk, dots)) {
867 		*walk = (*walk)->next;
868 		continue;
869 	    }
870 	    if (!strncmp
871 		((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME))
872 		dot++;
873 	    else
874 		dotdot++;
875 	}
876 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
877 #ifndef __REACTOS__
878 	    puts(path_name(*walk));
879 	    printf("  Bad short file name (%s).\n",
880 		   file_name((*walk)->dir_ent.name));
881 #else
882 	    printf("%s\n  Bad short file name (%s).\n",
883 		   path_name(*walk), file_name((*walk)->dir_ent.name));
884 #endif
885 	    if (interactive)
886 		printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
887 		       "4) Keep it\n");
888 	    else
889 #ifdef __REACTOS__
890 	    if (rw)
891 #endif
892 		printf("  Auto-renaming it.\n");
893 #ifdef __REACTOS__
894 	    if (rw || interactive) {
895 #endif
896 	    switch (interactive ? get_key("1234", "?") : '3') {
897 	    case '1':
898 		drop_file(fs, *walk);
899 		walk = &(*walk)->next;
900 		continue;
901 	    case '2':
902 		rename_file(*walk);
903 		redo = 1;
904 		break;
905 	    case '3':
906 		auto_rename(*walk);
907 		printf("  Renamed to %s\n", file_name((*walk)->dir_ent.name));
908 		break;
909 	    case '4':
910 		break;
911 #ifdef __REACTOS__
912 		    }
913 #endif
914 	    }
915 	}
916 	/* don't check for duplicates of the volume label */
917 	if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
918 	    scan = &(*walk)->next;
919 	    skip = 0;
920 	    while (*scan && !skip) {
921 		if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
922 		    !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
923 			    MSDOS_NAME)) {
924 		    printf("%s\n  Duplicate directory entry.\n  First  %s\n",
925 			   path_name(*walk), file_stat(*walk));
926 		    printf("  Second %s\n", file_stat(*scan));
927 		    if (interactive)
928 			printf
929 			    ("1) Drop first\n2) Drop second\n3) Rename first\n"
930 			     "4) Rename second\n5) Auto-rename first\n"
931 			     "6) Auto-rename second\n");
932 		    else
933 #ifdef __REACTOS__
934 		    if (rw)
935 #endif
936 			printf("  Auto-renaming second.\n");
937 #ifdef __REACTOS__
938 		    if (rw || interactive) {
939 #endif
940 		    switch (interactive ? get_key("123456", "?") : '6') {
941 		    case '1':
942 			drop_file(fs, *walk);
943 			*walk = (*walk)->next;
944 			skip = 1;
945 			break;
946 		    case '2':
947 			drop_file(fs, *scan);
948 			*scan = (*scan)->next;
949 			continue;
950 		    case '3':
951 			rename_file(*walk);
952 			printf("  Renamed to %s\n", path_name(*walk));
953 			redo = 1;
954 			break;
955 		    case '4':
956 			rename_file(*scan);
957 			printf("  Renamed to %s\n", path_name(*walk));
958 			redo = 1;
959 			break;
960 		    case '5':
961 			auto_rename(*walk);
962 			printf("  Renamed to %s\n",
963 			       file_name((*walk)->dir_ent.name));
964 			break;
965 		    case '6':
966 			auto_rename(*scan);
967 			printf("  Renamed to %s\n",
968 			       file_name((*scan)->dir_ent.name));
969 			break;
970 #ifdef __REACTOS__
971 			    }
972 #endif
973 		    }
974 		}
975 		scan = &(*scan)->next;
976 	    }
977 	    if (skip)
978 		continue;
979 	}
980 	if (!redo)
981 	    walk = &(*walk)->next;
982 	else {
983 	    walk = root;
984 	    dot = dotdot = redo = 0;
985 	}
986     }
987     if (dots && !dot)
988 	printf("%s\n  \".\" is missing. Can't fix this yet.\n",
989 	       path_name(parent));
990     if (dots && !dotdot)
991 	printf("%s\n  \"..\" is missing. Can't fix this yet.\n",
992 	       path_name(parent));
993     return 0;
994 }
995 
996 /**
997  * Check a dentry's cluster chain for bad clusters.
998  * If requested, we verify readability and mark unreadable clusters as bad.
999  *
1000  * @param[inout]    fs          Information about the filesystem
1001  * @param[in]       file        dentry to check
1002  * @param[in]       read_test   Nonzero == verify that dentry's clusters can
1003  *                              be read
1004  */
1005 static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
1006 {
1007     DOS_FILE *owner;
1008     uint32_t walk, prev, clusters, next_clu;
1009 
1010     prev = clusters = 0;
1011     for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
1012 	 walk = next_clu) {
1013 	next_clu = next_cluster(fs, walk);
1014 
1015 	/* In this stage we are checking only for a loop within our own
1016 	 * cluster chain.
1017 	 * Cross-linking of clusters is handled in check_file()
1018 	 */
1019 	if ((owner = get_owner(fs, walk))) {
1020 	    if (owner == file) {
1021 		printf("%s\n  Circular cluster chain. Truncating to %lu "
1022 		       "cluster%s.\n", path_name(file), (unsigned long)clusters,
1023 		       clusters == 1 ? "" : "s");
1024 		if (prev)
1025 		    set_fat(fs, prev, -1);
1026 		else if (!file->offset)
1027 		    die("Bad FAT32 root directory! (bad start cluster)\n");
1028 		else
1029 		    MODIFY_START(file, 0, fs);
1030 	    }
1031 	    break;
1032 	}
1033 	if (bad_cluster(fs, walk))
1034 	    break;
1035 	if (read_test) {
1036 	    if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
1037 		prev = walk;
1038 		clusters++;
1039 	    } else {
1040 		printf("%s\n  Cluster %lu (%lu) is unreadable. Skipping it.\n",
1041 		       path_name(file), (unsigned long)clusters, (unsigned long)walk);
1042 		if (prev)
1043 		    set_fat(fs, prev, next_cluster(fs, walk));
1044 		else
1045 		    MODIFY_START(file, next_cluster(fs, walk), fs);
1046 		set_fat(fs, walk, -2);
1047 	    }
1048 	} else {
1049 	    prev = walk;
1050 	    clusters++;
1051 	}
1052 	set_owner(fs, walk, file);
1053     }
1054     /* Revert ownership (for now) */
1055     for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2;
1056 	 walk = next_cluster(fs, walk))
1057 	if (bad_cluster(fs, walk))
1058 	    break;
1059 	else if (get_owner(fs, walk) == file)
1060 	    set_owner(fs, walk, NULL);
1061 	else
1062 	    break;
1063 }
1064 
1065 static void undelete(DOS_FS * fs, DOS_FILE * file)
1066 {
1067     uint32_t clusters, left, prev, walk;
1068 
1069     clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) /
1070 	fs->cluster_size;
1071     prev = 0;
1072 
1073     walk = FSTART(file, fs);
1074 
1075     while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) {
1076 
1077 	FAT_ENTRY curEntry;
1078 	get_fat(&curEntry, fs->fat, walk, fs);
1079 
1080 	if (!curEntry.value)
1081 	    break;
1082 
1083 	left--;
1084 	if (prev)
1085 	    set_fat(fs, prev, walk);
1086 	prev = walk;
1087 	walk++;
1088     }
1089     if (prev)
1090 	set_fat(fs, prev, -1);
1091     else
1092 	MODIFY_START(file, 0, fs);
1093     if (left)
1094 	printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
1095 	       (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s");
1096 
1097 }
1098 
1099 static void new_dir(void)
1100 {
1101     lfn_reset();
1102 }
1103 
1104 /**
1105  * Create a description for a referenced dentry and insert it in our dentry
1106  * tree. Then, go check the dentry's cluster chain for bad clusters and
1107  * cluster loops.
1108  *
1109  * @param[inout]    fs      Information about the filesystem
1110  * @param[out]      chain
1111  * @param[in]       parent  Information about parent directory of this file
1112  *                          NULL == no parent ('file' is root directory)
1113  * @param[in]       offset  Partition-relative byte offset of directory entry of interest
1114  *                          0 == Root directory
1115  * @param           cp
1116  */
1117 static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
1118 		     off_t offset, FDSC ** cp)
1119 {
1120     DOS_FILE *new;
1121     DIR_ENT de;
1122     FD_TYPE type;
1123 
1124     if (offset)
1125 	fs_read(offset, sizeof(DIR_ENT), &de);
1126     else {
1127 	/* Construct a DIR_ENT for the root directory */
1128 	memset(&de, 0, sizeof de);
1129 	memcpy(de.name, "           ", MSDOS_NAME);
1130 	de.attr = ATTR_DIR;
1131 	de.start = htole16(fs->root_cluster & 0xffff);
1132 	de.starthi = htole16((fs->root_cluster >> 16) & 0xffff);
1133     }
1134     if ((type = file_type(cp, (char *)de.name)) != fdt_none) {
1135 	if (type == fdt_undelete && (de.attr & ATTR_DIR))
1136 	    die("Can't undelete directories.");
1137 	file_modify(cp, (char *)de.name);
1138 	fs_write(offset, 1, &de);
1139     }
1140     if (IS_FREE(de.name)) {
1141 	lfn_check_orphaned();
1142 	return;
1143     }
1144     if (de.attr == VFAT_LN_ATTR) {
1145 	lfn_add_slot(&de, offset);
1146 	return;
1147     }
1148     new = qalloc(&mem_queue, sizeof(DOS_FILE));
1149     new->lfn = lfn_get(&de, &new->lfn_offset);
1150     new->offset = offset;
1151     memcpy(&new->dir_ent, &de, sizeof(de));
1152     new->next = new->first = NULL;
1153     new->parent = parent;
1154     if (type == fdt_undelete)
1155 	undelete(fs, new);
1156     **chain = new;
1157     *chain = &new->next;
1158     if (list) {
1159 	printf("Checking file %s", path_name(new));
1160 	if (new->lfn)
1161 	    printf(" (%s)", file_name(new->dir_ent.name));	/* (8.3) */
1162 	printf("\n");
1163     }
1164     /* Don't include root directory, '.', or '..' in the total file count */
1165     if (offset &&
1166 	strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 &&
1167 	strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0)
1168 	++n_files;
1169     test_file(fs, new, test);	/* Bad cluster check */
1170 }
1171 
1172 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
1173 
1174 static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
1175 {
1176     DOS_FILE **chain;
1177     int i;
1178     uint32_t clu_num;
1179 
1180     chain = &this->first;
1181     i = 0;
1182     clu_num = FSTART(this, fs);
1183     new_dir();
1184     while (clu_num > 0 && clu_num != -1) {
1185 	add_file(fs, &chain, this,
1186 		 cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
1187 	i += sizeof(DIR_ENT);
1188 	if (!(i % fs->cluster_size))
1189 	    if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
1190 		break;
1191     }
1192     lfn_check_orphaned();
1193     if (check_dir(fs, &this->first, this->offset))
1194 	return 0;
1195     if (check_files(fs, this->first))
1196 	return 1;
1197     return subdirs(fs, this, cp);
1198 }
1199 
1200 /**
1201  * Recursively scan subdirectories of the specified parent directory.
1202  *
1203  * @param[inout]    fs      Information about the filesystem
1204  * @param[in]       parent  Identifies the directory to scan
1205  * @param[in]       cp
1206  *
1207  * @return  0   Success
1208  * @return  1   Error
1209  */
1210 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
1211 {
1212     DOS_FILE *walk;
1213 
1214     for (walk = parent ? parent->first : root; walk; walk = walk->next)
1215 	if (walk->dir_ent.attr & ATTR_DIR)
1216 	    if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME)
1217 		&& strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT,
1218 			   MSDOS_NAME))
1219 		if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
1220 		    return 1;
1221     return 0;
1222 }
1223 
1224 /**
1225  * Scan all directory and file information for errors.
1226  *
1227  * @param[inout]    fs      Information about the filesystem
1228  *
1229  * @return  0   Success
1230  * @return  1   Error
1231  */
1232 int scan_root(DOS_FS * fs)
1233 {
1234     DOS_FILE **chain;
1235     int i;
1236 
1237     root = NULL;
1238     chain = &root;
1239     new_dir();
1240     if (fs->root_cluster) {
1241 	add_file(fs, &chain, NULL, 0, &fp_root);
1242     } else {
1243 	for (i = 0; i < fs->root_entries; i++)
1244 	    add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
1245 		     &fp_root);
1246     }
1247     lfn_check_orphaned();
1248     (void)check_dir(fs, &root, 0);
1249     if (check_files(fs, root))
1250 	return 1;
1251     return subdirs(fs, NULL, &fp_root);
1252 }
1253