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