1 /* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1995 John T. Kohl 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #ifndef lint 32 static const char rcsid[] = 33 "$FreeBSD$"; 34 #endif /* not lint */ 35 36 #include <sys/param.h> 37 #include <sys/time.h> 38 #include <ctype.h> 39 #include <err.h> 40 #include <grp.h> 41 #include <histedit.h> 42 #include <pwd.h> 43 #include <string.h> 44 45 #include <ufs/ufs/dinode.h> 46 #include <ufs/ufs/dir.h> 47 #include <ufs/ffs/fs.h> 48 49 #include "fsdb.h" 50 #include "fsck.h" 51 52 static void usage __P((void)); 53 int cmdloop __P((void)); 54 55 static void 56 usage() 57 { 58 fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n"); 59 exit(1); 60 } 61 62 int returntosingle = 0; 63 char nflag = 0; 64 65 /* 66 * We suck in lots of fsck code, and just pick & choose the stuff we want. 67 * 68 * fsreadfd is set up to read from the file system, fswritefd to write to 69 * the file system. 70 */ 71 int 72 main(argc, argv) 73 int argc; 74 char *argv[]; 75 { 76 int ch, rval; 77 char *fsys = NULL; 78 79 while (-1 != (ch = getopt(argc, argv, "fdr"))) { 80 switch (ch) { 81 case 'f': 82 /* The -f option is left for historical 83 * reasons and has no meaning. 84 */ 85 break; 86 case 'd': 87 debug++; 88 break; 89 case 'r': 90 nflag++; /* "no" in fsck, readonly for us */ 91 break; 92 default: 93 usage(); 94 } 95 } 96 argc -= optind; 97 argv += optind; 98 if (argc != 1) 99 usage(); 100 else 101 fsys = argv[0]; 102 103 if (!setup(fsys)) 104 errx(1, "cannot set up file system `%s'", fsys); 105 printf("%s file system `%s'\nLast Mounted on %s\n", 106 nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt); 107 rval = cmdloop(); 108 if (!nflag) { 109 sblock.fs_clean = 0; /* mark it dirty */ 110 sbdirty(); 111 ckfini(0); 112 printf("*** FILE SYSTEM MARKED DIRTY\n"); 113 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 114 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 115 } 116 exit(rval); 117 } 118 119 #define CMDFUNC(func) int func __P((int argc, char *argv[])) 120 #define CMDFUNCSTART(func) int func(argc, argv) \ 121 int argc; \ 122 char *argv[]; 123 124 CMDFUNC(helpfn); 125 CMDFUNC(focus); /* focus on inode */ 126 CMDFUNC(active); /* print active inode */ 127 CMDFUNC(focusname); /* focus by name */ 128 CMDFUNC(zapi); /* clear inode */ 129 CMDFUNC(uplink); /* incr link */ 130 CMDFUNC(downlink); /* decr link */ 131 CMDFUNC(linkcount); /* set link count */ 132 CMDFUNC(quit); /* quit */ 133 CMDFUNC(ls); /* list directory */ 134 CMDFUNC(rm); /* remove name */ 135 CMDFUNC(ln); /* add name */ 136 CMDFUNC(newtype); /* change type */ 137 CMDFUNC(chmode); /* change mode */ 138 CMDFUNC(chlen); /* change length */ 139 CMDFUNC(chaflags); /* change flags */ 140 CMDFUNC(chgen); /* change generation */ 141 CMDFUNC(chowner); /* change owner */ 142 CMDFUNC(chgroup); /* Change group */ 143 CMDFUNC(back); /* pop back to last ino */ 144 CMDFUNC(chmtime); /* Change mtime */ 145 CMDFUNC(chctime); /* Change ctime */ 146 CMDFUNC(chatime); /* Change atime */ 147 CMDFUNC(chinum); /* Change inode # of dirent */ 148 CMDFUNC(chname); /* Change dirname of dirent */ 149 150 struct cmdtable cmds[] = { 151 { "help", "Print out help", 1, 1, FL_RO, helpfn }, 152 { "?", "Print out help", 1, 1, FL_RO, helpfn }, 153 { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus }, 154 { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi }, 155 { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname }, 156 { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname }, 157 { "back", "Go to previous active inode", 1, 1, FL_RO, back }, 158 { "active", "Print active inode", 1, 1, FL_RO, active }, 159 { "print", "Print active inode", 1, 1, FL_RO, active }, 160 { "uplink", "Increment link count", 1, 1, FL_WR, uplink }, 161 { "downlink", "Decrement link count", 1, 1, FL_WR, downlink }, 162 { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount }, 163 { "ls", "List current inode as directory", 1, 1, FL_RO, ls }, 164 { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm }, 165 { "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm }, 166 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln }, 167 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum }, 168 { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname }, 169 { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype }, 170 { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode }, 171 { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen }, 172 { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner }, 173 { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup }, 174 { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags }, 175 { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen }, 176 { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime }, 177 { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime }, 178 { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime }, 179 { "quit", "Exit", 1, 1, FL_RO, quit }, 180 { "q", "Exit", 1, 1, FL_RO, quit }, 181 { "exit", "Exit", 1, 1, FL_RO, quit }, 182 { NULL, 0, 0, 0 }, 183 }; 184 185 int 186 helpfn(argc, argv) 187 int argc; 188 char *argv[]; 189 { 190 register struct cmdtable *cmdtp; 191 192 printf("Commands are:\n%-10s %5s %5s %s\n", 193 "command", "min argc", "max argc", "what"); 194 195 for (cmdtp = cmds; cmdtp->cmd; cmdtp++) 196 printf("%-10s %5u %5u %s\n", 197 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt); 198 return 0; 199 } 200 201 char * 202 prompt(el) 203 EditLine *el; 204 { 205 static char pstring[64]; 206 snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum); 207 return pstring; 208 } 209 210 211 int 212 cmdloop() 213 { 214 char *line; 215 const char *elline; 216 int cmd_argc, rval = 0, known; 217 #define scratch known 218 char **cmd_argv; 219 struct cmdtable *cmdp; 220 History *hist; 221 EditLine *elptr; 222 223 curinode = ginode(ROOTINO); 224 curinum = ROOTINO; 225 printactive(); 226 227 hist = history_init(); 228 history(hist, H_EVENT, 100); /* 100 elt history buffer */ 229 230 elptr = el_init("fsdb", stdin, stdout); 231 el_set(elptr, EL_EDITOR, "emacs"); 232 el_set(elptr, EL_PROMPT, prompt); 233 el_set(elptr, EL_HIST, history, hist); 234 el_source(elptr, NULL); 235 236 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 237 if (debug) 238 printf("command `%s'\n", elline); 239 240 history(hist, H_ENTER, elline); 241 242 line = strdup(elline); 243 cmd_argv = crack(line, &cmd_argc); 244 /* 245 * el_parse returns -1 to signal that it's not been handled 246 * internally. 247 */ 248 if (el_parse(elptr, cmd_argc, cmd_argv) != -1) 249 continue; 250 if (cmd_argc) { 251 known = 0; 252 for (cmdp = cmds; cmdp->cmd; cmdp++) { 253 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 254 if ((cmdp->flags & FL_WR) == FL_WR && nflag) 255 warnx("`%s' requires write access", cmd_argv[0]), 256 rval = 1; 257 else if (cmd_argc >= cmdp->minargc && 258 cmd_argc <= cmdp->maxargc) 259 rval = (*cmdp->handler)(cmd_argc, cmd_argv); 260 else 261 rval = argcount(cmdp, cmd_argc, cmd_argv); 262 known = 1; 263 break; 264 } 265 } 266 if (!known) 267 warnx("unknown command `%s'", cmd_argv[0]), rval = 1; 268 } else 269 rval = 0; 270 free(line); 271 if (rval < 0) 272 return rval; 273 if (rval) 274 warnx("rval was %d", rval); 275 } 276 el_end(elptr); 277 history_end(hist); 278 return rval; 279 } 280 281 struct dinode *curinode; 282 ino_t curinum, ocurrent; 283 284 #define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \ 285 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \ 286 printf("inode %d out of range; range is [%d,%d]\n", \ 287 inum, ROOTINO, maxino); \ 288 return 1; \ 289 } 290 291 /* 292 * Focus on given inode number 293 */ 294 CMDFUNCSTART(focus) 295 { 296 ino_t inum; 297 char *cp; 298 299 GETINUM(1,inum); 300 curinode = ginode(inum); 301 ocurrent = curinum; 302 curinum = inum; 303 printactive(); 304 return 0; 305 } 306 307 CMDFUNCSTART(back) 308 { 309 curinum = ocurrent; 310 curinode = ginode(curinum); 311 printactive(); 312 return 0; 313 } 314 315 CMDFUNCSTART(zapi) 316 { 317 ino_t inum; 318 struct dinode *dp; 319 char *cp; 320 321 GETINUM(1,inum); 322 dp = ginode(inum); 323 clearinode(dp); 324 inodirty(); 325 if (curinode) /* re-set after potential change */ 326 curinode = ginode(curinum); 327 return 0; 328 } 329 330 CMDFUNCSTART(active) 331 { 332 printactive(); 333 return 0; 334 } 335 336 337 CMDFUNCSTART(quit) 338 { 339 return -1; 340 } 341 342 CMDFUNCSTART(uplink) 343 { 344 if (!checkactive()) 345 return 1; 346 printf("inode %d link count now %d\n", curinum, ++curinode->di_nlink); 347 inodirty(); 348 return 0; 349 } 350 351 CMDFUNCSTART(downlink) 352 { 353 if (!checkactive()) 354 return 1; 355 printf("inode %d link count now %d\n", curinum, --curinode->di_nlink); 356 inodirty(); 357 return 0; 358 } 359 360 const char *typename[] = { 361 "unknown", 362 "fifo", 363 "char special", 364 "unregistered #3", 365 "directory", 366 "unregistered #5", 367 "blk special", 368 "unregistered #7", 369 "regular", 370 "unregistered #9", 371 "symlink", 372 "unregistered #11", 373 "socket", 374 "unregistered #13", 375 "whiteout", 376 }; 377 378 int slot; 379 380 int 381 scannames(idesc) 382 struct inodesc *idesc; 383 { 384 register struct direct *dirp = idesc->id_dirp; 385 386 printf("slot %d ino %d reclen %d: %s, `%.*s'\n", 387 slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type], 388 dirp->d_namlen, dirp->d_name); 389 return (KEEPON); 390 } 391 392 CMDFUNCSTART(ls) 393 { 394 struct inodesc idesc; 395 checkactivedir(); /* let it go on anyway */ 396 397 slot = 0; 398 idesc.id_number = curinum; 399 idesc.id_func = scannames; 400 idesc.id_type = DATA; 401 idesc.id_fix = IGNORE; 402 ckinode(curinode, &idesc); 403 curinode = ginode(curinum); 404 405 return 0; 406 } 407 408 int findino __P((struct inodesc *idesc)); /* from fsck */ 409 static int dolookup __P((char *name)); 410 411 static int 412 dolookup(name) 413 char *name; 414 { 415 struct inodesc idesc; 416 417 if (!checkactivedir()) 418 return 0; 419 idesc.id_number = curinum; 420 idesc.id_func = findino; 421 idesc.id_name = name; 422 idesc.id_type = DATA; 423 idesc.id_fix = IGNORE; 424 if (ckinode(curinode, &idesc) & FOUND) { 425 curinum = idesc.id_parent; 426 curinode = ginode(curinum); 427 printactive(); 428 return 1; 429 } else { 430 warnx("name `%s' not found in current inode directory", name); 431 return 0; 432 } 433 } 434 435 CMDFUNCSTART(focusname) 436 { 437 char *p, *val; 438 439 if (!checkactive()) 440 return 1; 441 442 ocurrent = curinum; 443 444 if (argv[1][0] == '/') { 445 curinum = ROOTINO; 446 curinode = ginode(ROOTINO); 447 } else { 448 if (!checkactivedir()) 449 return 1; 450 } 451 for (p = argv[1]; p != NULL;) { 452 while ((val = strsep(&p, "/")) != NULL && *val == '\0'); 453 if (val) { 454 printf("component `%s': ", val); 455 fflush(stdout); 456 if (!dolookup(val)) { 457 curinode = ginode(curinum); 458 return(1); 459 } 460 } 461 } 462 return 0; 463 } 464 465 CMDFUNCSTART(ln) 466 { 467 ino_t inum; 468 int rval; 469 char *cp; 470 471 GETINUM(1,inum); 472 473 if (!checkactivedir()) 474 return 1; 475 rval = makeentry(curinum, inum, argv[2]); 476 if (rval) 477 printf("Ino %d entered as `%s'\n", inum, argv[2]); 478 else 479 printf("could not enter name? weird.\n"); 480 curinode = ginode(curinum); 481 return rval; 482 } 483 484 CMDFUNCSTART(rm) 485 { 486 int rval; 487 488 if (!checkactivedir()) 489 return 1; 490 rval = changeino(curinum, argv[1], 0); 491 if (rval & ALTERED) { 492 printf("Name `%s' removed\n", argv[1]); 493 return 0; 494 } else { 495 printf("could not remove name? weird.\n"); 496 return 1; 497 } 498 } 499 500 long slotcount, desired; 501 502 int 503 chinumfunc(idesc) 504 struct inodesc *idesc; 505 { 506 register struct direct *dirp = idesc->id_dirp; 507 508 if (slotcount++ == desired) { 509 dirp->d_ino = idesc->id_parent; 510 return STOP|ALTERED|FOUND; 511 } 512 return KEEPON; 513 } 514 515 CMDFUNCSTART(chinum) 516 { 517 char *cp; 518 ino_t inum; 519 struct inodesc idesc; 520 521 slotcount = 0; 522 if (!checkactivedir()) 523 return 1; 524 GETINUM(2,inum); 525 526 desired = strtol(argv[1], &cp, 0); 527 if (cp == argv[1] || *cp != '\0' || desired < 0) { 528 printf("invalid slot number `%s'\n", argv[1]); 529 return 1; 530 } 531 532 idesc.id_number = curinum; 533 idesc.id_func = chinumfunc; 534 idesc.id_fix = IGNORE; 535 idesc.id_type = DATA; 536 idesc.id_parent = inum; /* XXX convenient hiding place */ 537 538 if (ckinode(curinode, &idesc) & FOUND) 539 return 0; 540 else { 541 warnx("no %sth slot in current directory", argv[1]); 542 return 1; 543 } 544 } 545 546 int 547 chnamefunc(idesc) 548 struct inodesc *idesc; 549 { 550 register struct direct *dirp = idesc->id_dirp; 551 struct direct testdir; 552 553 if (slotcount++ == desired) { 554 /* will name fit? */ 555 testdir.d_namlen = strlen(idesc->id_name); 556 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) { 557 dirp->d_namlen = testdir.d_namlen; 558 strcpy(dirp->d_name, idesc->id_name); 559 return STOP|ALTERED|FOUND; 560 } else 561 return STOP|FOUND; /* won't fit, so give up */ 562 } 563 return KEEPON; 564 } 565 566 CMDFUNCSTART(chname) 567 { 568 int rval; 569 char *cp; 570 struct inodesc idesc; 571 572 slotcount = 0; 573 if (!checkactivedir()) 574 return 1; 575 576 desired = strtoul(argv[1], &cp, 0); 577 if (cp == argv[1] || *cp != '\0') { 578 printf("invalid slot number `%s'\n", argv[1]); 579 return 1; 580 } 581 582 idesc.id_number = curinum; 583 idesc.id_func = chnamefunc; 584 idesc.id_fix = IGNORE; 585 idesc.id_type = DATA; 586 idesc.id_name = argv[2]; 587 588 rval = ckinode(curinode, &idesc); 589 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED)) 590 return 0; 591 else if (rval & FOUND) { 592 warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]); 593 return 1; 594 } else { 595 warnx("no %sth slot in current directory", argv[1]); 596 return 1; 597 } 598 } 599 600 struct typemap { 601 const char *typename; 602 int typebits; 603 } typenamemap[] = { 604 {"file", IFREG}, 605 {"dir", IFDIR}, 606 {"socket", IFSOCK}, 607 {"fifo", IFIFO}, 608 }; 609 610 CMDFUNCSTART(newtype) 611 { 612 int type; 613 struct typemap *tp; 614 615 if (!checkactive()) 616 return 1; 617 type = curinode->di_mode & IFMT; 618 for (tp = typenamemap; 619 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]; 620 tp++) { 621 if (!strcmp(argv[1], tp->typename)) { 622 printf("setting type to %s\n", tp->typename); 623 type = tp->typebits; 624 break; 625 } 626 } 627 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) { 628 warnx("type `%s' not known", argv[1]); 629 warnx("try one of `file', `dir', `socket', `fifo'"); 630 return 1; 631 } 632 curinode->di_mode &= ~IFMT; 633 curinode->di_mode |= type; 634 inodirty(); 635 printactive(); 636 return 0; 637 } 638 639 CMDFUNCSTART(chlen) 640 { 641 int rval = 1; 642 long len; 643 char *cp; 644 645 if (!checkactive()) 646 return 1; 647 648 len = strtol(argv[1], &cp, 0); 649 if (cp == argv[1] || *cp != '\0' || len < 0) { 650 warnx("bad length `%s'", argv[1]); 651 return 1; 652 } 653 654 curinode->di_size = len; 655 inodirty(); 656 printactive(); 657 return rval; 658 } 659 660 CMDFUNCSTART(chmode) 661 { 662 int rval = 1; 663 long modebits; 664 char *cp; 665 666 if (!checkactive()) 667 return 1; 668 669 modebits = strtol(argv[1], &cp, 8); 670 if (cp == argv[1] || *cp != '\0' ) { 671 warnx("bad modebits `%s'", argv[1]); 672 return 1; 673 } 674 675 curinode->di_mode &= ~07777; 676 curinode->di_mode |= modebits; 677 inodirty(); 678 printactive(); 679 return rval; 680 } 681 682 CMDFUNCSTART(chaflags) 683 { 684 int rval = 1; 685 u_long flags; 686 char *cp; 687 688 if (!checkactive()) 689 return 1; 690 691 flags = strtoul(argv[1], &cp, 0); 692 if (cp == argv[1] || *cp != '\0' ) { 693 warnx("bad flags `%s'", argv[1]); 694 return 1; 695 } 696 697 if (flags > UINT_MAX) { 698 warnx("flags set beyond 32-bit range of field (%lx)\n", flags); 699 return(1); 700 } 701 curinode->di_flags = flags; 702 inodirty(); 703 printactive(); 704 return rval; 705 } 706 707 CMDFUNCSTART(chgen) 708 { 709 int rval = 1; 710 long gen; 711 char *cp; 712 713 if (!checkactive()) 714 return 1; 715 716 gen = strtol(argv[1], &cp, 0); 717 if (cp == argv[1] || *cp != '\0' ) { 718 warnx("bad gen `%s'", argv[1]); 719 return 1; 720 } 721 722 if (gen > INT_MAX || gen < INT_MIN) { 723 warnx("gen set beyond 32-bit range of field (%lx)\n", gen); 724 return(1); 725 } 726 curinode->di_gen = gen; 727 inodirty(); 728 printactive(); 729 return rval; 730 } 731 732 CMDFUNCSTART(linkcount) 733 { 734 int rval = 1; 735 int lcnt; 736 char *cp; 737 738 if (!checkactive()) 739 return 1; 740 741 lcnt = strtol(argv[1], &cp, 0); 742 if (cp == argv[1] || *cp != '\0' ) { 743 warnx("bad link count `%s'", argv[1]); 744 return 1; 745 } 746 if (lcnt > USHRT_MAX || lcnt < 0) { 747 warnx("max link count is %d\n", USHRT_MAX); 748 return 1; 749 } 750 751 curinode->di_nlink = lcnt; 752 inodirty(); 753 printactive(); 754 return rval; 755 } 756 757 CMDFUNCSTART(chowner) 758 { 759 int rval = 1; 760 unsigned long uid; 761 char *cp; 762 struct passwd *pwd; 763 764 if (!checkactive()) 765 return 1; 766 767 uid = strtoul(argv[1], &cp, 0); 768 if (cp == argv[1] || *cp != '\0' ) { 769 /* try looking up name */ 770 if ((pwd = getpwnam(argv[1]))) { 771 uid = pwd->pw_uid; 772 } else { 773 warnx("bad uid `%s'", argv[1]); 774 return 1; 775 } 776 } 777 778 curinode->di_uid = uid; 779 inodirty(); 780 printactive(); 781 return rval; 782 } 783 784 CMDFUNCSTART(chgroup) 785 { 786 int rval = 1; 787 unsigned long gid; 788 char *cp; 789 struct group *grp; 790 791 if (!checkactive()) 792 return 1; 793 794 gid = strtoul(argv[1], &cp, 0); 795 if (cp == argv[1] || *cp != '\0' ) { 796 if ((grp = getgrnam(argv[1]))) { 797 gid = grp->gr_gid; 798 } else { 799 warnx("bad gid `%s'", argv[1]); 800 return 1; 801 } 802 } 803 804 curinode->di_gid = gid; 805 inodirty(); 806 printactive(); 807 return rval; 808 } 809 810 int 811 dotime(name, rts) 812 char *name; 813 struct timespec *rts; 814 { 815 char *p, *val; 816 struct tm t; 817 int32_t sec; 818 int32_t nsec; 819 p = strchr(name, '.'); 820 if (p) { 821 *p = '\0'; 822 nsec = strtoul(++p, &val, 0); 823 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 824 warnx("invalid nanoseconds"); 825 goto badformat; 826 } 827 } else 828 nsec = 0; 829 if (strlen(name) != 14) { 830 badformat: 831 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 832 return 1; 833 } 834 835 for (p = name; *p; p++) 836 if (*p < '0' || *p > '9') 837 goto badformat; 838 839 p = name; 840 #define VAL() ((*p++) - '0') 841 t.tm_year = VAL(); 842 t.tm_year = VAL() + t.tm_year * 10; 843 t.tm_year = VAL() + t.tm_year * 10; 844 t.tm_year = VAL() + t.tm_year * 10 - 1900; 845 t.tm_mon = VAL(); 846 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 847 t.tm_mday = VAL(); 848 t.tm_mday = VAL() + t.tm_mday * 10; 849 t.tm_hour = VAL(); 850 t.tm_hour = VAL() + t.tm_hour * 10; 851 t.tm_min = VAL(); 852 t.tm_min = VAL() + t.tm_min * 10; 853 t.tm_sec = VAL(); 854 t.tm_sec = VAL() + t.tm_sec * 10; 855 t.tm_isdst = -1; 856 857 sec = mktime(&t); 858 if (sec == -1) { 859 warnx("date/time out of range"); 860 return 1; 861 } 862 rts->tv_sec = sec; 863 rts->tv_nsec = nsec; 864 return 0; 865 } 866 867 CMDFUNCSTART(chmtime) 868 { 869 if (dotime(argv[1], &curinode->di_ctime)) 870 return 1; 871 inodirty(); 872 printactive(); 873 return 0; 874 } 875 876 CMDFUNCSTART(chatime) 877 { 878 if (dotime(argv[1], &curinode->di_ctime)) 879 return 1; 880 inodirty(); 881 printactive(); 882 return 0; 883 } 884 885 CMDFUNCSTART(chctime) 886 { 887 if (dotime(argv[1], &curinode->di_ctime)) 888 return 1; 889 inodirty(); 890 printactive(); 891 return 0; 892 } 893