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