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