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