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