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