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 * $FreeBSD: src/sbin/fsdb/fsdb.c,v 1.13.2.3 2002/03/20 13:39:02 joerg Exp $ 31 */ 32 33 #include <sys/param.h> 34 #include <sys/time.h> 35 #include <ctype.h> 36 #include <err.h> 37 #include <grp.h> 38 #include <histedit.h> 39 #include <pwd.h> 40 #include <string.h> 41 42 #include <vfs/ufs/dinode.h> 43 #include <vfs/ufs/dir.h> 44 #include <vfs/ufs/fs.h> 45 46 #include "fsdb.h" 47 #include "fsck.h" 48 49 static void usage(void); 50 int cmdloop(void); 51 52 static void 53 usage(void) 54 { 55 fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n"); 56 exit(1); 57 } 58 59 int returntosingle = 0; 60 char nflag = 0; 61 62 /* 63 * We suck in lots of fsck code, and just pick & choose the stuff we want. 64 * 65 * fsreadfd is set up to read from the file system, fswritefd to write to 66 * the file system. 67 */ 68 int 69 main(int argc, char **argv) 70 { 71 int ch, rval; 72 char *fsys = NULL; 73 74 while (-1 != (ch = getopt(argc, argv, "fdr"))) { 75 switch (ch) { 76 case 'f': 77 /* The -f option is left for historical 78 * reasons and has no meaning. 79 */ 80 break; 81 case 'd': 82 debug++; 83 break; 84 case 'r': 85 nflag++; /* "no" in fsck, readonly for us */ 86 break; 87 default: 88 usage(); 89 } 90 } 91 argc -= optind; 92 argv += optind; 93 if (argc != 1) 94 usage(); 95 else 96 fsys = argv[0]; 97 98 if (!setup(fsys)) 99 errx(1, "cannot set up file system `%s'", fsys); 100 printf("%s file system `%s'\nLast Mounted on %s\n", 101 nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt); 102 rval = cmdloop(); 103 if (!nflag) { 104 sblock.fs_clean = 0; /* mark it dirty */ 105 sbdirty(); 106 ckfini(0); 107 printf("*** FILE SYSTEM MARKED DIRTY\n"); 108 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 109 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 110 } 111 exit(rval); 112 } 113 114 #define CMDFUNC(func) int func (int argc, char **argv) 115 #define CMDFUNCSTART(func) int func(int argc, char **argv) 116 117 CMDFUNC(helpfn); 118 CMDFUNC(focus); /* focus on inode */ 119 CMDFUNC(active); /* print active inode */ 120 CMDFUNC(blocks); /* print blocks for active inode */ 121 CMDFUNC(focusname); /* focus by name */ 122 CMDFUNC(zapi); /* clear inode */ 123 CMDFUNC(uplink); /* incr link */ 124 CMDFUNC(downlink); /* decr link */ 125 CMDFUNC(linkcount); /* set link count */ 126 CMDFUNC(quit); /* quit */ 127 CMDFUNC(ls); /* list directory */ 128 CMDFUNC(rm); /* remove name */ 129 CMDFUNC(ln); /* add name */ 130 CMDFUNC(newtype); /* change type */ 131 CMDFUNC(chmode); /* change mode */ 132 CMDFUNC(chlen); /* change length */ 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, FL_RO, helpfn }, 146 { "?", "Print out help", 1, 1, FL_RO, helpfn }, 147 { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus }, 148 { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi }, 149 { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname }, 150 { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname }, 151 { "back", "Go to previous active inode", 1, 1, FL_RO, back }, 152 { "active", "Print active inode", 1, 1, FL_RO, active }, 153 { "print", "Print active inode", 1, 1, FL_RO, active }, 154 { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks }, 155 { "uplink", "Increment link count", 1, 1, FL_WR, uplink }, 156 { "downlink", "Decrement link count", 1, 1, FL_WR, downlink }, 157 { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount }, 158 { "ls", "List current inode as directory", 1, 1, FL_RO, ls }, 159 { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm }, 160 { "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm }, 161 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln }, 162 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum }, 163 { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname }, 164 { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype }, 165 { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode }, 166 { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen }, 167 { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner }, 168 { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup }, 169 { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags }, 170 { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen }, 171 { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime }, 172 { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime }, 173 { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime }, 174 { "quit", "Exit", 1, 1, FL_RO, quit }, 175 { "q", "Exit", 1, 1, FL_RO, quit }, 176 { "exit", "Exit", 1, 1, FL_RO, quit }, 177 { NULL, 0, 0, 0 }, 178 }; 179 180 int 181 helpfn(int argc, char **argv) 182 { 183 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(EditLine *el) 196 { 197 static char pstring[64]; 198 snprintf(pstring, sizeof(pstring), "fsdb (inum: %ju)> ", 199 (uintmax_t)curinum); 200 return pstring; 201 } 202 203 204 int 205 cmdloop(void) 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 HistEvent he; 215 EditLine *elptr; 216 217 curinode = ginode(UFS_ROOTINO); 218 curinum = UFS_ROOTINO; 219 printactive(0); 220 221 hist = history_init(); 222 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 223 224 elptr = el_init("fsdb", stdin, stdout, stderr); 225 el_set(elptr, EL_EDITOR, "emacs"); 226 el_set(elptr, EL_PROMPT, prompt); 227 el_set(elptr, EL_HIST, history, hist); 228 el_source(elptr, NULL); 229 230 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 231 if (debug) 232 printf("command `%s'\n", elline); 233 234 history(hist, &he, H_ENTER, elline); 235 236 line = strdup(elline); 237 cmd_argv = crack(line, &cmd_argc); 238 /* 239 * el_parse returns -1 to signal that it's not been handled 240 * internally. 241 */ 242 if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1) 243 continue; 244 if (cmd_argc) { 245 known = 0; 246 for (cmdp = cmds; cmdp->cmd; cmdp++) { 247 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 248 if ((cmdp->flags & FL_WR) == FL_WR && nflag) 249 warnx("`%s' requires write access", cmd_argv[0]), 250 rval = 1; 251 else if (cmd_argc >= cmdp->minargc && 252 cmd_argc <= cmdp->maxargc) 253 rval = (*cmdp->handler)(cmd_argc, cmd_argv); 254 else 255 rval = argcount(cmdp, cmd_argc, cmd_argv); 256 known = 1; 257 break; 258 } 259 } 260 if (!known) 261 warnx("unknown command `%s'", cmd_argv[0]), rval = 1; 262 } else 263 rval = 0; 264 free(line); 265 if (rval < 0) 266 return rval; 267 if (rval) 268 warnx("rval was %d", rval); 269 } 270 el_end(elptr); 271 history_end(hist); 272 return rval; 273 } 274 275 struct ufs1_dinode *curinode; 276 ino_t curinum, ocurrent; 277 278 #define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \ 279 if (inum < UFS_ROOTINO || inum > maxino || \ 280 cp == argv[ac] || *cp != '\0' ) { \ 281 printf("inode %ju out of range; range is [%ju,%ju]\n", \ 282 (uintmax_t)inum, (uintmax_t)UFS_ROOTINO, (uintmax_t)maxino); \ 283 return 1; \ 284 } 285 286 /* 287 * Focus on given inode number 288 */ 289 CMDFUNCSTART(focus) 290 { 291 ino_t inum; 292 char *cp; 293 294 GETINUM(1,inum); 295 curinode = ginode(inum); 296 ocurrent = curinum; 297 curinum = inum; 298 printactive(0); 299 return 0; 300 } 301 302 CMDFUNCSTART(back) 303 { 304 curinum = ocurrent; 305 curinode = ginode(curinum); 306 printactive(0); 307 return 0; 308 } 309 310 CMDFUNCSTART(zapi) 311 { 312 ino_t inum; 313 struct ufs1_dinode *dp; 314 char *cp; 315 316 GETINUM(1,inum); 317 dp = ginode(inum); 318 clearinode(dp); 319 inodirty(); 320 if (curinode) /* re-set after potential change */ 321 curinode = ginode(curinum); 322 return 0; 323 } 324 325 CMDFUNCSTART(active) 326 { 327 printactive(0); 328 return 0; 329 } 330 331 CMDFUNCSTART(blocks) 332 { 333 printactive(1); 334 return 0; 335 } 336 337 CMDFUNCSTART(quit) 338 { 339 return -1; 340 } 341 342 CMDFUNCSTART(uplink) 343 { 344 if (!checkactive()) 345 return 1; 346 printf("inode %ju link count now %d\n", (uintmax_t)curinum, 347 ++curinode->di_nlink); 348 inodirty(); 349 return 0; 350 } 351 352 CMDFUNCSTART(downlink) 353 { 354 if (!checkactive()) 355 return 1; 356 printf("inode %ju link count now %d\n", (uintmax_t)curinum, 357 --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(struct inodesc *idesc) 384 { 385 struct direct *dirp = idesc->id_dirp; 386 387 printf("slot %d ino %d reclen %d: %s, `%.*s'\n", 388 slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type], 389 dirp->d_namlen, dirp->d_name); 390 return (KEEPON); 391 } 392 393 CMDFUNCSTART(ls) 394 { 395 struct inodesc idesc; 396 checkactivedir(); /* let it go on anyway */ 397 398 slot = 0; 399 idesc.id_number = curinum; 400 idesc.id_func = scannames; 401 idesc.id_type = DATA; 402 idesc.id_fix = IGNORE; 403 ckinode(curinode, &idesc); 404 curinode = ginode(curinum); 405 406 return 0; 407 } 408 409 int findino(struct inodesc *idesc); /* from fsck */ 410 static int dolookup(char *name); 411 412 static int 413 dolookup(char *name) 414 { 415 struct inodesc idesc; 416 417 if (!checkactivedir()) 418 return 0; 419 idesc.id_number = curinum; 420 idesc.id_func = findino; 421 idesc.id_name = name; 422 idesc.id_type = DATA; 423 idesc.id_fix = IGNORE; 424 if (ckinode(curinode, &idesc) & FOUND) { 425 curinum = idesc.id_parent; 426 curinode = ginode(curinum); 427 printactive(0); 428 return 1; 429 } else { 430 warnx("name `%s' not found in current inode directory", name); 431 return 0; 432 } 433 } 434 435 CMDFUNCSTART(focusname) 436 { 437 char *p, *val; 438 439 if (!checkactive()) 440 return 1; 441 442 ocurrent = curinum; 443 444 if (argv[1][0] == '/') { 445 curinum = UFS_ROOTINO; 446 curinode = ginode(UFS_ROOTINO); 447 } else { 448 if (!checkactivedir()) 449 return 1; 450 } 451 for (p = argv[1]; p != NULL;) { 452 while ((val = strsep(&p, "/")) != NULL && *val == '\0'); 453 if (val) { 454 printf("component `%s': ", val); 455 fflush(stdout); 456 if (!dolookup(val)) { 457 curinode = ginode(curinum); 458 return(1); 459 } 460 } 461 } 462 return 0; 463 } 464 465 CMDFUNCSTART(ln) 466 { 467 ino_t inum; 468 int rval; 469 char *cp; 470 471 GETINUM(1,inum); 472 473 if (!checkactivedir()) 474 return 1; 475 rval = makeentry(curinum, inum, argv[2]); 476 if (rval) 477 printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]); 478 else 479 printf("could not enter name? weird.\n"); 480 curinode = ginode(curinum); 481 return rval; 482 } 483 484 CMDFUNCSTART(rm) 485 { 486 int rval; 487 488 if (!checkactivedir()) 489 return 1; 490 rval = changeino(curinum, argv[1], 0); 491 if (rval & ALTERED) { 492 printf("Name `%s' removed\n", argv[1]); 493 return 0; 494 } else { 495 printf("could not remove name? weird.\n"); 496 return 1; 497 } 498 } 499 500 long slotcount, desired; 501 502 int 503 chinumfunc(struct inodesc *idesc) 504 { 505 struct direct *dirp = idesc->id_dirp; 506 507 if (slotcount++ == desired) { 508 dirp->d_ino = idesc->id_parent; 509 return STOP|ALTERED|FOUND; 510 } 511 return KEEPON; 512 } 513 514 CMDFUNCSTART(chinum) 515 { 516 char *cp; 517 ino_t inum; 518 struct inodesc idesc; 519 520 slotcount = 0; 521 if (!checkactivedir()) 522 return 1; 523 GETINUM(2,inum); 524 525 desired = strtol(argv[1], &cp, 0); 526 if (cp == argv[1] || *cp != '\0' || desired < 0) { 527 printf("invalid slot number `%s'\n", argv[1]); 528 return 1; 529 } 530 531 idesc.id_number = curinum; 532 idesc.id_func = chinumfunc; 533 idesc.id_fix = IGNORE; 534 idesc.id_type = DATA; 535 idesc.id_parent = inum; /* XXX convenient hiding place */ 536 537 if (ckinode(curinode, &idesc) & FOUND) 538 return 0; 539 else { 540 warnx("no %sth slot in current directory", argv[1]); 541 return 1; 542 } 543 } 544 545 int 546 chnamefunc(struct inodesc *idesc) 547 { 548 struct direct *dirp = idesc->id_dirp; 549 struct direct testdir; 550 551 if (slotcount++ == desired) { 552 /* will name fit? */ 553 testdir.d_namlen = strlen(idesc->id_name); 554 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) { 555 dirp->d_namlen = testdir.d_namlen; 556 strcpy(dirp->d_name, idesc->id_name); 557 return STOP|ALTERED|FOUND; 558 } else 559 return STOP|FOUND; /* won't fit, so give up */ 560 } 561 return KEEPON; 562 } 563 564 CMDFUNCSTART(chname) 565 { 566 int rval; 567 char *cp; 568 struct inodesc idesc; 569 570 slotcount = 0; 571 if (!checkactivedir()) 572 return 1; 573 574 desired = strtoul(argv[1], &cp, 0); 575 if (cp == argv[1] || *cp != '\0') { 576 printf("invalid slot number `%s'\n", argv[1]); 577 return 1; 578 } 579 580 idesc.id_number = curinum; 581 idesc.id_func = chnamefunc; 582 idesc.id_fix = IGNORE; 583 idesc.id_type = DATA; 584 idesc.id_name = argv[2]; 585 586 rval = ckinode(curinode, &idesc); 587 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED)) 588 return 0; 589 else if (rval & FOUND) { 590 warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]); 591 return 1; 592 } else { 593 warnx("no %sth slot in current directory", argv[1]); 594 return 1; 595 } 596 } 597 598 struct typemap { 599 const char *typename; 600 int typebits; 601 } typenamemap[] = { 602 {"file", IFREG}, 603 {"dir", IFDIR}, 604 {"socket", IFSOCK}, 605 {"fifo", IFIFO}, 606 }; 607 608 CMDFUNCSTART(newtype) 609 { 610 int type; 611 struct typemap *tp; 612 613 if (!checkactive()) 614 return 1; 615 type = curinode->di_mode & IFMT; 616 for (tp = typenamemap; tp < &typenamemap[NELEM(typenamemap)]; 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[NELEM(typenamemap)]) { 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(0); 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(0); 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' || (modebits & ~07777)) { 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(0); 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(0); 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(0); 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(0); 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(0); 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(0); 803 return rval; 804 } 805 806 int 807 dotime(char *name, int32_t *rts) 808 { 809 char *p, *val; 810 struct tm t; 811 int32_t sec; 812 int32_t nsec; 813 p = strchr(name, '.'); 814 if (p) { 815 *p = '\0'; 816 nsec = strtoul(++p, &val, 0); 817 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 818 warnx("invalid nanoseconds"); 819 goto badformat; 820 } 821 } else 822 nsec = 0; 823 if (strlen(name) != 14) { 824 badformat: 825 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 826 return 1; 827 } 828 829 for (p = name; *p; p++) 830 if (*p < '0' || *p > '9') 831 goto badformat; 832 833 p = name; 834 #define VAL() ((*p++) - '0') 835 t.tm_year = VAL(); 836 t.tm_year = VAL() + t.tm_year * 10; 837 t.tm_year = VAL() + t.tm_year * 10; 838 t.tm_year = VAL() + t.tm_year * 10 - 1900; 839 t.tm_mon = VAL(); 840 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 841 t.tm_mday = VAL(); 842 t.tm_mday = VAL() + t.tm_mday * 10; 843 t.tm_hour = VAL(); 844 t.tm_hour = VAL() + t.tm_hour * 10; 845 t.tm_min = VAL(); 846 t.tm_min = VAL() + t.tm_min * 10; 847 t.tm_sec = VAL(); 848 t.tm_sec = VAL() + t.tm_sec * 10; 849 t.tm_isdst = -1; 850 851 sec = mktime(&t); 852 if (sec == -1) { 853 warnx("date/time out of range"); 854 return 1; 855 } 856 rts[0] = sec; 857 rts[1] = nsec; 858 return 0; 859 } 860 861 CMDFUNCSTART(chmtime) 862 { 863 if (dotime(argv[1], &curinode->di_mtime)) 864 return 1; 865 inodirty(); 866 printactive(0); 867 return 0; 868 } 869 870 CMDFUNCSTART(chatime) 871 { 872 if (dotime(argv[1], &curinode->di_atime)) 873 return 1; 874 inodirty(); 875 printactive(0); 876 return 0; 877 } 878 879 CMDFUNCSTART(chctime) 880 { 881 if (dotime(argv[1], &curinode->di_ctime)) 882 return 1; 883 inodirty(); 884 printactive(0); 885 return 0; 886 } 887