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