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