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