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