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