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