1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 Google LLC 5 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 6 * Copyright (c) 1995 Martin Husemann 7 * Some structure declaration borrowed from Paul Popelka 8 * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $"); 35 static const char rcsid[] = 36 "$FreeBSD$"; 37 #endif /* not lint */ 38 39 #include <assert.h> 40 #include <inttypes.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <ctype.h> 45 #include <unistd.h> 46 #include <time.h> 47 48 #include <sys/param.h> 49 50 #include "ext.h" 51 #include "fsutil.h" 52 53 #define SLOT_EMPTY 0x00 /* slot has never been used */ 54 #define SLOT_E5 0x05 /* the real value is 0xe5 */ 55 #define SLOT_DELETED 0xe5 /* file in this slot deleted */ 56 57 #define ATTR_NORMAL 0x00 /* normal file */ 58 #define ATTR_READONLY 0x01 /* file is readonly */ 59 #define ATTR_HIDDEN 0x02 /* file is hidden */ 60 #define ATTR_SYSTEM 0x04 /* file is a system file */ 61 #define ATTR_VOLUME 0x08 /* entry is a volume label */ 62 #define ATTR_DIRECTORY 0x10 /* entry is a directory name */ 63 #define ATTR_ARCHIVE 0x20 /* file is new or modified */ 64 65 #define ATTR_WIN95 0x0f /* long name record */ 66 67 /* 68 * This is the format of the contents of the deTime field in the direntry 69 * structure. 70 * We don't use bitfields because we don't know how compilers for 71 * arbitrary machines will lay them out. 72 */ 73 #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ 74 #define DT_2SECONDS_SHIFT 0 75 #define DT_MINUTES_MASK 0x7E0 /* minutes */ 76 #define DT_MINUTES_SHIFT 5 77 #define DT_HOURS_MASK 0xF800 /* hours */ 78 #define DT_HOURS_SHIFT 11 79 80 /* 81 * This is the format of the contents of the deDate field in the direntry 82 * structure. 83 */ 84 #define DD_DAY_MASK 0x1F /* day of month */ 85 #define DD_DAY_SHIFT 0 86 #define DD_MONTH_MASK 0x1E0 /* month */ 87 #define DD_MONTH_SHIFT 5 88 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */ 89 #define DD_YEAR_SHIFT 9 90 91 92 /* dir.c */ 93 static struct dosDirEntry *newDosDirEntry(void); 94 static void freeDosDirEntry(struct dosDirEntry *); 95 static struct dirTodoNode *newDirTodo(void); 96 static void freeDirTodo(struct dirTodoNode *); 97 static char *fullpath(struct dosDirEntry *); 98 static u_char calcShortSum(u_char *); 99 static int delete(struct fat_descriptor *, cl_t, int, cl_t, int, int); 100 static int removede(struct fat_descriptor *, u_char *, u_char *, 101 cl_t, cl_t, cl_t, char *, int); 102 static int checksize(struct fat_descriptor *, u_char *, struct dosDirEntry *); 103 static int readDosDirSection(struct fat_descriptor *, struct dosDirEntry *); 104 105 /* 106 * Manage free dosDirEntry structures. 107 */ 108 static struct dosDirEntry *freede; 109 110 static struct dosDirEntry * 111 newDosDirEntry(void) 112 { 113 struct dosDirEntry *de; 114 115 if (!(de = freede)) { 116 if (!(de = malloc(sizeof *de))) 117 return (NULL); 118 } else 119 freede = de->next; 120 return de; 121 } 122 123 static void 124 freeDosDirEntry(struct dosDirEntry *de) 125 { 126 de->next = freede; 127 freede = de; 128 } 129 130 /* 131 * The same for dirTodoNode structures. 132 */ 133 static struct dirTodoNode *freedt; 134 135 static struct dirTodoNode * 136 newDirTodo(void) 137 { 138 struct dirTodoNode *dt; 139 140 if (!(dt = freedt)) { 141 if (!(dt = malloc(sizeof *dt))) 142 return 0; 143 } else 144 freedt = dt->next; 145 return dt; 146 } 147 148 static void 149 freeDirTodo(struct dirTodoNode *dt) 150 { 151 dt->next = freedt; 152 freedt = dt; 153 } 154 155 /* 156 * The stack of unread directories 157 */ 158 static struct dirTodoNode *pendingDirectories = NULL; 159 160 /* 161 * Return the full pathname for a directory entry. 162 */ 163 static char * 164 fullpath(struct dosDirEntry *dir) 165 { 166 static char namebuf[MAXPATHLEN + 1]; 167 char *cp, *np; 168 int nl; 169 170 cp = namebuf + sizeof namebuf; 171 *--cp = '\0'; 172 173 for(;;) { 174 np = dir->lname[0] ? dir->lname : dir->name; 175 nl = strlen(np); 176 if (cp <= namebuf + 1 + nl) { 177 *--cp = '?'; 178 break; 179 } 180 cp -= nl; 181 memcpy(cp, np, nl); 182 dir = dir->parent; 183 if (!dir) 184 break; 185 *--cp = '/'; 186 } 187 188 return cp; 189 } 190 191 /* 192 * Calculate a checksum over an 8.3 alias name 193 */ 194 static inline u_char 195 calcShortSum(u_char *p) 196 { 197 u_char sum = 0; 198 int i; 199 200 for (i = 0; i < 11; i++) { 201 sum = (sum << 7)|(sum >> 1); /* rotate right */ 202 sum += p[i]; 203 } 204 205 return sum; 206 } 207 208 /* 209 * Global variables temporarily used during a directory scan 210 */ 211 static char longName[DOSLONGNAMELEN] = ""; 212 static u_char *buffer = NULL; 213 static u_char *delbuf = NULL; 214 215 static struct dosDirEntry *rootDir; 216 static struct dosDirEntry *lostDir; 217 218 /* 219 * Init internal state for a new directory scan. 220 */ 221 int 222 resetDosDirSection(struct fat_descriptor *fat) 223 { 224 int rootdir_size, cluster_size; 225 int ret = FSOK; 226 size_t len; 227 struct bootblock *boot; 228 229 boot = fat_get_boot(fat); 230 231 rootdir_size = boot->bpbRootDirEnts * 32; 232 cluster_size = boot->bpbSecPerClust * boot->bpbBytesPerSec; 233 234 if ((buffer = malloc(len = MAX(rootdir_size, cluster_size))) == NULL) { 235 perr("No space for directory buffer (%zu)", len); 236 return FSFATAL; 237 } 238 239 if ((delbuf = malloc(len = cluster_size)) == NULL) { 240 free(buffer); 241 perr("No space for directory delbuf (%zu)", len); 242 return FSFATAL; 243 } 244 245 if ((rootDir = newDosDirEntry()) == NULL) { 246 free(buffer); 247 free(delbuf); 248 perr("No space for directory entry"); 249 return FSFATAL; 250 } 251 252 memset(rootDir, 0, sizeof *rootDir); 253 if (boot->flags & FAT32) { 254 if (!fat_is_cl_head(fat, boot->bpbRootClust)) { 255 pfatal("Root directory doesn't start a cluster chain"); 256 return FSFATAL; 257 } 258 rootDir->head = boot->bpbRootClust; 259 } 260 261 return ret; 262 } 263 264 /* 265 * Cleanup after a directory scan 266 */ 267 void 268 finishDosDirSection(void) 269 { 270 struct dirTodoNode *p, *np; 271 struct dosDirEntry *d, *nd; 272 273 for (p = pendingDirectories; p; p = np) { 274 np = p->next; 275 freeDirTodo(p); 276 } 277 pendingDirectories = NULL; 278 for (d = rootDir; d; d = nd) { 279 if ((nd = d->child) != NULL) { 280 d->child = 0; 281 continue; 282 } 283 if (!(nd = d->next)) 284 nd = d->parent; 285 freeDosDirEntry(d); 286 } 287 rootDir = lostDir = NULL; 288 free(buffer); 289 free(delbuf); 290 buffer = NULL; 291 delbuf = NULL; 292 } 293 294 /* 295 * Delete directory entries between startcl, startoff and endcl, endoff. 296 */ 297 static int 298 delete(struct fat_descriptor *fat, cl_t startcl, 299 int startoff, cl_t endcl, int endoff, int notlast) 300 { 301 u_char *s, *e; 302 off_t off; 303 int clsz, fd; 304 struct bootblock *boot; 305 306 boot = fat_get_boot(fat); 307 fd = fat_get_fd(fat); 308 clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec; 309 310 s = delbuf + startoff; 311 e = delbuf + clsz; 312 while (fat_is_valid_cl(fat, startcl)) { 313 if (startcl == endcl) { 314 if (notlast) 315 break; 316 e = delbuf + endoff; 317 } 318 off = (startcl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; 319 320 off *= boot->bpbBytesPerSec; 321 if (lseek(fd, off, SEEK_SET) != off) { 322 perr("Unable to lseek to %" PRId64, off); 323 return FSFATAL; 324 } 325 if (read(fd, delbuf, clsz) != clsz) { 326 perr("Unable to read directory"); 327 return FSFATAL; 328 } 329 while (s < e) { 330 *s = SLOT_DELETED; 331 s += 32; 332 } 333 if (lseek(fd, off, SEEK_SET) != off) { 334 perr("Unable to lseek to %" PRId64, off); 335 return FSFATAL; 336 } 337 if (write(fd, delbuf, clsz) != clsz) { 338 perr("Unable to write directory"); 339 return FSFATAL; 340 } 341 if (startcl == endcl) 342 break; 343 startcl = fat_get_cl_next(fat, startcl); 344 s = delbuf; 345 } 346 return FSOK; 347 } 348 349 static int 350 removede(struct fat_descriptor *fat, u_char *start, 351 u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, 352 char *path, int type) 353 { 354 switch (type) { 355 case 0: 356 pwarn("Invalid long filename entry for %s\n", path); 357 break; 358 case 1: 359 pwarn("Invalid long filename entry at end of directory %s\n", 360 path); 361 break; 362 case 2: 363 pwarn("Invalid long filename entry for volume label\n"); 364 break; 365 } 366 if (ask(0, "Remove")) { 367 if (startcl != curcl) { 368 if (delete(fat, 369 startcl, start - buffer, 370 endcl, end - buffer, 371 endcl == curcl) == FSFATAL) 372 return FSFATAL; 373 start = buffer; 374 } 375 /* startcl is < CLUST_FIRST for !FAT32 root */ 376 if ((endcl == curcl) || (startcl < CLUST_FIRST)) 377 for (; start < end; start += 32) 378 *start = SLOT_DELETED; 379 return FSDIRMOD; 380 } 381 return FSERROR; 382 } 383 384 /* 385 * Check an in-memory file entry 386 */ 387 static int 388 checksize(struct fat_descriptor *fat, u_char *p, struct dosDirEntry *dir) 389 { 390 int ret = FSOK; 391 size_t chainsize; 392 u_int64_t physicalSize; 393 struct bootblock *boot; 394 395 boot = fat_get_boot(fat); 396 397 /* 398 * Check size on ordinary files 399 */ 400 if (dir->head == CLUST_FREE) { 401 physicalSize = 0; 402 } else { 403 if (!fat_is_valid_cl(fat, dir->head) || !fat_is_cl_head(fat, dir->head)) { 404 pwarn("Directory entry %s of size %u referencing invalid cluster %u\n", 405 fullpath(dir), dir->size, dir->head); 406 if (ask(1, "Truncate")) { 407 p[28] = p[29] = p[30] = p[31] = 0; 408 p[26] = p[27] = 0; 409 if (boot->ClustMask == CLUST32_MASK) 410 p[20] = p[21] = 0; 411 dir->size = 0; 412 dir->head = CLUST_FREE; 413 return FSDIRMOD; 414 } else { 415 return FSERROR; 416 } 417 } 418 ret = checkchain(fat, dir->head, &chainsize); 419 /* 420 * Upon return, chainsize would hold the chain length 421 * that checkchain() was able to validate, but if the user 422 * refused the proposed repair, it would be unsafe to 423 * proceed with directory entry fix, so bail out in that 424 * case. 425 */ 426 if (ret == FSERROR) { 427 return (FSERROR); 428 } 429 /* 430 * The maximum file size on FAT32 is 4GiB - 1, which 431 * will occupy a cluster chain of exactly 4GiB in 432 * size. On 32-bit platforms, since size_t is 32-bit, 433 * it would wrap back to 0. 434 */ 435 physicalSize = (u_int64_t)chainsize * boot->ClusterSize; 436 } 437 if (physicalSize < dir->size) { 438 pwarn("size of %s is %u, should at most be %ju\n", 439 fullpath(dir), dir->size, (uintmax_t)physicalSize); 440 if (ask(1, "Truncate")) { 441 dir->size = physicalSize; 442 p[28] = (u_char)physicalSize; 443 p[29] = (u_char)(physicalSize >> 8); 444 p[30] = (u_char)(physicalSize >> 16); 445 p[31] = (u_char)(physicalSize >> 24); 446 return FSDIRMOD; 447 } else 448 return FSERROR; 449 } else if (physicalSize - dir->size >= boot->ClusterSize) { 450 pwarn("%s has too many clusters allocated\n", 451 fullpath(dir)); 452 if (ask(1, "Drop superfluous clusters")) { 453 cl_t cl; 454 u_int32_t sz, len; 455 456 for (cl = dir->head, len = sz = 0; 457 (sz += boot->ClusterSize) < dir->size; len++) 458 cl = fat_get_cl_next(fat, cl); 459 clearchain(fat, fat_get_cl_next(fat, cl)); 460 ret = fat_set_cl_next(fat, cl, CLUST_EOF); 461 return (FSFATMOD | ret); 462 } else 463 return FSERROR; 464 } 465 return FSOK; 466 } 467 468 static const u_char dot_name[11] = ". "; 469 static const u_char dotdot_name[11] = ".. "; 470 471 /* 472 * Basic sanity check if the subdirectory have good '.' and '..' entries, 473 * and they are directory entries. Further sanity checks are performed 474 * when we traverse into it. 475 */ 476 static int 477 check_subdirectory(struct fat_descriptor *fat, struct dosDirEntry *dir) 478 { 479 u_char *buf, *cp; 480 off_t off; 481 cl_t cl; 482 int retval = FSOK; 483 int fd; 484 struct bootblock *boot; 485 486 boot = fat_get_boot(fat); 487 fd = fat_get_fd(fat); 488 489 cl = dir->head; 490 if (dir->parent && !fat_is_valid_cl(fat, cl)) { 491 return FSERROR; 492 } 493 494 if (!(boot->flags & FAT32) && !dir->parent) { 495 off = boot->bpbResSectors + boot->bpbFATs * 496 boot->FATsecs; 497 } else { 498 off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; 499 } 500 501 /* 502 * We only need to check the first two entries of the directory, 503 * which is found in the first sector of the directory entry, 504 * so read in only the first sector. 505 */ 506 buf = malloc(boot->bpbBytesPerSec); 507 if (buf == NULL) { 508 perr("No space for directory buffer (%u)", 509 boot->bpbBytesPerSec); 510 return FSFATAL; 511 } 512 513 off *= boot->bpbBytesPerSec; 514 if (lseek(fd, off, SEEK_SET) != off || 515 read(fd, buf, boot->bpbBytesPerSec) != (ssize_t)boot->bpbBytesPerSec) { 516 perr("Unable to read directory"); 517 free(buf); 518 return FSFATAL; 519 } 520 521 /* 522 * Both `.' and `..' must be present and be the first two entries 523 * and be ATTR_DIRECTORY of a valid subdirectory. 524 */ 525 cp = buf; 526 if (memcmp(cp, dot_name, sizeof(dot_name)) != 0 || 527 (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { 528 pwarn("%s: Incorrect `.' for %s.\n", __func__, dir->name); 529 retval |= FSERROR; 530 } 531 cp += 32; 532 if (memcmp(cp, dotdot_name, sizeof(dotdot_name)) != 0 || 533 (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { 534 pwarn("%s: Incorrect `..' for %s. \n", __func__, dir->name); 535 retval |= FSERROR; 536 } 537 538 free(buf); 539 return retval; 540 } 541 542 /* 543 * Read a directory and 544 * - resolve long name records 545 * - enter file and directory records into the parent's list 546 * - push directories onto the todo-stack 547 */ 548 static int 549 readDosDirSection(struct fat_descriptor *fat, struct dosDirEntry *dir) 550 { 551 struct bootblock *boot; 552 struct dosDirEntry dirent, *d; 553 u_char *p, *vallfn, *invlfn, *empty; 554 off_t off; 555 int fd, i, j, k, iosize, entries; 556 bool is_legacyroot; 557 cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0; 558 char *t; 559 u_int lidx = 0; 560 int shortSum; 561 int mod = FSOK; 562 size_t dirclusters; 563 #define THISMOD 0x8000 /* Only used within this routine */ 564 565 boot = fat_get_boot(fat); 566 fd = fat_get_fd(fat); 567 568 cl = dir->head; 569 if (dir->parent && (!fat_is_valid_cl(fat, cl))) { 570 /* 571 * Already handled somewhere else. 572 */ 573 return FSOK; 574 } 575 shortSum = -1; 576 vallfn = invlfn = empty = NULL; 577 578 /* 579 * If we are checking the legacy root (for FAT12/FAT16), 580 * we will operate on the whole directory; otherwise, we 581 * will operate on one cluster at a time, and also take 582 * this opportunity to examine the chain. 583 * 584 * Derive how many entries we are going to encounter from 585 * the I/O size. 586 */ 587 is_legacyroot = (dir->parent == NULL && !(boot->flags & FAT32)); 588 if (is_legacyroot) { 589 iosize = boot->bpbRootDirEnts * 32; 590 entries = boot->bpbRootDirEnts; 591 } else { 592 iosize = boot->bpbSecPerClust * boot->bpbBytesPerSec; 593 entries = iosize / 32; 594 mod |= checkchain(fat, dir->head, &dirclusters); 595 } 596 597 do { 598 if (is_legacyroot) { 599 /* 600 * Special case for FAT12/FAT16 root -- read 601 * in the whole root directory. 602 */ 603 off = boot->bpbResSectors + boot->bpbFATs * 604 boot->FATsecs; 605 } else { 606 /* 607 * Otherwise, read in a cluster of the 608 * directory. 609 */ 610 off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; 611 } 612 613 off *= boot->bpbBytesPerSec; 614 if (lseek(fd, off, SEEK_SET) != off || 615 read(fd, buffer, iosize) != iosize) { 616 perr("Unable to read directory"); 617 return FSFATAL; 618 } 619 620 for (p = buffer, i = 0; i < entries; i++, p += 32) { 621 if (dir->fsckflags & DIREMPWARN) { 622 *p = SLOT_EMPTY; 623 continue; 624 } 625 626 if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { 627 if (*p == SLOT_EMPTY) { 628 dir->fsckflags |= DIREMPTY; 629 empty = p; 630 empcl = cl; 631 } 632 continue; 633 } 634 635 if (dir->fsckflags & DIREMPTY) { 636 if (!(dir->fsckflags & DIREMPWARN)) { 637 pwarn("%s has entries after end of directory\n", 638 fullpath(dir)); 639 if (ask(1, "Extend")) { 640 u_char *q; 641 642 dir->fsckflags &= ~DIREMPTY; 643 if (delete(fat, 644 empcl, empty - buffer, 645 cl, p - buffer, 1) == FSFATAL) 646 return FSFATAL; 647 q = ((empcl == cl) ? empty : buffer); 648 assert(q != NULL); 649 for (; q < p; q += 32) 650 *q = SLOT_DELETED; 651 mod |= THISMOD|FSDIRMOD; 652 } else if (ask(0, "Truncate")) 653 dir->fsckflags |= DIREMPWARN; 654 } 655 if (dir->fsckflags & DIREMPWARN) { 656 *p = SLOT_DELETED; 657 mod |= THISMOD|FSDIRMOD; 658 continue; 659 } else if (dir->fsckflags & DIREMPTY) 660 mod |= FSERROR; 661 empty = NULL; 662 } 663 664 if (p[11] == ATTR_WIN95) { 665 if (*p & LRFIRST) { 666 if (shortSum != -1) { 667 if (!invlfn) { 668 invlfn = vallfn; 669 invcl = valcl; 670 } 671 } 672 memset(longName, 0, sizeof longName); 673 shortSum = p[13]; 674 vallfn = p; 675 valcl = cl; 676 } else if (shortSum != p[13] 677 || lidx != (*p & LRNOMASK)) { 678 if (!invlfn) { 679 invlfn = vallfn; 680 invcl = valcl; 681 } 682 if (!invlfn) { 683 invlfn = p; 684 invcl = cl; 685 } 686 vallfn = NULL; 687 } 688 lidx = *p & LRNOMASK; 689 if (lidx == 0) { 690 pwarn("invalid long name\n"); 691 if (!invlfn) { 692 invlfn = vallfn; 693 invcl = valcl; 694 } 695 vallfn = NULL; 696 continue; 697 } 698 t = longName + --lidx * 13; 699 for (k = 1; k < 11 && t < longName + 700 sizeof(longName); k += 2) { 701 if (!p[k] && !p[k + 1]) 702 break; 703 *t++ = p[k]; 704 /* 705 * Warn about those unusable chars in msdosfs here? XXX 706 */ 707 if (p[k + 1]) 708 t[-1] = '?'; 709 } 710 if (k >= 11) 711 for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) { 712 if (!p[k] && !p[k + 1]) 713 break; 714 *t++ = p[k]; 715 if (p[k + 1]) 716 t[-1] = '?'; 717 } 718 if (k >= 26) 719 for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) { 720 if (!p[k] && !p[k + 1]) 721 break; 722 *t++ = p[k]; 723 if (p[k + 1]) 724 t[-1] = '?'; 725 } 726 if (t >= longName + sizeof(longName)) { 727 pwarn("long filename too long\n"); 728 if (!invlfn) { 729 invlfn = vallfn; 730 invcl = valcl; 731 } 732 vallfn = NULL; 733 } 734 if (p[26] | (p[27] << 8)) { 735 pwarn("long filename record cluster start != 0\n"); 736 if (!invlfn) { 737 invlfn = vallfn; 738 invcl = cl; 739 } 740 vallfn = NULL; 741 } 742 continue; /* long records don't carry further 743 * information */ 744 } 745 746 /* 747 * This is a standard msdosfs directory entry. 748 */ 749 memset(&dirent, 0, sizeof dirent); 750 751 /* 752 * it's a short name record, but we need to know 753 * more, so get the flags first. 754 */ 755 dirent.flags = p[11]; 756 757 /* 758 * Translate from 850 to ISO here XXX 759 */ 760 for (j = 0; j < 8; j++) 761 dirent.name[j] = p[j]; 762 dirent.name[8] = '\0'; 763 for (k = 7; k >= 0 && dirent.name[k] == ' '; k--) 764 dirent.name[k] = '\0'; 765 if (k < 0 || dirent.name[k] != '\0') 766 k++; 767 if (dirent.name[0] == SLOT_E5) 768 dirent.name[0] = 0xe5; 769 770 if (dirent.flags & ATTR_VOLUME) { 771 if (vallfn || invlfn) { 772 mod |= removede(fat, 773 invlfn ? invlfn : vallfn, p, 774 invlfn ? invcl : valcl, -1, 0, 775 fullpath(dir), 2); 776 vallfn = NULL; 777 invlfn = NULL; 778 } 779 continue; 780 } 781 782 if (p[8] != ' ') 783 dirent.name[k++] = '.'; 784 for (j = 0; j < 3; j++) 785 dirent.name[k++] = p[j+8]; 786 dirent.name[k] = '\0'; 787 for (k--; k >= 0 && dirent.name[k] == ' '; k--) 788 dirent.name[k] = '\0'; 789 790 if (vallfn && shortSum != calcShortSum(p)) { 791 if (!invlfn) { 792 invlfn = vallfn; 793 invcl = valcl; 794 } 795 vallfn = NULL; 796 } 797 dirent.head = p[26] | (p[27] << 8); 798 if (boot->ClustMask == CLUST32_MASK) 799 dirent.head |= (p[20] << 16) | (p[21] << 24); 800 dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24); 801 if (vallfn) { 802 strlcpy(dirent.lname, longName, 803 sizeof(dirent.lname)); 804 longName[0] = '\0'; 805 shortSum = -1; 806 } 807 808 dirent.parent = dir; 809 dirent.next = dir->child; 810 811 if (invlfn) { 812 mod |= k = removede(fat, 813 invlfn, vallfn ? vallfn : p, 814 invcl, vallfn ? valcl : cl, cl, 815 fullpath(&dirent), 0); 816 if (mod & FSFATAL) 817 return FSFATAL; 818 if (vallfn 819 ? (valcl == cl && vallfn != buffer) 820 : p != buffer) 821 if (k & FSDIRMOD) 822 mod |= THISMOD; 823 } 824 825 vallfn = NULL; /* not used any longer */ 826 invlfn = NULL; 827 828 /* 829 * Check if the directory entry is sane. 830 * 831 * '.' and '..' are skipped, their sanity is 832 * checked somewhere else. 833 * 834 * For everything else, check if we have a new, 835 * valid cluster chain (beginning of a file or 836 * directory that was never previously claimed 837 * by another file) when it's a non-empty file 838 * or a directory. The sanity of the cluster 839 * chain is checked at a later time when we 840 * traverse into the directory, or examine the 841 * file's directory entry. 842 * 843 * The only possible fix is to delete the entry 844 * if it's a directory; for file, we have to 845 * truncate the size to 0. 846 */ 847 if (!(dirent.flags & ATTR_DIRECTORY) || 848 (strcmp(dirent.name, ".") != 0 && 849 strcmp(dirent.name, "..") != 0)) { 850 if ((dirent.size != 0 || (dirent.flags & ATTR_DIRECTORY)) && 851 ((!fat_is_valid_cl(fat, dirent.head) || 852 !fat_is_cl_head(fat, dirent.head)))) { 853 if (!fat_is_valid_cl(fat, dirent.head)) { 854 pwarn("%s starts with cluster out of range(%u)\n", 855 fullpath(&dirent), 856 dirent.head); 857 } else { 858 pwarn("%s doesn't start a new cluster chain\n", 859 fullpath(&dirent)); 860 } 861 862 if (dirent.flags & ATTR_DIRECTORY) { 863 if (ask(0, "Remove")) { 864 *p = SLOT_DELETED; 865 mod |= THISMOD|FSDIRMOD; 866 } else 867 mod |= FSERROR; 868 continue; 869 } else { 870 if (ask(1, "Truncate")) { 871 p[28] = p[29] = p[30] = p[31] = 0; 872 p[26] = p[27] = 0; 873 if (boot->ClustMask == CLUST32_MASK) 874 p[20] = p[21] = 0; 875 dirent.size = 0; 876 dirent.head = 0; 877 mod |= THISMOD|FSDIRMOD; 878 } else 879 mod |= FSERROR; 880 } 881 } 882 } 883 if (dirent.flags & ATTR_DIRECTORY) { 884 /* 885 * gather more info for directories 886 */ 887 struct dirTodoNode *n; 888 889 if (dirent.size) { 890 pwarn("Directory %s has size != 0\n", 891 fullpath(&dirent)); 892 if (ask(1, "Correct")) { 893 p[28] = p[29] = p[30] = p[31] = 0; 894 dirent.size = 0; 895 mod |= THISMOD|FSDIRMOD; 896 } else 897 mod |= FSERROR; 898 } 899 /* 900 * handle `.' and `..' specially 901 */ 902 if (strcmp(dirent.name, ".") == 0) { 903 if (dirent.head != dir->head) { 904 pwarn("`.' entry in %s has incorrect start cluster\n", 905 fullpath(dir)); 906 if (ask(1, "Correct")) { 907 dirent.head = dir->head; 908 p[26] = (u_char)dirent.head; 909 p[27] = (u_char)(dirent.head >> 8); 910 if (boot->ClustMask == CLUST32_MASK) { 911 p[20] = (u_char)(dirent.head >> 16); 912 p[21] = (u_char)(dirent.head >> 24); 913 } 914 mod |= THISMOD|FSDIRMOD; 915 } else 916 mod |= FSERROR; 917 } 918 continue; 919 } else if (strcmp(dirent.name, "..") == 0) { 920 if (dir->parent) { /* XXX */ 921 if (!dir->parent->parent) { 922 if (dirent.head) { 923 pwarn("`..' entry in %s has non-zero start cluster\n", 924 fullpath(dir)); 925 if (ask(1, "Correct")) { 926 dirent.head = 0; 927 p[26] = p[27] = 0; 928 if (boot->ClustMask == CLUST32_MASK) 929 p[20] = p[21] = 0; 930 mod |= THISMOD|FSDIRMOD; 931 } else 932 mod |= FSERROR; 933 } 934 } else if (dirent.head != dir->parent->head) { 935 pwarn("`..' entry in %s has incorrect start cluster\n", 936 fullpath(dir)); 937 if (ask(1, "Correct")) { 938 dirent.head = dir->parent->head; 939 p[26] = (u_char)dirent.head; 940 p[27] = (u_char)(dirent.head >> 8); 941 if (boot->ClustMask == CLUST32_MASK) { 942 p[20] = (u_char)(dirent.head >> 16); 943 p[21] = (u_char)(dirent.head >> 24); 944 } 945 mod |= THISMOD|FSDIRMOD; 946 } else 947 mod |= FSERROR; 948 } 949 } 950 continue; 951 } else { 952 /* 953 * Only one directory entry can point 954 * to dir->head, it's '.'. 955 */ 956 if (dirent.head == dir->head) { 957 pwarn("%s entry in %s has incorrect start cluster\n", 958 dirent.name, fullpath(dir)); 959 if (ask(1, "Remove")) { 960 *p = SLOT_DELETED; 961 mod |= THISMOD|FSDIRMOD; 962 } else 963 mod |= FSERROR; 964 continue; 965 } else if ((check_subdirectory(fat, 966 &dirent) & FSERROR) == FSERROR) { 967 /* 968 * A subdirectory should have 969 * a dot (.) entry and a dot-dot 970 * (..) entry of ATTR_DIRECTORY, 971 * we will inspect further when 972 * traversing into it. 973 */ 974 if (ask(1, "Remove")) { 975 *p = SLOT_DELETED; 976 mod |= THISMOD|FSDIRMOD; 977 } else 978 mod |= FSERROR; 979 continue; 980 } 981 } 982 983 /* create directory tree node */ 984 if (!(d = newDosDirEntry())) { 985 perr("No space for directory"); 986 return FSFATAL; 987 } 988 memcpy(d, &dirent, sizeof(struct dosDirEntry)); 989 /* link it into the tree */ 990 dir->child = d; 991 992 /* Enter this directory into the todo list */ 993 if (!(n = newDirTodo())) { 994 perr("No space for todo list"); 995 return FSFATAL; 996 } 997 n->next = pendingDirectories; 998 n->dir = d; 999 pendingDirectories = n; 1000 } else { 1001 mod |= k = checksize(fat, p, &dirent); 1002 if (k & FSDIRMOD) 1003 mod |= THISMOD; 1004 } 1005 boot->NumFiles++; 1006 } 1007 1008 if (is_legacyroot) { 1009 /* 1010 * Don't bother to write back right now because 1011 * we may continue to make modification to the 1012 * non-FAT32 root directory below. 1013 */ 1014 break; 1015 } else if (mod & THISMOD) { 1016 if (lseek(fd, off, SEEK_SET) != off 1017 || write(fd, buffer, iosize) != iosize) { 1018 perr("Unable to write directory"); 1019 return FSFATAL; 1020 } 1021 mod &= ~THISMOD; 1022 } 1023 } while (fat_is_valid_cl(fat, (cl = fat_get_cl_next(fat, cl)))); 1024 if (invlfn || vallfn) 1025 mod |= removede(fat, 1026 invlfn ? invlfn : vallfn, p, 1027 invlfn ? invcl : valcl, -1, 0, 1028 fullpath(dir), 1); 1029 1030 /* 1031 * The root directory of non-FAT32 filesystems is in a special 1032 * area and may have been modified above removede() without 1033 * being written out. 1034 */ 1035 if ((mod & FSDIRMOD) && is_legacyroot) { 1036 if (lseek(fd, off, SEEK_SET) != off 1037 || write(fd, buffer, iosize) != iosize) { 1038 perr("Unable to write directory"); 1039 return FSFATAL; 1040 } 1041 mod &= ~THISMOD; 1042 } 1043 return mod & ~THISMOD; 1044 } 1045 1046 int 1047 handleDirTree(struct fat_descriptor *fat) 1048 { 1049 int mod; 1050 1051 mod = readDosDirSection(fat, rootDir); 1052 if (mod & FSFATAL) 1053 return FSFATAL; 1054 1055 /* 1056 * process the directory todo list 1057 */ 1058 while (pendingDirectories) { 1059 struct dosDirEntry *dir = pendingDirectories->dir; 1060 struct dirTodoNode *n = pendingDirectories->next; 1061 1062 /* 1063 * remove TODO entry now, the list might change during 1064 * directory reads 1065 */ 1066 freeDirTodo(pendingDirectories); 1067 pendingDirectories = n; 1068 1069 /* 1070 * handle subdirectory 1071 */ 1072 mod |= readDosDirSection(fat, dir); 1073 if (mod & FSFATAL) 1074 return FSFATAL; 1075 } 1076 1077 return mod; 1078 } 1079 1080 /* 1081 * Try to reconnect a FAT chain into dir 1082 */ 1083 static u_char *lfbuf; 1084 static cl_t lfcl; 1085 static off_t lfoff; 1086 1087 int 1088 reconnect(struct fat_descriptor *fat, cl_t head, size_t length) 1089 { 1090 struct bootblock *boot = fat_get_boot(fat); 1091 struct dosDirEntry d; 1092 int len, dosfs; 1093 u_char *p; 1094 1095 dosfs = fat_get_fd(fat); 1096 1097 if (!ask(1, "Reconnect")) 1098 return FSERROR; 1099 1100 if (!lostDir) { 1101 for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) { 1102 if (!strcmp(lostDir->name, LOSTDIR)) 1103 break; 1104 } 1105 if (!lostDir) { /* Create LOSTDIR? XXX */ 1106 pwarn("No %s directory\n", LOSTDIR); 1107 return FSERROR; 1108 } 1109 } 1110 if (!lfbuf) { 1111 lfbuf = malloc(boot->ClusterSize); 1112 if (!lfbuf) { 1113 perr("No space for buffer"); 1114 return FSFATAL; 1115 } 1116 p = NULL; 1117 } else 1118 p = lfbuf; 1119 while (1) { 1120 if (p) 1121 for (; p < lfbuf + boot->ClusterSize; p += 32) 1122 if (*p == SLOT_EMPTY 1123 || *p == SLOT_DELETED) 1124 break; 1125 if (p && p < lfbuf + boot->ClusterSize) 1126 break; 1127 lfcl = p ? fat_get_cl_next(fat, lfcl) : lostDir->head; 1128 if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { 1129 /* Extend LOSTDIR? XXX */ 1130 pwarn("No space in %s\n", LOSTDIR); 1131 lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0; 1132 return FSERROR; 1133 } 1134 lfoff = (lfcl - CLUST_FIRST) * boot->ClusterSize 1135 + boot->FirstCluster * boot->bpbBytesPerSec; 1136 1137 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff 1138 || (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { 1139 perr("could not read LOST.DIR"); 1140 return FSFATAL; 1141 } 1142 p = lfbuf; 1143 } 1144 1145 boot->NumFiles++; 1146 /* Ensure uniqueness of entry here! XXX */ 1147 memset(&d, 0, sizeof d); 1148 /* worst case -1 = 4294967295, 10 digits */ 1149 len = snprintf(d.name, sizeof(d.name), "%u", head); 1150 d.flags = 0; 1151 d.head = head; 1152 d.size = length * boot->ClusterSize; 1153 1154 memcpy(p, d.name, len); 1155 memset(p + len, ' ', 11 - len); 1156 memset(p + 11, 0, 32 - 11); 1157 p[26] = (u_char)d.head; 1158 p[27] = (u_char)(d.head >> 8); 1159 if (boot->ClustMask == CLUST32_MASK) { 1160 p[20] = (u_char)(d.head >> 16); 1161 p[21] = (u_char)(d.head >> 24); 1162 } 1163 p[28] = (u_char)d.size; 1164 p[29] = (u_char)(d.size >> 8); 1165 p[30] = (u_char)(d.size >> 16); 1166 p[31] = (u_char)(d.size >> 24); 1167 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff 1168 || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { 1169 perr("could not write LOST.DIR"); 1170 return FSFATAL; 1171 } 1172 return FSDIRMOD; 1173 } 1174 1175 void 1176 finishlf(void) 1177 { 1178 if (lfbuf) 1179 free(lfbuf); 1180 lfbuf = NULL; 1181 } 1182