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