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