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