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