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