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