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