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