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