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