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