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