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