1 /* $NetBSD: fsdb.c,v 1.25 2003/04/26 08:42:49 bouyer 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.25 2003/04/26 08:42:49 bouyer 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 strcpy(dirp->d_name, idesc->id_name); 1026 return STOP | ALTERED | FOUND; 1027 } else 1028 return STOP | FOUND; /* won't fit, so give up */ 1029 } 1030 return KEEPON; 1031 } 1032 1033 CMDFUNCSTART(chname) 1034 { 1035 int rval; 1036 char *cp; 1037 struct inodesc idesc; 1038 1039 slotcount = 0; 1040 if (!checkactivedir()) 1041 return 1; 1042 1043 desired = strtoul(argv[1], &cp, 0); 1044 if (cp == argv[1] || *cp != '\0') { 1045 printf("invalid slot number `%s'\n", argv[1]); 1046 return 1; 1047 } 1048 idesc.id_number = curinum; 1049 idesc.id_func = chnamefunc; 1050 idesc.id_fix = IGNORE; 1051 idesc.id_type = DATA; 1052 idesc.id_name = argv[2]; 1053 1054 rval = ckinode(curinode, &idesc); 1055 if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED)) 1056 return 0; 1057 else 1058 if (rval & FOUND) { 1059 warnx("new name `%s' does not fit in slot %s", 1060 argv[2], argv[1]); 1061 return 1; 1062 } else { 1063 warnx("no %sth slot in current directory", argv[1]); 1064 return 1; 1065 } 1066 } 1067 1068 static struct typemap { 1069 const char *typename; 1070 int typebits; 1071 } typenamemap[] = { 1072 { "file", IFREG }, 1073 { "dir", IFDIR }, 1074 { "socket", IFSOCK }, 1075 { "fifo", IFIFO }, 1076 }; 1077 1078 CMDFUNCSTART(newtype) 1079 { 1080 int type; 1081 uint16_t mode; 1082 struct typemap *tp; 1083 1084 if (!checkactive()) 1085 return 1; 1086 mode = iswap16(DIP(curinode, mode)); 1087 type = mode & IFMT; 1088 for (tp = typenamemap; 1089 tp < &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]; 1090 tp++) { 1091 if (!strcmp(argv[1], tp->typename)) { 1092 printf("setting type to %s\n", tp->typename); 1093 type = tp->typebits; 1094 break; 1095 } 1096 } 1097 if (tp == &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]) { 1098 warnx("type `%s' not known", argv[1]); 1099 warnx("try one of `file', `dir', `socket', `fifo'"); 1100 return 1; 1101 } 1102 DIP(curinode, mode) = iswap16((mode & ~IFMT) | type); 1103 inodirty(); 1104 printactive(); 1105 return 0; 1106 } 1107 1108 CMDFUNCSTART(chmode) 1109 { 1110 long modebits; 1111 char *cp; 1112 uint16_t mode; 1113 1114 if (!checkactive()) 1115 return 1; 1116 1117 modebits = strtol(argv[1], &cp, 8); 1118 if (cp == argv[1] || *cp != '\0') { 1119 warnx("bad modebits `%s'", argv[1]); 1120 return 1; 1121 } 1122 mode = iswap16(DIP(curinode, mode)); 1123 DIP(curinode, mode) = iswap16((mode & ~07777) | modebits); 1124 inodirty(); 1125 printactive(); 1126 return 0; 1127 } 1128 1129 CMDFUNCSTART(chlen) 1130 { 1131 long len; 1132 char *cp; 1133 1134 if (!checkactive()) 1135 return 1; 1136 1137 len = strtol(argv[1], &cp, 0); 1138 if (cp == argv[1] || *cp != '\0' || len < 0) { 1139 warnx("bad length '%s'", argv[1]); 1140 return 1; 1141 } 1142 DIP(curinode, size) = iswap64(len); 1143 inodirty(); 1144 printactive(); 1145 return 0; 1146 } 1147 1148 CMDFUNCSTART(chaflags) 1149 { 1150 u_long flags; 1151 char *cp; 1152 1153 if (!checkactive()) 1154 return 1; 1155 1156 flags = strtoul(argv[1], &cp, 0); 1157 if (cp == argv[1] || *cp != '\0') { 1158 warnx("bad flags `%s'", argv[1]); 1159 return 1; 1160 } 1161 if (flags > UINT_MAX) { 1162 warnx("flags set beyond 32-bit range of field (0x%lx)", 1163 flags); 1164 return (1); 1165 } 1166 DIP(curinode, flags) = iswap32(flags); 1167 inodirty(); 1168 printactive(); 1169 return 0; 1170 } 1171 1172 CMDFUNCSTART(chgen) 1173 { 1174 long gen; 1175 char *cp; 1176 1177 if (!checkactive()) 1178 return 1; 1179 1180 gen = strtol(argv[1], &cp, 0); 1181 if (cp == argv[1] || *cp != '\0') { 1182 warnx("bad gen `%s'", argv[1]); 1183 return 1; 1184 } 1185 if (gen > INT_MAX || gen < INT_MIN) { 1186 warnx("gen set beyond 32-bit range of field (0x%lx)", gen); 1187 return (1); 1188 } 1189 DIP(curinode, gen) = iswap32(gen); 1190 inodirty(); 1191 printactive(); 1192 return 0; 1193 } 1194 1195 CMDFUNCSTART(linkcount) 1196 { 1197 int lcnt; 1198 char *cp; 1199 1200 if (!checkactive()) 1201 return 1; 1202 1203 lcnt = strtol(argv[1], &cp, 0); 1204 if (cp == argv[1] || *cp != '\0') { 1205 warnx("bad link count `%s'", argv[1]); 1206 return 1; 1207 } 1208 if (lcnt > USHRT_MAX || lcnt < 0) { 1209 warnx("max link count is %d", USHRT_MAX); 1210 return 1; 1211 } 1212 DIP(curinode, nlink) = iswap16(lcnt); 1213 inodirty(); 1214 printactive(); 1215 return 0; 1216 } 1217 1218 CMDFUNCSTART(chowner) 1219 { 1220 unsigned long uid; 1221 char *cp; 1222 struct passwd *pwd; 1223 1224 if (!checkactive()) 1225 return 1; 1226 1227 uid = strtoul(argv[1], &cp, 0); 1228 if (cp == argv[1] || *cp != '\0') { 1229 /* try looking up name */ 1230 if ((pwd = getpwnam(argv[1])) != 0) { 1231 uid = pwd->pw_uid; 1232 } else { 1233 warnx("bad uid `%s'", argv[1]); 1234 return 1; 1235 } 1236 } 1237 DIP(curinode, uid) = iswap32(uid); 1238 inodirty(); 1239 printactive(); 1240 return 0; 1241 } 1242 1243 CMDFUNCSTART(chgroup) 1244 { 1245 unsigned long gid; 1246 char *cp; 1247 struct group *grp; 1248 1249 if (!checkactive()) 1250 return 1; 1251 1252 gid = strtoul(argv[1], &cp, 0); 1253 if (cp == argv[1] || *cp != '\0') { 1254 if ((grp = getgrnam(argv[1])) != 0) { 1255 gid = grp->gr_gid; 1256 } else { 1257 warnx("bad gid `%s'", argv[1]); 1258 return 1; 1259 } 1260 } 1261 DIP(curinode, gid) = iswap32(gid); 1262 inodirty(); 1263 printactive(); 1264 return 0; 1265 } 1266 1267 static int 1268 dotime(name, rsec, rnsec) 1269 char *name; 1270 int32_t *rsec, *rnsec; 1271 { 1272 char *p, *val; 1273 struct tm t; 1274 int32_t sec; 1275 int32_t nsec; 1276 p = strchr(name, '.'); 1277 if (p) { 1278 *p = '\0'; 1279 nsec = strtoul(++p, &val, 0); 1280 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 1281 warnx("invalid nanoseconds"); 1282 goto badformat; 1283 } 1284 } else 1285 nsec = 0; 1286 if (strlen(name) != 14) { 1287 badformat: 1288 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 1289 return 1; 1290 } 1291 for (p = name; *p; p++) 1292 if (*p < '0' || *p > '9') 1293 goto badformat; 1294 1295 p = name; 1296 #define VAL() ((*p++) - '0') 1297 t.tm_year = VAL(); 1298 t.tm_year = VAL() + t.tm_year * 10; 1299 t.tm_year = VAL() + t.tm_year * 10; 1300 t.tm_year = VAL() + t.tm_year * 10 - 1900; 1301 t.tm_mon = VAL(); 1302 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 1303 t.tm_mday = VAL(); 1304 t.tm_mday = VAL() + t.tm_mday * 10; 1305 t.tm_hour = VAL(); 1306 t.tm_hour = VAL() + t.tm_hour * 10; 1307 t.tm_min = VAL(); 1308 t.tm_min = VAL() + t.tm_min * 10; 1309 t.tm_sec = VAL(); 1310 t.tm_sec = VAL() + t.tm_sec * 10; 1311 t.tm_isdst = -1; 1312 1313 sec = mktime(&t); 1314 if (sec == -1) { 1315 warnx("date/time out of range"); 1316 return 1; 1317 } 1318 *rsec = iswap32(sec); 1319 *rnsec = iswap32(nsec); 1320 return 0; 1321 } 1322 1323 CMDFUNCSTART(chmtime) 1324 { 1325 int32_t rsec, nsec; 1326 1327 if (dotime(argv[1], &rsec, &nsec)) 1328 return 1; 1329 DIP(curinode, mtime) = rsec; 1330 DIP(curinode, mtimensec) = nsec; 1331 inodirty(); 1332 printactive(); 1333 return 0; 1334 } 1335 1336 CMDFUNCSTART(chatime) 1337 { 1338 int32_t rsec, nsec; 1339 1340 if (dotime(argv[1], &rsec, &nsec)) 1341 return 1; 1342 DIP(curinode, atime) = rsec; 1343 DIP(curinode, atimensec) = nsec; 1344 inodirty(); 1345 printactive(); 1346 return 0; 1347 } 1348 1349 CMDFUNCSTART(chctime) 1350 { 1351 int32_t rsec, nsec; 1352 1353 if (dotime(argv[1], &rsec, &nsec)) 1354 return 1; 1355 DIP(curinode, ctime) = rsec; 1356 DIP(curinode, ctimensec) = nsec; 1357 inodirty(); 1358 printactive(); 1359 return 0; 1360 } 1361