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