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