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