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