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