xref: /freebsd/sbin/fsdb/fsdb.c (revision 70fe064ad7cab6c0444b91622f60ec6a462f308a)
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