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