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