dir.c revision 1.17
1/* $NetBSD: dir.c,v 1.17 2005/01/19 20:00:45 xtraeme Exp $ */ 2 3/* 4 * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank 5 * Copyright (c) 1995 Martin Husemann 6 * Some structure declaration borrowed from Paul Popelka 7 * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Martin Husemann 20 * and Wolfgang Solfrank. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 38#include <sys/cdefs.h> 39#ifndef lint 40__RCSID("$NetBSD: dir.c,v 1.17 2005/01/19 20:00:45 xtraeme Exp $"); 41#endif /* not lint */ 42 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <ctype.h> 47#include <stdio.h> 48#include <unistd.h> 49#include <time.h> 50 51#include <sys/param.h> 52 53#include "ext.h" 54#include "fsutil.h" 55 56#define SLOT_EMPTY 0x00 /* slot has never been used */ 57#define SLOT_E5 0x05 /* the real value is 0xe5 */ 58#define SLOT_DELETED 0xe5 /* file in this slot deleted */ 59 60#define ATTR_NORMAL 0x00 /* normal file */ 61#define ATTR_READONLY 0x01 /* file is readonly */ 62#define ATTR_HIDDEN 0x02 /* file is hidden */ 63#define ATTR_SYSTEM 0x04 /* file is a system file */ 64#define ATTR_VOLUME 0x08 /* entry is a volume label */ 65#define ATTR_DIRECTORY 0x10 /* entry is a directory name */ 66#define ATTR_ARCHIVE 0x20 /* file is new or modified */ 67 68#define ATTR_WIN95 0x0f /* long name record */ 69 70/* 71 * This is the format of the contents of the deTime field in the direntry 72 * structure. 73 * We don't use bitfields because we don't know how compilers for 74 * arbitrary machines will lay them out. 75 */ 76#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ 77#define DT_2SECONDS_SHIFT 0 78#define DT_MINUTES_MASK 0x7E0 /* minutes */ 79#define DT_MINUTES_SHIFT 5 80#define DT_HOURS_MASK 0xF800 /* hours */ 81#define DT_HOURS_SHIFT 11 82 83/* 84 * This is the format of the contents of the deDate field in the direntry 85 * structure. 86 */ 87#define DD_DAY_MASK 0x1F /* day of month */ 88#define DD_DAY_SHIFT 0 89#define DD_MONTH_MASK 0x1E0 /* month */ 90#define DD_MONTH_SHIFT 5 91#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ 92#define DD_YEAR_SHIFT 9 93 94 95/* dir.c */ 96static struct dosDirEntry *newDosDirEntry(void); 97static void freeDosDirEntry(struct dosDirEntry *); 98static struct dirTodoNode *newDirTodo(void); 99static void freeDirTodo(struct dirTodoNode *); 100static char *fullpath(struct dosDirEntry *); 101static u_char calcShortSum(u_char *); 102static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int, 103 cl_t, int, int); 104static int removede(int, struct bootblock *, struct fatEntry *, u_char *, 105 u_char *, cl_t, cl_t, cl_t, char *, int); 106static int checksize(struct bootblock *, struct fatEntry *, u_char *, 107 struct dosDirEntry *); 108static int readDosDirSection(int, struct bootblock *, struct fatEntry *, 109 struct dosDirEntry *); 110 111/* 112 * Manage free dosDirEntry structures. 113 */ 114static struct dosDirEntry *freede; 115 116static struct dosDirEntry * 117newDosDirEntry(void) 118{ 119 struct dosDirEntry *de; 120 121 if (!(de = freede)) { 122 if (!(de = (struct dosDirEntry *)malloc(sizeof *de))) 123 return 0; 124 } else 125 freede = de->next; 126 return de; 127} 128 129static void 130freeDosDirEntry(struct dosDirEntry *de) 131{ 132 de->next = freede; 133 freede = de; 134} 135 136/* 137 * The same for dirTodoNode structures. 138 */ 139static struct dirTodoNode *freedt; 140 141static struct dirTodoNode * 142newDirTodo(void) 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 154static void 155freeDirTodo(struct dirTodoNode *dt) 156{ 157 dt->next = freedt; 158 freedt = dt; 159} 160 161/* 162 * The stack of unread directories 163 */ 164struct dirTodoNode *pendingDirectories = NULL; 165 166/* 167 * Return the full pathname for a directory entry. 168 */ 169static char * 170fullpath(struct dosDirEntry *dir) 171{ 172 static char namebuf[MAXPATHLEN + 1]; 173 char *cp, *np; 174 int nl; 175 176 cp = namebuf + sizeof namebuf - 1; 177 *cp = '\0'; 178 do { 179 np = dir->lname[0] ? dir->lname : dir->name; 180 nl = strlen(np); 181 if ((cp -= nl) <= namebuf + 1) 182 break; 183 memcpy(cp, np, nl); 184 *--cp = '/'; 185 } while ((dir = dir->parent) != NULL); 186 if (dir) 187 *--cp = '?'; 188 else 189 cp++; 190 return cp; 191} 192 193/* 194 * Calculate a checksum over an 8.3 alias name 195 */ 196static u_char 197calcShortSum(u_char *p) 198{ 199 u_char sum = 0; 200 int i; 201 202 for (i = 0; i < 11; i++) { 203 sum = (sum << 7)|(sum >> 1); /* rotate right */ 204 sum += p[i]; 205 } 206 207 return sum; 208} 209 210/* 211 * Global variables temporarily used during a directory scan 212 */ 213static char longName[DOSLONGNAMELEN] = ""; 214static u_char *buffer = NULL; 215static u_char *delbuf = NULL; 216 217struct dosDirEntry *rootDir; 218static struct dosDirEntry *lostDir; 219 220/* 221 * Init internal state for a new directory scan. 222 */ 223int 224resetDosDirSection(struct bootblock *boot, struct fatEntry *fat) 225{ 226 int b1, b2; 227 cl_t cl; 228 int ret = FSOK; 229 230 b1 = boot->RootDirEnts * 32; 231 b2 = boot->SecPerClust * boot->BytesPerSec; 232 233 if (!(buffer = malloc(b1 > b2 ? b1 : b2)) 234 || !(delbuf = malloc(b2)) 235 || !(rootDir = newDosDirEntry())) { 236 perror("No space for directory"); 237 return FSFATAL; 238 } 239 memset(rootDir, 0, sizeof *rootDir); 240 if (boot->flags & FAT32) { 241 if (boot->RootCl < CLUST_FIRST || boot->RootCl >= boot->NumClusters) { 242 pfatal("Root directory starts with cluster out of range(%u)", 243 boot->RootCl); 244 return FSFATAL; 245 } 246 cl = fat[boot->RootCl].next; 247 if (cl < CLUST_FIRST 248 || (cl >= CLUST_RSRVD && cl< CLUST_EOFS) 249 || fat[boot->RootCl].head != boot->RootCl) { 250 if (cl == CLUST_FREE) 251 pwarn("Root directory starts with free cluster\n"); 252 else if (cl >= CLUST_RSRVD) 253 pwarn("Root directory starts with cluster marked %s\n", 254 rsrvdcltype(cl)); 255 else { 256 pfatal("Root directory doesn't start a cluster chain"); 257 return FSFATAL; 258 } 259 if (ask(1, "Fix")) { 260 fat[boot->RootCl].next = CLUST_FREE; 261 ret = FSFATMOD; 262 } else 263 ret = FSFATAL; 264 } 265 266 fat[boot->RootCl].flags |= FAT_USED; 267 rootDir->head = boot->RootCl; 268 } 269 270 return ret; 271} 272 273/* 274 * Cleanup after a directory scan 275 */ 276void 277finishDosDirSection(void) 278{ 279 struct dirTodoNode *p, *np; 280 struct dosDirEntry *d, *nd; 281 282 for (p = pendingDirectories; p; p = np) { 283 np = p->next; 284 freeDirTodo(p); 285 } 286 pendingDirectories = 0; 287 for (d = rootDir; d; d = nd) { 288 if ((nd = d->child) != NULL) { 289 d->child = 0; 290 continue; 291 } 292 if (!(nd = d->next)) 293 nd = d->parent; 294 freeDosDirEntry(d); 295 } 296 rootDir = lostDir = NULL; 297 free(buffer); 298 free(delbuf); 299 buffer = NULL; 300 delbuf = NULL; 301} 302 303/* 304 * Delete directory entries between startcl, startoff and endcl, endoff. 305 */ 306static int 307delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl, 308 int startoff, cl_t endcl, int endoff, int notlast) 309{ 310 u_char *s, *e; 311 off_t off; 312 int clsz = boot->SecPerClust * boot->BytesPerSec; 313 314 s = delbuf + startoff; 315 e = delbuf + clsz; 316 while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) { 317 if (startcl == endcl) { 318 if (notlast) 319 break; 320 e = delbuf + endoff; 321 } 322 off = startcl * boot->SecPerClust + boot->ClusterOffset; 323 off *= boot->BytesPerSec; 324 if (lseek(f, off, SEEK_SET) != off 325 || read(f, delbuf, clsz) != clsz) { 326 perror("Unable to read directory"); 327 return FSFATAL; 328 } 329 while (s < e) { 330 *s = SLOT_DELETED; 331 s += 32; 332 } 333 if (lseek(f, off, SEEK_SET) != off 334 || write(f, delbuf, clsz) != clsz) { 335 perror("Unable to write directory"); 336 return FSFATAL; 337 } 338 if (startcl == endcl) 339 break; 340 startcl = fat[startcl].next; 341 s = delbuf; 342 } 343 return FSOK; 344} 345 346static int 347removede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start, 348 u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, 349 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 */ 382static int 383checksize(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 */ 434static int 435readDosDirSection(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 perror("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 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 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, 649 sizeof(dirent.lname)); 650 longName[0] = '\0'; 651 shortSum = -1; 652 } 653 654 dirent.parent = dir; 655 dirent.next = dir->child; 656 657 if (invlfn) { 658 mod |= k = removede(f, boot, fat, 659 invlfn, vallfn ? vallfn : p, 660 invcl, vallfn ? valcl : cl, cl, 661 fullpath(&dirent), 0); 662 if (mod & FSFATAL) 663 return FSFATAL; 664 if (vallfn 665 ? (valcl == cl && vallfn != buffer) 666 : p != buffer) 667 if (k & FSDIRMOD) 668 mod |= THISMOD; 669 } 670 671 vallfn = NULL; /* not used any longer */ 672 invlfn = NULL; 673 674 if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) { 675 if (dirent.head != 0) { 676 pwarn("%s has clusters, but size 0\n", 677 fullpath(&dirent)); 678 if (ask(1, "Drop allocated clusters")) { 679 p[26] = p[27] = 0; 680 if (boot->ClustMask == CLUST32_MASK) 681 p[20] = p[21] = 0; 682 clearchain(boot, fat, dirent.head); 683 dirent.head = 0; 684 mod |= THISMOD|FSDIRMOD|FSFATMOD; 685 } else 686 mod |= FSERROR; 687 } 688 } else if (dirent.head == 0 689 && !strcmp(dirent.name, "..") 690 && dir->parent /* XXX */ 691 && !dir->parent->parent) { 692 /* 693 * Do nothing, the parent is the root 694 */ 695 } else if (dirent.head < CLUST_FIRST 696 || dirent.head >= boot->NumClusters 697 || fat[dirent.head].next == CLUST_FREE 698 || (fat[dirent.head].next >= CLUST_RSRVD 699 && fat[dirent.head].next < CLUST_EOFS) 700 || fat[dirent.head].head != dirent.head) { 701 if (dirent.head == 0) 702 pwarn("%s has no clusters\n", 703 fullpath(&dirent)); 704 else if (dirent.head < CLUST_FIRST 705 || dirent.head >= boot->NumClusters) 706 pwarn("%s starts with cluster out of range(%u)\n", 707 fullpath(&dirent), 708 dirent.head); 709 else if (fat[dirent.head].next == CLUST_FREE) 710 pwarn("%s starts with free cluster\n", 711 fullpath(&dirent)); 712 else if (fat[dirent.head].next >= CLUST_RSRVD) 713 pwarn("%s starts with cluster marked %s\n", 714 fullpath(&dirent), 715 rsrvdcltype(fat[dirent.head].next)); 716 else 717 pwarn("%s doesn't start a cluster chain\n", 718 fullpath(&dirent)); 719 if (dirent.flags & ATTR_DIRECTORY) { 720 if (ask(0, "Remove")) { 721 *p = SLOT_DELETED; 722 mod |= THISMOD|FSDIRMOD; 723 } else 724 mod |= FSERROR; 725 continue; 726 } else { 727 if (ask(1, "Truncate")) { 728 p[28] = p[29] = p[30] = p[31] = 0; 729 p[26] = p[27] = 0; 730 if (boot->ClustMask == CLUST32_MASK) 731 p[20] = p[21] = 0; 732 dirent.size = 0; 733 mod |= THISMOD|FSDIRMOD; 734 } else 735 mod |= FSERROR; 736 } 737 } 738 739 if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters) 740 fat[dirent.head].flags |= FAT_USED; 741 742 if (dirent.flags & ATTR_DIRECTORY) { 743 /* 744 * gather more info for directories 745 */ 746 struct dirTodoNode *n; 747 748 if (dirent.size) { 749 pwarn("Directory %s has size != 0\n", 750 fullpath(&dirent)); 751 if (ask(1, "Correct")) { 752 p[28] = p[29] = p[30] = p[31] = 0; 753 dirent.size = 0; 754 mod |= THISMOD|FSDIRMOD; 755 } else 756 mod |= FSERROR; 757 } 758 /* 759 * handle `.' and `..' specially 760 */ 761 if (strcmp(dirent.name, ".") == 0) { 762 if (dirent.head != dir->head) { 763 pwarn("`.' entry in %s has incorrect start cluster\n", 764 fullpath(dir)); 765 if (ask(1, "Correct")) { 766 dirent.head = dir->head; 767 p[26] = (u_char)dirent.head; 768 p[27] = (u_char)(dirent.head >> 8); 769 if (boot->ClustMask == CLUST32_MASK) { 770 p[20] = (u_char)(dirent.head >> 16); 771 p[21] = (u_char)(dirent.head >> 24); 772 } 773 mod |= THISMOD|FSDIRMOD; 774 } else 775 mod |= FSERROR; 776 } 777 continue; 778 } 779 if (strcmp(dirent.name, "..") == 0) { 780 if (dir->parent) { /* XXX */ 781 if (!dir->parent->parent) { 782 if (dirent.head) { 783 pwarn("`..' entry in %s has non-zero start cluster\n", 784 fullpath(dir)); 785 if (ask(1, "Correct")) { 786 dirent.head = 0; 787 p[26] = p[27] = 0; 788 if (boot->ClustMask == CLUST32_MASK) 789 p[20] = p[21] = 0; 790 mod |= THISMOD|FSDIRMOD; 791 } else 792 mod |= FSERROR; 793 } 794 } else if (dirent.head != dir->parent->head) { 795 pwarn("`..' entry in %s has incorrect start cluster\n", 796 fullpath(dir)); 797 if (ask(1, "Correct")) { 798 dirent.head = dir->parent->head; 799 p[26] = (u_char)dirent.head; 800 p[27] = (u_char)(dirent.head >> 8); 801 if (boot->ClustMask == CLUST32_MASK) { 802 p[20] = (u_char)(dirent.head >> 16); 803 p[21] = (u_char)(dirent.head >> 24); 804 } 805 mod |= THISMOD|FSDIRMOD; 806 } else 807 mod |= FSERROR; 808 } 809 } 810 continue; 811 } 812 813 /* create directory tree node */ 814 if (!(d = newDosDirEntry())) { 815 perror("No space for directory"); 816 return FSFATAL; 817 } 818 memcpy(d, &dirent, sizeof(struct dosDirEntry)); 819 /* link it into the tree */ 820 dir->child = d; 821 822 /* Enter this directory into the todo list */ 823 if (!(n = newDirTodo())) { 824 perror("No space for todo list"); 825 return FSFATAL; 826 } 827 n->next = pendingDirectories; 828 n->dir = d; 829 pendingDirectories = n; 830 } else { 831 mod |= k = checksize(boot, fat, p, &dirent); 832 if (k & FSDIRMOD) 833 mod |= THISMOD; 834 } 835 boot->NumFiles++; 836 } 837 if (mod & THISMOD) { 838 last *= 32; 839 if (lseek(f, off, SEEK_SET) != off 840 || write(f, buffer, last) != last) { 841 perror("Unable to write directory"); 842 return FSFATAL; 843 } 844 mod &= ~THISMOD; 845 } 846 } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters); 847 if (invlfn || vallfn) 848 mod |= removede(f, boot, fat, 849 invlfn ? invlfn : vallfn, p, 850 invlfn ? invcl : valcl, -1, 0, 851 fullpath(dir), 1); 852 return mod & ~THISMOD; 853} 854 855int 856handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat) 857{ 858 int mod; 859 860 mod = readDosDirSection(dosfs, boot, fat, rootDir); 861 if (mod & FSFATAL) 862 return FSFATAL; 863 864 /* 865 * process the directory todo list 866 */ 867 while (pendingDirectories) { 868 struct dosDirEntry *dir = pendingDirectories->dir; 869 struct dirTodoNode *n = pendingDirectories->next; 870 871 /* 872 * remove TODO entry now, the list might change during 873 * directory reads 874 */ 875 freeDirTodo(pendingDirectories); 876 pendingDirectories = n; 877 878 /* 879 * handle subdirectory 880 */ 881 mod |= readDosDirSection(dosfs, boot, fat, dir); 882 if (mod & FSFATAL) 883 return FSFATAL; 884 } 885 886 return mod; 887} 888 889/* 890 * Try to reconnect a FAT chain into dir 891 */ 892static u_char *lfbuf; 893static cl_t lfcl; 894static off_t lfoff; 895 896int 897reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head) 898{ 899 struct dosDirEntry d; 900 u_char *p; 901 902 if (!ask(1, "Reconnect")) 903 return FSERROR; 904 905 if (!lostDir) { 906 for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) { 907 if (!strcmp(lostDir->name, LOSTDIR)) 908 break; 909 } 910 if (!lostDir) { /* Create LOSTDIR? XXX */ 911 pwarn("No %s directory\n", LOSTDIR); 912 return FSERROR; 913 } 914 } 915 if (!lfbuf) { 916 lfbuf = malloc(boot->ClusterSize); 917 if (!lfbuf) { 918 perror("No space for buffer"); 919 return FSFATAL; 920 } 921 p = NULL; 922 } else 923 p = lfbuf; 924 while (1) { 925 if (p) 926 for (; p < lfbuf + boot->ClusterSize; p += 32) 927 if (*p == SLOT_EMPTY 928 || *p == SLOT_DELETED) 929 break; 930 if (p && p < lfbuf + boot->ClusterSize) 931 break; 932 lfcl = p ? fat[lfcl].next : lostDir->head; 933 if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { 934 /* Extend LOSTDIR? XXX */ 935 pwarn("No space in %s\n", LOSTDIR); 936 return FSERROR; 937 } 938 lfoff = lfcl * boot->ClusterSize 939 + boot->ClusterOffset * boot->BytesPerSec; 940 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff 941 || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { 942 perror("could not read LOST.DIR"); 943 return FSFATAL; 944 } 945 p = lfbuf; 946 } 947 948 boot->NumFiles++; 949 /* Ensure uniqueness of entry here! XXX */ 950 memset(&d, 0, sizeof d); 951 (void)snprintf(d.name, sizeof(d.name), "%u", head); 952 d.flags = 0; 953 d.head = head; 954 d.size = fat[head].length * boot->ClusterSize; 955 956 memset(p, 0, 32); 957 memset(p, ' ', 11); 958 memcpy(p, d.name, strlen(d.name)); 959 p[26] = (u_char)d.head; 960 p[27] = (u_char)(d.head >> 8); 961 if (boot->ClustMask == CLUST32_MASK) { 962 p[20] = (u_char)(d.head >> 16); 963 p[21] = (u_char)(d.head >> 24); 964 } 965 p[28] = (u_char)d.size; 966 p[29] = (u_char)(d.size >> 8); 967 p[30] = (u_char)(d.size >> 16); 968 p[31] = (u_char)(d.size >> 24); 969 fat[head].flags |= FAT_USED; 970 if (lseek(dosfs, lfoff, SEEK_SET) != lfoff 971 || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { 972 perror("could not write LOST.DIR"); 973 return FSFATAL; 974 } 975 return FSDIRMOD; 976} 977 978void 979finishlf(void) 980{ 981 if (lfbuf) 982 free(lfbuf); 983 lfbuf = NULL; 984} 985