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