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