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