1 /* $OpenBSD: fsdb.c,v 1.22 2008/06/26 05:42:06 ray 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 #ifndef lint 34 static const char rcsid[] = "$OpenBSD: fsdb.c,v 1.22 2008/06/26 05:42:06 ray Exp $"; 35 #endif /* not lint */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/param.h> 40 #include <sys/time.h> 41 #include <sys/mount.h> 42 #include <ctype.h> 43 #include <err.h> 44 #include <fcntl.h> 45 #include <grp.h> 46 #include <histedit.h> 47 #include <limits.h> 48 #include <pwd.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include <ufs/ufs/dinode.h> 55 #include <ufs/ufs/dir.h> 56 #include <ufs/ffs/fs.h> 57 58 #include "fsdb.h" 59 #include "fsck.h" 60 #include "extern.h" 61 62 extern char *__progname; /* from crt0.o */ 63 64 int main(int, char *[]); 65 static void usage(void); 66 static int cmdloop(void); 67 static int helpfn(int, char *[]); 68 static char *prompt(EditLine *); 69 static int scannames(struct inodesc *); 70 static int dolookup(char *); 71 static int chinumfunc(struct inodesc *); 72 static int chnamefunc(struct inodesc *); 73 static int dotime(char *, int32_t *, int32_t *); 74 75 int returntosingle = 0; 76 union dinode *curinode; 77 ino_t curinum; 78 79 static void 80 usage(void) 81 { 82 fprintf(stderr, "usage: %s [-d] -f fsname\n", __progname); 83 exit(1); 84 } 85 86 /* 87 * We suck in lots of fsck code, and just pick & choose the stuff we want. 88 * 89 * fsreadfd is set up to read from the file system, fswritefd to write to 90 * the file system. 91 */ 92 int 93 main(int argc, char *argv[]) 94 { 95 int ch, rval; 96 char *fsys = NULL; 97 98 while (-1 != (ch = getopt(argc, argv, "f:d"))) { 99 switch (ch) { 100 case 'f': 101 fsys = optarg; 102 break; 103 case 'd': 104 debug++; 105 break; 106 default: 107 usage(); 108 } 109 } 110 if (fsys == NULL) 111 usage(); 112 if (!setup(fsys)) 113 errx(1, "cannot set up file system `%s'", fsys); 114 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys, 115 sblock.fs_fsmnt); 116 rval = cmdloop(); 117 sblock.fs_clean = 0; /* mark it dirty */ 118 sbdirty(); 119 ckfini(0); 120 printf("*** FILE SYSTEM MARKED DIRTY\n"); 121 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 122 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 123 exit(rval); 124 } 125 126 #define CMDFUNC(func) static int func(int argc, char *argv[]) 127 #define CMDFUNCSTART(func) static int func(int argc, char *argv[]) 128 129 CMDFUNC(helpfn); 130 CMDFUNC(focus); /* focus on inode */ 131 CMDFUNC(active); /* print active inode */ 132 CMDFUNC(focusname); /* focus by name */ 133 CMDFUNC(zapi); /* clear inode */ 134 CMDFUNC(uplink); /* incr link */ 135 CMDFUNC(downlink); /* decr link */ 136 CMDFUNC(linkcount); /* set link count */ 137 CMDFUNC(quit); /* quit */ 138 CMDFUNC(ls); /* list directory */ 139 CMDFUNC(rm); /* remove name */ 140 CMDFUNC(ln); /* add name */ 141 CMDFUNC(newtype); /* change type */ 142 CMDFUNC(chmode); /* change mode */ 143 CMDFUNC(chlen); /* change length */ 144 CMDFUNC(chaflags); /* change flags */ 145 CMDFUNC(chgen); /* change generation */ 146 CMDFUNC(chowner); /* change owner */ 147 CMDFUNC(chgroup); /* Change group */ 148 CMDFUNC(back); /* pop back to last ino */ 149 CMDFUNC(chmtime); /* Change mtime */ 150 CMDFUNC(chctime); /* Change ctime */ 151 CMDFUNC(chatime); /* Change atime */ 152 CMDFUNC(chinum); /* Change inode # of dirent */ 153 CMDFUNC(chname); /* Change dirname of dirent */ 154 155 static struct cmdtable cmds[] = { 156 { "help", "Print out help", 1, 1, helpfn }, 157 { "?", "Print out help", 1, 1, helpfn }, 158 { "inode", "Set active inode to INUM", 2, 2, focus }, 159 { "clri", "Clear inode INUM", 2, 2, zapi }, 160 { "lookup", "Set active inode by looking up NAME", 2, 2, focusname }, 161 { "cd", "Set active inode by looking up NAME", 2, 2, focusname }, 162 { "back", "Go to previous active inode", 1, 1, back }, 163 { "active", "Print active inode", 1, 1, active }, 164 { "print", "Print active inode", 1, 1, active }, 165 { "uplink", "Increment link count", 1, 1, uplink }, 166 { "downlink", "Decrement link count", 1, 1, downlink }, 167 { "linkcount", "Set link count to COUNT", 2, 2, linkcount }, 168 { "ls", "List current inode as directory", 1, 1, ls }, 169 { "rm", "Remove NAME from current inode directory", 2, 2, rm }, 170 { "del", "Remove NAME from current inode directory", 2, 2, rm }, 171 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln }, 172 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum }, 173 { "chname", "Change dir entry number INDEX to NAME", 3, 3, chname }, 174 { "chtype", "Change type of current inode to TYPE", 2, 2, newtype }, 175 { "chmod", "Change mode of current inode to MODE", 2, 2, chmode }, 176 { "chown", "Change owner of current inode to OWNER", 2, 2, chowner }, 177 { "chlen", "Change length of current inode to LENGTH", 2, 2, chlen }, 178 { "chgrp", "Change group of current inode to GROUP", 2, 2, chgroup }, 179 { "chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags }, 180 { "chgen", "Change generation number of current inode to GEN", 2, 2, chgen }, 181 { "mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime }, 182 { "ctime", "Change ctime of current inode to CTIME", 2, 2, chctime }, 183 { "atime", "Change atime of current inode to ATIME", 2, 2, chatime }, 184 { "quit", "Exit", 1, 1, quit }, 185 { "q", "Exit", 1, 1, quit }, 186 { "exit", "Exit", 1, 1, quit }, 187 { NULL, 0, 0, 0 }, 188 }; 189 190 static int 191 helpfn(int argc, char *argv[]) 192 { 193 struct cmdtable *cmdtp; 194 195 printf("Commands are:\n%-10s %5s %5s %s\n", 196 "command", "min argc", "max argc", "what"); 197 198 for (cmdtp = cmds; cmdtp->cmd; cmdtp++) 199 printf("%-10s %5u %5u %s\n", 200 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt); 201 return 0; 202 } 203 204 static char * 205 prompt(EditLine *el) 206 { 207 static char pstring[64]; 208 209 snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum); 210 return pstring; 211 } 212 213 214 static int 215 cmdloop(void) 216 { 217 char *line = NULL; 218 const char *elline; 219 int cmd_argc, rval = 0, known; 220 #define scratch known 221 char **cmd_argv; 222 struct cmdtable *cmdp; 223 History *hist; 224 EditLine *elptr; 225 HistEvent hev; 226 227 curinode = ginode(ROOTINO); 228 curinum = ROOTINO; 229 printactive(); 230 231 hist = history_init(); 232 history(hist, &hev, H_SETSIZE, 100); /* 100 elt history buffer */ 233 234 elptr = el_init(__progname, stdin, stdout, stderr); 235 el_set(elptr, EL_EDITOR, "emacs"); 236 el_set(elptr, EL_PROMPT, prompt); 237 el_set(elptr, EL_HIST, history, hist); 238 el_source(elptr, NULL); 239 240 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 241 if (debug) 242 printf("command `%s'\n", line); 243 244 history(hist, &hev, H_ENTER, elline); 245 246 line = strdup(elline); 247 if (line == NULL) 248 errx(1, "out of memory"); 249 cmd_argv = crack(line, &cmd_argc); 250 if (cmd_argc) { 251 /* 252 * el_parse returns -1 to signal that it's not been handled 253 * internally. 254 */ 255 if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1) 256 continue; 257 known = 0; 258 for (cmdp = cmds; cmdp->cmd; cmdp++) { 259 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 260 if (cmd_argc >= cmdp->minargc && 261 cmd_argc <= cmdp->maxargc) 262 rval = (*cmdp->handler)(cmd_argc, 263 cmd_argv); 264 else 265 rval = argcount(cmdp, 266 cmd_argc, cmd_argv); 267 known = 1; 268 break; 269 } 270 } 271 if (!known) { 272 warnx("unknown command `%s'", cmd_argv[0]); 273 rval = 1; 274 } 275 } else 276 rval = 0; 277 free(line); 278 if (rval < 0) 279 return rval; 280 if (rval) 281 warnx("rval was %d", rval); 282 } 283 el_end(elptr); 284 history_end(hist); 285 return rval; 286 } 287 288 static ino_t ocurrent; 289 290 #define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \ 291 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \ 292 printf("inode %d out of range; range is [%d,%d]\n", \ 293 inum, ROOTINO, maxino); \ 294 return 1; \ 295 } 296 297 /* 298 * Focus on given inode number 299 */ 300 CMDFUNCSTART(focus) 301 { 302 ino_t inum; 303 char *cp; 304 305 GETINUM(1,inum); 306 curinode = ginode(inum); 307 ocurrent = curinum; 308 curinum = inum; 309 printactive(); 310 return 0; 311 } 312 313 CMDFUNCSTART(back) 314 { 315 curinum = ocurrent; 316 curinode = ginode(curinum); 317 printactive(); 318 return 0; 319 } 320 321 CMDFUNCSTART(zapi) 322 { 323 ino_t inum; 324 union dinode *dp; 325 char *cp; 326 327 GETINUM(1,inum); 328 dp = ginode(inum); 329 clearinode(dp); 330 inodirty(); 331 if (curinode) /* re-set after potential change */ 332 curinode = ginode(curinum); 333 return 0; 334 } 335 336 CMDFUNCSTART(active) 337 { 338 printactive(); 339 return 0; 340 } 341 342 343 CMDFUNCSTART(quit) 344 { 345 return -1; 346 } 347 348 CMDFUNCSTART(uplink) 349 { 350 if (!checkactive()) 351 return 1; 352 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1); 353 printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink)); 354 inodirty(); 355 return 0; 356 } 357 358 CMDFUNCSTART(downlink) 359 { 360 if (!checkactive()) 361 return 1; 362 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1); 363 printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink)); 364 inodirty(); 365 return 0; 366 } 367 368 static const char *typename[] = { 369 "unknown", 370 "fifo", 371 "char special", 372 "unregistered #3", 373 "directory", 374 "unregistered #5", 375 "blk special", 376 "unregistered #7", 377 "regular", 378 "unregistered #9", 379 "symlink", 380 "unregistered #11", 381 "socket", 382 "unregistered #13", 383 "whiteout", 384 }; 385 386 static int slot; 387 388 static int 389 scannames(struct inodesc *idesc) 390 { 391 struct direct *dirp = idesc->id_dirp; 392 393 printf("slot %d ino %d reclen %d: %s, `%.*s'\n", 394 slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type], 395 dirp->d_namlen, dirp->d_name); 396 return (KEEPON); 397 } 398 399 CMDFUNCSTART(ls) 400 { 401 struct inodesc idesc; 402 checkactivedir(); /* let it go on anyway */ 403 404 slot = 0; 405 idesc.id_number = curinum; 406 idesc.id_func = scannames; 407 idesc.id_type = DATA; 408 idesc.id_fix = IGNORE; 409 ckinode(curinode, &idesc); 410 curinode = ginode(curinum); 411 412 return 0; 413 } 414 415 static int 416 dolookup(char *name) 417 { 418 struct inodesc idesc; 419 420 if (!checkactivedir()) 421 return 0; 422 idesc.id_number = curinum; 423 idesc.id_func = findino; 424 idesc.id_name = name; 425 idesc.id_type = DATA; 426 idesc.id_fix = IGNORE; 427 if (ckinode(curinode, &idesc) & FOUND) { 428 curinum = idesc.id_parent; 429 curinode = ginode(curinum); 430 printactive(); 431 return 1; 432 } else { 433 warnx("name `%s' not found in current inode directory", name); 434 return 0; 435 } 436 } 437 438 CMDFUNCSTART(focusname) 439 { 440 char *p, *val; 441 442 if (!checkactive()) 443 return 1; 444 445 ocurrent = curinum; 446 447 if (argv[1][0] == '/') { 448 curinum = ROOTINO; 449 curinode = ginode(ROOTINO); 450 } else { 451 if (!checkactivedir()) 452 return 1; 453 } 454 for (p = argv[1]; p != NULL;) { 455 while ((val = strsep(&p, "/")) != NULL && *val == '\0') 456 ; 457 if (val) { 458 printf("component `%s': ", val); 459 fflush(stdout); 460 if (!dolookup(val)) { 461 curinode = ginode(curinum); 462 return(1); 463 } 464 } 465 } 466 return 0; 467 } 468 469 CMDFUNCSTART(ln) 470 { 471 ino_t inum; 472 int rval; 473 char *cp; 474 475 GETINUM(1,inum); 476 477 if (!checkactivedir()) 478 return 1; 479 rval = makeentry(curinum, inum, argv[2]); 480 if (rval) 481 printf("Ino %d entered as `%s'\n", inum, argv[2]); 482 else 483 printf("could not enter name? weird.\n"); 484 curinode = ginode(curinum); 485 return rval; 486 } 487 488 CMDFUNCSTART(rm) 489 { 490 int rval; 491 492 if (!checkactivedir()) 493 return 1; 494 rval = changeino(curinum, argv[1], 0); 495 if (rval & ALTERED) { 496 printf("Name `%s' removed\n", argv[1]); 497 return 0; 498 } else { 499 printf("could not remove name? weird.\n"); 500 return 1; 501 } 502 } 503 504 static long slotcount, desired; 505 506 static int 507 chinumfunc(struct inodesc *idesc) 508 { 509 struct direct *dirp = idesc->id_dirp; 510 511 if (slotcount++ == desired) { 512 dirp->d_ino = idesc->id_parent; 513 return STOP|ALTERED|FOUND; 514 } 515 return KEEPON; 516 } 517 518 CMDFUNCSTART(chinum) 519 { 520 char *cp; 521 ino_t inum; 522 struct inodesc idesc; 523 524 slotcount = 0; 525 if (!checkactivedir()) 526 return 1; 527 GETINUM(2,inum); 528 529 desired = strtol(argv[1], &cp, 0); 530 if (cp == argv[1] || *cp != '\0' || desired < 0) { 531 printf("invalid slot number `%s'\n", argv[1]); 532 return 1; 533 } 534 535 idesc.id_number = curinum; 536 idesc.id_func = chinumfunc; 537 idesc.id_fix = IGNORE; 538 idesc.id_type = DATA; 539 idesc.id_parent = inum; /* XXX convenient hiding place */ 540 541 if (ckinode(curinode, &idesc) & FOUND) 542 return 0; 543 else { 544 warnx("no %sth slot in current directory", argv[1]); 545 return 1; 546 } 547 } 548 549 static int 550 chnamefunc(struct inodesc *idesc) 551 { 552 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 strlcpy(dirp->d_name, idesc->id_name, sizeof dirp->d_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", 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 static 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 = DIP(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 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT); 635 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type); 636 inodirty(); 637 printactive(); 638 return 0; 639 } 640 641 CMDFUNCSTART(chmode) 642 { 643 int rval = 1; 644 long modebits; 645 char *cp; 646 647 if (!checkactive()) 648 return 1; 649 650 modebits = strtol(argv[1], &cp, 8); 651 if (cp == argv[1] || *cp != '\0' ) { 652 warnx("bad modebits `%s'", argv[1]); 653 return 1; 654 } 655 656 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777); 657 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits); 658 inodirty(); 659 printactive(); 660 return rval; 661 } 662 663 CMDFUNCSTART(chlen) 664 { 665 int rval = 1; 666 long len; 667 char *cp; 668 669 if (!checkactive()) 670 return 1; 671 672 len = strtol(argv[1], &cp, 0); 673 if (cp == argv[1] || *cp != '\0' || len < 0) { 674 warnx("bad length '%s'", argv[1]); 675 return 1; 676 } 677 678 DIP_SET(curinode, di_size, len); 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)", flags); 701 return(1); 702 } 703 DIP_SET(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)", gen); 726 return(1); 727 } 728 DIP_SET(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", USHRT_MAX); 750 return 1; 751 } 752 753 DIP_SET(curinode, di_nlink, lcnt); 754 inodirty(); 755 printactive(); 756 return rval; 757 } 758 759 CMDFUNCSTART(chowner) 760 { 761 int rval = 1; 762 uid_t 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 DIP_SET(curinode, di_uid, uid); 781 inodirty(); 782 printactive(); 783 return rval; 784 } 785 786 CMDFUNCSTART(chgroup) 787 { 788 int rval = 1; 789 gid_t 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 DIP_SET(curinode, di_gid, gid); 807 inodirty(); 808 printactive(); 809 return rval; 810 } 811 812 static int 813 dotime(char *name, int32_t *rsec, int32_t *rnsec) 814 { 815 char *p, *val; 816 struct tm t; 817 int32_t sec; 818 int32_t nsec; 819 820 p = strchr(name, '.'); 821 if (p) { 822 *p = '\0'; 823 nsec = strtoul(++p, &val, 0); 824 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 825 warnx("invalid nanoseconds"); 826 goto badformat; 827 } 828 } else 829 nsec = 0; 830 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 *rsec = sec; 865 *rnsec = nsec; 866 return 0; 867 } 868 869 CMDFUNCSTART(chmtime) 870 { 871 int32_t rsec, nsec; 872 873 if (dotime(argv[1], &rsec, &nsec)) 874 return 1; 875 DIP_SET(curinode, di_mtime, rsec); 876 DIP_SET(curinode, di_mtimensec, nsec); 877 inodirty(); 878 printactive(); 879 return 0; 880 } 881 882 CMDFUNCSTART(chatime) 883 { 884 int32_t rsec, nsec; 885 886 if (dotime(argv[1], &rsec, &nsec)) 887 return 1; 888 DIP_SET(curinode, di_atime, rsec); 889 DIP_SET(curinode, di_atimensec, nsec); 890 inodirty(); 891 printactive(); 892 return 0; 893 } 894 895 CMDFUNCSTART(chctime) 896 { 897 int32_t rsec, nsec; 898 899 if (dotime(argv[1], &rsec, &nsec)) 900 return 1; 901 DIP_SET(curinode, di_ctime, rsec); 902 DIP_SET(curinode, di_ctimensec, nsec); 903 inodirty(); 904 printactive(); 905 return 0; 906 } 907