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