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