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