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 cg *cgp = &cgrp; 445 int c, i, is_ufs2; 446 447 wantedblksize = (argc - 1); 448 is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC; 449 ocurrent = curinum; 450 451 if (is_ufs2) { 452 wantedblk64 = calloc(wantedblksize, sizeof(uint64_t)); 453 if (wantedblk64 == NULL) 454 err(1, "malloc"); 455 for (i = 1; i < argc; i++) 456 wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); 457 } else { 458 wantedblk32 = calloc(wantedblksize, sizeof(uint32_t)); 459 if (wantedblk32 == NULL) 460 err(1, "malloc"); 461 for (i = 1; i < argc; i++) 462 wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); 463 } 464 findblk_numtofind = wantedblksize; 465 /* 466 * sblock.fs_ncg holds a number of cylinder groups. 467 * Iterate over all cylinder groups. 468 */ 469 for (c = 0; c < sblock.fs_ncg; c++) { 470 /* 471 * sblock.fs_ipg holds a number of inodes per cylinder group. 472 * Calculate a highest inode number for a given cylinder group. 473 */ 474 inum = c * sblock.fs_ipg; 475 /* Read cylinder group. */ 476 getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize); 477 memcpy(cgp, cgblk.b_un.b_cg, sblock.fs_cgsize); 478 /* 479 * Get a highest used inode number for a given cylinder group. 480 * For UFS1 all inodes initialized at the newfs stage. 481 */ 482 if (is_ufs2) 483 inosused = cgp->cg_initediblk; 484 else 485 inosused = sblock.fs_ipg; 486 487 for (; inosused > 0; inum++, inosused--) { 488 /* Skip magic inodes: 0, WINO, ROOTINO. */ 489 if (inum < ROOTINO) 490 continue; 491 /* 492 * Check if the block we are looking for is just an inode block. 493 * 494 * ino_to_fsba() - get block containing inode from its number. 495 * INOPB() - get a number of inodes in one disk block. 496 */ 497 if (is_ufs2 ? 498 compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) : 499 compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) { 500 printf("block %llu: inode block (%ju-%ju)\n", 501 (unsigned long long)fsbtodb(&sblock, 502 ino_to_fsba(&sblock, inum)), 503 (uintmax_t)(inum / INOPB(&sblock)) * INOPB(&sblock), 504 (uintmax_t)(inum / INOPB(&sblock) + 1) * INOPB(&sblock)); 505 findblk_numtofind--; 506 if (findblk_numtofind == 0) 507 goto end; 508 } 509 /* Get on-disk inode aka dinode. */ 510 curinum = inum; 511 curinode = ginode(inum); 512 /* Find IFLNK dinode with allocated data blocks. */ 513 switch (DIP(curinode, di_mode) & IFMT) { 514 case IFDIR: 515 case IFREG: 516 if (DIP(curinode, di_blocks) == 0) 517 continue; 518 break; 519 case IFLNK: 520 { 521 uint64_t size = DIP(curinode, di_size); 522 if (size > 0 && size < sblock.fs_maxsymlinklen && 523 DIP(curinode, di_blocks) == 0) 524 continue; 525 else 526 break; 527 } 528 default: 529 continue; 530 } 531 /* Look through direct data blocks. */ 532 if (is_ufs2 ? 533 find_blks64(curinode->dp2.di_db, NDADDR, wantedblk64) : 534 find_blks32(curinode->dp1.di_db, NDADDR, wantedblk32)) 535 goto end; 536 for (i = 0; i < NIADDR; i++) { 537 /* 538 * Does the block we are looking for belongs to the 539 * indirect blocks? 540 */ 541 if (is_ufs2 ? 542 compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) : 543 compare_blk32(wantedblk32, curinode->dp1.di_ib[i])) 544 if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] : 545 curinode->dp1.di_ib[i])) 546 goto end; 547 /* 548 * Search through indirect, double and triple indirect 549 * data blocks. 550 */ 551 if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) : 552 (curinode->dp1.di_ib[i] != 0)) 553 if (is_ufs2 ? 554 find_indirblks64(curinode->dp2.di_ib[i], i, 555 wantedblk64) : 556 find_indirblks32(curinode->dp1.di_ib[i], i, 557 wantedblk32)) 558 goto end; 559 } 560 } 561 } 562 end: 563 curinum = ocurrent; 564 curinode = ginode(curinum); 565 return 0; 566 } 567 568 static int 569 compare_blk32(uint32_t *wantedblk, uint32_t curblk) 570 { 571 int i; 572 573 for (i = 0; i < wantedblksize; i++) { 574 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 575 wantedblk[i] = 0; 576 return 1; 577 } 578 } 579 return 0; 580 } 581 582 static int 583 compare_blk64(uint64_t *wantedblk, uint64_t curblk) 584 { 585 int i; 586 587 for (i = 0; i < wantedblksize; i++) { 588 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 589 wantedblk[i] = 0; 590 return 1; 591 } 592 } 593 return 0; 594 } 595 596 static int 597 founddatablk(uint64_t blk) 598 { 599 600 printf("%llu: data block of inode %ju\n", 601 (unsigned long long)fsbtodb(&sblock, blk), (uintmax_t)curinum); 602 findblk_numtofind--; 603 if (findblk_numtofind == 0) 604 return 1; 605 return 0; 606 } 607 608 static int 609 find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) 610 { 611 int blk; 612 for (blk = 0; blk < size; blk++) { 613 if (buf[blk] == 0) 614 continue; 615 if (compare_blk32(wantedblk, buf[blk])) { 616 if (founddatablk(buf[blk])) 617 return 1; 618 } 619 } 620 return 0; 621 } 622 623 static int 624 find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) 625 { 626 #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) 627 uint32_t idblk[MAXNINDIR]; 628 int i; 629 630 blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); 631 if (ind_level <= 0) { 632 if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk)) 633 return 1; 634 } else { 635 ind_level--; 636 for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) { 637 if (compare_blk32(wantedblk, idblk[i])) { 638 if (founddatablk(idblk[i])) 639 return 1; 640 } 641 if (idblk[i] != 0) 642 if (find_indirblks32(idblk[i], ind_level, wantedblk)) 643 return 1; 644 } 645 } 646 #undef MAXNINDIR 647 return 0; 648 } 649 650 static int 651 find_blks64(uint64_t *buf, int size, uint64_t *wantedblk) 652 { 653 int blk; 654 for (blk = 0; blk < size; blk++) { 655 if (buf[blk] == 0) 656 continue; 657 if (compare_blk64(wantedblk, buf[blk])) { 658 if (founddatablk(buf[blk])) 659 return 1; 660 } 661 } 662 return 0; 663 } 664 665 static int 666 find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk) 667 { 668 #define MAXNINDIR (MAXBSIZE / sizeof(uint64_t)) 669 uint64_t idblk[MAXNINDIR]; 670 int i; 671 672 blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); 673 if (ind_level <= 0) { 674 if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk)) 675 return 1; 676 } else { 677 ind_level--; 678 for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) { 679 if (compare_blk64(wantedblk, idblk[i])) { 680 if (founddatablk(idblk[i])) 681 return 1; 682 } 683 if (idblk[i] != 0) 684 if (find_indirblks64(idblk[i], ind_level, wantedblk)) 685 return 1; 686 } 687 } 688 #undef MAXNINDIR 689 return 0; 690 } 691 692 int findino(struct inodesc *idesc); /* from fsck */ 693 static int dolookup(char *name); 694 695 static int 696 dolookup(char *name) 697 { 698 struct inodesc idesc; 699 700 if (!checkactivedir()) 701 return 0; 702 idesc.id_number = curinum; 703 idesc.id_func = findino; 704 idesc.id_name = name; 705 idesc.id_type = DATA; 706 idesc.id_fix = IGNORE; 707 if (ckinode(curinode, &idesc) & FOUND) { 708 curinum = idesc.id_parent; 709 curinode = ginode(curinum); 710 printactive(0); 711 return 1; 712 } else { 713 warnx("name `%s' not found in current inode directory", name); 714 return 0; 715 } 716 } 717 718 CMDFUNCSTART(focusname) 719 { 720 char *p, *val; 721 722 if (!checkactive()) 723 return 1; 724 725 ocurrent = curinum; 726 727 if (argv[1][0] == '/') { 728 curinum = ROOTINO; 729 curinode = ginode(ROOTINO); 730 } else { 731 if (!checkactivedir()) 732 return 1; 733 } 734 for (p = argv[1]; p != NULL;) { 735 while ((val = strsep(&p, "/")) != NULL && *val == '\0'); 736 if (val) { 737 printf("component `%s': ", val); 738 fflush(stdout); 739 if (!dolookup(val)) { 740 curinode = ginode(curinum); 741 return(1); 742 } 743 } 744 } 745 return 0; 746 } 747 748 CMDFUNCSTART(ln) 749 { 750 ino_t inum; 751 int rval; 752 char *cp; 753 754 GETINUM(1,inum); 755 756 if (!checkactivedir()) 757 return 1; 758 rval = makeentry(curinum, inum, argv[2]); 759 if (rval) 760 printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]); 761 else 762 printf("could not enter name? weird.\n"); 763 curinode = ginode(curinum); 764 return rval; 765 } 766 767 CMDFUNCSTART(rm) 768 { 769 int rval; 770 771 if (!checkactivedir()) 772 return 1; 773 rval = changeino(curinum, argv[1], 0); 774 if (rval & ALTERED) { 775 printf("Name `%s' removed\n", argv[1]); 776 return 0; 777 } else { 778 printf("could not remove name ('%s')? weird.\n", argv[1]); 779 return 1; 780 } 781 } 782 783 long slotcount, desired; 784 785 int 786 chinumfunc(struct inodesc *idesc) 787 { 788 struct direct *dirp = idesc->id_dirp; 789 790 if (slotcount++ == desired) { 791 dirp->d_ino = idesc->id_parent; 792 return STOP|ALTERED|FOUND; 793 } 794 return KEEPON; 795 } 796 797 CMDFUNCSTART(chinum) 798 { 799 char *cp; 800 ino_t inum; 801 struct inodesc idesc; 802 803 slotcount = 0; 804 if (!checkactivedir()) 805 return 1; 806 GETINUM(2,inum); 807 808 desired = strtol(argv[1], &cp, 0); 809 if (cp == argv[1] || *cp != '\0' || desired < 0) { 810 printf("invalid slot number `%s'\n", argv[1]); 811 return 1; 812 } 813 814 idesc.id_number = curinum; 815 idesc.id_func = chinumfunc; 816 idesc.id_fix = IGNORE; 817 idesc.id_type = DATA; 818 idesc.id_parent = inum; /* XXX convenient hiding place */ 819 820 if (ckinode(curinode, &idesc) & FOUND) 821 return 0; 822 else { 823 warnx("no %sth slot in current directory", argv[1]); 824 return 1; 825 } 826 } 827 828 int 829 chnamefunc(struct inodesc *idesc) 830 { 831 struct direct *dirp = idesc->id_dirp; 832 struct direct testdir; 833 834 if (slotcount++ == desired) { 835 /* will name fit? */ 836 testdir.d_namlen = strlen(idesc->id_name); 837 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) { 838 dirp->d_namlen = testdir.d_namlen; 839 strcpy(dirp->d_name, idesc->id_name); 840 return STOP|ALTERED|FOUND; 841 } else 842 return STOP|FOUND; /* won't fit, so give up */ 843 } 844 return KEEPON; 845 } 846 847 CMDFUNCSTART(chname) 848 { 849 int rval; 850 char *cp; 851 struct inodesc idesc; 852 853 slotcount = 0; 854 if (!checkactivedir()) 855 return 1; 856 857 desired = strtoul(argv[1], &cp, 0); 858 if (cp == argv[1] || *cp != '\0') { 859 printf("invalid slot number `%s'\n", argv[1]); 860 return 1; 861 } 862 863 idesc.id_number = curinum; 864 idesc.id_func = chnamefunc; 865 idesc.id_fix = IGNORE; 866 idesc.id_type = DATA; 867 idesc.id_name = argv[2]; 868 869 rval = ckinode(curinode, &idesc); 870 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED)) 871 return 0; 872 else if (rval & FOUND) { 873 warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]); 874 return 1; 875 } else { 876 warnx("no %sth slot in current directory", argv[1]); 877 return 1; 878 } 879 } 880 881 struct typemap { 882 const char *typename; 883 int typebits; 884 } typenamemap[] = { 885 {"file", IFREG}, 886 {"dir", IFDIR}, 887 {"socket", IFSOCK}, 888 {"fifo", IFIFO}, 889 }; 890 891 CMDFUNCSTART(newtype) 892 { 893 int type; 894 struct typemap *tp; 895 896 if (!checkactive()) 897 return 1; 898 type = DIP(curinode, di_mode) & IFMT; 899 for (tp = typenamemap; 900 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]; 901 tp++) { 902 if (!strcmp(argv[1], tp->typename)) { 903 printf("setting type to %s\n", tp->typename); 904 type = tp->typebits; 905 break; 906 } 907 } 908 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) { 909 warnx("type `%s' not known", argv[1]); 910 warnx("try one of `file', `dir', `socket', `fifo'"); 911 return 1; 912 } 913 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT); 914 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type); 915 inodirty(); 916 printactive(0); 917 return 0; 918 } 919 920 CMDFUNCSTART(chlen) 921 { 922 int rval = 1; 923 long len; 924 char *cp; 925 926 if (!checkactive()) 927 return 1; 928 929 len = strtol(argv[1], &cp, 0); 930 if (cp == argv[1] || *cp != '\0' || len < 0) { 931 warnx("bad length `%s'", argv[1]); 932 return 1; 933 } 934 935 DIP_SET(curinode, di_size, len); 936 inodirty(); 937 printactive(0); 938 return rval; 939 } 940 941 CMDFUNCSTART(chmode) 942 { 943 int rval = 1; 944 long modebits; 945 char *cp; 946 947 if (!checkactive()) 948 return 1; 949 950 modebits = strtol(argv[1], &cp, 8); 951 if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) { 952 warnx("bad modebits `%s'", argv[1]); 953 return 1; 954 } 955 956 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777); 957 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits); 958 inodirty(); 959 printactive(0); 960 return rval; 961 } 962 963 CMDFUNCSTART(chaflags) 964 { 965 int rval = 1; 966 u_long flags; 967 char *cp; 968 969 if (!checkactive()) 970 return 1; 971 972 flags = strtoul(argv[1], &cp, 0); 973 if (cp == argv[1] || *cp != '\0' ) { 974 warnx("bad flags `%s'", argv[1]); 975 return 1; 976 } 977 978 if (flags > UINT_MAX) { 979 warnx("flags set beyond 32-bit range of field (%lx)\n", flags); 980 return(1); 981 } 982 DIP_SET(curinode, di_flags, flags); 983 inodirty(); 984 printactive(0); 985 return rval; 986 } 987 988 CMDFUNCSTART(chgen) 989 { 990 int rval = 1; 991 long gen; 992 char *cp; 993 994 if (!checkactive()) 995 return 1; 996 997 gen = strtol(argv[1], &cp, 0); 998 if (cp == argv[1] || *cp != '\0' ) { 999 warnx("bad gen `%s'", argv[1]); 1000 return 1; 1001 } 1002 1003 if (gen > INT_MAX || gen < INT_MIN) { 1004 warnx("gen set beyond 32-bit range of field (%lx)\n", gen); 1005 return(1); 1006 } 1007 DIP_SET(curinode, di_gen, gen); 1008 inodirty(); 1009 printactive(0); 1010 return rval; 1011 } 1012 1013 CMDFUNCSTART(linkcount) 1014 { 1015 int rval = 1; 1016 int lcnt; 1017 char *cp; 1018 1019 if (!checkactive()) 1020 return 1; 1021 1022 lcnt = strtol(argv[1], &cp, 0); 1023 if (cp == argv[1] || *cp != '\0' ) { 1024 warnx("bad link count `%s'", argv[1]); 1025 return 1; 1026 } 1027 if (lcnt > USHRT_MAX || lcnt < 0) { 1028 warnx("max link count is %d\n", USHRT_MAX); 1029 return 1; 1030 } 1031 1032 DIP_SET(curinode, di_nlink, lcnt); 1033 inodirty(); 1034 printactive(0); 1035 return rval; 1036 } 1037 1038 CMDFUNCSTART(chowner) 1039 { 1040 int rval = 1; 1041 unsigned long uid; 1042 char *cp; 1043 struct passwd *pwd; 1044 1045 if (!checkactive()) 1046 return 1; 1047 1048 uid = strtoul(argv[1], &cp, 0); 1049 if (cp == argv[1] || *cp != '\0' ) { 1050 /* try looking up name */ 1051 if ((pwd = getpwnam(argv[1]))) { 1052 uid = pwd->pw_uid; 1053 } else { 1054 warnx("bad uid `%s'", argv[1]); 1055 return 1; 1056 } 1057 } 1058 1059 DIP_SET(curinode, di_uid, uid); 1060 inodirty(); 1061 printactive(0); 1062 return rval; 1063 } 1064 1065 CMDFUNCSTART(chgroup) 1066 { 1067 int rval = 1; 1068 unsigned long gid; 1069 char *cp; 1070 struct group *grp; 1071 1072 if (!checkactive()) 1073 return 1; 1074 1075 gid = strtoul(argv[1], &cp, 0); 1076 if (cp == argv[1] || *cp != '\0' ) { 1077 if ((grp = getgrnam(argv[1]))) { 1078 gid = grp->gr_gid; 1079 } else { 1080 warnx("bad gid `%s'", argv[1]); 1081 return 1; 1082 } 1083 } 1084 1085 DIP_SET(curinode, di_gid, gid); 1086 inodirty(); 1087 printactive(0); 1088 return rval; 1089 } 1090 1091 int 1092 dotime(char *name, time_t *secp, int32_t *nsecp) 1093 { 1094 char *p, *val; 1095 struct tm t; 1096 int32_t nsec; 1097 p = strchr(name, '.'); 1098 if (p) { 1099 *p = '\0'; 1100 nsec = strtoul(++p, &val, 0); 1101 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 1102 warnx("invalid nanoseconds"); 1103 goto badformat; 1104 } 1105 } else 1106 nsec = 0; 1107 if (strlen(name) != 14) { 1108 badformat: 1109 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 1110 return 1; 1111 } 1112 *nsecp = nsec; 1113 1114 for (p = name; *p; p++) 1115 if (*p < '0' || *p > '9') 1116 goto badformat; 1117 1118 p = name; 1119 #define VAL() ((*p++) - '0') 1120 t.tm_year = VAL(); 1121 t.tm_year = VAL() + t.tm_year * 10; 1122 t.tm_year = VAL() + t.tm_year * 10; 1123 t.tm_year = VAL() + t.tm_year * 10 - 1900; 1124 t.tm_mon = VAL(); 1125 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 1126 t.tm_mday = VAL(); 1127 t.tm_mday = VAL() + t.tm_mday * 10; 1128 t.tm_hour = VAL(); 1129 t.tm_hour = VAL() + t.tm_hour * 10; 1130 t.tm_min = VAL(); 1131 t.tm_min = VAL() + t.tm_min * 10; 1132 t.tm_sec = VAL(); 1133 t.tm_sec = VAL() + t.tm_sec * 10; 1134 t.tm_isdst = -1; 1135 1136 *secp = mktime(&t); 1137 if (*secp == -1) { 1138 warnx("date/time out of range"); 1139 return 1; 1140 } 1141 return 0; 1142 } 1143 1144 CMDFUNCSTART(chbtime) 1145 { 1146 time_t secs; 1147 int32_t nsecs; 1148 1149 if (dotime(argv[1], &secs, &nsecs)) 1150 return 1; 1151 if (sblock.fs_magic == FS_UFS1_MAGIC) 1152 return 1; 1153 curinode->dp2.di_birthtime = _time_to_time64(secs); 1154 curinode->dp2.di_birthnsec = nsecs; 1155 inodirty(); 1156 printactive(0); 1157 return 0; 1158 } 1159 1160 CMDFUNCSTART(chmtime) 1161 { 1162 time_t secs; 1163 int32_t nsecs; 1164 1165 if (dotime(argv[1], &secs, &nsecs)) 1166 return 1; 1167 if (sblock.fs_magic == FS_UFS1_MAGIC) 1168 curinode->dp1.di_mtime = _time_to_time32(secs); 1169 else 1170 curinode->dp2.di_mtime = _time_to_time64(secs); 1171 DIP_SET(curinode, di_mtimensec, nsecs); 1172 inodirty(); 1173 printactive(0); 1174 return 0; 1175 } 1176 1177 CMDFUNCSTART(chatime) 1178 { 1179 time_t secs; 1180 int32_t nsecs; 1181 1182 if (dotime(argv[1], &secs, &nsecs)) 1183 return 1; 1184 if (sblock.fs_magic == FS_UFS1_MAGIC) 1185 curinode->dp1.di_atime = _time_to_time32(secs); 1186 else 1187 curinode->dp2.di_atime = _time_to_time64(secs); 1188 DIP_SET(curinode, di_atimensec, nsecs); 1189 inodirty(); 1190 printactive(0); 1191 return 0; 1192 } 1193 1194 CMDFUNCSTART(chctime) 1195 { 1196 time_t secs; 1197 int32_t nsecs; 1198 1199 if (dotime(argv[1], &secs, &nsecs)) 1200 return 1; 1201 if (sblock.fs_magic == FS_UFS1_MAGIC) 1202 curinode->dp1.di_ctime = _time_to_time32(secs); 1203 else 1204 curinode->dp2.di_ctime = _time_to_time64(secs); 1205 DIP_SET(curinode, di_ctimensec, nsecs); 1206 inodirty(); 1207 printactive(0); 1208 return 0; 1209 } 1210