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 set_fat(fs, prev, -1); 670 else if (!file->offset) 671 die("FAT32 root dir starts with a bad cluster!"); 672 else 673 #ifdef __REACTOS__ 674 if (rw) 675 #endif 676 MODIFY_START(file, 0, fs); 677 break; 678 } 679 if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <= 680 (uint64_t)clusters * fs->cluster_size) { 681 #ifdef __REACTOS__ 682 if (rw) { 683 #endif 684 printf 685 ("%s\n File size is %u bytes, cluster chain length is > %llu " 686 "bytes.\n Truncating file to %u bytes.\n", path_name(file), 687 le32toh(file->dir_ent.size), 688 (unsigned long long)clusters * fs->cluster_size, 689 le32toh(file->dir_ent.size)); 690 truncate_file(fs, file, clusters); 691 #ifdef __REACTOS__ 692 } else { 693 printf 694 ("%s\n File size is %u bytes, cluster chain length is > %llu " 695 "bytes.\n", path_name(file), 696 le32toh(file->dir_ent.size), 697 (unsigned long long)clusters * fs->cluster_size); 698 } 699 #endif 700 break; 701 } 702 if ((owner = get_owner(fs, curr))) { 703 int do_trunc = 0; 704 printf("%s and\n", path_name(owner)); 705 printf("%s\n share clusters.\n", path_name(file)); 706 clusters2 = 0; 707 for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk = 708 next_cluster(fs, walk)) 709 if (walk == curr) 710 break; 711 else 712 clusters2++; 713 restart = file->dir_ent.attr & ATTR_DIR; 714 #ifndef __REACTOS__ 715 if (!owner->offset) { 716 #else 717 if (!owner->offset && rw) { 718 #endif 719 printf(" Truncating second to %llu bytes because first " 720 "is FAT32 root dir.\n", 721 (unsigned long long)clusters * fs->cluster_size); 722 do_trunc = 2; 723 #ifndef __REACTOS__ 724 } else if (!file->offset) { 725 #else 726 } else if (!file->offset && rw) { 727 #endif 728 printf(" Truncating first to %llu bytes because second " 729 "is FAT32 root dir.\n", 730 (unsigned long long)clusters2 * fs->cluster_size); 731 do_trunc = 1; 732 } else if (interactive) 733 printf("1) Truncate first to %llu bytes%s\n" 734 "2) Truncate second to %llu bytes\n", 735 (unsigned long long)clusters2 * fs->cluster_size, 736 restart ? " and restart" : "", 737 (unsigned long long)clusters * fs->cluster_size); 738 else 739 #ifdef __REACTOS__ 740 if (rw) 741 #endif 742 printf(" Truncating second to %llu bytes.\n", 743 (unsigned long long)clusters * fs->cluster_size); 744 #ifndef __REACTOS__ 745 if (do_trunc != 2 746 && (do_trunc == 1 747 #else 748 if ((do_trunc != 2 && rw) 749 && ((do_trunc == 1 && rw) 750 #endif 751 || (interactive && get_key("12", "?") == '1'))) { 752 prev = 0; 753 clusters = 0; 754 for (this = FSTART(owner, fs); this > 0 && this != -1; this = 755 next_cluster(fs, this)) { 756 if (this == curr) { 757 if (prev) 758 set_fat(fs, prev, -1); 759 else 760 MODIFY_START(owner, 0, fs); 761 MODIFY(owner, size, 762 htole32((uint64_t)clusters * 763 fs->cluster_size)); 764 if (restart) 765 return 1; 766 while (this > 0 && this != -1) { 767 set_owner(fs, this, NULL); 768 this = next_cluster(fs, this); 769 } 770 this = curr; 771 break; 772 } 773 clusters++; 774 prev = this; 775 } 776 if (this != curr) 777 die("Internal error: didn't find cluster %d in chain" 778 " starting at %d", curr, FSTART(owner, fs)); 779 } else { 780 if (prev) 781 set_fat(fs, prev, -1); 782 else 783 MODIFY_START(file, 0, fs); 784 break; 785 } 786 } 787 set_owner(fs, curr, file); 788 clusters++; 789 prev = curr; 790 } 791 if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) > 792 #ifndef __REACTOS__ 793 (uint64_t)clusters * fs->cluster_size) { 794 #else 795 (uint64_t)clusters * fs->cluster_size && rw) { 796 #endif 797 printf 798 ("%s\n File size is %u bytes, cluster chain length is %llu bytes." 799 "\n Truncating file to %llu bytes.\n", path_name(file), 800 le32toh(file->dir_ent.size), 801 (unsigned long long)clusters * fs->cluster_size, 802 (unsigned long long)clusters * fs->cluster_size); 803 MODIFY(file, size, 804 htole32((uint64_t)clusters * fs->cluster_size)); 805 } 806 return 0; 807 } 808 809 static int check_files(DOS_FS * fs, DOS_FILE * start) 810 { 811 while (start) { 812 if (check_file(fs, start)) 813 return 1; 814 start = start->next; 815 } 816 return 0; 817 } 818 819 static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots) 820 { 821 DOS_FILE *parent, **walk, **scan; 822 int dot, dotdot, skip, redo; 823 int good, bad; 824 825 if (!*root) 826 return 0; 827 parent = (*root)->parent; 828 good = bad = 0; 829 for (walk = root; *walk; walk = &(*walk)->next) 830 if (bad_name(*walk)) 831 bad++; 832 else 833 good++; 834 if (*root && parent && good + bad > 4 && bad > good / 2) { 835 printf("%s\n Has a large number of bad entries. (%d/%d)\n", 836 path_name(parent), bad, good + bad); 837 if (!dots) 838 printf(" Not dropping root directory.\n"); 839 #ifndef __REACTOS__ 840 else if (!interactive) 841 #else 842 else if (!interactive || !rw) 843 #endif 844 printf(" Not dropping it in auto-mode.\n"); 845 else if (get_key("yn", "Drop directory ? (y/n)") == 'y') { 846 truncate_file(fs, parent, 0); 847 MODIFY(parent, name[0], DELETED_FLAG); 848 /* buglet: deleted directory stays in the list. */ 849 return 1; 850 } 851 } 852 dot = dotdot = redo = 0; 853 walk = root; 854 while (*walk) { 855 if (!strncmp 856 ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME) 857 || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT, 858 MSDOS_NAME)) { 859 if (handle_dot(fs, *walk, dots)) { 860 *walk = (*walk)->next; 861 continue; 862 } 863 if (!strncmp 864 ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)) 865 dot++; 866 else 867 dotdot++; 868 } 869 if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) { 870 #ifndef __REACTOS__ 871 puts(path_name(*walk)); 872 printf(" Bad short file name (%s).\n", 873 file_name((*walk)->dir_ent.name)); 874 #else 875 printf("%s\n Bad short file name (%s).\n", 876 path_name(*walk), file_name((*walk)->dir_ent.name)); 877 #endif 878 if (interactive) 879 printf("1) Drop file\n2) Rename file\n3) Auto-rename\n" 880 "4) Keep it\n"); 881 else 882 #ifdef __REACTOS__ 883 if (rw) 884 #endif 885 printf(" Auto-renaming it.\n"); 886 #ifdef __REACTOS__ 887 if (rw || interactive) { 888 #endif 889 switch (interactive ? get_key("1234", "?") : '3') { 890 case '1': 891 drop_file(fs, *walk); 892 walk = &(*walk)->next; 893 continue; 894 case '2': 895 rename_file(*walk); 896 redo = 1; 897 break; 898 case '3': 899 auto_rename(*walk); 900 printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name)); 901 break; 902 case '4': 903 break; 904 #ifdef __REACTOS__ 905 } 906 #endif 907 } 908 } 909 /* don't check for duplicates of the volume label */ 910 if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) { 911 scan = &(*walk)->next; 912 skip = 0; 913 while (*scan && !skip) { 914 if (!((*scan)->dir_ent.attr & ATTR_VOLUME) && 915 !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name, 916 MSDOS_NAME)) { 917 printf("%s\n Duplicate directory entry.\n First %s\n", 918 path_name(*walk), file_stat(*walk)); 919 printf(" Second %s\n", file_stat(*scan)); 920 if (interactive) 921 printf 922 ("1) Drop first\n2) Drop second\n3) Rename first\n" 923 "4) Rename second\n5) Auto-rename first\n" 924 "6) Auto-rename second\n"); 925 else 926 #ifdef __REACTOS__ 927 if (rw) 928 #endif 929 printf(" Auto-renaming second.\n"); 930 #ifdef __REACTOS__ 931 if (rw || interactive) { 932 #endif 933 switch (interactive ? get_key("123456", "?") : '6') { 934 case '1': 935 drop_file(fs, *walk); 936 *walk = (*walk)->next; 937 skip = 1; 938 break; 939 case '2': 940 drop_file(fs, *scan); 941 *scan = (*scan)->next; 942 continue; 943 case '3': 944 rename_file(*walk); 945 printf(" Renamed to %s\n", path_name(*walk)); 946 redo = 1; 947 break; 948 case '4': 949 rename_file(*scan); 950 printf(" Renamed to %s\n", path_name(*walk)); 951 redo = 1; 952 break; 953 case '5': 954 auto_rename(*walk); 955 printf(" Renamed to %s\n", 956 file_name((*walk)->dir_ent.name)); 957 break; 958 case '6': 959 auto_rename(*scan); 960 printf(" Renamed to %s\n", 961 file_name((*scan)->dir_ent.name)); 962 break; 963 #ifdef __REACTOS__ 964 } 965 #endif 966 } 967 } 968 scan = &(*scan)->next; 969 } 970 if (skip) 971 continue; 972 } 973 if (!redo) 974 walk = &(*walk)->next; 975 else { 976 walk = root; 977 dot = dotdot = redo = 0; 978 } 979 } 980 if (dots && !dot) 981 printf("%s\n \".\" is missing. Can't fix this yet.\n", 982 path_name(parent)); 983 if (dots && !dotdot) 984 printf("%s\n \"..\" is missing. Can't fix this yet.\n", 985 path_name(parent)); 986 return 0; 987 } 988 989 /** 990 * Check a dentry's cluster chain for bad clusters. 991 * If requested, we verify readability and mark unreadable clusters as bad. 992 * 993 * @param[inout] fs Information about the filesystem 994 * @param[in] file dentry to check 995 * @param[in] read_test Nonzero == verify that dentry's clusters can 996 * be read 997 */ 998 static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) 999 { 1000 DOS_FILE *owner; 1001 uint32_t walk, prev, clusters, next_clu; 1002 1003 prev = clusters = 0; 1004 for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2; 1005 walk = next_clu) { 1006 next_clu = next_cluster(fs, walk); 1007 1008 /* In this stage we are checking only for a loop within our own 1009 * cluster chain. 1010 * Cross-linking of clusters is handled in check_file() 1011 */ 1012 if ((owner = get_owner(fs, walk))) { 1013 if (owner == file) { 1014 printf("%s\n Circular cluster chain. Truncating to %lu " 1015 "cluster%s.\n", path_name(file), (unsigned long)clusters, 1016 clusters == 1 ? "" : "s"); 1017 if (prev) 1018 set_fat(fs, prev, -1); 1019 else if (!file->offset) 1020 die("Bad FAT32 root directory! (bad start cluster)\n"); 1021 else 1022 MODIFY_START(file, 0, fs); 1023 } 1024 break; 1025 } 1026 if (bad_cluster(fs, walk)) 1027 break; 1028 if (read_test) { 1029 if (fs_test(cluster_start(fs, walk), fs->cluster_size)) { 1030 prev = walk; 1031 clusters++; 1032 } else { 1033 printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n", 1034 path_name(file), (unsigned long)clusters, (unsigned long)walk); 1035 if (prev) 1036 set_fat(fs, prev, next_cluster(fs, walk)); 1037 else 1038 MODIFY_START(file, next_cluster(fs, walk), fs); 1039 set_fat(fs, walk, -2); 1040 } 1041 } else { 1042 prev = walk; 1043 clusters++; 1044 } 1045 set_owner(fs, walk, file); 1046 } 1047 /* Revert ownership (for now) */ 1048 for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2; 1049 walk = next_cluster(fs, walk)) 1050 if (bad_cluster(fs, walk)) 1051 break; 1052 else if (get_owner(fs, walk) == file) 1053 set_owner(fs, walk, NULL); 1054 else 1055 break; 1056 } 1057 1058 static void undelete(DOS_FS * fs, DOS_FILE * file) 1059 { 1060 uint32_t clusters, left, prev, walk; 1061 1062 clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) / 1063 fs->cluster_size; 1064 prev = 0; 1065 1066 walk = FSTART(file, fs); 1067 1068 while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) { 1069 1070 FAT_ENTRY curEntry; 1071 get_fat(&curEntry, fs->fat, walk, fs); 1072 1073 if (!curEntry.value) 1074 break; 1075 1076 left--; 1077 if (prev) 1078 set_fat(fs, prev, walk); 1079 prev = walk; 1080 walk++; 1081 } 1082 if (prev) 1083 set_fat(fs, prev, -1); 1084 else 1085 MODIFY_START(file, 0, fs); 1086 if (left) 1087 printf("Warning: Did only undelete %lu of %lu cluster%s.\n", 1088 (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s"); 1089 1090 } 1091 1092 static void new_dir(void) 1093 { 1094 lfn_reset(); 1095 } 1096 1097 /** 1098 * Create a description for a referenced dentry and insert it in our dentry 1099 * tree. Then, go check the dentry's cluster chain for bad clusters and 1100 * cluster loops. 1101 * 1102 * @param[inout] fs Information about the filesystem 1103 * @param[out] chain 1104 * @param[in] parent Information about parent directory of this file 1105 * NULL == no parent ('file' is root directory) 1106 * @param[in] offset Partition-relative byte offset of directory entry of interest 1107 * 0 == Root directory 1108 * @param cp 1109 */ 1110 static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent, 1111 off_t offset, FDSC ** cp) 1112 { 1113 DOS_FILE *new; 1114 DIR_ENT de; 1115 FD_TYPE type; 1116 1117 if (offset) 1118 fs_read(offset, sizeof(DIR_ENT), &de); 1119 else { 1120 /* Construct a DIR_ENT for the root directory */ 1121 memset(&de, 0, sizeof de); 1122 memcpy(de.name, " ", MSDOS_NAME); 1123 de.attr = ATTR_DIR; 1124 de.start = htole16(fs->root_cluster & 0xffff); 1125 de.starthi = htole16((fs->root_cluster >> 16) & 0xffff); 1126 } 1127 if ((type = file_type(cp, (char *)de.name)) != fdt_none) { 1128 if (type == fdt_undelete && (de.attr & ATTR_DIR)) 1129 die("Can't undelete directories."); 1130 file_modify(cp, (char *)de.name); 1131 fs_write(offset, 1, &de); 1132 } 1133 if (IS_FREE(de.name)) { 1134 lfn_check_orphaned(); 1135 return; 1136 } 1137 if (de.attr == VFAT_LN_ATTR) { 1138 lfn_add_slot(&de, offset); 1139 return; 1140 } 1141 new = qalloc(&mem_queue, sizeof(DOS_FILE)); 1142 new->lfn = lfn_get(&de, &new->lfn_offset); 1143 new->offset = offset; 1144 memcpy(&new->dir_ent, &de, sizeof(de)); 1145 new->next = new->first = NULL; 1146 new->parent = parent; 1147 if (type == fdt_undelete) 1148 undelete(fs, new); 1149 **chain = new; 1150 *chain = &new->next; 1151 if (list) { 1152 printf("Checking file %s", path_name(new)); 1153 if (new->lfn) 1154 printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */ 1155 printf("\n"); 1156 } 1157 /* Don't include root directory, '.', or '..' in the total file count */ 1158 if (offset && 1159 strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 && 1160 strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0) 1161 ++n_files; 1162 test_file(fs, new, test); /* Bad cluster check */ 1163 } 1164 1165 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp); 1166 1167 static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp) 1168 { 1169 DOS_FILE **chain; 1170 int i; 1171 uint32_t clu_num; 1172 1173 chain = &this->first; 1174 i = 0; 1175 clu_num = FSTART(this, fs); 1176 new_dir(); 1177 while (clu_num > 0 && clu_num != -1) { 1178 add_file(fs, &chain, this, 1179 cluster_start(fs, clu_num) + (i % fs->cluster_size), cp); 1180 i += sizeof(DIR_ENT); 1181 if (!(i % fs->cluster_size)) 1182 if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1) 1183 break; 1184 } 1185 lfn_check_orphaned(); 1186 if (check_dir(fs, &this->first, this->offset)) 1187 return 0; 1188 if (check_files(fs, this->first)) 1189 return 1; 1190 return subdirs(fs, this, cp); 1191 } 1192 1193 /** 1194 * Recursively scan subdirectories of the specified parent directory. 1195 * 1196 * @param[inout] fs Information about the filesystem 1197 * @param[in] parent Identifies the directory to scan 1198 * @param[in] cp 1199 * 1200 * @return 0 Success 1201 * @return 1 Error 1202 */ 1203 static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp) 1204 { 1205 DOS_FILE *walk; 1206 1207 for (walk = parent ? parent->first : root; walk; walk = walk->next) 1208 if (walk->dir_ent.attr & ATTR_DIR) 1209 if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME) 1210 && strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT, 1211 MSDOS_NAME)) 1212 if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name))) 1213 return 1; 1214 return 0; 1215 } 1216 1217 /** 1218 * Scan all directory and file information for errors. 1219 * 1220 * @param[inout] fs Information about the filesystem 1221 * 1222 * @return 0 Success 1223 * @return 1 Error 1224 */ 1225 int scan_root(DOS_FS * fs) 1226 { 1227 DOS_FILE **chain; 1228 int i; 1229 1230 root = NULL; 1231 chain = &root; 1232 new_dir(); 1233 if (fs->root_cluster) { 1234 add_file(fs, &chain, NULL, 0, &fp_root); 1235 } else { 1236 for (i = 0; i < fs->root_entries; i++) 1237 add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT), 1238 &fp_root); 1239 } 1240 lfn_check_orphaned(); 1241 (void)check_dir(fs, &root, 0); 1242 if (check_files(fs, root)) 1243 return 1; 1244 return subdirs(fs, NULL, &fp_root); 1245 } 1246