1 /* $NetBSD: fsdb.c,v 1.20 2002/03/18 20:04:49 thorpej 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.20 2002/03/18 20:04:49 thorpej 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_blks __P((ufs_daddr_t *buf, int size, int *blknum)); 81 static void print_indirblks __P((daddr_t blk, int ind_level, int *blknum)); 82 83 int returntosingle = 0; 84 struct dinode *curinode; 85 ino_t curinum; 86 87 static void 88 usage() 89 { 90 errx(1, "usage: %s [-d] [-n] -f <fsname>", getprogname()); 91 } 92 /* 93 * We suck in lots of fsck code, and just pick & choose the stuff we want. 94 * 95 * fsreadfd is set up to read from the file system, fswritefd to write to 96 * the file system. 97 */ 98 int 99 main(argc, argv) 100 int argc; 101 char *argv[]; 102 { 103 int ch, rval; 104 char *fsys = NULL; 105 106 while ((ch = getopt(argc, argv, "f:dn")) != -1) { 107 switch (ch) { 108 case 'f': 109 fsys = optarg; 110 break; 111 case 'd': 112 debug++; 113 break; 114 case 'n': 115 nflag++; 116 break; 117 default: 118 usage(); 119 } 120 } 121 if (fsys == NULL) 122 usage(); 123 endian = 0; 124 if (!setup(fsys)) 125 errx(1, "cannot set up file system `%s'", fsys); 126 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys, 127 sblock->fs_fsmnt); 128 rval = cmdloop(); 129 if (nflag) 130 exit(rval); 131 sblock->fs_clean = 0; /* mark it dirty */ 132 sbdirty(); 133 markclean = 0; 134 ckfini(); 135 printf("*** FILE SYSTEM MARKED DIRTY\n"); 136 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 137 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 138 exit(rval); 139 } 140 141 #define CMDFUNC(func) static int func __P((int argc, char *argv[])) 142 #define CMDFUNCSTART(func) static int func(argc, argv) \ 143 int argc; \ 144 char *argv[]; 145 146 CMDFUNC(helpfn); 147 CMDFUNC(focus); /* focus on inode */ 148 CMDFUNC(active); /* print active inode */ 149 CMDFUNC(focusname); /* focus by name */ 150 CMDFUNC(zapi); /* clear inode */ 151 CMDFUNC(uplink); /* incr link */ 152 CMDFUNC(downlink); /* decr link */ 153 CMDFUNC(linkcount); /* set link count */ 154 CMDFUNC(quit); /* quit */ 155 CMDFUNC(ls); /* list directory */ 156 CMDFUNC(blks); /* list blocks */ 157 CMDFUNC(rm); /* remove name */ 158 CMDFUNC(ln); /* add name */ 159 CMDFUNC(newtype); /* change type */ 160 CMDFUNC(chmode); /* change mode */ 161 CMDFUNC(chlen); /* change length */ 162 CMDFUNC(chaflags); /* change flags */ 163 CMDFUNC(chgen); /* change generation */ 164 CMDFUNC(chowner); /* change owner */ 165 CMDFUNC(chgroup); /* Change group */ 166 CMDFUNC(back); /* pop back to last ino */ 167 CMDFUNC(chmtime); /* Change mtime */ 168 CMDFUNC(chctime); /* Change ctime */ 169 CMDFUNC(chatime); /* Change atime */ 170 CMDFUNC(chinum); /* Change inode # of dirent */ 171 CMDFUNC(chname); /* Change dirname of dirent */ 172 173 static struct cmdtable cmds[] = { 174 {"help", "Print out help", 1, 1, helpfn}, 175 {"?", "Print out help", 1, 1, helpfn}, 176 {"inode", "Set active inode to INUM", 2, 2, focus}, 177 {"clri", "Clear inode INUM", 2, 2, zapi}, 178 {"lookup", "Set active inode by looking up NAME", 2, 2, focusname}, 179 {"cd", "Set active inode by looking up NAME", 2, 2, focusname}, 180 {"back", "Go to previous active inode", 1, 1, back}, 181 {"active", "Print active inode", 1, 1, active}, 182 {"print", "Print active inode", 1, 1, active}, 183 {"uplink", "Increment link count", 1, 1, uplink}, 184 {"downlink", "Decrement link count", 1, 1, downlink}, 185 {"linkcount", "Set link count to COUNT", 2, 2, linkcount}, 186 {"ls", "List current inode as directory", 1, 1, ls}, 187 {"blks", "List current inode's data blocks", 1, 1, blks}, 188 {"rm", "Remove NAME from current inode directory", 2, 2, rm}, 189 {"del", "Remove NAME from current inode directory", 2, 2, rm}, 190 {"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln}, 191 {"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum}, 192 {"chname", "Change dir entry number INDEX to NAME", 3, 3, chname}, 193 {"chtype", "Change type of current inode to TYPE", 2, 2, newtype}, 194 {"chmod", "Change mode of current inode to MODE", 2, 2, chmode}, 195 {"chown", "Change owner of current inode to OWNER", 2, 2, chowner}, 196 {"chlen", "Change length of current inode to LENGTH", 2, 2, chlen}, 197 {"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup}, 198 {"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags}, 199 {"chgen", "Change generation number of current inode to GEN", 2, 2, 200 chgen}, 201 {"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime}, 202 {"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime}, 203 {"atime", "Change atime of current inode to ATIME", 2, 2, chatime}, 204 {"quit", "Exit", 1, 1, quit}, 205 {"q", "Exit", 1, 1, quit}, 206 {"exit", "Exit", 1, 1, quit}, 207 {NULL, 0, 0, 0}, 208 }; 209 210 static int 211 helpfn(argc, argv) 212 int argc; 213 char *argv[]; 214 { 215 struct cmdtable *cmdtp; 216 217 printf("Commands are:\n%-10s %5s %5s %s\n", 218 "command", "min argc", "max argc", "what"); 219 220 for (cmdtp = cmds; cmdtp->cmd; cmdtp++) 221 printf("%-10s %5u %5u %s\n", 222 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt); 223 return 0; 224 } 225 226 static char * 227 prompt(el) 228 EditLine *el; 229 { 230 static char pstring[64]; 231 snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum); 232 return pstring; 233 } 234 235 236 static int 237 cmdloop() 238 { 239 char *line; 240 const char *elline; 241 int cmd_argc, rval = 0, known; 242 #define scratch known 243 char **cmd_argv; 244 struct cmdtable *cmdp; 245 History *hist; 246 HistEvent he; 247 EditLine *elptr; 248 249 curinode = ginode(ROOTINO); 250 curinum = ROOTINO; 251 printactive(); 252 253 hist = history_init(); 254 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 255 256 elptr = el_init(getprogname(), stdin, stdout, stderr); 257 el_set(elptr, EL_EDITOR, "emacs"); 258 el_set(elptr, EL_PROMPT, prompt); 259 el_set(elptr, EL_HIST, history, hist); 260 el_source(elptr, NULL); 261 262 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 263 if (debug) 264 printf("command `%s'\n", elline); 265 266 history(hist, &he, H_ENTER, elline); 267 268 line = strdup(elline); 269 cmd_argv = crack(line, &cmd_argc); 270 if (cmd_argc) { 271 /* 272 * el_parse returns -1 to signal that it's not been 273 * handled internally. 274 */ 275 if (el_parse(elptr, cmd_argc, 276 (const char **)cmd_argv) != -1) 277 continue; 278 known = 0; 279 for (cmdp = cmds; cmdp->cmd; cmdp++) { 280 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 281 if (cmd_argc >= cmdp->minargc && 282 cmd_argc <= cmdp->maxargc) 283 rval = 284 (*cmdp->handler)(cmd_argc, 285 cmd_argv); 286 else 287 rval = argcount(cmdp, cmd_argc, 288 cmd_argv); 289 known = 1; 290 break; 291 } 292 } 293 if (!known) 294 warnx("unknown command `%s'", cmd_argv[0]), 295 rval = 1; 296 } else 297 rval = 0; 298 free(line); 299 if (rval < 0) 300 return rval; 301 if (rval) 302 warnx("rval was %d", rval); 303 } 304 el_end(elptr); 305 history_end(hist); 306 return rval; 307 } 308 309 static ino_t ocurrent; 310 311 #define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \ 312 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \ 313 printf("inode %d out of range; range is [%d,%d]\n", \ 314 inum, ROOTINO, maxino); \ 315 return 1; \ 316 } 317 318 /* 319 * Focus on given inode number 320 */ 321 CMDFUNCSTART(focus) 322 { 323 ino_t inum; 324 char *cp; 325 326 GETINUM(1, inum); 327 curinode = ginode(inum); 328 ocurrent = curinum; 329 curinum = inum; 330 printactive(); 331 return 0; 332 } 333 334 CMDFUNCSTART(back) 335 { 336 curinum = ocurrent; 337 curinode = ginode(curinum); 338 printactive(); 339 return 0; 340 } 341 342 CMDFUNCSTART(zapi) 343 { 344 ino_t inum; 345 struct dinode *dp; 346 char *cp; 347 348 GETINUM(1, inum); 349 dp = ginode(inum); 350 clearinode(dp); 351 inodirty(); 352 if (curinode) /* re-set after potential change */ 353 curinode = ginode(curinum); 354 return 0; 355 } 356 357 CMDFUNCSTART(active) 358 { 359 printactive(); 360 return 0; 361 } 362 363 CMDFUNCSTART(quit) 364 { 365 return -1; 366 } 367 368 CMDFUNCSTART(uplink) 369 { 370 if (!checkactive()) 371 return 1; 372 curinode->di_nlink = iswap16(iswap16(curinode->di_nlink) + 1); 373 printf("inode %d link count now %d\n", curinum, 374 iswap16(curinode->di_nlink)); 375 inodirty(); 376 return 0; 377 } 378 379 CMDFUNCSTART(downlink) 380 { 381 if (!checkactive()) 382 return 1; 383 curinode->di_nlink = iswap16(iswap16(curinode->di_nlink) - 1); 384 printf("inode %d link count now %d\n", curinum, 385 iswap16(curinode->di_nlink)); 386 inodirty(); 387 return 0; 388 } 389 390 static const char *typename[] = { 391 "unknown", 392 "fifo", 393 "char special", 394 "unregistered #3", 395 "directory", 396 "unregistered #5", 397 "blk special", 398 "unregistered #7", 399 "regular", 400 "unregistered #9", 401 "symlink", 402 "unregistered #11", 403 "socket", 404 "unregistered #13", 405 "whiteout", 406 }; 407 408 static int slot; 409 410 static int 411 scannames(idesc) 412 struct inodesc *idesc; 413 { 414 struct direct *dirp = idesc->id_dirp; 415 416 printf("slot %d ino %d reclen %d: %s, `%.*s'\n", 417 slot++, iswap32(dirp->d_ino), iswap16(dirp->d_reclen), 418 typename[dirp->d_type], 419 dirp->d_namlen, dirp->d_name); 420 return (KEEPON); 421 } 422 423 CMDFUNCSTART(ls) 424 { 425 struct inodesc idesc; 426 checkactivedir(); /* let it go on anyway */ 427 428 slot = 0; 429 idesc.id_number = curinum; 430 idesc.id_func = scannames; 431 idesc.id_type = DATA; 432 idesc.id_fix = IGNORE; 433 ckinode(curinode, &idesc); 434 curinode = ginode(curinum); 435 436 return 0; 437 } 438 439 CMDFUNCSTART(blks) 440 { 441 int blkno = 0; 442 int i, type; 443 if (!curinode) { 444 warnx("no current inode\n"); 445 return 0; 446 } 447 type = iswap16(curinode->di_mode) & IFMT; 448 if (type != IFDIR && type != IFREG) { 449 warnx("inode %d not a file or directory", curinum); 450 return 0; 451 } 452 printf("I=%d %d block%c\n", curinum, 453 (iswap32(curinode->di_blocks) + NSPB(sblock) -1) / 454 NSPB(sblock), 455 (iswap32(curinode->di_blocks) > NSPB(sblock)) ? 's' : ' '); 456 printf("Direct blocks:\n"); 457 print_blks(curinode->di_db, NDADDR, &blkno); 458 for (i = 0; i < NIADDR; i++) { 459 if (curinode->di_ib[i] != 0) 460 print_indirblks(iswap32(curinode->di_ib[i]), i, 461 &blkno); 462 } 463 return 0; 464 } 465 466 static void 467 print_blks(buf, size, blknum) 468 ufs_daddr_t *buf; 469 int size; 470 int *blknum; 471 { 472 #define CHARS_PER_LINES 70 473 int chars; 474 char prbuf[CHARS_PER_LINES+1]; 475 int blk; 476 477 chars = 0; 478 for(blk = 0; blk < size; blk++, (*blknum)++) { 479 if (buf[blk] == 0) 480 continue; 481 snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk])); 482 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { 483 printf("\n"); 484 chars = 0; 485 } 486 if (chars == 0) 487 printf("%d: ", *blknum); 488 printf("%s", prbuf); 489 chars += strlen(prbuf); 490 } 491 printf("\n"); 492 #undef CHARS_PER_LINES 493 } 494 495 static void 496 print_indirblks(blk,ind_level, blknum) 497 daddr_t blk; 498 int ind_level; 499 int *blknum; 500 { 501 #define MAXNINDIR (MAXBSIZE / sizeof(daddr_t)) 502 daddr_t idblk[MAXNINDIR]; 503 int i; 504 505 printf("Indirect block %d (level %d):\n", blk, ind_level+1); 506 bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk), 507 (int)sblock->fs_bsize); 508 if (ind_level <= 0) { 509 print_blks(idblk, sblock->fs_bsize / sizeof(daddr_t), blknum); 510 } else { 511 ind_level--; 512 for (i = 0; i < sblock->fs_bsize / sizeof(daddr_t); i++) { 513 if(idblk[i] != 0) 514 print_indirblks(iswap32(idblk[i]), 515 ind_level, blknum); 516 } 517 } 518 #undef MAXNINDIR 519 } 520 521 static int 522 dolookup(name) 523 char *name; 524 { 525 struct inodesc idesc; 526 527 if (!checkactivedir()) 528 return 0; 529 idesc.id_number = curinum; 530 idesc.id_func = findino; 531 idesc.id_name = name; 532 idesc.id_type = DATA; 533 idesc.id_fix = IGNORE; 534 if (ckinode(curinode, &idesc) & FOUND) { 535 curinum = idesc.id_parent; 536 curinode = ginode(curinum); 537 printactive(); 538 return 1; 539 } else { 540 warnx("name `%s' not found in current inode directory", name); 541 return 0; 542 } 543 } 544 545 CMDFUNCSTART(focusname) 546 { 547 char *p, *val; 548 549 if (!checkactive()) 550 return 1; 551 552 ocurrent = curinum; 553 554 if (argv[1][0] == '/') { 555 curinum = ROOTINO; 556 curinode = ginode(ROOTINO); 557 } else { 558 if (!checkactivedir()) 559 return 1; 560 } 561 for (p = argv[1]; p != NULL;) { 562 while ((val = strsep(&p, "/")) != NULL && *val == '\0'); 563 if (val) { 564 printf("component `%s': ", val); 565 fflush(stdout); 566 if (!dolookup(val)) { 567 curinode = ginode(curinum); 568 return (1); 569 } 570 } 571 } 572 return 0; 573 } 574 575 CMDFUNCSTART(ln) 576 { 577 ino_t inum; 578 int rval; 579 char *cp; 580 581 GETINUM(1, inum); 582 583 if (!checkactivedir()) 584 return 1; 585 rval = makeentry(curinum, inum, argv[2]); 586 if (rval) 587 printf("Ino %d entered as `%s'\n", inum, argv[2]); 588 else 589 printf("could not enter name? weird.\n"); 590 curinode = ginode(curinum); 591 return rval; 592 } 593 594 CMDFUNCSTART(rm) 595 { 596 int rval; 597 598 if (!checkactivedir()) 599 return 1; 600 rval = changeino(curinum, argv[1], 0); 601 if (rval & ALTERED) { 602 printf("Name `%s' removed\n", argv[1]); 603 return 0; 604 } else { 605 printf("could not remove name? weird.\n"); 606 return 1; 607 } 608 } 609 610 static long slotcount, desired; 611 612 static int 613 chinumfunc(idesc) 614 struct inodesc *idesc; 615 { 616 struct direct *dirp = idesc->id_dirp; 617 618 if (slotcount++ == desired) { 619 dirp->d_ino = iswap32(idesc->id_parent); 620 return STOP | ALTERED | FOUND; 621 } 622 return KEEPON; 623 } 624 625 CMDFUNCSTART(chinum) 626 { 627 char *cp; 628 ino_t inum; 629 struct inodesc idesc; 630 631 slotcount = 0; 632 if (!checkactivedir()) 633 return 1; 634 GETINUM(2, inum); 635 636 desired = strtol(argv[1], &cp, 0); 637 if (cp == argv[1] || *cp != '\0' || desired < 0) { 638 printf("invalid slot number `%s'\n", argv[1]); 639 return 1; 640 } 641 idesc.id_number = curinum; 642 idesc.id_func = chinumfunc; 643 idesc.id_fix = IGNORE; 644 idesc.id_type = DATA; 645 idesc.id_parent = inum; /* XXX convenient hiding place */ 646 647 if (ckinode(curinode, &idesc) & FOUND) 648 return 0; 649 else { 650 warnx("no %sth slot in current directory", argv[1]); 651 return 1; 652 } 653 } 654 655 static int 656 chnamefunc(idesc) 657 struct inodesc *idesc; 658 { 659 struct direct *dirp = idesc->id_dirp; 660 struct direct testdir; 661 662 if (slotcount++ == desired) { 663 /* will name fit? */ 664 testdir.d_namlen = strlen(idesc->id_name); 665 if (DIRSIZ(NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) { 666 dirp->d_namlen = testdir.d_namlen; 667 strcpy(dirp->d_name, idesc->id_name); 668 return STOP | ALTERED | FOUND; 669 } else 670 return STOP | FOUND; /* won't fit, so give up */ 671 } 672 return KEEPON; 673 } 674 675 CMDFUNCSTART(chname) 676 { 677 int rval; 678 char *cp; 679 struct inodesc idesc; 680 681 slotcount = 0; 682 if (!checkactivedir()) 683 return 1; 684 685 desired = strtoul(argv[1], &cp, 0); 686 if (cp == argv[1] || *cp != '\0') { 687 printf("invalid slot number `%s'\n", argv[1]); 688 return 1; 689 } 690 idesc.id_number = curinum; 691 idesc.id_func = chnamefunc; 692 idesc.id_fix = IGNORE; 693 idesc.id_type = DATA; 694 idesc.id_name = argv[2]; 695 696 rval = ckinode(curinode, &idesc); 697 if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED)) 698 return 0; 699 else 700 if (rval & FOUND) { 701 warnx("new name `%s' does not fit in slot %s\n", 702 argv[2], argv[1]); 703 return 1; 704 } else { 705 warnx("no %sth slot in current directory", argv[1]); 706 return 1; 707 } 708 } 709 710 static struct typemap { 711 const char *typename; 712 int typebits; 713 } typenamemap[] = { 714 { "file", IFREG }, 715 { "dir", IFDIR }, 716 { "socket", IFSOCK }, 717 { "fifo", IFIFO }, 718 }; 719 720 CMDFUNCSTART(newtype) 721 { 722 int type; 723 struct typemap *tp; 724 725 if (!checkactive()) 726 return 1; 727 type = iswap16(curinode->di_mode) & IFMT; 728 for (tp = typenamemap; 729 tp < &typenamemap[sizeof(typemap) / sizeof(*typemap)]; 730 tp++) { 731 if (!strcmp(argv[1], tp->typename)) { 732 printf("setting type to %s\n", tp->typename); 733 type = tp->typebits; 734 break; 735 } 736 } 737 if (tp == &typenamemap[sizeof(typemap) / sizeof(*typemap)]) { 738 warnx("type `%s' not known", argv[1]); 739 warnx("try one of `file', `dir', `socket', `fifo'"); 740 return 1; 741 } 742 curinode->di_mode = iswap16((iswap16(curinode->di_mode) & ~IFMT) | type); 743 inodirty(); 744 printactive(); 745 return 0; 746 } 747 748 CMDFUNCSTART(chmode) 749 { 750 long modebits; 751 char *cp; 752 753 if (!checkactive()) 754 return 1; 755 756 modebits = strtol(argv[1], &cp, 8); 757 if (cp == argv[1] || *cp != '\0') { 758 warnx("bad modebits `%s'", argv[1]); 759 return 1; 760 } 761 curinode->di_mode = 762 iswap16((iswap16(curinode->di_mode) & ~07777) | modebits); 763 inodirty(); 764 printactive(); 765 return 0; 766 } 767 768 CMDFUNCSTART(chlen) 769 { 770 long len; 771 char *cp; 772 773 if (!checkactive()) 774 return 1; 775 776 len = strtol(argv[1], &cp, 0); 777 if (cp == argv[1] || *cp != '\0' || len < 0) { 778 warnx("bad length '%s'", argv[1]); 779 return 1; 780 } 781 curinode->di_size = iswap64(len); 782 inodirty(); 783 printactive(); 784 return 0; 785 } 786 787 CMDFUNCSTART(chaflags) 788 { 789 u_long flags; 790 char *cp; 791 792 if (!checkactive()) 793 return 1; 794 795 flags = strtoul(argv[1], &cp, 0); 796 if (cp == argv[1] || *cp != '\0') { 797 warnx("bad flags `%s'", argv[1]); 798 return 1; 799 } 800 if (flags > UINT_MAX) { 801 warnx("flags set beyond 32-bit range of field (0x%lx)\n", 802 flags); 803 return (1); 804 } 805 curinode->di_flags = iswap32(flags); 806 inodirty(); 807 printactive(); 808 return 0; 809 } 810 811 CMDFUNCSTART(chgen) 812 { 813 long gen; 814 char *cp; 815 816 if (!checkactive()) 817 return 1; 818 819 gen = strtol(argv[1], &cp, 0); 820 if (cp == argv[1] || *cp != '\0') { 821 warnx("bad gen `%s'", argv[1]); 822 return 1; 823 } 824 if (gen > INT_MAX || gen < INT_MIN) { 825 warnx("gen set beyond 32-bit range of field (0x%lx)\n", gen); 826 return (1); 827 } 828 curinode->di_gen = iswap32(gen); 829 inodirty(); 830 printactive(); 831 return 0; 832 } 833 834 CMDFUNCSTART(linkcount) 835 { 836 int lcnt; 837 char *cp; 838 839 if (!checkactive()) 840 return 1; 841 842 lcnt = strtol(argv[1], &cp, 0); 843 if (cp == argv[1] || *cp != '\0') { 844 warnx("bad link count `%s'", argv[1]); 845 return 1; 846 } 847 if (lcnt > USHRT_MAX || lcnt < 0) { 848 warnx("max link count is %d\n", USHRT_MAX); 849 return 1; 850 } 851 curinode->di_nlink = iswap16(lcnt); 852 inodirty(); 853 printactive(); 854 return 0; 855 } 856 857 CMDFUNCSTART(chowner) 858 { 859 unsigned long uid; 860 char *cp; 861 struct passwd *pwd; 862 863 if (!checkactive()) 864 return 1; 865 866 uid = strtoul(argv[1], &cp, 0); 867 if (cp == argv[1] || *cp != '\0') { 868 /* try looking up name */ 869 if ((pwd = getpwnam(argv[1])) != 0) { 870 uid = pwd->pw_uid; 871 } else { 872 warnx("bad uid `%s'", argv[1]); 873 return 1; 874 } 875 } 876 curinode->di_uid = iswap32(uid); 877 inodirty(); 878 printactive(); 879 return 0; 880 } 881 882 CMDFUNCSTART(chgroup) 883 { 884 unsigned long gid; 885 char *cp; 886 struct group *grp; 887 888 if (!checkactive()) 889 return 1; 890 891 gid = strtoul(argv[1], &cp, 0); 892 if (cp == argv[1] || *cp != '\0') { 893 if ((grp = getgrnam(argv[1])) != 0) { 894 gid = grp->gr_gid; 895 } else { 896 warnx("bad gid `%s'", argv[1]); 897 return 1; 898 } 899 } 900 curinode->di_gid = iswap32(gid); 901 inodirty(); 902 printactive(); 903 return 0; 904 } 905 906 static int 907 dotime(name, rsec, rnsec) 908 char *name; 909 int32_t *rsec, *rnsec; 910 { 911 char *p, *val; 912 struct tm t; 913 int32_t sec; 914 int32_t nsec; 915 p = strchr(name, '.'); 916 if (p) { 917 *p = '\0'; 918 nsec = strtoul(++p, &val, 0); 919 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 920 warnx("invalid nanoseconds"); 921 goto badformat; 922 } 923 } else 924 nsec = 0; 925 if (strlen(name) != 14) { 926 badformat: 927 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 928 return 1; 929 } 930 for (p = name; *p; p++) 931 if (*p < '0' || *p > '9') 932 goto badformat; 933 934 p = name; 935 #define VAL() ((*p++) - '0') 936 t.tm_year = VAL(); 937 t.tm_year = VAL() + t.tm_year * 10; 938 t.tm_year = VAL() + t.tm_year * 10; 939 t.tm_year = VAL() + t.tm_year * 10 - 1900; 940 t.tm_mon = VAL(); 941 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 942 t.tm_mday = VAL(); 943 t.tm_mday = VAL() + t.tm_mday * 10; 944 t.tm_hour = VAL(); 945 t.tm_hour = VAL() + t.tm_hour * 10; 946 t.tm_min = VAL(); 947 t.tm_min = VAL() + t.tm_min * 10; 948 t.tm_sec = VAL(); 949 t.tm_sec = VAL() + t.tm_sec * 10; 950 t.tm_isdst = -1; 951 952 sec = mktime(&t); 953 if (sec == -1) { 954 warnx("date/time out of range"); 955 return 1; 956 } 957 *rsec = iswap32(sec); 958 *rnsec = iswap32(nsec); 959 return 0; 960 } 961 962 CMDFUNCSTART(chmtime) 963 { 964 if (dotime(argv[1], &curinode->di_ctime, &curinode->di_ctimensec)) 965 return 1; 966 inodirty(); 967 printactive(); 968 return 0; 969 } 970 971 CMDFUNCSTART(chatime) 972 { 973 if (dotime(argv[1], &curinode->di_ctime, &curinode->di_ctimensec)) 974 return 1; 975 inodirty(); 976 printactive(); 977 return 0; 978 } 979 980 CMDFUNCSTART(chctime) 981 { 982 if (dotime(argv[1], &curinode->di_ctime, &curinode->di_ctimensec)) 983 return 1; 984 inodirty(); 985 printactive(); 986 return 0; 987 } 988