1 /* $NetBSD: fsdb.c,v 1.37 2008/04/28 20:23:08 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by John T. Kohl. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: fsdb.c,v 1.37 2008/04/28 20:23:08 martin Exp $"); 35 #endif /* not lint */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/param.h> 40 #include <sys/time.h> 41 #include <sys/mount.h> 42 #include <ctype.h> 43 #include <fcntl.h> 44 #include <grp.h> 45 #include <histedit.h> 46 #include <limits.h> 47 #include <pwd.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <time.h> 52 #include <unistd.h> 53 #include <err.h> 54 55 #include <ufs/ufs/dinode.h> 56 #include <ufs/ufs/dir.h> 57 #include <ufs/ffs/fs.h> 58 #include <ufs/ffs/ffs_extern.h> 59 60 #include "fsdb.h" 61 #include "fsck.h" 62 #include "extern.h" 63 64 static void usage(void); 65 static int cmdloop(void); 66 static char *prompt(EditLine *); 67 static int scannames(struct inodesc *); 68 static int dolookup(char *); 69 static int chinumfunc(struct inodesc *); 70 static int chnamefunc(struct inodesc *); 71 static int dotime(char *, int32_t *, int32_t *); 72 static void print_blks32(int32_t *buf, int size, uint64_t *blknum); 73 static void print_blks64(int64_t *buf, int size, uint64_t *blknum); 74 static void print_indirblks32(uint32_t blk, int ind_level, 75 uint64_t *blknum); 76 static void print_indirblks64(uint64_t blk, int ind_level, 77 uint64_t *blknum); 78 static int compare_blk32(uint32_t *, uint32_t); 79 static int compare_blk64(uint64_t *, uint64_t); 80 static int founddatablk(uint64_t); 81 static int find_blks32(uint32_t *buf, int size, uint32_t *blknum); 82 static int find_blks64(uint64_t *buf, int size, uint64_t *blknum); 83 static int find_indirblks32(uint32_t blk, int ind_level, 84 uint32_t *blknum); 85 static int find_indirblks64(uint64_t blk, int ind_level, 86 uint64_t *blknum); 87 88 int returntosingle = 0; 89 union dinode *curinode; 90 ino_t curinum; 91 92 static void 93 usage(void) 94 { 95 errx(1, "usage: %s [-dFn] -f <fsname>", getprogname()); 96 } 97 /* 98 * We suck in lots of fsck code, and just pick & choose the stuff we want. 99 * 100 * fsreadfd is set up to read from the file system, fswritefd to write to 101 * the file system. 102 */ 103 int 104 main(int argc, char *argv[]) 105 { 106 int ch, rval; 107 char *fsys = NULL; 108 109 forceimage = 0; 110 debug = 0; 111 isappleufs = 0; 112 while ((ch = getopt(argc, argv, "dFf:n")) != -1) { 113 switch (ch) { 114 case 'd': 115 debug++; 116 break; 117 case 'F': 118 forceimage = 1; 119 break; 120 case 'f': 121 fsys = optarg; 122 break; 123 case 'n': 124 nflag++; 125 break; 126 default: 127 usage(); 128 } 129 } 130 if (fsys == NULL) 131 usage(); 132 endian = 0; 133 if (setup(fsys) <= 0) 134 errx(1, "cannot set up file system `%s'", fsys); 135 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys, 136 sblock->fs_fsmnt); 137 rval = cmdloop(); 138 if (nflag) 139 exit(rval); 140 sblock->fs_clean = 0; /* mark it dirty */ 141 sbdirty(); 142 markclean = 0; 143 ckfini(); 144 printf("*** FILE SYSTEM MARKED DIRTY\n"); 145 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 146 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 147 exit(rval); 148 } 149 150 #define CMDFUNC(func) static int func (int argc, char *argv[]) 151 #define CMDFUNCSTART(func) static int func(argc, argv) \ 152 int argc; \ 153 char *argv[]; 154 155 CMDFUNC(helpfn); 156 CMDFUNC(focus); /* focus on inode */ 157 CMDFUNC(active); /* print active inode */ 158 CMDFUNC(focusname); /* focus by name */ 159 CMDFUNC(zapi); /* clear inode */ 160 CMDFUNC(uplink); /* incr link */ 161 CMDFUNC(downlink); /* decr link */ 162 CMDFUNC(linkcount); /* set link count */ 163 CMDFUNC(quit); /* quit */ 164 CMDFUNC(ls); /* list directory */ 165 CMDFUNC(blks); /* list blocks */ 166 CMDFUNC(findblk); /* find block */ 167 CMDFUNC(rm); /* remove name */ 168 CMDFUNC(ln); /* add name */ 169 CMDFUNC(newtype); /* change type */ 170 CMDFUNC(chmode); /* change mode */ 171 CMDFUNC(chlen); /* change length */ 172 CMDFUNC(chaflags); /* change flags */ 173 CMDFUNC(chgen); /* change generation */ 174 CMDFUNC(chowner); /* change owner */ 175 CMDFUNC(chgroup); /* Change group */ 176 CMDFUNC(back); /* pop back to last ino */ 177 CMDFUNC(chmtime); /* Change mtime */ 178 CMDFUNC(chctime); /* Change ctime */ 179 CMDFUNC(chatime); /* Change atime */ 180 CMDFUNC(chinum); /* Change inode # of dirent */ 181 CMDFUNC(chname); /* Change dirname of dirent */ 182 183 static struct cmdtable cmds[] = { 184 {"help", "Print out help", 1, 1, helpfn}, 185 {"?", "Print out help", 1, 1, helpfn}, 186 {"inode", "Set active inode to INUM", 2, 2, focus}, 187 {"clri", "Clear inode INUM", 2, 2, zapi}, 188 {"lookup", "Set active inode by looking up NAME", 2, 2, focusname}, 189 {"cd", "Set active inode by looking up NAME", 2, 2, focusname}, 190 {"back", "Go to previous active inode", 1, 1, back}, 191 {"active", "Print active inode", 1, 1, active}, 192 {"print", "Print active inode", 1, 1, active}, 193 {"uplink", "Increment link count", 1, 1, uplink}, 194 {"downlink", "Decrement link count", 1, 1, downlink}, 195 {"linkcount", "Set link count to COUNT", 2, 2, linkcount}, 196 {"ls", "List current inode as directory", 1, 1, ls}, 197 {"blks", "List current inode's data blocks", 1, 1, blks}, 198 {"findblk", "Find inode owning disk block(s)", 2, 33, findblk}, 199 {"rm", "Remove NAME from current inode directory", 2, 2, rm}, 200 {"del", "Remove NAME from current inode directory", 2, 2, rm}, 201 {"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln}, 202 {"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum}, 203 {"chname", "Change dir entry number INDEX to NAME", 3, 3, chname}, 204 {"chtype", "Change type of current inode to TYPE", 2, 2, newtype}, 205 {"chmod", "Change mode of current inode to MODE", 2, 2, chmode}, 206 {"chown", "Change owner of current inode to OWNER", 2, 2, chowner}, 207 {"chlen", "Change length of current inode to LENGTH", 2, 2, chlen}, 208 {"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup}, 209 {"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags}, 210 {"chgen", "Change generation number of current inode to GEN", 2, 2, 211 chgen}, 212 {"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime}, 213 {"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime}, 214 {"atime", "Change atime of current inode to ATIME", 2, 2, chatime}, 215 {"quit", "Exit", 1, 1, quit}, 216 {"q", "Exit", 1, 1, quit}, 217 {"exit", "Exit", 1, 1, quit}, 218 { .cmd = NULL}, 219 }; 220 221 static int 222 helpfn(int argc, char *argv[]) 223 { 224 struct cmdtable *cmdtp; 225 226 printf("Commands are:\n%-10s %5s %5s %s\n", 227 "command", "min argc", "max argc", "what"); 228 229 for (cmdtp = cmds; cmdtp->cmd; cmdtp++) 230 printf("%-10s %5u %5u %s\n", 231 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt); 232 return 0; 233 } 234 235 static char * 236 prompt(EditLine *el) 237 { 238 static char pstring[64]; 239 snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ", 240 (unsigned long long)curinum); 241 return pstring; 242 } 243 244 245 static int 246 cmdloop(void) 247 { 248 char *line; 249 const char *elline; 250 int cmd_argc, rval = 0, known; 251 #define scratch known 252 char **cmd_argv; 253 struct cmdtable *cmdp; 254 History *hist; 255 HistEvent he; 256 EditLine *elptr; 257 258 curinode = ginode(ROOTINO); 259 curinum = ROOTINO; 260 printactive(); 261 262 hist = history_init(); 263 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 264 265 elptr = el_init(getprogname(), stdin, stdout, stderr); 266 el_set(elptr, EL_EDITOR, "emacs"); 267 el_set(elptr, EL_PROMPT, prompt); 268 el_set(elptr, EL_HIST, history, hist); 269 el_source(elptr, NULL); 270 271 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 272 if (debug) 273 printf("command `%s'\n", elline); 274 275 history(hist, &he, H_ENTER, elline); 276 277 line = strdup(elline); 278 cmd_argv = crack(line, &cmd_argc); 279 if (cmd_argc) { 280 /* 281 * el_parse returns -1 to signal that it's not been 282 * handled internally. 283 */ 284 if (el_parse(elptr, cmd_argc, 285 (const char **)cmd_argv) != -1) 286 continue; 287 known = 0; 288 for (cmdp = cmds; cmdp->cmd; cmdp++) { 289 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 290 if (cmd_argc >= cmdp->minargc && 291 cmd_argc <= cmdp->maxargc) 292 rval = 293 (*cmdp->handler)(cmd_argc, 294 cmd_argv); 295 else 296 rval = argcount(cmdp, cmd_argc, 297 cmd_argv); 298 known = 1; 299 break; 300 } 301 } 302 if (!known) 303 warnx("unknown command `%s'", cmd_argv[0]), 304 rval = 1; 305 } else 306 rval = 0; 307 free(line); 308 if (rval < 0) 309 return rval; 310 if (rval) 311 warnx("rval was %d", rval); 312 } 313 el_end(elptr); 314 history_end(hist); 315 return rval; 316 } 317 318 static ino_t ocurrent; 319 320 #define GETINUM(ac,inum) inum = strtoull(argv[ac], &cp, 0); \ 321 if (inum < ROOTINO || inum >= maxino || cp == argv[ac] || *cp != '\0' ) { \ 322 printf("inode %llu out of range; range is [%llu,%llu]\n", \ 323 (unsigned long long)inum, (unsigned long long)ROOTINO, \ 324 (unsigned long long)maxino); \ 325 return 1; \ 326 } 327 328 /* 329 * Focus on given inode number 330 */ 331 CMDFUNCSTART(focus) 332 { 333 ino_t inum; 334 char *cp; 335 336 GETINUM(1, inum); 337 curinode = ginode(inum); 338 ocurrent = curinum; 339 curinum = inum; 340 printactive(); 341 return 0; 342 } 343 344 CMDFUNCSTART(back) 345 { 346 curinum = ocurrent; 347 curinode = ginode(curinum); 348 printactive(); 349 return 0; 350 } 351 352 CMDFUNCSTART(zapi) 353 { 354 ino_t inum; 355 union dinode *dp; 356 char *cp; 357 358 GETINUM(1, inum); 359 dp = ginode(inum); 360 clearinode(dp); 361 inodirty(); 362 if (curinode) /* re-set after potential change */ 363 curinode = ginode(curinum); 364 return 0; 365 } 366 367 CMDFUNCSTART(active) 368 { 369 printactive(); 370 return 0; 371 } 372 373 CMDFUNCSTART(quit) 374 { 375 return -1; 376 } 377 378 CMDFUNCSTART(uplink) 379 { 380 int16_t nlink; 381 382 if (!checkactive()) 383 return 1; 384 nlink = iswap16(DIP(curinode, nlink)); 385 nlink++; 386 DIP_SET(curinode, nlink, iswap16(nlink)); 387 printf("inode %llu link count now %d\n", (unsigned long long)curinum, 388 nlink); 389 inodirty(); 390 return 0; 391 } 392 393 CMDFUNCSTART(downlink) 394 { 395 int16_t nlink; 396 397 if (!checkactive()) 398 return 1; 399 nlink = iswap16(DIP(curinode, nlink)); 400 nlink--; 401 DIP_SET(curinode, nlink, iswap16(nlink)); 402 printf("inode %llu link count now %d\n", (unsigned long long)curinum, 403 nlink); 404 inodirty(); 405 return 0; 406 } 407 408 static const char *typename[] = { 409 "unknown", 410 "fifo", 411 "char special", 412 "unregistered #3", 413 "directory", 414 "unregistered #5", 415 "blk special", 416 "unregistered #7", 417 "regular", 418 "unregistered #9", 419 "symlink", 420 "unregistered #11", 421 "socket", 422 "unregistered #13", 423 "whiteout", 424 }; 425 426 static int slot; 427 428 static int 429 scannames(struct inodesc *idesc) 430 { 431 struct direct *dirp = idesc->id_dirp; 432 433 printf("slot %d ino %d reclen %d: %s, `%.*s'\n", 434 slot++, iswap32(dirp->d_ino), iswap16(dirp->d_reclen), 435 typename[dirp->d_type], 436 dirp->d_namlen, dirp->d_name); 437 return (KEEPON); 438 } 439 440 CMDFUNCSTART(ls) 441 { 442 struct inodesc idesc; 443 checkactivedir(); /* let it go on anyway */ 444 445 slot = 0; 446 idesc.id_number = curinum; 447 idesc.id_func = scannames; 448 idesc.id_type = DATA; 449 idesc.id_fix = IGNORE; 450 ckinode(curinode, &idesc); 451 curinode = ginode(curinum); 452 453 return 0; 454 } 455 456 CMDFUNCSTART(blks) 457 { 458 uint64_t blkno = 0; 459 int i, type; 460 if (!curinode) { 461 warnx("no current inode"); 462 return 0; 463 } 464 type = iswap16(DIP(curinode, mode)) & IFMT; 465 if (type != IFDIR && type != IFREG) { 466 warnx("inode %llu not a file or directory", 467 (unsigned long long)curinum); 468 return 0; 469 } 470 if (is_ufs2) { 471 printf("I=%llu %lld blocks\n", (unsigned long long)curinum, 472 (long long)(iswap64(curinode->dp2.di_blocks))); 473 } else { 474 printf("I=%llu %d blocks\n", (unsigned long long)curinum, 475 iswap32(curinode->dp1.di_blocks)); 476 } 477 printf("Direct blocks:\n"); 478 if (is_ufs2) 479 print_blks64(curinode->dp2.di_db, NDADDR, &blkno); 480 else 481 print_blks32(curinode->dp1.di_db, NDADDR, &blkno); 482 483 if (is_ufs2) { 484 for (i = 0; i < NIADDR; i++) 485 print_indirblks64(iswap64(curinode->dp2.di_ib[i]), i, 486 &blkno); 487 } else { 488 for (i = 0; i < NIADDR; i++) 489 print_indirblks32(iswap32(curinode->dp1.di_ib[i]), i, 490 &blkno); 491 } 492 return 0; 493 } 494 495 static int findblk_numtofind; 496 static int wantedblksize; 497 CMDFUNCSTART(findblk) 498 { 499 ino_t inum, inosused; 500 uint32_t *wantedblk32 = NULL; 501 uint64_t *wantedblk64 = NULL; 502 struct cg *cgp = cgrp; 503 int i, c; 504 505 ocurrent = curinum; 506 wantedblksize = (argc - 1); 507 if (is_ufs2) { 508 wantedblk64 = malloc(sizeof(uint64_t) * wantedblksize); 509 if (wantedblk64 == NULL) { 510 perror("malloc"); 511 return 1; 512 } 513 memset(wantedblk64, 0, sizeof(uint64_t) * wantedblksize); 514 for (i = 1; i < argc; i++) 515 wantedblk64[i - 1] = 516 dbtofsb(sblock, strtoull(argv[i], NULL, 0)); 517 } else { 518 wantedblk32 = malloc(sizeof(uint32_t) * wantedblksize); 519 if (wantedblk32 == NULL) { 520 perror("malloc"); 521 return 1; 522 } 523 memset(wantedblk32, 0, sizeof(uint32_t) * wantedblksize); 524 for (i = 1; i < argc; i++) 525 wantedblk32[i - 1] = 526 dbtofsb(sblock, strtoull(argv[i], NULL, 0)); 527 } 528 findblk_numtofind = wantedblksize; 529 for (c = 0; c < sblock->fs_ncg; c++) { 530 inum = c * sblock->fs_ipg; 531 getblk(&cgblk, cgtod(sblock, c), sblock->fs_cgsize); 532 memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize); 533 if (needswap) 534 ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock); 535 if (is_ufs2) 536 inosused = cgp->cg_initediblk; 537 else 538 inosused = sblock->fs_ipg; 539 for (; inosused > 0; inum++, inosused--) { 540 if (inum < ROOTINO) 541 continue; 542 if (is_ufs2 ? compare_blk64(wantedblk64, 543 ino_to_fsba(sblock, inum)) : 544 compare_blk32(wantedblk32, 545 ino_to_fsba(sblock, inum))) { 546 printf("block %llu: inode block (%llu-%llu)\n", 547 (unsigned long long)fsbtodb(sblock, 548 ino_to_fsba(sblock, inum)), 549 (unsigned long long) 550 (inum / INOPB(sblock)) * INOPB(sblock), 551 (unsigned long long) 552 (inum / INOPB(sblock) + 1) * INOPB(sblock)); 553 findblk_numtofind--; 554 if (findblk_numtofind == 0) 555 goto end; 556 } 557 curinum = inum; 558 curinode = ginode(inum); 559 switch (iswap16(DIP(curinode, mode)) & IFMT) { 560 case IFDIR: 561 case IFREG: 562 if (DIP(curinode, blocks) == 0) 563 continue; 564 break; 565 case IFLNK: 566 { 567 uint64_t size = iswap64(DIP(curinode, size)); 568 if (size > 0 && 569 size < sblock->fs_maxsymlinklen && 570 DIP(curinode, blocks) == 0) 571 continue; 572 else 573 break; 574 } 575 default: 576 continue; 577 } 578 if (is_ufs2 ? 579 find_blks64(curinode->dp2.di_db, NDADDR, 580 wantedblk64) : 581 find_blks32(curinode->dp1.di_db, NDADDR, 582 wantedblk32)) 583 goto end; 584 for (i = 0; i < NIADDR; i++) { 585 if (is_ufs2 ? 586 compare_blk64(wantedblk64, 587 iswap64(curinode->dp2.di_ib[i])) : 588 compare_blk32(wantedblk32, 589 iswap32(curinode->dp1.di_ib[i]))) 590 if (founddatablk(is_ufs2 ? 591 iswap64(curinode->dp2.di_ib[i]) : 592 iswap32(curinode->dp1.di_ib[i]))) 593 goto end; 594 if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) : 595 (curinode->dp1.di_ib[i] != 0)) 596 if (is_ufs2 ? 597 find_indirblks64( 598 iswap64(curinode->dp2.di_ib[i]), 599 i, wantedblk64) : 600 find_indirblks32( 601 iswap32(curinode->dp1.di_ib[i]), 602 i, wantedblk32)) 603 goto end; 604 } 605 } 606 } 607 end: 608 if (wantedblk32) 609 free(wantedblk32); 610 if (wantedblk64) 611 free(wantedblk64); 612 curinum = ocurrent; 613 curinode = ginode(curinum); 614 return 0; 615 } 616 617 static int 618 compare_blk32(uint32_t *wantedblk, uint32_t curblk) 619 { 620 int i; 621 for (i = 0; i < wantedblksize; i++) { 622 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 623 wantedblk[i] = 0; 624 return 1; 625 } 626 } 627 return 0; 628 } 629 630 static int 631 compare_blk64(uint64_t *wantedblk, uint64_t curblk) 632 { 633 int i; 634 for (i = 0; i < wantedblksize; i++) { 635 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 636 wantedblk[i] = 0; 637 return 1; 638 } 639 } 640 return 0; 641 } 642 643 static int 644 founddatablk(uint64_t blk) 645 { 646 printf("%llu: data block of inode %llu\n", 647 (unsigned long long)fsbtodb(sblock, blk), 648 (unsigned long long)curinum); 649 findblk_numtofind--; 650 if (findblk_numtofind == 0) 651 return 1; 652 return 0; 653 } 654 655 static int 656 find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) 657 { 658 int blk; 659 for(blk = 0; blk < size; blk++) { 660 if (buf[blk] == 0) 661 continue; 662 if (compare_blk32(wantedblk, iswap32(buf[blk]))) { 663 if (founddatablk(iswap32(buf[blk]))) 664 return 1; 665 } 666 } 667 return 0; 668 } 669 670 static int 671 find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) 672 { 673 #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) 674 uint32_t idblk[MAXNINDIR]; 675 int i; 676 677 bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk), 678 (int)sblock->fs_bsize); 679 if (ind_level <= 0) { 680 if (find_blks32(idblk, 681 sblock->fs_bsize / sizeof(uint32_t), wantedblk)) 682 return 1; 683 } else { 684 ind_level--; 685 for (i = 0; i < sblock->fs_bsize / sizeof(uint32_t); i++) { 686 if (compare_blk32(wantedblk, iswap32(idblk[i]))) { 687 if (founddatablk(iswap32(idblk[i]))) 688 return 1; 689 } 690 if(idblk[i] != 0) 691 if (find_indirblks32(iswap32(idblk[i]), 692 ind_level, wantedblk)) 693 return 1; 694 } 695 } 696 #undef MAXNINDIR 697 return 0; 698 } 699 700 701 static int 702 find_blks64(uint64_t *buf, int size, uint64_t *wantedblk) 703 { 704 int blk; 705 for(blk = 0; blk < size; blk++) { 706 if (buf[blk] == 0) 707 continue; 708 if (compare_blk64(wantedblk, iswap64(buf[blk]))) { 709 if (founddatablk(iswap64(buf[blk]))) 710 return 1; 711 } 712 } 713 return 0; 714 } 715 716 static int 717 find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk) 718 { 719 #define MAXNINDIR (MAXBSIZE / sizeof(uint64_t)) 720 uint64_t idblk[MAXNINDIR]; 721 int i; 722 723 bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk), 724 (int)sblock->fs_bsize); 725 if (ind_level <= 0) { 726 if (find_blks64(idblk, 727 sblock->fs_bsize / sizeof(uint64_t), wantedblk)) 728 return 1; 729 } else { 730 ind_level--; 731 for (i = 0; i < sblock->fs_bsize / sizeof(uint64_t); i++) { 732 if (compare_blk64(wantedblk, iswap64(idblk[i]))) { 733 if (founddatablk(iswap64(idblk[i]))) 734 return 1; 735 } 736 if(idblk[i] != 0) 737 if (find_indirblks64(iswap64(idblk[i]), 738 ind_level, wantedblk)) 739 return 1; 740 } 741 } 742 #undef MAXNINDIR 743 return 0; 744 } 745 746 747 #define CHARS_PER_LINES 70 748 749 static void 750 print_blks32(int32_t *buf, int size, uint64_t *blknum) 751 { 752 int chars; 753 char prbuf[CHARS_PER_LINES+1]; 754 int blk; 755 756 chars = 0; 757 for(blk = 0; blk < size; blk++, (*blknum)++) { 758 if (buf[blk] == 0) 759 continue; 760 snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk])); 761 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { 762 printf("\n"); 763 chars = 0; 764 } 765 if (chars == 0) 766 printf("%" PRIu64 ": ", *blknum); 767 printf("%s", prbuf); 768 chars += strlen(prbuf); 769 } 770 printf("\n"); 771 } 772 773 static void 774 print_blks64(int64_t *buf, int size, uint64_t *blknum) 775 { 776 int chars; 777 char prbuf[CHARS_PER_LINES+1]; 778 int blk; 779 780 chars = 0; 781 for(blk = 0; blk < size; blk++, (*blknum)++) { 782 if (buf[blk] == 0) 783 continue; 784 snprintf(prbuf, CHARS_PER_LINES, "%lld ", 785 (long long)iswap64(buf[blk])); 786 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { 787 printf("\n"); 788 chars = 0; 789 } 790 if (chars == 0) 791 printf("%" PRIu64 ": ", *blknum); 792 printf("%s", prbuf); 793 chars += strlen(prbuf); 794 } 795 printf("\n"); 796 } 797 798 #undef CHARS_PER_LINES 799 800 static void 801 print_indirblks32(uint32_t blk, int ind_level, uint64_t *blknum) 802 { 803 #define MAXNINDIR (MAXBSIZE / sizeof(int32_t)) 804 const int ptrperblk_shift = sblock->fs_bshift - 2; 805 const int ptrperblk = 1 << ptrperblk_shift; 806 int32_t idblk[MAXNINDIR]; 807 int i; 808 809 if (blk == 0) { 810 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level); 811 return; 812 } 813 814 printf("Indirect block %lld (level %d):\n", (long long)blk, 815 ind_level+1); 816 bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk), 817 (int)sblock->fs_bsize); 818 if (ind_level <= 0) { 819 print_blks32(idblk, ptrperblk, blknum); 820 } else { 821 ind_level--; 822 for (i = 0; i < ptrperblk; i++) 823 print_indirblks32(iswap32(idblk[i]), ind_level, blknum); 824 } 825 #undef MAXNINDIR 826 } 827 828 static void 829 print_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum) 830 { 831 #define MAXNINDIR (MAXBSIZE / sizeof(int64_t)) 832 const int ptrperblk_shift = sblock->fs_bshift - 3; 833 const int ptrperblk = 1 << ptrperblk_shift; 834 int64_t idblk[MAXNINDIR]; 835 int i; 836 837 if (blk == 0) { 838 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level); 839 return; 840 } 841 842 printf("Indirect block %lld (level %d):\n", (long long)blk, 843 ind_level+1); 844 bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk), 845 (int)sblock->fs_bsize); 846 if (ind_level <= 0) { 847 print_blks64(idblk, ptrperblk, blknum); 848 } else { 849 ind_level--; 850 for (i = 0; i < ptrperblk; i++) 851 print_indirblks64(iswap64(idblk[i]), ind_level, blknum); 852 } 853 #undef MAXNINDIR 854 } 855 856 static int 857 dolookup(char *name) 858 { 859 struct inodesc idesc; 860 861 if (!checkactivedir()) 862 return 0; 863 idesc.id_number = curinum; 864 idesc.id_func = findino; 865 idesc.id_name = name; 866 idesc.id_type = DATA; 867 idesc.id_fix = IGNORE; 868 if (ckinode(curinode, &idesc) & FOUND) { 869 curinum = idesc.id_parent; 870 curinode = ginode(curinum); 871 printactive(); 872 return 1; 873 } else { 874 warnx("name `%s' not found in current inode directory", name); 875 return 0; 876 } 877 } 878 879 CMDFUNCSTART(focusname) 880 { 881 char *p, *val; 882 883 if (!checkactive()) 884 return 1; 885 886 ocurrent = curinum; 887 888 if (argv[1][0] == '/') { 889 curinum = ROOTINO; 890 curinode = ginode(ROOTINO); 891 } else { 892 if (!checkactivedir()) 893 return 1; 894 } 895 for (p = argv[1]; p != NULL;) { 896 while ((val = strsep(&p, "/")) != NULL && *val == '\0'); 897 if (val) { 898 printf("component `%s': ", val); 899 fflush(stdout); 900 if (!dolookup(val)) { 901 curinode = ginode(curinum); 902 return (1); 903 } 904 } 905 } 906 return 0; 907 } 908 909 CMDFUNCSTART(ln) 910 { 911 ino_t inum; 912 int rval; 913 char *cp; 914 915 GETINUM(1, inum); 916 917 if (!checkactivedir()) 918 return 1; 919 rval = makeentry(curinum, inum, argv[2]); 920 if (rval) 921 printf("Ino %llu entered as `%s'\n", (unsigned long long)inum, 922 argv[2]); 923 else 924 printf("could not enter name? weird.\n"); 925 curinode = ginode(curinum); 926 return rval; 927 } 928 929 CMDFUNCSTART(rm) 930 { 931 int rval; 932 933 if (!checkactivedir()) 934 return 1; 935 rval = changeino(curinum, argv[1], 0); 936 if (rval & ALTERED) { 937 printf("Name `%s' removed\n", argv[1]); 938 return 0; 939 } else { 940 printf("could not remove name? weird.\n"); 941 return 1; 942 } 943 } 944 945 static long slotcount, desired; 946 947 static int 948 chinumfunc(struct inodesc *idesc) 949 { 950 struct direct *dirp = idesc->id_dirp; 951 952 if (slotcount++ == desired) { 953 dirp->d_ino = iswap32(idesc->id_parent); 954 return STOP | ALTERED | FOUND; 955 } 956 return KEEPON; 957 } 958 959 CMDFUNCSTART(chinum) 960 { 961 char *cp; 962 ino_t inum; 963 struct inodesc idesc; 964 965 slotcount = 0; 966 if (!checkactivedir()) 967 return 1; 968 GETINUM(2, inum); 969 970 desired = strtol(argv[1], &cp, 0); 971 if (cp == argv[1] || *cp != '\0' || desired < 0) { 972 printf("invalid slot number `%s'\n", argv[1]); 973 return 1; 974 } 975 idesc.id_number = curinum; 976 idesc.id_func = chinumfunc; 977 idesc.id_fix = IGNORE; 978 idesc.id_type = DATA; 979 idesc.id_parent = inum; /* XXX convenient hiding place */ 980 981 if (ckinode(curinode, &idesc) & FOUND) 982 return 0; 983 else { 984 warnx("no %sth slot in current directory", argv[1]); 985 return 1; 986 } 987 } 988 989 static int 990 chnamefunc(struct inodesc *idesc) 991 { 992 struct direct *dirp = idesc->id_dirp; 993 struct direct testdir; 994 995 if (slotcount++ == desired) { 996 /* will name fit? */ 997 testdir.d_namlen = strlen(idesc->id_name); 998 if (DIRSIZ(NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) { 999 dirp->d_namlen = testdir.d_namlen; 1000 strlcpy(dirp->d_name, idesc->id_name, 1001 sizeof(dirp->d_name)); 1002 return STOP | ALTERED | FOUND; 1003 } else 1004 return STOP | FOUND; /* won't fit, so give up */ 1005 } 1006 return KEEPON; 1007 } 1008 1009 CMDFUNCSTART(chname) 1010 { 1011 int rval; 1012 char *cp; 1013 struct inodesc idesc; 1014 1015 slotcount = 0; 1016 if (!checkactivedir()) 1017 return 1; 1018 1019 desired = strtoul(argv[1], &cp, 0); 1020 if (cp == argv[1] || *cp != '\0') { 1021 printf("invalid slot number `%s'\n", argv[1]); 1022 return 1; 1023 } 1024 idesc.id_number = curinum; 1025 idesc.id_func = chnamefunc; 1026 idesc.id_fix = IGNORE; 1027 idesc.id_type = DATA; 1028 idesc.id_name = argv[2]; 1029 1030 rval = ckinode(curinode, &idesc); 1031 if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED)) 1032 return 0; 1033 else 1034 if (rval & FOUND) { 1035 warnx("new name `%s' does not fit in slot %s", 1036 argv[2], argv[1]); 1037 return 1; 1038 } else { 1039 warnx("no %sth slot in current directory", argv[1]); 1040 return 1; 1041 } 1042 } 1043 1044 static struct typemap { 1045 const char *typename; 1046 int typebits; 1047 } typenamemap[] = { 1048 { "file", IFREG }, 1049 { "dir", IFDIR }, 1050 { "socket", IFSOCK }, 1051 { "fifo", IFIFO }, 1052 }; 1053 1054 CMDFUNCSTART(newtype) 1055 { 1056 int type; 1057 uint16_t mode; 1058 struct typemap *tp; 1059 1060 if (!checkactive()) 1061 return 1; 1062 mode = iswap16(DIP(curinode, mode)); 1063 type = mode & IFMT; 1064 for (tp = typenamemap; 1065 tp < &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]; 1066 tp++) { 1067 if (!strcmp(argv[1], tp->typename)) { 1068 printf("setting type to %s\n", tp->typename); 1069 type = tp->typebits; 1070 break; 1071 } 1072 } 1073 if (tp == &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]) { 1074 warnx("type `%s' not known", argv[1]); 1075 warnx("try one of `file', `dir', `socket', `fifo'"); 1076 return 1; 1077 } 1078 DIP_SET(curinode, mode, iswap16((mode & ~IFMT) | type)); 1079 inodirty(); 1080 printactive(); 1081 return 0; 1082 } 1083 1084 CMDFUNCSTART(chmode) 1085 { 1086 long modebits; 1087 char *cp; 1088 uint16_t mode; 1089 1090 if (!checkactive()) 1091 return 1; 1092 1093 modebits = strtol(argv[1], &cp, 8); 1094 if (cp == argv[1] || *cp != '\0') { 1095 warnx("bad modebits `%s'", argv[1]); 1096 return 1; 1097 } 1098 mode = iswap16(DIP(curinode, mode)); 1099 DIP_SET(curinode, mode, iswap16((mode & ~07777) | modebits)); 1100 inodirty(); 1101 printactive(); 1102 return 0; 1103 } 1104 1105 CMDFUNCSTART(chlen) 1106 { 1107 long len; 1108 char *cp; 1109 1110 if (!checkactive()) 1111 return 1; 1112 1113 len = strtol(argv[1], &cp, 0); 1114 if (cp == argv[1] || *cp != '\0' || len < 0) { 1115 warnx("bad length '%s'", argv[1]); 1116 return 1; 1117 } 1118 DIP_SET(curinode, size, iswap64(len)); 1119 inodirty(); 1120 printactive(); 1121 return 0; 1122 } 1123 1124 CMDFUNCSTART(chaflags) 1125 { 1126 u_long flags; 1127 char *cp; 1128 1129 if (!checkactive()) 1130 return 1; 1131 1132 flags = strtoul(argv[1], &cp, 0); 1133 if (cp == argv[1] || *cp != '\0') { 1134 warnx("bad flags `%s'", argv[1]); 1135 return 1; 1136 } 1137 if (flags > UINT_MAX) { 1138 warnx("flags set beyond 32-bit range of field (0x%lx)", 1139 flags); 1140 return (1); 1141 } 1142 DIP_SET(curinode, flags, iswap32(flags)); 1143 inodirty(); 1144 printactive(); 1145 return 0; 1146 } 1147 1148 CMDFUNCSTART(chgen) 1149 { 1150 long gen; 1151 char *cp; 1152 1153 if (!checkactive()) 1154 return 1; 1155 1156 gen = strtol(argv[1], &cp, 0); 1157 if (cp == argv[1] || *cp != '\0') { 1158 warnx("bad gen `%s'", argv[1]); 1159 return 1; 1160 } 1161 if (gen > INT_MAX || gen < INT_MIN) { 1162 warnx("gen set beyond 32-bit range of field (0x%lx)", gen); 1163 return (1); 1164 } 1165 DIP_SET(curinode, gen, iswap32(gen)); 1166 inodirty(); 1167 printactive(); 1168 return 0; 1169 } 1170 1171 CMDFUNCSTART(linkcount) 1172 { 1173 int lcnt; 1174 char *cp; 1175 1176 if (!checkactive()) 1177 return 1; 1178 1179 lcnt = strtol(argv[1], &cp, 0); 1180 if (cp == argv[1] || *cp != '\0') { 1181 warnx("bad link count `%s'", argv[1]); 1182 return 1; 1183 } 1184 if (lcnt > USHRT_MAX || lcnt < 0) { 1185 warnx("max link count is %d", USHRT_MAX); 1186 return 1; 1187 } 1188 DIP_SET(curinode, nlink, iswap16(lcnt)); 1189 inodirty(); 1190 printactive(); 1191 return 0; 1192 } 1193 1194 CMDFUNCSTART(chowner) 1195 { 1196 unsigned long uid; 1197 char *cp; 1198 struct passwd *pwd; 1199 1200 if (!checkactive()) 1201 return 1; 1202 1203 uid = strtoul(argv[1], &cp, 0); 1204 if (cp == argv[1] || *cp != '\0') { 1205 /* try looking up name */ 1206 if ((pwd = getpwnam(argv[1])) != 0) { 1207 uid = pwd->pw_uid; 1208 } else { 1209 warnx("bad uid `%s'", argv[1]); 1210 return 1; 1211 } 1212 } 1213 if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT) 1214 curinode->dp1.di_ouid = iswap32(uid); 1215 else 1216 DIP_SET(curinode, uid, iswap32(uid)); 1217 inodirty(); 1218 printactive(); 1219 return 0; 1220 } 1221 1222 CMDFUNCSTART(chgroup) 1223 { 1224 unsigned long gid; 1225 char *cp; 1226 struct group *grp; 1227 1228 if (!checkactive()) 1229 return 1; 1230 1231 gid = strtoul(argv[1], &cp, 0); 1232 if (cp == argv[1] || *cp != '\0') { 1233 if ((grp = getgrnam(argv[1])) != 0) { 1234 gid = grp->gr_gid; 1235 } else { 1236 warnx("bad gid `%s'", argv[1]); 1237 return 1; 1238 } 1239 } 1240 if (sblock->fs_old_inodefmt < FS_44INODEFMT) 1241 curinode->dp1.di_ogid = iswap32(gid); 1242 else 1243 DIP_SET(curinode, gid, iswap32(gid)); 1244 inodirty(); 1245 printactive(); 1246 return 0; 1247 } 1248 1249 static int 1250 dotime(char *name, int32_t *rsec, int32_t *rnsec) 1251 { 1252 char *p, *val; 1253 struct tm t; 1254 int32_t sec; 1255 int32_t nsec; 1256 p = strchr(name, '.'); 1257 if (p) { 1258 *p = '\0'; 1259 nsec = strtoul(++p, &val, 0); 1260 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 1261 warnx("invalid nanoseconds"); 1262 goto badformat; 1263 } 1264 } else 1265 nsec = 0; 1266 if (strlen(name) != 14) { 1267 badformat: 1268 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 1269 return 1; 1270 } 1271 for (p = name; *p; p++) 1272 if (*p < '0' || *p > '9') 1273 goto badformat; 1274 1275 p = name; 1276 #define VAL() ((*p++) - '0') 1277 t.tm_year = VAL(); 1278 t.tm_year = VAL() + t.tm_year * 10; 1279 t.tm_year = VAL() + t.tm_year * 10; 1280 t.tm_year = VAL() + t.tm_year * 10 - 1900; 1281 t.tm_mon = VAL(); 1282 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 1283 t.tm_mday = VAL(); 1284 t.tm_mday = VAL() + t.tm_mday * 10; 1285 t.tm_hour = VAL(); 1286 t.tm_hour = VAL() + t.tm_hour * 10; 1287 t.tm_min = VAL(); 1288 t.tm_min = VAL() + t.tm_min * 10; 1289 t.tm_sec = VAL(); 1290 t.tm_sec = VAL() + t.tm_sec * 10; 1291 t.tm_isdst = -1; 1292 1293 sec = mktime(&t); 1294 if (sec == -1) { 1295 warnx("date/time out of range"); 1296 return 1; 1297 } 1298 *rsec = iswap32(sec); 1299 *rnsec = iswap32(nsec); 1300 return 0; 1301 } 1302 1303 CMDFUNCSTART(chmtime) 1304 { 1305 int32_t rsec, nsec; 1306 1307 if (dotime(argv[1], &rsec, &nsec)) 1308 return 1; 1309 DIP_SET(curinode, mtime, rsec); 1310 DIP_SET(curinode, mtimensec, nsec); 1311 inodirty(); 1312 printactive(); 1313 return 0; 1314 } 1315 1316 CMDFUNCSTART(chatime) 1317 { 1318 int32_t rsec, nsec; 1319 1320 if (dotime(argv[1], &rsec, &nsec)) 1321 return 1; 1322 DIP_SET(curinode, atime, rsec); 1323 DIP_SET(curinode, atimensec, nsec); 1324 inodirty(); 1325 printactive(); 1326 return 0; 1327 } 1328 1329 CMDFUNCSTART(chctime) 1330 { 1331 int32_t rsec, nsec; 1332 1333 if (dotime(argv[1], &rsec, &nsec)) 1334 return 1; 1335 DIP_SET(curinode, ctime, rsec); 1336 DIP_SET(curinode, ctimensec, nsec); 1337 inodirty(); 1338 printactive(); 1339 return 0; 1340 } 1341