1/* $NetBSD: fsdb.c,v 1.54 2023/01/07 19:41:29 chs Exp $ */ 2 3/*- 4 * Copyright (c) 1996, 2017 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by John T. Kohl. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__RCSID("$NetBSD: fsdb.c,v 1.54 2023/01/07 19:41:29 chs Exp $"); 35#endif /* not lint */ 36 37#include <sys/types.h> 38#include <sys/stat.h> 39#include <sys/param.h> 40#include <sys/time.h> 41#include <sys/mount.h> 42#include <ctype.h> 43#include <fcntl.h> 44#include <grp.h> 45#include <histedit.h> 46#include <limits.h> 47#include <pwd.h> 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51#include <time.h> 52#include <unistd.h> 53#include <err.h> 54#include <stdbool.h> 55 56#include <ufs/ufs/dinode.h> 57#include <ufs/ufs/dir.h> 58#include <ufs/ffs/fs.h> 59#include <ufs/ffs/ffs_extern.h> 60 61#include "fsdb.h" 62#include "fsck.h" 63#include "extern.h" 64 65struct bufarea bufhead; 66struct bufarea sblk; 67struct bufarea asblk; 68struct bufarea cgblk; 69struct bufarea appleufsblk; 70struct bufarea *pdirbp; 71struct bufarea *pbp; 72struct fs *sblock; 73struct fs *altsblock; 74struct cg *cgrp; 75struct fs *sblocksave; 76struct dups *duplist; 77struct dups *muldup; 78struct zlncnt *zlnhead; 79struct inoinfo **inphead, **inpsort; 80long numdirs, dirhash, listmax, inplast; 81struct uquot_hash *uquot_user_hash; 82struct uquot_hash *uquot_group_hash; 83uint8_t q2h_hash_shift; 84uint16_t q2h_hash_mask; 85struct inostatlist *inostathead; 86long dev_bsize; 87long secsize; 88char nflag; 89char yflag; 90int Uflag; 91int bflag; 92int debug; 93int zflag; 94int cvtlevel; 95int eaflag; 96int doinglevel1; 97int doinglevel2; 98int doing2ea; 99int doing2noea; 100int newinofmt; 101char usedsoftdep; 102int preen; 103int forceimage; 104int is_ufs2; 105int is_ufs2ea; 106int markclean; 107char havesb; 108char skipclean; 109int fsmodified; 110int fsreadfd; 111int fswritefd; 112int rerun; 113char resolved; 114int endian; 115int doswap; 116int needswap; 117int do_blkswap; 118int do_dirswap; 119int isappleufs; 120daddr_t maxfsblock; 121char *blockmap; 122ino_t maxino; 123int dirblksiz; 124daddr_t n_blks; 125ino_t n_files; 126long countdirs; 127int got_siginfo; 128struct ufs1_dinode ufs1_zino; 129struct ufs2_dinode ufs2_zino; 130 131/* Used to keep state for "saveblks" command. */ 132struct wrinfo { 133 off_t size; 134 off_t written_size; 135 int fd; 136}; 137 138__dead static void usage(void); 139static int cmdloop(void); 140static char *prompt(EditLine *); 141static int scannames(struct inodesc *); 142static int dolookup(char *); 143static int chinumfunc(struct inodesc *); 144static int chnamefunc(struct inodesc *); 145static int chreclenfunc(struct inodesc *); 146static int dotime(char *, int64_t *, int32_t *); 147static void print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp); 148static void print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp); 149static void print_indirblks32(uint32_t blk, int ind_level, 150 uint64_t *blknum, struct wrinfo *wrp); 151static void print_indirblks64(uint64_t blk, int ind_level, 152 uint64_t *blknum, struct wrinfo *wrp); 153static int compare_blk32(uint32_t *, uint32_t); 154static int compare_blk64(uint64_t *, uint64_t); 155static int founddatablk(uint64_t); 156static int find_blks32(uint32_t *buf, int size, uint32_t *blknum); 157static int find_blks64(uint64_t *buf, int size, uint64_t *blknum); 158static int find_indirblks32(uint32_t blk, int ind_level, 159 uint32_t *blknum); 160static int find_indirblks64(uint64_t blk, int ind_level, 161 uint64_t *blknum); 162 163union dinode *curinode; 164ino_t curinum; 165 166static void 167usage(void) 168{ 169 errx(1, "usage: %s [-dFfNn] <fsname>", getprogname()); 170} 171/* 172 * We suck in lots of fsck code, and just pick & choose the stuff we want. 173 * 174 * fsreadfd is set up to read from the file system, fswritefd to write to 175 * the file system. 176 */ 177int 178main(int argc, char *argv[]) 179{ 180 int ch, rval; 181 char *fsys = NULL; 182 bool makedirty = true; 183 184 forceimage = 0; 185 debug = 0; 186 isappleufs = 0; 187 while ((ch = getopt(argc, argv, "dFf:Nn")) != -1) { 188 switch (ch) { 189 case 'd': 190 debug++; 191 break; 192 case 'F': 193 forceimage = 1; 194 break; 195 case 'f': 196 fsys = optarg; 197 break; 198 case 'N': 199 makedirty = false; 200 break; 201 case 'n': 202 nflag++; 203 break; 204 default: 205 usage(); 206 } 207 } 208 argc -= optind; 209 argv += optind; 210 if (fsys == NULL) 211 fsys = argv[0]; 212 if (fsys == NULL) 213 usage(); 214 endian = 0; 215 if (setup(fsys, fsys) <= 0) 216 errx(1, "cannot set up file system `%s'", fsys); 217 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys, 218 sblock->fs_fsmnt); 219 rval = cmdloop(); 220 if (nflag) 221 exit(rval); 222 if (!makedirty) { 223 ckfini(1); 224 exit(rval); 225 } 226 sblock->fs_clean = 0; /* mark it dirty */ 227 sbdirty(); 228 markclean = 0; 229 ckfini(1); 230 printf("*** FILE SYSTEM MARKED DIRTY\n"); 231 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 232 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 233 exit(rval); 234} 235 236#define CMDFUNC(func) static int func (int argc, char *argv[]) 237 238CMDFUNC(helpfn); 239CMDFUNC(focus); /* focus on inode */ 240CMDFUNC(active); /* print active inode */ 241CMDFUNC(focusname); /* focus by name */ 242CMDFUNC(zapi); /* clear inode */ 243CMDFUNC(uplink); /* incr link */ 244CMDFUNC(downlink); /* decr link */ 245CMDFUNC(linkcount); /* set link count */ 246CMDFUNC(quit); /* quit */ 247CMDFUNC(ls); /* list directory */ 248CMDFUNC(blks); /* list blocks */ 249CMDFUNC(findblk); /* find block */ 250CMDFUNC(rm); /* remove name */ 251CMDFUNC(ln); /* add name */ 252CMDFUNC(newtype); /* change type */ 253CMDFUNC(chmode); /* change mode */ 254CMDFUNC(chlen); /* change length */ 255CMDFUNC(chaflags); /* change flags */ 256CMDFUNC(chgen); /* change generation */ 257CMDFUNC(chowner); /* change owner */ 258CMDFUNC(chgroup); /* Change group */ 259CMDFUNC(back); /* pop back to last ino */ 260CMDFUNC(chmtime); /* Change mtime */ 261CMDFUNC(chctime); /* Change ctime */ 262CMDFUNC(chatime); /* Change atime */ 263CMDFUNC(chbirthtime); /* Change birthtime */ 264CMDFUNC(chinum); /* Change inode # of dirent */ 265CMDFUNC(chname); /* Change dirname of dirent */ 266CMDFUNC(chreclen); /* Change reclen of dirent */ 267CMDFUNC(chextsize); /* Change extsize */ 268CMDFUNC(chblocks); /* Change blocks */ 269CMDFUNC(chdb); /* Change direct block pointer */ 270CMDFUNC(chib); /* Change indirect block pointer */ 271CMDFUNC(chextb); /* Change extattr block pointer */ 272CMDFUNC(chfreelink); /* Change freelink pointer */ 273CMDFUNC(iptrs); /* print raw block pointers for active inode */ 274CMDFUNC(saveea); /* Save extattrs */ 275 276static struct cmdtable cmds[] = { 277 {"help", "Print out help", 1, 1, helpfn}, 278 {"?", "Print out help", 1, 1, helpfn}, 279 {"inode", "Set active inode to INUM", 2, 2, focus}, 280 {"clri", "Clear inode INUM", 2, 2, zapi}, 281 {"lookup", "Set active inode by looking up NAME", 2, 2, focusname}, 282 {"cd", "Set active inode by looking up NAME", 2, 2, focusname}, 283 {"back", "Go to previous active inode", 1, 1, back}, 284 {"active", "Print active inode", 1, 1, active}, 285 {"print", "Print active inode", 1, 1, active}, 286 {"uplink", "Increment link count", 1, 1, uplink}, 287 {"downlink", "Decrement link count", 1, 1, downlink}, 288 {"linkcount", "Set link count to COUNT", 2, 2, linkcount}, 289 {"ls", "List current inode as directory", 1, 1, ls}, 290 {"blks", "List current inode's data blocks", 1, 1, blks}, 291 {"saveblks", "Save current inode's data blocks to FILE", 2, 2, blks}, 292 {"findblk", "Find inode owning disk block(s)", 2, 33, findblk}, 293 {"rm", "Remove NAME from current inode directory", 2, 2, rm}, 294 {"del", "Remove NAME from current inode directory", 2, 2, rm}, 295 {"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln}, 296 {"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum}, 297 {"chname", "Change dir entry number INDEX to NAME", 3, 3, chname}, 298 {"chreclen", "Change dir entry number INDEX to RECLEN", 3, 3, chreclen}, 299 {"chtype", "Change type of current inode to TYPE", 2, 2, newtype}, 300 {"chmod", "Change mode of current inode to MODE", 2, 2, chmode}, 301 {"chown", "Change owner of current inode to OWNER", 2, 2, chowner}, 302 {"chlen", "Change length of current inode to LENGTH", 2, 2, chlen}, 303 {"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup}, 304 {"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags}, 305 {"chgen", "Change generation number of current inode to GEN", 2, 2, 306 chgen}, 307 { "chextsize", "Change extsize of current inode to EXTSIZE", 2, 2, chextsize }, 308 { "chblocks", "Change blocks of current inode to BLOCKS", 2, 2, chblocks }, 309 { "chdb", "Change db pointer N of current inode to BLKNO", 3, 3, chdb }, 310 { "chib", "Change ib pointer N of current inode to BLKNO", 3, 3, chib }, 311 { "chextb", "Change extb pointer N of current inode to BLKNO", 3, 3, chextb }, 312 { "chfreelink", "Change freelink of current inode to FREELINK", 2, 2, chfreelink }, 313 { "iptrs", "Print raw block pointers of current inode", 1, 1, iptrs }, 314 {"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime}, 315 {"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime}, 316 {"atime", "Change atime of current inode to ATIME", 2, 2, chatime}, 317 {"birthtime", "Change atime of current inode to BIRTHTIME", 2, 2, 318 chbirthtime}, 319 {"saveea", "Save current inode's extattr blocks to FILE", 2, 2, saveea}, 320 {"quit", "Exit", 1, 1, quit}, 321 {"q", "Exit", 1, 1, quit}, 322 {"exit", "Exit", 1, 1, quit}, 323 { .cmd = NULL}, 324}; 325 326static int 327helpfn(int argc, char *argv[]) 328{ 329 struct cmdtable *cmdtp; 330 331 printf("Commands are:\n%-10s %5s %5s %s\n", 332 "command", "min argc", "max argc", "what"); 333 334 for (cmdtp = cmds; cmdtp->cmd; cmdtp++) 335 printf("%-10s %5u %5u %s\n", 336 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt); 337 return 0; 338} 339 340static char * 341prompt(EditLine *el) 342{ 343 static char pstring[64]; 344 snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ", 345 (unsigned long long)curinum); 346 return pstring; 347} 348 349 350static int 351cmdloop(void) 352{ 353 char *line; 354 const char *elline; 355 int cmd_argc, rval = 0, known; 356#define scratch known 357 char **cmd_argv; 358 struct cmdtable *cmdp; 359 History *hist; 360 HistEvent he; 361 EditLine *elptr; 362 363 curinode = ginode(UFS_ROOTINO); 364 curinum = UFS_ROOTINO; 365 printactive(); 366 367 hist = history_init(); 368 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 369 370 elptr = el_init(getprogname(), stdin, stdout, stderr); 371 el_set(elptr, EL_EDITOR, "emacs"); 372 el_set(elptr, EL_PROMPT, prompt); 373 el_set(elptr, EL_HIST, history, hist); 374 el_source(elptr, NULL); 375 376 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 377 if (debug) 378 printf("command `%s'\n", elline); 379 380 history(hist, &he, H_ENTER, elline); 381 382 line = strdup(elline); 383 cmd_argv = crack(line, &cmd_argc); 384 if (cmd_argc) { 385 /* 386 * el_parse returns -1 to signal that it's not been 387 * handled internally. 388 */ 389 if (el_parse(elptr, cmd_argc, (void *)cmd_argv) != -1) 390 continue; 391 known = 0; 392 for (cmdp = cmds; cmdp->cmd; cmdp++) { 393 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 394 if (cmd_argc >= cmdp->minargc && 395 cmd_argc <= cmdp->maxargc) 396 rval = 397 (*cmdp->handler)(cmd_argc, 398 cmd_argv); 399 else 400 rval = argcount(cmdp, cmd_argc, 401 cmd_argv); 402 known = 1; 403 break; 404 } 405 } 406 if (!known) 407 warnx("unknown command `%s'", cmd_argv[0]), 408 rval = 1; 409 } else 410 rval = 0; 411 free(line); 412 if (rval < 0) 413 return rval; 414 if (rval) 415 warnx("rval was %d", rval); 416 } 417 el_end(elptr); 418 history_end(hist); 419 return rval; 420} 421 422static ino_t ocurrent; 423 424#define GETINUM(ac,inum) inum = strtoull(argv[ac], &cp, 0); \ 425 if (inum < UFS_ROOTINO || inum >= maxino || cp == argv[ac] || *cp != '\0' ) { \ 426 printf("inode %llu out of range; range is [%llu,%llu]\n", \ 427 (unsigned long long)inum, (unsigned long long)UFS_ROOTINO, \ 428 (unsigned long long)maxino); \ 429 return 1; \ 430 } 431 432/* 433 * Focus on given inode number 434 */ 435CMDFUNC(focus) 436{ 437 ino_t inum; 438 char *cp; 439 440 GETINUM(1, inum); 441 curinode = ginode(inum); 442 ocurrent = curinum; 443 curinum = inum; 444 printactive(); 445 return 0; 446} 447 448CMDFUNC(back) 449{ 450 curinum = ocurrent; 451 curinode = ginode(curinum); 452 printactive(); 453 return 0; 454} 455 456CMDFUNC(zapi) 457{ 458 ino_t inum; 459 union dinode *dp; 460 char *cp; 461 462 GETINUM(1, inum); 463 dp = ginode(inum); 464 clearinode(dp); 465 inodirty(); 466 if (curinode) /* re-set after potential change */ 467 curinode = ginode(curinum); 468 return 0; 469} 470 471CMDFUNC(active) 472{ 473 printactive(); 474 return 0; 475} 476 477CMDFUNC(quit) 478{ 479 return -1; 480} 481 482CMDFUNC(uplink) 483{ 484 int16_t nlink; 485 486 if (!checkactive()) 487 return 1; 488 nlink = iswap16(DIP(curinode, nlink)); 489 nlink++; 490 DIP_SET(curinode, nlink, iswap16(nlink)); 491 printf("inode %llu link count now %d\n", (unsigned long long)curinum, 492 nlink); 493 inodirty(); 494 return 0; 495} 496 497CMDFUNC(downlink) 498{ 499 int16_t nlink; 500 501 if (!checkactive()) 502 return 1; 503 nlink = iswap16(DIP(curinode, nlink)); 504 nlink--; 505 DIP_SET(curinode, nlink, iswap16(nlink)); 506 printf("inode %llu link count now %d\n", (unsigned long long)curinum, 507 nlink); 508 inodirty(); 509 return 0; 510} 511 512static const char *typename[] = { 513 "unknown", 514 "fifo", 515 "char special", 516 "unregistered #3", 517 "directory", 518 "unregistered #5", 519 "blk special", 520 "unregistered #7", 521 "regular", 522 "unregistered #9", 523 "symlink", 524 "unregistered #11", 525 "socket", 526 "unregistered #13", 527 "whiteout", 528}; 529 530static int diroff; 531static int slot; 532 533static int 534scannames(struct inodesc *idesc) 535{ 536 struct direct *dirp = idesc->id_dirp; 537 538 printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n", 539 slot++, diroff, iswap32(dirp->d_ino), iswap16(dirp->d_reclen), 540 typename[dirp->d_type], 541 dirp->d_namlen, dirp->d_name); 542 diroff += dirp->d_reclen; 543 return (KEEPON); 544} 545 546CMDFUNC(ls) 547{ 548 struct inodesc idesc; 549 checkactivedir(); /* let it go on anyway */ 550 551 slot = 0; 552 diroff = 0; 553 idesc.id_number = curinum; 554 idesc.id_func = scannames; 555 idesc.id_type = DATA; 556 idesc.id_fix = IGNORE; 557 ckinode(curinode, &idesc); 558 curinode = ginode(curinum); 559 560 return 0; 561} 562 563CMDFUNC(blks) 564{ 565 uint64_t blkno = 0; 566 int i; 567 struct wrinfo wrinfo, *wrp = NULL; 568 bool saveblks; 569 570 saveblks = strcmp(argv[0], "saveblks") == 0; 571 if (saveblks) { 572 wrinfo.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644); 573 if (wrinfo.fd == -1) { 574 warn("unable to create file %s", argv[1]); 575 return 0; 576 } 577 wrinfo.size = iswap64(DIP(curinode, size)); 578 wrinfo.written_size = 0; 579 wrp = &wrinfo; 580 } 581 if (!curinode) { 582 warnx("no current inode"); 583 return 0; 584 } 585 if (is_ufs2) { 586 printf("I=%llu %lld blocks\n", (unsigned long long)curinum, 587 (long long)(iswap64(curinode->dp2.di_blocks))); 588 } else { 589 printf("I=%llu %d blocks\n", (unsigned long long)curinum, 590 iswap32(curinode->dp1.di_blocks)); 591 } 592 printf("Direct blocks:\n"); 593 if (is_ufs2) 594 print_blks64(curinode->dp2.di_db, UFS_NDADDR, &blkno, wrp); 595 else 596 print_blks32(curinode->dp1.di_db, UFS_NDADDR, &blkno, wrp); 597 598 if (is_ufs2) { 599 for (i = 0; i < UFS_NIADDR; i++) 600 print_indirblks64(iswap64(curinode->dp2.di_ib[i]), i, 601 &blkno, wrp); 602 printf("Extattr blocks:\n"); 603 blkno = 0; 604 if (saveblks) 605 wrinfo.size += iswap32(curinode->dp2.di_extsize); 606 print_blks64(curinode->dp2.di_extb, UFS_NXADDR, &blkno, wrp); 607 } else { 608 for (i = 0; i < UFS_NIADDR; i++) 609 print_indirblks32(iswap32(curinode->dp1.di_ib[i]), i, 610 &blkno, wrp); 611 } 612 return 0; 613} 614 615static int findblk_numtofind; 616static int wantedblksize; 617CMDFUNC(findblk) 618{ 619 ino_t inum, inosused; 620 uint32_t *wantedblk32 = NULL; 621 uint64_t *wantedblk64 = NULL; 622 struct cg *cgp = cgrp; 623 int i; 624 uint32_t c; 625 626 ocurrent = curinum; 627 wantedblksize = (argc - 1); 628 if (is_ufs2) { 629 wantedblk64 = malloc(sizeof(uint64_t) * wantedblksize); 630 if (wantedblk64 == NULL) { 631 perror("malloc"); 632 return 1; 633 } 634 memset(wantedblk64, 0, sizeof(uint64_t) * wantedblksize); 635 for (i = 1; i < argc; i++) 636 wantedblk64[i - 1] = 637 FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0)); 638 } else { 639 wantedblk32 = malloc(sizeof(uint32_t) * wantedblksize); 640 if (wantedblk32 == NULL) { 641 perror("malloc"); 642 return 1; 643 } 644 memset(wantedblk32, 0, sizeof(uint32_t) * wantedblksize); 645 for (i = 1; i < argc; i++) 646 wantedblk32[i - 1] = 647 FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0)); 648 } 649 findblk_numtofind = wantedblksize; 650 for (c = 0; c < sblock->fs_ncg; c++) { 651 inum = c * sblock->fs_ipg; 652 getblk(&cgblk, cgtod(sblock, c), sblock->fs_cgsize); 653 memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize); 654 if (needswap) 655 ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock); 656 if (is_ufs2) 657 inosused = cgp->cg_initediblk; 658 else 659 inosused = sblock->fs_ipg; 660 for (; inosused > 0; inum++, inosused--) { 661 if (inum < UFS_ROOTINO) 662 continue; 663 if (is_ufs2 ? compare_blk64(wantedblk64, 664 ino_to_fsba(sblock, inum)) : 665 compare_blk32(wantedblk32, 666 ino_to_fsba(sblock, inum))) { 667 printf("block %llu: inode block (%llu-%llu)\n", 668 (unsigned long long)FFS_FSBTODB(sblock, 669 ino_to_fsba(sblock, inum)), 670 (unsigned long long) 671 (inum / FFS_INOPB(sblock)) * FFS_INOPB(sblock), 672 (unsigned long long) 673 (inum / FFS_INOPB(sblock) + 1) * FFS_INOPB(sblock)); 674 findblk_numtofind--; 675 if (findblk_numtofind == 0) 676 goto end; 677 } 678 curinum = inum; 679 curinode = ginode(inum); 680 switch (iswap16(DIP(curinode, mode)) & IFMT) { 681 case IFDIR: 682 case IFREG: 683 if (DIP(curinode, blocks) == 0) 684 continue; 685 break; 686 case IFLNK: 687 { 688 uint64_t size = iswap64(DIP(curinode, size)); 689 if (size > 0 && 690 size < (uint64_t)sblock->fs_maxsymlinklen && 691 DIP(curinode, blocks) == 0) 692 continue; 693 else 694 break; 695 } 696 default: 697 continue; 698 } 699 if (is_ufs2 ? 700 find_blks64(curinode->dp2.di_db, UFS_NDADDR, 701 wantedblk64) : 702 find_blks32(curinode->dp1.di_db, UFS_NDADDR, 703 wantedblk32)) 704 goto end; 705 for (i = 0; i < UFS_NIADDR; i++) { 706 if (is_ufs2 ? 707 compare_blk64(wantedblk64, 708 iswap64(curinode->dp2.di_ib[i])) : 709 compare_blk32(wantedblk32, 710 iswap32(curinode->dp1.di_ib[i]))) 711 if (founddatablk(is_ufs2 ? 712 iswap64(curinode->dp2.di_ib[i]) : 713 iswap32(curinode->dp1.di_ib[i]))) 714 goto end; 715 if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) : 716 (curinode->dp1.di_ib[i] != 0)) 717 if (is_ufs2 ? 718 find_indirblks64( 719 iswap64(curinode->dp2.di_ib[i]), 720 i, wantedblk64) : 721 find_indirblks32( 722 iswap32(curinode->dp1.di_ib[i]), 723 i, wantedblk32)) 724 goto end; 725 } 726 } 727 } 728end: 729 if (wantedblk32) 730 free(wantedblk32); 731 if (wantedblk64) 732 free(wantedblk64); 733 curinum = ocurrent; 734 curinode = ginode(curinum); 735 return 0; 736} 737 738static int 739compare_blk32(uint32_t *wantedblk, uint32_t curblk) 740{ 741 int i; 742 for (i = 0; i < wantedblksize; i++) { 743 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 744 wantedblk[i] = 0; 745 return 1; 746 } 747 } 748 return 0; 749} 750 751static int 752compare_blk64(uint64_t *wantedblk, uint64_t curblk) 753{ 754 int i; 755 for (i = 0; i < wantedblksize; i++) { 756 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 757 wantedblk[i] = 0; 758 return 1; 759 } 760 } 761 return 0; 762} 763 764static int 765founddatablk(uint64_t blk) 766{ 767 printf("%llu: data block of inode %llu\n", 768 (unsigned long long)FFS_FSBTODB(sblock, blk), 769 (unsigned long long)curinum); 770 findblk_numtofind--; 771 if (findblk_numtofind == 0) 772 return 1; 773 return 0; 774} 775 776static int 777find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) 778{ 779 int blk; 780 for(blk = 0; blk < size; blk++) { 781 if (buf[blk] == 0) 782 continue; 783 if (compare_blk32(wantedblk, iswap32(buf[blk]))) { 784 if (founddatablk(iswap32(buf[blk]))) 785 return 1; 786 } 787 } 788 return 0; 789} 790 791static int 792find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) 793{ 794#define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) 795 uint32_t idblk[MAXNINDIR]; 796 size_t i; 797 798 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 799 (int)sblock->fs_bsize); 800 if (ind_level <= 0) { 801 if (find_blks32(idblk, 802 sblock->fs_bsize / sizeof(uint32_t), wantedblk)) 803 return 1; 804 } else { 805 ind_level--; 806 for (i = 0; i < sblock->fs_bsize / sizeof(uint32_t); i++) { 807 if (compare_blk32(wantedblk, iswap32(idblk[i]))) { 808 if (founddatablk(iswap32(idblk[i]))) 809 return 1; 810 } 811 if(idblk[i] != 0) 812 if (find_indirblks32(iswap32(idblk[i]), 813 ind_level, wantedblk)) 814 return 1; 815 } 816 } 817#undef MAXNINDIR 818 return 0; 819} 820 821 822static int 823find_blks64(uint64_t *buf, int size, uint64_t *wantedblk) 824{ 825 int blk; 826 for(blk = 0; blk < size; blk++) { 827 if (buf[blk] == 0) 828 continue; 829 if (compare_blk64(wantedblk, iswap64(buf[blk]))) { 830 if (founddatablk(iswap64(buf[blk]))) 831 return 1; 832 } 833 } 834 return 0; 835} 836 837static int 838find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk) 839{ 840#define MAXNINDIR (MAXBSIZE / sizeof(uint64_t)) 841 uint64_t idblk[MAXNINDIR]; 842 size_t i; 843 844 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 845 (int)sblock->fs_bsize); 846 if (ind_level <= 0) { 847 if (find_blks64(idblk, 848 sblock->fs_bsize / sizeof(uint64_t), wantedblk)) 849 return 1; 850 } else { 851 ind_level--; 852 for (i = 0; i < sblock->fs_bsize / sizeof(uint64_t); i++) { 853 if (compare_blk64(wantedblk, iswap64(idblk[i]))) { 854 if (founddatablk(iswap64(idblk[i]))) 855 return 1; 856 } 857 if (idblk[i] != 0) 858 if (find_indirblks64(iswap64(idblk[i]), 859 ind_level, wantedblk)) 860 return 1; 861 } 862 } 863#undef MAXNINDIR 864 return 0; 865} 866 867static int 868writefileblk(struct wrinfo *wrp, uint64_t blk) 869{ 870 char buf[MAXBSIZE]; 871 long long size, rsize; 872 873 size = wrp->size - wrp->written_size; 874 if (size > sblock->fs_bsize) 875 size = sblock->fs_bsize; 876 if (size > (long long)sizeof buf) { 877 warnx("sblock->fs_bsize > MAX_BSIZE"); 878 return -1; 879 } 880 881 rsize = roundup(size, DEV_BSIZE); 882 if (bread(fsreadfd, buf, FFS_FSBTODB(sblock, blk), rsize) != 0) 883 return -1; 884 if (write(wrp->fd, buf, size) != size) 885 return -1; 886 wrp->written_size += size; 887 return 0; 888} 889 890 891#define CHARS_PER_LINES 70 892 893static void 894print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp) 895{ 896 int chars; 897 char prbuf[CHARS_PER_LINES+1]; 898 int blk; 899 900 chars = 0; 901 for (blk = 0; blk < size; blk++, (*blknum)++) { 902 if (buf[blk] == 0) 903 continue; 904 if (wrp && writefileblk(wrp, iswap32(buf[blk])) != 0) { 905 warn("unable to write block %d", iswap32(buf[blk])); 906 return; 907 } 908 snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk])); 909 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { 910 printf("\n"); 911 chars = 0; 912 } 913 if (chars == 0) 914 printf("%" PRIu64 ": ", *blknum); 915 printf("%s", prbuf); 916 chars += strlen(prbuf); 917 } 918 printf("\n"); 919} 920 921static void 922print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp) 923{ 924 int chars; 925 char prbuf[CHARS_PER_LINES+1]; 926 int blk; 927 928 chars = 0; 929 for (blk = 0; blk < size; blk++, (*blknum)++) { 930 if (buf[blk] == 0) 931 continue; 932 if (wrp && writefileblk(wrp, iswap64(buf[blk])) != 0) { 933 warn("unable to write block %lld", 934 (long long)iswap64(buf[blk])); 935 return; 936 } 937 snprintf(prbuf, CHARS_PER_LINES, "%lld ", 938 (long long)iswap64(buf[blk])); 939 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { 940 printf("\n"); 941 chars = 0; 942 } 943 if (chars == 0) 944 printf("%" PRIu64 ": ", *blknum); 945 printf("%s", prbuf); 946 chars += strlen(prbuf); 947 } 948 printf("\n"); 949} 950 951#undef CHARS_PER_LINES 952 953static void 954print_indirblks32(uint32_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp) 955{ 956#define MAXNINDIR (MAXBSIZE / sizeof(int32_t)) 957 const int ptrperblk_shift = sblock->fs_bshift - 2; 958 const int ptrperblk = 1 << ptrperblk_shift; 959 int32_t idblk[MAXNINDIR]; 960 int i; 961 962 if (blk == 0) { 963 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level); 964 return; 965 } 966 967 printf("Indirect block %lld (level %d):\n", (long long)blk, 968 ind_level+1); 969 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 970 (int)sblock->fs_bsize); 971 if (ind_level <= 0) { 972 print_blks32(idblk, ptrperblk, blknum, wrp); 973 } else { 974 ind_level--; 975 for (i = 0; i < ptrperblk; i++) 976 print_indirblks32(iswap32(idblk[i]), ind_level, blknum, 977 wrp); 978 } 979#undef MAXNINDIR 980} 981 982static void 983print_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp) 984{ 985#define MAXNINDIR (MAXBSIZE / sizeof(int64_t)) 986 const int ptrperblk_shift = sblock->fs_bshift - 3; 987 const int ptrperblk = 1 << ptrperblk_shift; 988 int64_t idblk[MAXNINDIR]; 989 int i; 990 991 if (blk == 0) { 992 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level); 993 return; 994 } 995 996 printf("Indirect block %lld (level %d):\n", (long long)blk, 997 ind_level+1); 998 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 999 (int)sblock->fs_bsize); 1000 if (ind_level <= 0) { 1001 print_blks64(idblk, ptrperblk, blknum, wrp); 1002 } else { 1003 ind_level--; 1004 for (i = 0; i < ptrperblk; i++) 1005 print_indirblks64(iswap64(idblk[i]), ind_level, blknum, 1006 wrp); 1007 } 1008#undef MAXNINDIR 1009} 1010 1011static int 1012dolookup(char *name) 1013{ 1014 struct inodesc idesc; 1015 1016 if (!checkactivedir()) 1017 return 0; 1018 idesc.id_number = curinum; 1019 idesc.id_func = findino; 1020 idesc.id_name = name; 1021 idesc.id_type = DATA; 1022 idesc.id_fix = IGNORE; 1023 if (ckinode(curinode, &idesc) & FOUND) { 1024 curinum = idesc.id_parent; 1025 curinode = ginode(curinum); 1026 printactive(); 1027 return 1; 1028 } else { 1029 warnx("name `%s' not found in current inode directory", name); 1030 return 0; 1031 } 1032} 1033 1034CMDFUNC(focusname) 1035{ 1036 char *p, *val; 1037 1038 if (!checkactive()) 1039 return 1; 1040 1041 ocurrent = curinum; 1042 1043 if (argv[1][0] == '/') { 1044 curinum = UFS_ROOTINO; 1045 curinode = ginode(UFS_ROOTINO); 1046 } else { 1047 if (!checkactivedir()) 1048 return 1; 1049 } 1050 for (p = argv[1]; p != NULL;) { 1051 while ((val = strsep(&p, "/")) != NULL && *val == '\0'); 1052 if (val) { 1053 printf("component `%s': ", val); 1054 fflush(stdout); 1055 if (!dolookup(val)) { 1056 curinode = ginode(curinum); 1057 return (1); 1058 } 1059 } 1060 } 1061 return 0; 1062} 1063 1064CMDFUNC(ln) 1065{ 1066 ino_t inum; 1067 int rval; 1068 char *cp; 1069 1070 GETINUM(1, inum); 1071 1072 if (!checkactivedir()) 1073 return 1; 1074 rval = makeentry(curinum, inum, argv[2]); 1075 if (rval) 1076 printf("Ino %llu entered as `%s'\n", (unsigned long long)inum, 1077 argv[2]); 1078 else 1079 printf("could not enter name? weird.\n"); 1080 curinode = ginode(curinum); 1081 return rval; 1082} 1083 1084CMDFUNC(rm) 1085{ 1086 int rval; 1087 1088 if (!checkactivedir()) 1089 return 1; 1090 rval = changeino(curinum, argv[1], 0); 1091 if (rval & ALTERED) { 1092 printf("Name `%s' removed\n", argv[1]); 1093 return 0; 1094 } else { 1095 printf("could not remove name? weird.\n"); 1096 return 1; 1097 } 1098} 1099 1100static long slotcount, desired; 1101 1102static int 1103chinumfunc(struct inodesc *idesc) 1104{ 1105 struct direct *dirp = idesc->id_dirp; 1106 1107 if (slotcount++ == desired) { 1108 dirp->d_ino = iswap32(idesc->id_parent); 1109 return STOP | ALTERED | FOUND; 1110 } 1111 return KEEPON; 1112} 1113 1114CMDFUNC(chinum) 1115{ 1116 char *cp; 1117 ino_t inum; 1118 struct inodesc idesc; 1119 1120 slotcount = 0; 1121 if (!checkactivedir()) 1122 return 1; 1123 GETINUM(2, inum); 1124 1125 desired = strtol(argv[1], &cp, 0); 1126 if (cp == argv[1] || *cp != '\0' || desired < 0) { 1127 printf("invalid slot number `%s'\n", argv[1]); 1128 return 1; 1129 } 1130 idesc.id_number = curinum; 1131 idesc.id_func = chinumfunc; 1132 idesc.id_fix = IGNORE; 1133 idesc.id_type = DATA; 1134 idesc.id_parent = inum; /* XXX convenient hiding place */ 1135 1136 if (ckinode(curinode, &idesc) & FOUND) 1137 return 0; 1138 else { 1139 warnx("no %sth slot in current directory", argv[1]); 1140 return 1; 1141 } 1142} 1143 1144static int 1145chnamefunc(struct inodesc *idesc) 1146{ 1147 struct direct *dirp = idesc->id_dirp; 1148 struct direct testdir; 1149 1150 if (slotcount++ == desired) { 1151 /* will name fit? */ 1152 testdir.d_namlen = strlen(idesc->id_name); 1153 if (UFS_DIRSIZ(UFS_NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) { 1154 dirp->d_namlen = testdir.d_namlen; 1155 strlcpy(dirp->d_name, idesc->id_name, 1156 sizeof(dirp->d_name)); 1157 return STOP | ALTERED | FOUND; 1158 } else 1159 return STOP | FOUND; /* won't fit, so give up */ 1160 } 1161 return KEEPON; 1162} 1163 1164CMDFUNC(chname) 1165{ 1166 int rval; 1167 char *cp; 1168 struct inodesc idesc; 1169 1170 slotcount = 0; 1171 if (!checkactivedir()) 1172 return 1; 1173 1174 desired = strtoul(argv[1], &cp, 0); 1175 if (cp == argv[1] || *cp != '\0') { 1176 printf("invalid slot number `%s'\n", argv[1]); 1177 return 1; 1178 } 1179 idesc.id_number = curinum; 1180 idesc.id_func = chnamefunc; 1181 idesc.id_fix = IGNORE; 1182 idesc.id_type = DATA; 1183 idesc.id_name = argv[2]; 1184 1185 rval = ckinode(curinode, &idesc); 1186 if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED)) 1187 return 0; 1188 else 1189 if (rval & FOUND) { 1190 warnx("new name `%s' does not fit in slot %s", 1191 argv[2], argv[1]); 1192 return 1; 1193 } else { 1194 warnx("no %sth slot in current directory", argv[1]); 1195 return 1; 1196 } 1197} 1198 1199static int 1200chreclenfunc(struct inodesc *idesc) 1201{ 1202 struct direct *dirp = idesc->id_dirp; 1203 1204 if (slotcount++ == desired) { 1205 dirp->d_reclen = iswap16(idesc->id_parent); 1206 return STOP | ALTERED | FOUND; 1207 } 1208 return KEEPON; 1209} 1210 1211CMDFUNC(chreclen) 1212{ 1213 char *cp; 1214 uint32_t reclen; 1215 struct inodesc idesc; 1216 1217 slotcount = 0; 1218 if (!checkactivedir()) 1219 return 1; 1220 1221 desired = strtoul(argv[1], &cp, 0); 1222 if (cp == argv[1] || *cp != '\0') { 1223 printf("invalid slot number `%s'\n", argv[1]); 1224 return 1; 1225 } 1226 reclen = strtoul(argv[2], &cp, 0); 1227 if (reclen >= UINT16_MAX) { 1228 printf("invalid reclen `%s'\n", argv[2]); 1229 return 1; 1230 } 1231 1232 idesc.id_number = curinum; 1233 idesc.id_func = chreclenfunc; 1234 idesc.id_fix = IGNORE; 1235 idesc.id_type = DATA; 1236 idesc.id_parent = reclen; /* XXX convenient hiding place */ 1237 1238 if (ckinode(curinode, &idesc) & FOUND) 1239 return 0; 1240 else { 1241 warnx("no %sth slot in current directory", argv[1]); 1242 return 1; 1243 } 1244} 1245 1246static struct typemap { 1247 const char *typename; 1248 int typebits; 1249} typenamemap[] = { 1250 { "file", IFREG }, 1251 { "dir", IFDIR }, 1252 { "socket", IFSOCK }, 1253 { "fifo", IFIFO }, 1254 {"link", IFLNK}, 1255 {"chr", IFCHR}, 1256 {"blk", IFBLK}, 1257}; 1258 1259CMDFUNC(newtype) 1260{ 1261 int type; 1262 uint16_t mode; 1263 struct typemap *tp; 1264 1265 if (!checkactive()) 1266 return 1; 1267 mode = iswap16(DIP(curinode, mode)); 1268 type = mode & IFMT; 1269 for (tp = typenamemap; 1270 tp < &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]; 1271 tp++) { 1272 if (!strcmp(argv[1], tp->typename)) { 1273 printf("setting type to %s\n", tp->typename); 1274 type = tp->typebits; 1275 break; 1276 } 1277 } 1278 if (tp == &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]) { 1279 warnx("type `%s' not known", argv[1]); 1280 warnx("try one of `file', `dir', `socket', `fifo'"); 1281 return 1; 1282 } 1283 DIP_SET(curinode, mode, iswap16((mode & ~IFMT) | type)); 1284 inodirty(); 1285 printactive(); 1286 return 0; 1287} 1288 1289CMDFUNC(chmode) 1290{ 1291 long modebits; 1292 char *cp; 1293 uint16_t mode; 1294 1295 if (!checkactive()) 1296 return 1; 1297 1298 modebits = strtol(argv[1], &cp, 8); 1299 if (cp == argv[1] || *cp != '\0') { 1300 warnx("bad modebits `%s'", argv[1]); 1301 return 1; 1302 } 1303 mode = iswap16(DIP(curinode, mode)); 1304 DIP_SET(curinode, mode, iswap16((mode & ~07777) | modebits)); 1305 inodirty(); 1306 printactive(); 1307 return 0; 1308} 1309 1310CMDFUNC(chlen) 1311{ 1312 off_t len; 1313 char *cp; 1314 1315 if (!checkactive()) 1316 return 1; 1317 1318 len = strtoull(argv[1], &cp, 0); 1319 if (cp == argv[1] || *cp != '\0' || len < 0) { 1320 warnx("bad length '%s'", argv[1]); 1321 return 1; 1322 } 1323 DIP_SET(curinode, size, iswap64(len)); 1324 inodirty(); 1325 printactive(); 1326 return 0; 1327} 1328 1329CMDFUNC(chaflags) 1330{ 1331 u_long flags; 1332 char *cp; 1333 1334 if (!checkactive()) 1335 return 1; 1336 1337 flags = strtoul(argv[1], &cp, 0); 1338 if (cp == argv[1] || *cp != '\0') { 1339 warnx("bad flags `%s'", argv[1]); 1340 return 1; 1341 } 1342 if (flags > UINT_MAX) { 1343 warnx("flags set beyond 32-bit range of field (0x%lx)", 1344 flags); 1345 return (1); 1346 } 1347 DIP_SET(curinode, flags, iswap32(flags)); 1348 inodirty(); 1349 printactive(); 1350 return 0; 1351} 1352 1353CMDFUNC(chgen) 1354{ 1355 long gen; 1356 char *cp; 1357 1358 if (!checkactive()) 1359 return 1; 1360 1361 gen = strtol(argv[1], &cp, 0); 1362 if (cp == argv[1] || *cp != '\0') { 1363 warnx("bad gen `%s'", argv[1]); 1364 return 1; 1365 } 1366 if (gen > INT_MAX || gen < INT_MIN) { 1367 warnx("gen set beyond 32-bit range of field (0x%lx)", gen); 1368 return (1); 1369 } 1370 DIP_SET(curinode, gen, iswap32(gen)); 1371 inodirty(); 1372 printactive(); 1373 return 0; 1374} 1375 1376CMDFUNC(chextsize) 1377{ 1378 uint32_t extsize; 1379 char *cp; 1380 1381 if (!is_ufs2) 1382 return 1; 1383 if (!checkactive()) 1384 return 1; 1385 1386 extsize = strtol(argv[1], &cp, 0); 1387 if (cp == argv[1] || *cp != '\0') { 1388 warnx("bad extsize `%s'", argv[1]); 1389 return 1; 1390 } 1391 1392 curinode->dp2.di_extsize = extsize; 1393 inodirty(); 1394 printactive(); 1395 return 0; 1396} 1397 1398CMDFUNC(chblocks) 1399{ 1400 uint64_t blocks; 1401 char *cp; 1402 1403 if (!checkactive()) 1404 return 1; 1405 1406 blocks = strtoll(argv[1], &cp, 0); 1407 if (cp == argv[1] || *cp != '\0') { 1408 warnx("bad blocks `%s'", argv[1]); 1409 return 1; 1410 } 1411 1412 DIP_SET(curinode, blocks, blocks); 1413 inodirty(); 1414 printactive(); 1415 return 0; 1416} 1417 1418CMDFUNC(chdb) 1419{ 1420 unsigned int idx; 1421 daddr_t bno; 1422 char *cp; 1423 1424 if (!checkactive()) 1425 return 1; 1426 1427 idx = strtoull(argv[1], &cp, 0); 1428 if (cp == argv[1] || *cp != '\0') { 1429 warnx("bad pointer idx `%s'", argv[1]); 1430 return 1; 1431 } 1432 bno = strtoll(argv[2], &cp, 0); 1433 if (cp == argv[2] || *cp != '\0') { 1434 warnx("bad block number `%s'", argv[2]); 1435 return 1; 1436 } 1437 if (idx >= UFS_NDADDR) { 1438 warnx("pointer index %d is out of range", idx); 1439 return 1; 1440 } 1441 1442 DIP_SET(curinode, db[idx], bno); 1443 inodirty(); 1444 printactive(); 1445 return 0; 1446} 1447 1448CMDFUNC(chib) 1449{ 1450 unsigned int idx; 1451 daddr_t bno; 1452 char *cp; 1453 1454 if (!checkactive()) 1455 return 1; 1456 1457 idx = strtoull(argv[1], &cp, 0); 1458 if (cp == argv[1] || *cp != '\0') { 1459 warnx("bad pointer idx `%s'", argv[1]); 1460 return 1; 1461 } 1462 bno = strtoll(argv[2], &cp, 0); 1463 if (cp == argv[2] || *cp != '\0') { 1464 warnx("bad block number `%s'", argv[2]); 1465 return 1; 1466 } 1467 if (idx >= UFS_NIADDR) { 1468 warnx("pointer index %d is out of range", idx); 1469 return 1; 1470 } 1471 1472 DIP_SET(curinode, ib[idx], bno); 1473 inodirty(); 1474 printactive(); 1475 return 0; 1476} 1477 1478CMDFUNC(chextb) 1479{ 1480 unsigned int idx; 1481 daddr_t bno; 1482 char *cp; 1483 1484 if (!checkactive()) 1485 return 1; 1486 1487 idx = strtoull(argv[1], &cp, 0); 1488 if (cp == argv[1] || *cp != '\0') { 1489 warnx("bad pointer idx `%s'", argv[1]); 1490 return 1; 1491 } 1492 bno = strtoll(argv[2], &cp, 0); 1493 if (cp == argv[2] || *cp != '\0') { 1494 warnx("bad block number `%s'", argv[2]); 1495 return 1; 1496 } 1497 if (idx >= UFS_NXADDR) { 1498 warnx("pointer index %d is out of range", idx); 1499 return 1; 1500 } 1501 1502 curinode->dp2.di_extb[idx] = bno; 1503 inodirty(); 1504 printactive(); 1505 return 0; 1506} 1507 1508CMDFUNC(chfreelink) 1509{ 1510#if 0 1511 ino_t freelink; 1512 char *cp; 1513 1514 if (!checkactive()) 1515 return 1; 1516 1517 freelink = strtoll(argv[1], &cp, 0); 1518 if (cp == argv[1] || *cp != '\0') { 1519 warnx("bad freelink `%s'", argv[1]); 1520 return 1; 1521 } 1522 1523 DIP_SET(curinode, freelink, freelink); 1524 inodirty(); 1525 printactive(); 1526#endif 1527 return 0; 1528} 1529 1530CMDFUNC(linkcount) 1531{ 1532 int lcnt; 1533 char *cp; 1534 1535 if (!checkactive()) 1536 return 1; 1537 1538 lcnt = strtol(argv[1], &cp, 0); 1539 if (cp == argv[1] || *cp != '\0') { 1540 warnx("bad link count `%s'", argv[1]); 1541 return 1; 1542 } 1543 if (lcnt > USHRT_MAX || lcnt < 0) { 1544 warnx("max link count is %d", USHRT_MAX); 1545 return 1; 1546 } 1547 DIP_SET(curinode, nlink, iswap16(lcnt)); 1548 inodirty(); 1549 printactive(); 1550 return 0; 1551} 1552 1553CMDFUNC(chowner) 1554{ 1555 unsigned long uid; 1556 char *cp; 1557 struct passwd *pwd; 1558 1559 if (!checkactive()) 1560 return 1; 1561 1562 uid = strtoul(argv[1], &cp, 0); 1563 if (cp == argv[1] || *cp != '\0') { 1564 /* try looking up name */ 1565 if ((pwd = getpwnam(argv[1])) != 0) { 1566 uid = pwd->pw_uid; 1567 } else { 1568 warnx("bad uid `%s'", argv[1]); 1569 return 1; 1570 } 1571 } 1572 if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT) 1573 curinode->dp1.di_ouid = iswap32(uid); 1574 else 1575 DIP_SET(curinode, uid, iswap32(uid)); 1576 inodirty(); 1577 printactive(); 1578 return 0; 1579} 1580 1581CMDFUNC(chgroup) 1582{ 1583 unsigned long gid; 1584 char *cp; 1585 struct group *grp; 1586 1587 if (!checkactive()) 1588 return 1; 1589 1590 gid = strtoul(argv[1], &cp, 0); 1591 if (cp == argv[1] || *cp != '\0') { 1592 if ((grp = getgrnam(argv[1])) != 0) { 1593 gid = grp->gr_gid; 1594 } else { 1595 warnx("bad gid `%s'", argv[1]); 1596 return 1; 1597 } 1598 } 1599 if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT) 1600 curinode->dp1.di_ogid = iswap32(gid); 1601 else 1602 DIP_SET(curinode, gid, iswap32(gid)); 1603 inodirty(); 1604 printactive(); 1605 return 0; 1606} 1607 1608static int 1609dotime(char *name, int64_t *rsec, int32_t *rnsec) 1610{ 1611 char *p, *val; 1612 struct tm t; 1613 int64_t sec; 1614 int32_t nsec; 1615 p = strchr(name, '.'); 1616 if (p) { 1617 *p = '\0'; 1618 nsec = strtoul(++p, &val, 0); 1619 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 1620 warnx("invalid nanoseconds"); 1621 goto badformat; 1622 } 1623 } else 1624 nsec = 0; 1625 if (strlen(name) != 14) { 1626badformat: 1627 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 1628 return 1; 1629 } 1630 for (p = name; *p; p++) 1631 if (*p < '0' || *p > '9') 1632 goto badformat; 1633 1634 p = name; 1635#define VAL() ((*p++) - '0') 1636 t.tm_year = VAL(); 1637 t.tm_year = VAL() + t.tm_year * 10; 1638 t.tm_year = VAL() + t.tm_year * 10; 1639 t.tm_year = VAL() + t.tm_year * 10 - 1900; 1640 t.tm_mon = VAL(); 1641 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 1642 t.tm_mday = VAL(); 1643 t.tm_mday = VAL() + t.tm_mday * 10; 1644 t.tm_hour = VAL(); 1645 t.tm_hour = VAL() + t.tm_hour * 10; 1646 t.tm_min = VAL(); 1647 t.tm_min = VAL() + t.tm_min * 10; 1648 t.tm_sec = VAL(); 1649 t.tm_sec = VAL() + t.tm_sec * 10; 1650 t.tm_isdst = -1; 1651 1652 sec = mktime(&t); 1653 if (sec == -1) { 1654 warnx("date/time out of range"); 1655 return 1; 1656 } 1657 *rsec = iswap64(sec); 1658 *rnsec = iswap32(nsec); 1659 return 0; 1660} 1661 1662CMDFUNC(chmtime) 1663{ 1664 int64_t rsec; 1665 int32_t nsec; 1666 1667 if (!checkactive()) 1668 return 1; 1669 if (dotime(argv[1], &rsec, &nsec)) 1670 return 1; 1671 DIP_SET(curinode, mtime, rsec); 1672 DIP_SET(curinode, mtimensec, nsec); 1673 inodirty(); 1674 printactive(); 1675 return 0; 1676} 1677 1678CMDFUNC(chatime) 1679{ 1680 int64_t rsec; 1681 int32_t nsec; 1682 1683 if (!checkactive()) 1684 return 1; 1685 if (dotime(argv[1], &rsec, &nsec)) 1686 return 1; 1687 DIP_SET(curinode, atime, rsec); 1688 DIP_SET(curinode, atimensec, nsec); 1689 inodirty(); 1690 printactive(); 1691 return 0; 1692} 1693 1694CMDFUNC(chctime) 1695{ 1696 int64_t rsec; 1697 int32_t nsec; 1698 1699 if (!checkactive()) 1700 return 1; 1701 if (dotime(argv[1], &rsec, &nsec)) 1702 return 1; 1703 DIP_SET(curinode, ctime, rsec); 1704 DIP_SET(curinode, ctimensec, nsec); 1705 inodirty(); 1706 printactive(); 1707 return 0; 1708} 1709 1710CMDFUNC(chbirthtime) 1711{ 1712 int64_t rsec; 1713 int32_t nsec; 1714 1715 if (!is_ufs2) { 1716 warnx("birthtime can only be set in ufs2"); 1717 return 1; 1718 } 1719 if (!checkactive()) 1720 return 1; 1721 1722 if (dotime(argv[1], &rsec, &nsec)) 1723 return 1; 1724 curinode->dp2.di_birthtime = rsec; 1725 curinode->dp2.di_birthnsec = nsec; 1726 inodirty(); 1727 printactive(); 1728 return 0; 1729} 1730 1731CMDFUNC(iptrs) 1732{ 1733 int i; 1734 1735 if (!checkactive()) 1736 return 1; 1737 for (i = 0; i < UFS_NDADDR; i++) 1738 printf("di_db %d %ju\n", i, DIP(curinode, db[i])); 1739 for (i = 0; i < UFS_NIADDR; i++) 1740 printf("di_ib %d %ju\n", i, DIP(curinode, ib[i])); 1741 if (is_ufs2) 1742 for (i = 0; i < UFS_NXADDR; i++) 1743 printf("di_extb %d %ju\n", i, curinode->dp2.di_extb[i]); 1744 return 0; 1745} 1746 1747CMDFUNC(saveea) 1748{ 1749 struct wrinfo wrinfo; 1750 uint64_t blkno = 0; 1751 1752 if (!is_ufs2) { 1753 warnx("dumping extattrs is only supported for ufs2"); 1754 return 1; 1755 } 1756 if (!checkactive()) 1757 return 1; 1758 1759 wrinfo.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644); 1760 if (wrinfo.fd == -1) { 1761 warn("unable to create file %s", argv[1]); 1762 return 0; 1763 } 1764 1765 wrinfo.size = iswap32(curinode->dp2.di_extsize); 1766 wrinfo.written_size = 0; 1767 print_blks64(curinode->dp2.di_extb, UFS_NXADDR, &blkno, &wrinfo); 1768 return 0; 1769} 1770