[VFATLIB]
[reactos.git] / reactos / lib / fslib / vfatlib / check / check.c
1 /* check.c - Check and repair a PC/MS-DOS file system */
2
3 /* Written 1993 by Werner Almesberger */
4
5 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
6 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
7
8 #include "vfatlib.h"
9
10 #define NDEBUG
11 #include <debug.h>
12
13 #include "lfn.h"
14
15 static DOS_FILE *root;
16
17 /* get start field of a dir entry */
18 #define FSTART(p,fs) \
19 ((unsigned long)CF_LE_W(p->dir_ent.start) | \
20 (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0))
21
22 #define MODIFY(p,i,v) \
23 do { \
24 if (p->offset) { \
25 p->dir_ent.i = v; \
26 fs_write(p->offset+offsetof(DIR_ENT,i), \
27 sizeof(p->dir_ent.i),&p->dir_ent.i); \
28 } \
29 } while(0)
30
31 #define MODIFY_START(p,v,fs) \
32 do { \
33 unsigned long __v = (v); \
34 if (!p->offset) { \
35 /* writing to fake entry for FAT32 root dir */ \
36 if (!__v) die("Oops, deleting FAT32 root dir!"); \
37 fs->root_cluster = __v; \
38 p->dir_ent.start = CT_LE_W(__v&0xffff); \
39 p->dir_ent.starthi = CT_LE_W(__v>>16); \
40 __v = CT_LE_L(__v); \
41 fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \
42 sizeof(((struct boot_sector *)0)->root_cluster), \
43 &__v); \
44 } \
45 else { \
46 MODIFY(p,start,CT_LE_W((__v)&0xffff)); \
47 if (fs->fat_bits == 32) \
48 MODIFY(p,starthi,CT_LE_W((__v)>>16)); \
49 } \
50 } while(0)
51
52
53 loff_t alloc_rootdir_entry(DOS_FS *fs, DIR_ENT *de, const char *pattern)
54 {
55 static int curr_num = 0;
56 loff_t offset;
57
58 if (fs->root_cluster) {
59 DIR_ENT d2;
60 int i = 0, got = 0;
61 unsigned long clu_num, prev = 0;
62 loff_t offset2;
63
64 clu_num = fs->root_cluster;
65 offset = cluster_start(fs,clu_num);
66 while (clu_num > 0 && clu_num != -1) {
67 fs_read(offset,sizeof(DIR_ENT),&d2);
68 if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
69 got = 1;
70 break;
71 }
72 i += sizeof(DIR_ENT);
73 offset += sizeof(DIR_ENT);
74 if ((i % fs->cluster_size) == 0) {
75 prev = clu_num;
76 if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
77 break;
78 offset = cluster_start(fs,clu_num);
79 }
80 }
81 if (!got) {
82 /* no free slot, need to extend root dir: alloc next free cluster
83 * after previous one */
84 if (!prev)
85 die("Root directory has no cluster allocated!");
86 for (clu_num = prev+1; clu_num != prev; clu_num++) {
87 if (clu_num >= fs->clusters+2) clu_num = 2;
88 if (!fs->fat[clu_num].value)
89 break;
90 }
91 if (clu_num == prev)
92 die("Root directory full and no free cluster");
93 set_fat(fs,prev,clu_num);
94 set_fat(fs,clu_num,-1);
95 set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
96 /* clear new cluster */
97 memset( &d2, 0, sizeof(d2) );
98 offset = cluster_start(fs,clu_num);
99 for( i = 0; i < (int)fs->cluster_size; i += sizeof(DIR_ENT) )
100 fs_write( offset+i, sizeof(d2), &d2 );
101 }
102 memset(de,0,sizeof(DIR_ENT));
103 while (1) {
104 sprintf((char*)de->name,pattern,curr_num);
105 clu_num = fs->root_cluster;
106 i = 0;
107 offset2 = cluster_start(fs,clu_num);
108 while (clu_num > 0 && clu_num != -1) {
109 fs_read(offset2,sizeof(DIR_ENT),&d2);
110 if (offset2 != offset &&
111 !strncmp((char*)d2.name,(char*)de->name,MSDOS_NAME))
112 break;
113 i += sizeof(DIR_ENT);
114 offset2 += sizeof(DIR_ENT);
115 if ((i % fs->cluster_size) == 0) {
116 if ((clu_num = next_cluster(fs,clu_num)) == 0 ||
117 clu_num == -1)
118 break;
119 offset2 = cluster_start(fs,clu_num);
120 }
121 }
122 if (clu_num == 0 || clu_num == -1)
123 break;
124 if (++curr_num >= 10000) die("Unable to create unique name");
125 }
126 }
127 else {
128 DIR_ENT *root;
129 int next_free = 0, scan;
130
131 root = vfalloc(fs->root_entries*sizeof(DIR_ENT));
132 fs_read(fs->root_start,fs->root_entries*sizeof(DIR_ENT),root);
133
134 while (next_free < (int)fs->root_entries)
135 if (IS_FREE(root[next_free].name) &&
136 root[next_free].attr != VFAT_LN_ATTR)
137 break;
138 else next_free++;
139 if (next_free == (int)fs->root_entries)
140 die("Root directory is full.");
141 offset = fs->root_start+next_free*sizeof(DIR_ENT);
142 memset(de,0,sizeof(DIR_ENT));
143 while (1) {
144 sprintf((char*)de->name,pattern,curr_num);
145 for (scan = 0; scan < (int)fs->root_entries; scan++)
146 if (scan != next_free &&
147 !strncmp((char*)root[scan].name,(char*)de->name,MSDOS_NAME))
148 break;
149 if (scan == (int)fs->root_entries) break;
150 if (++curr_num >= 10000) die("Unable to create unique name");
151 }
152 vffree(root);
153 }
154 ++FsCheckTotalFiles;
155 return offset;
156 }
157
158
159 static char *path_name(DOS_FILE *file)
160 {
161 // static char path[PATH_MAX*2];
162 static char path[MAX_PATH*2];
163
164 if (!file) *path = 0;
165 else {
166 if (strlen(path_name(file->parent)) > MAX_PATH)
167 die("Path name too long.");
168 if (strcmp(path,"/") != 0) strcat(path,"/");
169 strcpy(strrchr(path,0),file->lfn?file->lfn:file_name(file->dir_ent.name));
170 }
171 return path;
172 }
173
174
175 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
176 /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
177
178
179 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
180
181 time_t date_dos2unix(unsigned short time,unsigned short date)
182 {
183 int month,year;
184 time_t secs;
185
186 month = ((date >> 5) & 15)-1;
187 year = date >> 9;
188 secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
189 ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
190 month < 2 ? 1 : 0)+3653);
191 /* days since 1.1.70 plus 80's leap day */
192 return secs;
193 }
194
195
196 static char *file_stat(DOS_FILE *file)
197 {
198 static char temp[100];
199 char tmp[100];
200 time_t date;
201 LARGE_INTEGER time;
202 TIME_FIELDS time_fields;
203
204 date = date_dos2unix(CF_LE_W(file->dir_ent.time),CF_LE_W(file->
205 dir_ent.date));
206
207 RtlSecondsSince1970ToTime(date, &time);
208 RtlTimeToTimeFields(&time, &time_fields);
209
210 _snprintf(tmp, sizeof(tmp), "%d:%d:%d %d.%d.%d",
211 time_fields.Hour, time_fields.Minute, time_fields.Second,
212 time_fields.Day, time_fields.Month, time_fields.Year);
213
214 _snprintf(temp, sizeof(temp), " Size %u bytes, date %s",CF_LE_L(file->dir_ent.size),tmp);
215 return temp;
216 }
217
218
219 static int bad_name(unsigned char *name)
220 {
221 int i, spc, suspicious = 0;
222 char *bad_chars = "*?<>|\"\\/:";
223
224 /* Do not complain about (and auto-correct) the extended attribute files
225 * of OS/2. */
226 if (strncmp((char*)name,"EA DATA SF",11) == 0 ||
227 strncmp((char*)name,"WP ROOT SF",11) == 0) return 0;
228
229 for (i = 0; i < 8; i++) {
230 if (name[i] < ' ' || name[i] == 0x7f) return 1;
231 if (name[i] > 0x7f) ++suspicious;
232 if (strchr(bad_chars,name[i])) return 1;
233 }
234
235 for (i = 8; i < 11; i++) {
236 if (name[i] < ' ' || name[i] == 0x7f) return 1;
237 if (name[i] > 0x7f) ++suspicious;
238 if (strchr(bad_chars,name[i])) return 1;
239 }
240
241 spc = 0;
242 for (i = 0; i < 8; i++) {
243 if (name[i] == ' ')
244 spc = 1;
245 else if (spc)
246 /* non-space after a space not allowed, space terminates the name
247 * part */
248 return 1;
249 }
250
251 spc = 0;
252 for (i = 8; i < 11; i++) {
253 if (name[i] == ' ')
254 spc = 1;
255 else if (spc)
256 /* non-space after a space not allowed, space terminates the name
257 * part */
258 return 1;
259 }
260
261 /* Only complain about too much suspicious chars in interactive mode,
262 * never correct them automatically. The chars are all basically ok, so we
263 * shouldn't auto-correct such names. */
264 if ((FsCheckFlags & FSCHECK_INTERACTIVE) && suspicious > 6)
265 return 1;
266 return 0;
267 }
268
269
270 static void drop_file(DOS_FS *fs,DOS_FILE *file)
271 {
272 unsigned long cluster;
273
274 MODIFY(file,name[0],DELETED_FLAG);
275 for (cluster = FSTART(file,fs); cluster > 0 && cluster <
276 fs->clusters+2; cluster = next_cluster(fs,cluster))
277 set_owner(fs,cluster,NULL);
278 --FsCheckTotalFiles;
279 }
280
281
282 static void truncate_file(DOS_FS *fs,DOS_FILE *file,unsigned long clusters)
283 {
284 int deleting;
285 unsigned long walk,next;
286 //unsigned long prev;
287
288 walk = FSTART(file,fs);
289 //prev = 0;
290 if ((deleting = !clusters)) MODIFY_START(file,0,fs);
291 while (walk > 0 && walk != -1) {
292 next = next_cluster(fs,walk);
293 if (deleting) set_fat(fs,walk,0);
294 else if ((deleting = !--clusters)) set_fat(fs,walk,-1);
295 //prev = walk;
296 walk = next;
297 }
298 }
299
300
301 static void auto_rename(DOS_FILE *file)
302 {
303 DOS_FILE *first,*walk;
304 int number;
305
306 if (!file->offset) return; /* cannot rename FAT32 root dir */
307 first = file->parent ? file->parent->first : root;
308 number = 0;
309 while (1) {
310 sprintf((char*)file->dir_ent.name,"FSCK%04d",number);
311 strncpy((char*)file->dir_ent.ext,"REN",3);
312 for (walk = first; walk; walk = walk->next)
313 if (walk != file && !strncmp((char*)walk->dir_ent.name,(char*)file->dir_ent.
314 name,MSDOS_NAME)) break;
315 if (!walk) {
316 fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
317 return;
318 }
319 number++;
320 }
321 die("Can't generate a unique name.");
322 }
323
324
325 static void rename_file(DOS_FILE *file)
326 {
327 //unsigned char name[46];
328 //unsigned char *walk,*here;
329
330 if (!file->offset) {
331 VfatPrint( "Cannot rename FAT32 root dir\n" );
332 return; /* cannot rename FAT32 root dir */
333 }
334 while (1) {
335 VfatPrint("New name: ");
336 #if 0
337 fflush(stdout);
338 if (fgets((char*)name,45,stdin)) {
339 if ((here = (unsigned char*)strchr((char*)name,'\n'))) *here = 0;
340 for (walk = (unsigned char*)strrchr((char*)name,0); walk >= name && (*walk == ' ' ||
341 *walk == '\t'); walk--);
342 walk[1] = 0;
343 for (walk = name; *walk == ' ' || *walk == '\t'; walk++);
344 if (file_cvt(walk,file->dir_ent.name)) {
345 fs_write(file->offset,MSDOS_NAME,file->dir_ent.name);
346 return;
347 }
348 }
349 #else
350 return;
351 #endif
352 }
353 }
354
355
356 static int handle_dot(DOS_FS *fs,DOS_FILE *file,int dots)
357 {
358 char *name;
359
360 name = strncmp((char*)file->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ? ".." : ".";
361 if (!(file->dir_ent.attr & ATTR_DIR)) {
362 VfatPrint("%s\n Is a non-directory.\n",path_name(file));
363 if (FsCheckFlags & FSCHECK_INTERACTIVE)
364 VfatPrint("1) Drop it\n2) Auto-rename\n3) Rename\n"
365 "4) Convert to directory\n");
366 else VfatPrint(" Auto-renaming it.\n");
367 switch ((FsCheckFlags & FSCHECK_INTERACTIVE) ? get_key("1234","?") : '2') {
368 case '1':
369 drop_file(fs,file);
370 return 1;
371 case '2':
372 auto_rename(file);
373 VfatPrint(" Renamed to %s\n",file_name(file->dir_ent.name));
374 return 0;
375 case '3':
376 rename_file(file);
377 return 0;
378 case '4':
379 MODIFY(file,size,CT_LE_L(0));
380 MODIFY(file,attr,file->dir_ent.attr | ATTR_DIR);
381 break;
382 }
383 }
384 if (!dots) {
385 VfatPrint("Root contains directory \"%s\". Dropping it.\n",name);
386 drop_file(fs,file);
387 return 1;
388 }
389 return 0;
390 }
391
392
393 static int check_file(DOS_FS *fs,DOS_FILE *file)
394 {
395 DOS_FILE *owner;
396 int restart;
397 unsigned long expect,curr,this,clusters,prev,walk,clusters2;
398
399 if (file->dir_ent.attr & ATTR_DIR) {
400 if (CF_LE_L(file->dir_ent.size)) {
401 VfatPrint("%s\n Directory has non-zero size. Fixing it.\n",
402 path_name(file));
403 MODIFY(file,size,CT_LE_L(0));
404 }
405 if (file->parent && !strncmp((char*)file->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) {
406 expect = FSTART(file->parent,fs);
407 if (FSTART(file,fs) != expect) {
408 VfatPrint("%s\n Start (%ld) does not point to parent (%ld)\n",
409 path_name(file),FSTART(file,fs),expect);
410 MODIFY_START(file,expect,fs);
411 }
412 return 0;
413 }
414 if (file->parent && !strncmp((char*)file->dir_ent.name,MSDOS_DOTDOT,
415 MSDOS_NAME)) {
416 expect = file->parent->parent ? FSTART(file->parent->parent,fs):0;
417 if (fs->root_cluster && expect == fs->root_cluster)
418 expect = 0;
419 if (FSTART(file,fs) != expect) {
420 VfatPrint("%s\n Start (%lu) does not point to .. (%lu)\n",
421 path_name(file),FSTART(file,fs),expect);
422 MODIFY_START(file,expect,fs);
423 }
424 return 0;
425 }
426 if (FSTART(file,fs)==0){
427 VfatPrint ("%s\n Start does point to root directory. Deleting dir. \n",
428 path_name(file));
429 MODIFY(file,name[0],DELETED_FLAG);
430 return 0;
431 }
432 }
433 if (FSTART(file,fs) >= fs->clusters+2) {
434 VfatPrint("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n",
435 path_name(file),FSTART(file,fs),fs->clusters+1);
436 if (!file->offset)
437 die( "Bad FAT32 root directory! (bad start cluster)\n" );
438 MODIFY_START(file,0,fs);
439 }
440 clusters = prev = 0;
441 for (curr = FSTART(file,fs) ? FSTART(file,fs) :
442 -1; curr != -1; curr = next_cluster(fs,curr)) {
443 if (!fs->fat[curr].value || bad_cluster(fs,curr)) {
444 VfatPrint("%s\n Contains a %s cluster (%lu). Assuming EOF.\n",
445 path_name(file),fs->fat[curr].value ? "bad" : "free",curr);
446 if (prev) set_fat(fs,prev,-1);
447 else if (!file->offset)
448 die( "FAT32 root dir starts with a bad cluster!" );
449 else MODIFY_START(file,0,fs);
450 break;
451 }
452 if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
453 clusters*fs->cluster_size) {
454 VfatPrint("%s\n File size is %u bytes, cluster chain length is > %lu "
455 "bytes.\n Truncating file to %u bytes.\n",path_name(file),
456 CF_LE_L(file->dir_ent.size),clusters*fs->cluster_size,
457 CF_LE_L(file->dir_ent.size));
458 truncate_file(fs,file,clusters);
459 break;
460 }
461 if ((owner = get_owner(fs,curr))) {
462 int do_trunc = 0;
463 VfatPrint("%s and\n",path_name(owner));
464 VfatPrint("%s\n share clusters.\n",path_name(file));
465 clusters2 = 0;
466 for (walk = FSTART(owner,fs); walk > 0 && walk != -1; walk =
467 next_cluster(fs,walk))
468 if (walk == curr) break;
469 else clusters2++;
470 restart = file->dir_ent.attr & ATTR_DIR;
471 if (!owner->offset) {
472 VfatPrint( " Truncating second to %lu bytes because first "
473 "is FAT32 root dir.\n", clusters2*fs->cluster_size );
474 do_trunc = 2;
475 }
476 else if (!file->offset) {
477 VfatPrint( " Truncating first to %lu bytes because second "
478 "is FAT32 root dir.\n", clusters*fs->cluster_size );
479 do_trunc = 1;
480 }
481 else if (FsCheckFlags & FSCHECK_INTERACTIVE)
482 VfatPrint("1) Truncate first to %lu bytes%s\n"
483 "2) Truncate second to %lu bytes\n",clusters*fs->cluster_size,
484 restart ? " and restart" : "",clusters2*fs->cluster_size);
485 else VfatPrint(" Truncating second to %lu bytes.\n",clusters2*
486 fs->cluster_size);
487 if (do_trunc != 2 &&
488 (do_trunc == 1 ||
489 ((FsCheckFlags & FSCHECK_INTERACTIVE) && get_key("12","?") == '1'))) {
490 prev = 0;
491 clusters = 0;
492 for (this = FSTART(owner,fs); this > 0 && this != -1; this =
493 next_cluster(fs,this)) {
494 if (this == curr) {
495 if (prev) set_fat(fs,prev,-1);
496 else MODIFY_START(owner,0,fs);
497 MODIFY(owner,size,CT_LE_L(clusters*fs->cluster_size));
498 if (restart) return 1;
499 while (this > 0 && this != -1) {
500 set_owner(fs,this,NULL);
501 this = next_cluster(fs,this);
502 }
503 break;
504 }
505 clusters++;
506 prev = this;
507 }
508 if (this != curr)
509 die("Internal error: didn't find cluster %d in chain"
510 " starting at %d",curr,FSTART(owner,fs));
511 }
512 else {
513 if (prev) set_fat(fs,prev,-1);
514 else MODIFY_START(file,0,fs);
515 break;
516 }
517 }
518 set_owner(fs,curr,file);
519 clusters++;
520 prev = curr;
521 }
522 if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
523 clusters*fs->cluster_size) {
524 VfatPrint("%s\n File size is %u bytes, cluster chain length is %lu bytes."
525 "\n Truncating file to %lu bytes.\n",path_name(file),CF_LE_L(file->
526 dir_ent.size),clusters*fs->cluster_size,clusters*fs->cluster_size);
527 MODIFY(file,size,CT_LE_L(clusters*fs->cluster_size));
528 }
529 return 0;
530 }
531
532
533 static int check_files(DOS_FS *fs,DOS_FILE *start)
534 {
535 while (start) {
536 if (check_file(fs,start)) return 1;
537 start = start->next;
538 }
539 return 0;
540 }
541
542
543 static int check_dir(DOS_FS *fs,DOS_FILE **root,int dots)
544 {
545 DOS_FILE *parent,**walk,**scan;
546 int dot,dotdot,skip,redo;
547 int good,bad;
548
549 if (!*root) return 0;
550 parent = (*root)->parent;
551 good = bad = 0;
552 for (walk = root; *walk; walk = &(*walk)->next)
553 if (bad_name((*walk)->dir_ent.name)) bad++;
554 else good++;
555 if (*root && parent && good+bad > 4 && bad > good/2) {
556 VfatPrint("%s\n Has a large number of bad entries. (%d/%d)\n",
557 path_name(parent),bad,good+bad);
558 if (!dots) VfatPrint( " Not dropping root directory.\n" );
559 else if (!(FsCheckFlags & FSCHECK_INTERACTIVE)) VfatPrint(" Not dropping it in auto-mode.\n");
560 else if (get_key("yn","Drop directory ? (y/n)") == 'y') {
561 truncate_file(fs,parent,0);
562 MODIFY(parent,name[0],DELETED_FLAG);
563 /* buglet: deleted directory stays in the list. */
564 return 1;
565 }
566 }
567 dot = dotdot = redo = 0;
568 walk = root;
569 while (*walk) {
570 if (!strncmp((char*)(*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ||
571 !strncmp((char*)(*walk)->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) {
572 if (handle_dot(fs,*walk,dots)) {
573 *walk = (*walk)->next;
574 continue;
575 }
576 if (!strncmp((char*)(*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) dot++;
577 else dotdot++;
578 }
579 if (!((*walk)->dir_ent.attr & ATTR_VOLUME) &&
580 bad_name((*walk)->dir_ent.name)) {
581 VfatPrint("%s\n Bad file name.\n",path_name(*walk));
582 if (FsCheckFlags & FSCHECK_INTERACTIVE)
583 VfatPrint("1) Drop file\n2) Rename file\n3) Auto-rename\n"
584 "4) Keep it\n");
585 else VfatPrint(" Auto-renaming it.\n");
586 switch ((FsCheckFlags & FSCHECK_INTERACTIVE) ? get_key("1234","?") : '3') {
587 case '1':
588 drop_file(fs,*walk);
589 walk = &(*walk)->next;
590 continue;
591 case '2':
592 rename_file(*walk);
593 redo = 1;
594 break;
595 case '3':
596 auto_rename(*walk);
597 VfatPrint(" Renamed to %s\n",file_name((*walk)->dir_ent.
598 name));
599 break;
600 case '4':
601 break;
602 }
603 }
604 /* don't check for duplicates of the volume label */
605 if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
606 scan = &(*walk)->next;
607 skip = 0;
608 while (*scan && !skip) {
609 if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
610 !strncmp((char*)(*walk)->dir_ent.name,(char*)(*scan)->dir_ent.name,MSDOS_NAME)) {
611 VfatPrint("%s\n Duplicate directory entry.\n First %s\n",
612 path_name(*walk),file_stat(*walk));
613 VfatPrint(" Second %s\n",file_stat(*scan));
614 if (FsCheckFlags & FSCHECK_INTERACTIVE)
615 VfatPrint("1) Drop first\n2) Drop second\n3) Rename first\n"
616 "4) Rename second\n5) Auto-rename first\n"
617 "6) Auto-rename second\n");
618 else VfatPrint(" Auto-renaming second.\n");
619 switch ((FsCheckFlags & FSCHECK_INTERACTIVE) ? get_key("123456","?") : '6') {
620 case '1':
621 drop_file(fs,*walk);
622 *walk = (*walk)->next;
623 skip = 1;
624 break;
625 case '2':
626 drop_file(fs,*scan);
627 *scan = (*scan)->next;
628 continue;
629 case '3':
630 rename_file(*walk);
631 VfatPrint(" Renamed to %s\n",path_name(*walk));
632 redo = 1;
633 break;
634 case '4':
635 rename_file(*scan);
636 VfatPrint(" Renamed to %s\n",path_name(*walk));
637 redo = 1;
638 break;
639 case '5':
640 auto_rename(*walk);
641 VfatPrint(" Renamed to %s\n",file_name((*walk)->dir_ent.
642 name));
643 break;
644 case '6':
645 auto_rename(*scan);
646 VfatPrint(" Renamed to %s\n",file_name((*scan)->dir_ent.
647 name));
648 break;
649 }
650 }
651 scan = &(*scan)->next;
652 }
653 if (skip) continue;
654 }
655 if (!redo) walk = &(*walk)->next;
656 else {
657 walk = root;
658 dot = dotdot = redo = 0;
659 }
660 }
661 if (dots && !dot)
662 VfatPrint("%s\n \".\" is missing. Can't fix this yet.\n",
663 path_name(parent));
664 if (dots && !dotdot)
665 VfatPrint("%s\n \"..\" is missing. Can't fix this yet.\n",
666 path_name(parent));
667 return 0;
668 }
669
670
671 static void test_file(DOS_FS *fs,DOS_FILE *file,int read_test)
672 {
673 DOS_FILE *owner;
674 unsigned long walk,prev,clusters,next_clu;
675
676 prev = clusters = 0;
677 for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
678 walk = next_clu) {
679 next_clu = next_cluster(fs,walk);
680 if ((owner = get_owner(fs,walk))) {
681 if (owner == file) {
682 VfatPrint("%s\n Circular cluster chain. Truncating to %lu "
683 "cluster%s.\n",path_name(file),clusters,clusters == 1 ? "" :
684 "s");
685 if (prev) set_fat(fs,prev,-1);
686 else if (!file->offset)
687 die( "Bad FAT32 root directory! (bad start cluster)\n" );
688 else MODIFY_START(file,0,fs);
689 }
690 break;
691 }
692 if (bad_cluster(fs,walk)) break;
693 if (read_test) {
694 if (fs_test(cluster_start(fs,walk),fs->cluster_size)) {
695 prev = walk;
696 clusters++;
697 }
698 else {
699 VfatPrint("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n",
700 path_name(file),clusters,walk);
701 if (prev) set_fat(fs,prev,next_cluster(fs,walk));
702 else MODIFY_START(file,next_cluster(fs,walk),fs);
703 set_fat(fs,walk,-2);
704 }
705 }
706 set_owner(fs,walk,file);
707 }
708 for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2;
709 walk = next_cluster(fs,walk))
710 if (bad_cluster(fs,walk)) break;
711 else if (get_owner(fs,walk) == file) set_owner(fs,walk,NULL);
712 else break;
713 }
714
715
716 static void undelete(DOS_FS *fs,DOS_FILE *file)
717 {
718 unsigned long clusters,left,prev,walk;
719
720 clusters = left = (CF_LE_L(file->dir_ent.size)+fs->cluster_size-1)/
721 fs->cluster_size;
722 prev = 0;
723 for (walk = FSTART(file,fs); left && walk >= 2 && walk <
724 fs->clusters+2 && !fs->fat[walk].value; walk++) {
725 left--;
726 if (prev) set_fat(fs,prev,walk);
727 prev = walk;
728 }
729 if (prev) set_fat(fs,prev,-1);
730 else MODIFY_START(file,0,fs);
731 if (left)
732 VfatPrint("Warning: Did only undelete %lu of %lu cluster%s.\n",clusters-left,
733 clusters,clusters == 1 ? "" : "s");
734
735 }
736
737
738 static void new_dir( void )
739 {
740 lfn_reset();
741 }
742
743
744 static void add_file(DOS_FS *fs,DOS_FILE ***chain,DOS_FILE *parent,
745 loff_t offset,FDSC **cp)
746 {
747 DOS_FILE *new;
748 DIR_ENT de;
749 FD_TYPE type;
750
751 if (offset) {
752 fs_read(offset,sizeof(DIR_ENT),&de);
753 } else {
754 memcpy(de.name," ",MSDOS_NAME);
755 de.attr = ATTR_DIR;
756 de.size = de.time = de.date = 0;
757 de.start = CT_LE_W(fs->root_cluster & 0xffff);
758 de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff);
759 }
760 if ((type = file_type(cp,(char*)de.name)) != fdt_none) {
761 if (type == fdt_undelete && (de.attr & ATTR_DIR))
762 die("Can't undelete directories.");
763 file_modify(cp,de.name);
764 fs_write(offset,1,&de);
765 }
766 if (IS_FREE(de.name)) {
767 lfn_check_orphaned();
768 return;
769 }
770 if (de.attr == VFAT_LN_ATTR) {
771 lfn_add_slot(&de,offset);
772 return;
773 }
774 new = qalloc(&FsCheckMemQueue,sizeof(DOS_FILE));
775 new->lfn = lfn_get(&de);
776 new->offset = offset;
777 memcpy(&new->dir_ent,&de,sizeof(de));
778 new->next = new->first = NULL;
779 new->parent = parent;
780 if (type == fdt_undelete) undelete(fs,new);
781 **chain = new;
782 *chain = &new->next;
783 if (FsCheckFlags & FSCHECK_LIST_FILES) {
784 VfatPrint("Checking file %s",path_name(new));
785 if (new->lfn)
786 VfatPrint(" (%s)", file_name(new->dir_ent.name) );
787 VfatPrint("\n");
788 }
789 if (offset &&
790 strncmp((char*)de.name,MSDOS_DOT,MSDOS_NAME) != 0 &&
791 strncmp((char*)de.name,MSDOS_DOTDOT,MSDOS_NAME) != 0)
792 ++FsCheckTotalFiles;
793 test_file(fs,new,FsCheckFlags & FSCHECK_TEST_READ);
794 }
795
796
797 static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp);
798
799
800 static int scan_dir(DOS_FS *fs,DOS_FILE *this,FDSC **cp)
801 {
802 DOS_FILE **chain;
803 int i;
804 unsigned long clu_num;
805
806 chain = &this->first;
807 i = 0;
808 clu_num = FSTART(this,fs);
809 new_dir();
810 while (clu_num > 0 && clu_num != -1) {
811 add_file(fs,&chain,this,cluster_start(fs,clu_num)+(i % fs->
812 cluster_size),cp);
813 i += sizeof(DIR_ENT);
814 if (!(i % fs->cluster_size))
815 if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1)
816 break;
817 }
818 lfn_check_orphaned();
819 if (check_dir(fs,&this->first,this->offset)) return 0;
820 if (check_files(fs,this->first)) return 1;
821 return subdirs(fs,this,cp);
822 }
823
824
825 static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp)
826 {
827 DOS_FILE *walk;
828
829 for (walk = parent ? parent->first : root; walk; walk = walk->next)
830 if (walk->dir_ent.attr & ATTR_DIR)
831 if (strncmp((char*)walk->dir_ent.name,MSDOS_DOT,MSDOS_NAME) &&
832 strncmp((char*)walk->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME))
833 if (scan_dir(fs,walk,file_cd(cp,(char*)walk->dir_ent.name))) return 1;
834 return 0;
835 }
836
837
838 int scan_root(DOS_FS *fs)
839 {
840 DOS_FILE **chain;
841 int i;
842
843 root = NULL;
844 chain = &root;
845 new_dir();
846 if (fs->root_cluster) {
847 add_file(fs,&chain,NULL,0,&fp_root);
848 }
849 else {
850 for (i = 0; i < fs->root_entries; i++)
851 add_file(fs,&chain,NULL,fs->root_start+i*sizeof(DIR_ENT),&fp_root);
852 }
853 lfn_check_orphaned();
854 (void) check_dir(fs,&root,0);
855 if (check_files(fs,root)) return 1;
856 return subdirs(fs,NULL,&fp_root);
857 }
858
859 /* Local Variables: */
860 /* tab-width: 8 */
861 /* End: */