1 /* $OpenBSD: fsdb.c,v 1.31 2016/09/09 15:37:14 tb Exp $ */ 2 /* $NetBSD: fsdb.c,v 1.7 1997/01/11 06:50:53 lukem Exp $ */ 3 4 /*- 5 * Copyright (c) 1996 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by John T. Kohl. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/stat.h> 34 #include <sys/time.h> 35 #include <sys/mount.h> 36 #include <ctype.h> 37 #include <err.h> 38 #include <fcntl.h> 39 #include <grp.h> 40 #include <histedit.h> 41 #include <limits.h> 42 #include <pwd.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include <ufs/ufs/dinode.h> 49 #include <ufs/ufs/dir.h> 50 #include <ufs/ffs/fs.h> 51 52 #include "fsdb.h" 53 #include "fsck.h" 54 #include "extern.h" 55 56 extern char *__progname; /* from crt0.o */ 57 58 int main(int, char *[]); 59 static void usage(void); 60 static int cmdloop(void); 61 static int helpfn(int, char *[]); 62 static char *prompt(EditLine *); 63 static int scannames(struct inodesc *); 64 static int dolookup(char *); 65 static int chinumfunc(struct inodesc *); 66 static int chnamefunc(struct inodesc *); 67 static int dotime(char *, time_t *, int32_t *); 68 69 int returntosingle = 0; 70 union dinode *curinode; 71 ino_t curinum; 72 73 static void 74 usage(void) 75 { 76 fprintf(stderr, "usage: %s [-d] -f fsname\n", __progname); 77 exit(1); 78 } 79 80 /* 81 * We suck in lots of fsck code, and just pick & choose the stuff we want. 82 * 83 * fsreadfd is set up to read from the file system, fswritefd to write to 84 * the file system. 85 */ 86 int 87 main(int argc, char *argv[]) 88 { 89 int ch, rval; 90 char *fsys = NULL; 91 92 while (-1 != (ch = getopt(argc, argv, "f:d"))) { 93 switch (ch) { 94 case 'f': 95 fsys = optarg; 96 break; 97 case 'd': 98 debug++; 99 break; 100 default: 101 usage(); 102 } 103 } 104 if (fsys == NULL) 105 usage(); 106 if (!setup(fsys, 1)) 107 errx(1, "cannot set up file system `%s'", fsys); 108 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys, 109 sblock.fs_fsmnt); 110 rval = cmdloop(); 111 sblock.fs_clean = 0; /* mark it dirty */ 112 sbdirty(); 113 ckfini(0); 114 printf("*** FILE SYSTEM MARKED DIRTY\n"); 115 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 116 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 117 exit(rval); 118 } 119 120 #define CMDFUNC(func) static int func(int argc, char *argv[]) 121 #define CMDFUNCSTART(func) static int func(int argc, char *argv[]) 122 123 CMDFUNC(helpfn); 124 CMDFUNC(focus); /* focus on inode */ 125 CMDFUNC(active); /* print active inode */ 126 CMDFUNC(focusname); /* focus by name */ 127 CMDFUNC(zapi); /* clear inode */ 128 CMDFUNC(uplink); /* incr link */ 129 CMDFUNC(downlink); /* decr link */ 130 CMDFUNC(linkcount); /* set link count */ 131 CMDFUNC(quit); /* quit */ 132 CMDFUNC(ls); /* list directory */ 133 CMDFUNC(rm); /* remove name */ 134 CMDFUNC(ln); /* add name */ 135 CMDFUNC(newtype); /* change type */ 136 CMDFUNC(chmode); /* change mode */ 137 CMDFUNC(chlen); /* change length */ 138 CMDFUNC(chaflags); /* change flags */ 139 CMDFUNC(chgen); /* change generation */ 140 CMDFUNC(chowner); /* change owner */ 141 CMDFUNC(chgroup); /* Change group */ 142 CMDFUNC(back); /* pop back to last ino */ 143 CMDFUNC(chmtime); /* Change mtime */ 144 CMDFUNC(chctime); /* Change ctime */ 145 CMDFUNC(chatime); /* Change atime */ 146 CMDFUNC(chinum); /* Change inode # of dirent */ 147 CMDFUNC(chname); /* Change dirname of dirent */ 148 149 static struct cmdtable cmds[] = { 150 { "help", "Print out help", 1, 1, helpfn }, 151 { "?", "Print out help", 1, 1, helpfn }, 152 { "inode", "Set active inode to INUM", 2, 2, focus }, 153 { "clri", "Clear inode INUM", 2, 2, zapi }, 154 { "lookup", "Set active inode by looking up NAME", 2, 2, focusname }, 155 { "cd", "Set active inode by looking up NAME", 2, 2, focusname }, 156 { "back", "Go to previous active inode", 1, 1, back }, 157 { "active", "Print active inode", 1, 1, active }, 158 { "print", "Print active inode", 1, 1, active }, 159 { "uplink", "Increment link count", 1, 1, uplink }, 160 { "downlink", "Decrement link count", 1, 1, downlink }, 161 { "linkcount", "Set link count to COUNT", 2, 2, linkcount }, 162 { "ls", "List current inode as directory", 1, 1, ls }, 163 { "rm", "Remove NAME from current inode directory", 2, 2, rm }, 164 { "del", "Remove NAME from current inode directory", 2, 2, rm }, 165 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln }, 166 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum }, 167 { "chname", "Change dir entry number INDEX to NAME", 3, 3, chname }, 168 { "chtype", "Change type of current inode to TYPE", 2, 2, newtype }, 169 { "chmod", "Change mode of current inode to MODE", 2, 2, chmode }, 170 { "chown", "Change owner of current inode to OWNER", 2, 2, chowner }, 171 { "chlen", "Change length of current inode to LENGTH", 2, 2, chlen }, 172 { "chgrp", "Change group of current inode to GROUP", 2, 2, chgroup }, 173 { "chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags }, 174 { "chgen", "Change generation number of current inode to GEN", 2, 2, chgen }, 175 { "mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime }, 176 { "ctime", "Change ctime of current inode to CTIME", 2, 2, chctime }, 177 { "atime", "Change atime of current inode to ATIME", 2, 2, chatime }, 178 { "quit", "Exit", 1, 1, quit }, 179 { "q", "Exit", 1, 1, quit }, 180 { "exit", "Exit", 1, 1, quit }, 181 { NULL, 0, 0, 0 }, 182 }; 183 184 static int 185 helpfn(int argc, char *argv[]) 186 { 187 struct cmdtable *cmdtp; 188 189 printf("Commands are:\n%-10s %5s %5s %s\n", 190 "command", "min argc", "max argc", "what"); 191 192 for (cmdtp = cmds; cmdtp->cmd; cmdtp++) 193 printf("%-10s %5u %5u %s\n", 194 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt); 195 return 0; 196 } 197 198 static char * 199 prompt(EditLine *el) 200 { 201 static char pstring[64]; 202 203 snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ", 204 (unsigned long long)curinum); 205 return pstring; 206 } 207 208 209 static int 210 cmdloop(void) 211 { 212 char *line = NULL; 213 const char *elline; 214 int cmd_argc, rval = 0, known; 215 #define scratch known 216 char **cmd_argv; 217 struct cmdtable *cmdp; 218 History *hist; 219 EditLine *elptr; 220 HistEvent hev; 221 222 curinode = ginode(ROOTINO); 223 curinum = ROOTINO; 224 printactive(); 225 226 hist = history_init(); 227 history(hist, &hev, H_SETSIZE, 100); /* 100 elt history buffer */ 228 229 elptr = el_init(__progname, stdin, stdout, stderr); 230 el_set(elptr, EL_EDITOR, "emacs"); 231 el_set(elptr, EL_PROMPT, prompt); 232 el_set(elptr, EL_HIST, history, hist); 233 el_source(elptr, NULL); 234 235 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 236 if (debug) 237 printf("command `%s'\n", line); 238 239 history(hist, &hev, H_ENTER, elline); 240 241 line = strdup(elline); 242 if (line == NULL) 243 errx(1, "out of memory"); 244 cmd_argv = crack(line, &cmd_argc); 245 if (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, (const char **)cmd_argv) != -1) 251 continue; 252 known = 0; 253 for (cmdp = cmds; cmdp->cmd; cmdp++) { 254 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 255 if (cmd_argc >= cmdp->minargc && 256 cmd_argc <= cmdp->maxargc) 257 rval = (*cmdp->handler)(cmd_argc, 258 cmd_argv); 259 else 260 rval = argcount(cmdp, 261 cmd_argc, cmd_argv); 262 known = 1; 263 break; 264 } 265 } 266 if (!known) { 267 warnx("unknown command `%s'", cmd_argv[0]); 268 rval = 1; 269 } 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 static ino_t ocurrent; 284 285 #define GETINUM(ac,inum) inum = strtoull(argv[ac], &cp, 0); \ 286 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \ 287 printf("inode %llu out of range; range is [%llu,%llu]\n", \ 288 (unsigned long long)inum, (unsigned long long)ROOTINO, \ 289 (unsigned long long)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 union 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 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1); 349 printf("inode %llu link count now %d\n", 350 (unsigned long long)curinum, DIP(curinode, di_nlink)); 351 inodirty(); 352 return 0; 353 } 354 355 CMDFUNCSTART(downlink) 356 { 357 if (!checkactive()) 358 return 1; 359 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1); 360 printf("inode %llu link count now %d\n", 361 (unsigned long long)curinum, DIP(curinode, di_nlink)); 362 inodirty(); 363 return 0; 364 } 365 366 static const char *typename[] = { 367 "unknown", 368 "fifo", 369 "char special", 370 "unregistered #3", 371 "directory", 372 "unregistered #5", 373 "blk special", 374 "unregistered #7", 375 "regular", 376 "unregistered #9", 377 "symlink", 378 "unregistered #11", 379 "socket", 380 "unregistered #13", 381 "whiteout", 382 }; 383 384 static int slot; 385 386 static int 387 scannames(struct inodesc *idesc) 388 { 389 struct direct *dirp = idesc->id_dirp; 390 391 printf("slot %d ino %llu reclen %d: %s, `%.*s'\n", 392 slot++, (unsigned long long)dirp->d_ino, dirp->d_reclen, 393 typename[dirp->d_type], dirp->d_namlen, dirp->d_name); 394 return (KEEPON); 395 } 396 397 CMDFUNCSTART(ls) 398 { 399 struct inodesc idesc; 400 checkactivedir(); /* let it go on anyway */ 401 402 slot = 0; 403 idesc.id_number = curinum; 404 idesc.id_func = scannames; 405 idesc.id_type = DATA; 406 idesc.id_fix = IGNORE; 407 ckinode(curinode, &idesc); 408 curinode = ginode(curinum); 409 410 return 0; 411 } 412 413 static int 414 dolookup(char *name) 415 { 416 struct inodesc idesc; 417 418 if (!checkactivedir()) 419 return 0; 420 idesc.id_number = curinum; 421 idesc.id_func = findino; 422 idesc.id_name = name; 423 idesc.id_type = DATA; 424 idesc.id_fix = IGNORE; 425 if (ckinode(curinode, &idesc) & FOUND) { 426 curinum = idesc.id_parent; 427 curinode = ginode(curinum); 428 printactive(); 429 return 1; 430 } else { 431 warnx("name `%s' not found in current inode directory", name); 432 return 0; 433 } 434 } 435 436 CMDFUNCSTART(focusname) 437 { 438 char *p, *val; 439 440 if (!checkactive()) 441 return 1; 442 443 ocurrent = curinum; 444 445 if (argv[1][0] == '/') { 446 curinum = ROOTINO; 447 curinode = ginode(ROOTINO); 448 } else { 449 if (!checkactivedir()) 450 return 1; 451 } 452 for (p = argv[1]; p != NULL;) { 453 while ((val = strsep(&p, "/")) != NULL && *val == '\0') 454 continue; 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 %llu entered as `%s'\n", 480 (unsigned long long)inum, argv[2]); 481 else 482 printf("could not enter name? weird.\n"); 483 curinode = ginode(curinum); 484 return rval; 485 } 486 487 CMDFUNCSTART(rm) 488 { 489 int rval; 490 491 if (!checkactivedir()) 492 return 1; 493 rval = changeino(curinum, argv[1], 0); 494 if (rval & ALTERED) { 495 printf("Name `%s' removed\n", argv[1]); 496 return 0; 497 } else { 498 printf("could not remove name? weird.\n"); 499 return 1; 500 } 501 } 502 503 static long slotcount, desired; 504 505 static int 506 chinumfunc(struct inodesc *idesc) 507 { 508 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 static int 549 chnamefunc(struct inodesc *idesc) 550 { 551 struct direct *dirp = idesc->id_dirp; 552 struct direct testdir; 553 554 if (slotcount++ == desired) { 555 /* will name fit? */ 556 testdir.d_namlen = strlen(idesc->id_name); 557 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) { 558 dirp->d_namlen = testdir.d_namlen; 559 strlcpy(dirp->d_name, idesc->id_name, sizeof dirp->d_name); 560 return STOP|ALTERED|FOUND; 561 } else 562 return STOP|FOUND; /* won't fit, so give up */ 563 } 564 return KEEPON; 565 } 566 567 CMDFUNCSTART(chname) 568 { 569 int rval; 570 char *cp; 571 struct inodesc idesc; 572 573 slotcount = 0; 574 if (!checkactivedir()) 575 return 1; 576 577 desired = strtoul(argv[1], &cp, 0); 578 if (cp == argv[1] || *cp != '\0') { 579 printf("invalid slot number `%s'\n", argv[1]); 580 return 1; 581 } 582 583 idesc.id_number = curinum; 584 idesc.id_func = chnamefunc; 585 idesc.id_fix = IGNORE; 586 idesc.id_type = DATA; 587 idesc.id_name = argv[2]; 588 589 rval = ckinode(curinode, &idesc); 590 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED)) 591 return 0; 592 else if (rval & FOUND) { 593 warnx("new name `%s' does not fit in slot %s", argv[2], argv[1]); 594 return 1; 595 } else { 596 warnx("no %sth slot in current directory", argv[1]); 597 return 1; 598 } 599 } 600 601 static struct typemap { 602 const char *typename; 603 int typebits; 604 } typenamemap[] = { 605 {"file", IFREG}, 606 {"dir", IFDIR}, 607 {"socket", IFSOCK}, 608 {"fifo", IFIFO}, 609 }; 610 611 CMDFUNCSTART(newtype) 612 { 613 int type; 614 struct typemap *tp; 615 616 if (!checkactive()) 617 return 1; 618 type = DIP(curinode, di_mode) & IFMT; 619 for (tp = typenamemap; 620 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]; 621 tp++) { 622 if (!strcmp(argv[1], tp->typename)) { 623 printf("setting type to %s\n", tp->typename); 624 type = tp->typebits; 625 break; 626 } 627 } 628 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) { 629 warnx("type `%s' not known", argv[1]); 630 warnx("try one of `file', `dir', `socket', `fifo'"); 631 return 1; 632 } 633 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT); 634 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type); 635 inodirty(); 636 printactive(); 637 return 0; 638 } 639 640 CMDFUNCSTART(chmode) 641 { 642 int rval = 1; 643 long modebits; 644 char *cp; 645 646 if (!checkactive()) 647 return 1; 648 649 modebits = strtol(argv[1], &cp, 8); 650 if (cp == argv[1] || *cp != '\0' ) { 651 warnx("bad modebits `%s'", argv[1]); 652 return 1; 653 } 654 655 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777); 656 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits); 657 inodirty(); 658 printactive(); 659 return rval; 660 } 661 662 CMDFUNCSTART(chlen) 663 { 664 int rval = 1; 665 long len; 666 char *cp; 667 668 if (!checkactive()) 669 return 1; 670 671 len = strtol(argv[1], &cp, 0); 672 if (cp == argv[1] || *cp != '\0' || len < 0) { 673 warnx("bad length '%s'", argv[1]); 674 return 1; 675 } 676 677 DIP_SET(curinode, di_size, len); 678 inodirty(); 679 printactive(); 680 return rval; 681 } 682 683 CMDFUNCSTART(chaflags) 684 { 685 int rval = 1; 686 u_long flags; 687 char *cp; 688 689 if (!checkactive()) 690 return 1; 691 692 flags = strtoul(argv[1], &cp, 0); 693 if (cp == argv[1] || *cp != '\0' ) { 694 warnx("bad flags `%s'", argv[1]); 695 return 1; 696 } 697 698 if (flags > UINT_MAX) { 699 warnx("flags set beyond 32-bit range of field (%lx)", flags); 700 return(1); 701 } 702 DIP_SET(curinode, di_flags, flags); 703 inodirty(); 704 printactive(); 705 return rval; 706 } 707 708 CMDFUNCSTART(chgen) 709 { 710 int rval = 1; 711 long gen; 712 char *cp; 713 714 if (!checkactive()) 715 return 1; 716 717 gen = strtol(argv[1], &cp, 0); 718 if (cp == argv[1] || *cp != '\0' ) { 719 warnx("bad gen `%s'", argv[1]); 720 return 1; 721 } 722 723 if (gen > INT_MAX || gen < INT_MIN) { 724 warnx("gen set beyond 32-bit range of field (%lx)", gen); 725 return(1); 726 } 727 DIP_SET(curinode, di_gen, gen); 728 inodirty(); 729 printactive(); 730 return rval; 731 } 732 733 CMDFUNCSTART(linkcount) 734 { 735 int rval = 1; 736 int lcnt; 737 char *cp; 738 739 if (!checkactive()) 740 return 1; 741 742 lcnt = strtol(argv[1], &cp, 0); 743 if (cp == argv[1] || *cp != '\0' ) { 744 warnx("bad link count `%s'", argv[1]); 745 return 1; 746 } 747 if (lcnt > USHRT_MAX || lcnt < 0) { 748 warnx("max link count is %d", USHRT_MAX); 749 return 1; 750 } 751 752 DIP_SET(curinode, di_nlink, lcnt); 753 inodirty(); 754 printactive(); 755 return rval; 756 } 757 758 CMDFUNCSTART(chowner) 759 { 760 int rval = 1; 761 uid_t uid; 762 char *cp; 763 struct passwd *pwd; 764 765 if (!checkactive()) 766 return 1; 767 768 uid = strtoul(argv[1], &cp, 0); 769 if (cp == argv[1] || *cp != '\0' ) { 770 /* try looking up name */ 771 if ((pwd = getpwnam(argv[1]))) { 772 uid = pwd->pw_uid; 773 } else { 774 warnx("bad uid `%s'", argv[1]); 775 return 1; 776 } 777 } 778 779 DIP_SET(curinode, di_uid, uid); 780 inodirty(); 781 printactive(); 782 return rval; 783 } 784 785 CMDFUNCSTART(chgroup) 786 { 787 int rval = 1; 788 gid_t gid; 789 char *cp; 790 struct group *grp; 791 792 if (!checkactive()) 793 return 1; 794 795 gid = strtoul(argv[1], &cp, 0); 796 if (cp == argv[1] || *cp != '\0' ) { 797 if ((grp = getgrnam(argv[1]))) { 798 gid = grp->gr_gid; 799 } else { 800 warnx("bad gid `%s'", argv[1]); 801 return 1; 802 } 803 } 804 805 DIP_SET(curinode, di_gid, gid); 806 inodirty(); 807 printactive(); 808 return rval; 809 } 810 811 static int 812 dotime(char *name, time_t *rsec, int32_t *rnsec) 813 { 814 char *p, *val; 815 struct tm t; 816 time_t sec; 817 int32_t nsec; 818 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 830 if (strlen(name) != 14) { 831 badformat: 832 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 833 return 1; 834 } 835 836 for (p = name; *p; p++) 837 if (*p < '0' || *p > '9') 838 goto badformat; 839 840 p = name; 841 #define VAL() ((*p++) - '0') 842 bzero(&t, sizeof t); 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 *rsec = sec; 865 *rnsec = nsec; 866 return 0; 867 } 868 869 CMDFUNCSTART(chmtime) 870 { 871 time_t rsec; 872 int32_t nsec; 873 874 if (dotime(argv[1], &rsec, &nsec)) 875 return 1; 876 DIP_SET(curinode, di_mtime, rsec); 877 DIP_SET(curinode, di_mtimensec, nsec); 878 inodirty(); 879 printactive(); 880 return 0; 881 } 882 883 CMDFUNCSTART(chatime) 884 { 885 time_t rsec; 886 int32_t nsec; 887 888 if (dotime(argv[1], &rsec, &nsec)) 889 return 1; 890 DIP_SET(curinode, di_atime, rsec); 891 DIP_SET(curinode, di_atimensec, nsec); 892 inodirty(); 893 printactive(); 894 return 0; 895 } 896 897 CMDFUNCSTART(chctime) 898 { 899 time_t rsec; 900 int32_t nsec; 901 902 if (dotime(argv[1], &rsec, &nsec)) 903 return 1; 904 DIP_SET(curinode, di_ctime, rsec); 905 DIP_SET(curinode, di_ctimensec, nsec); 906 inodirty(); 907 printactive(); 908 return 0; 909 } 910