xref: /netbsd-src/sbin/fsdb/fsdb.c (revision 89c5a767f8fc7a4633b2d409966e2becbb98ff92)
1 /*	$NetBSD: fsdb.c,v 1.16 1999/03/09 16:11:47 bouyer Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by John T. Kohl.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: fsdb.c,v 1.16 1999/03/09 16:11:47 bouyer Exp $");
42 #endif /* not lint */
43 
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/param.h>
47 #include <sys/time.h>
48 #include <sys/mount.h>
49 #include <ctype.h>
50 #include <fcntl.h>
51 #include <grp.h>
52 #include <histedit.h>
53 #include <limits.h>
54 #include <pwd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <time.h>
59 #include <unistd.h>
60 #include <err.h>
61 
62 #include <ufs/ufs/dinode.h>
63 #include <ufs/ufs/dir.h>
64 #include <ufs/ffs/fs.h>
65 #include <ufs/ffs/ffs_extern.h>
66 
67 #include "fsdb.h"
68 #include "fsck.h"
69 #include "extern.h"
70 
71 extern char *__progname;	/* from crt0.o */
72 
73 int main __P((int, char *[]));
74 static void usage __P((void));
75 static int cmdloop __P((void));
76 static int helpfn __P((int, char *[]));
77 static char *prompt __P((EditLine *));
78 static int scannames __P((struct inodesc *));
79 static int dolookup __P((char *));
80 static int chinumfunc __P((struct inodesc *));
81 static int chnamefunc __P((struct inodesc *));
82 static int dotime __P((char *, int32_t *, int32_t *));
83 static void print_blks __P((ufs_daddr_t *buf, int size, int *blknum));
84 static void print_indirblks __P((daddr_t blk, int ind_level, int *blknum));
85 
86 int     returntosingle = 0;
87 struct dinode *curinode;
88 ino_t   curinum;
89 
90 static void
91 usage()
92 {
93 	errx(1, "usage: %s [-d] [-n] -f <fsname>", __progname);
94 }
95 /*
96  * We suck in lots of fsck code, and just pick & choose the stuff we want.
97  *
98  * fsreadfd is set up to read from the file system, fswritefd to write to
99  * the file system.
100  */
101 int
102 main(argc, argv)
103 	int     argc;
104 	char   *argv[];
105 {
106 	int     ch, rval;
107 	char   *fsys = NULL;
108 
109 	while ((ch = getopt(argc, argv, "f:dn")) != -1) {
110 		switch (ch) {
111 		case 'f':
112 			fsys = optarg;
113 			break;
114 		case 'd':
115 			debug++;
116 			break;
117 		case 'n':
118 			nflag++;
119 			break;
120 		default:
121 			usage();
122 		}
123 	}
124 	if (fsys == NULL)
125 		usage();
126 	endian = 0;
127 	if (!setup(fsys))
128 		errx(1, "cannot set up file system `%s'", fsys);
129 	printf("Editing file system `%s'\nLast Mounted on %s\n", fsys,
130 	    sblock->fs_fsmnt);
131 	rval = cmdloop();
132 	if (nflag)
133 		exit(rval);
134 	sblock->fs_clean = 0;	/* mark it dirty */
135 	sbdirty();
136 	markclean = 0;
137 	ckfini();
138 	printf("*** FILE SYSTEM MARKED DIRTY\n");
139 	printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
140 	printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
141 	exit(rval);
142 }
143 
144 #define CMDFUNC(func) static int func __P((int argc, char *argv[]))
145 #define CMDFUNCSTART(func) static int func(argc, argv)		\
146 				int argc;			\
147 				char *argv[];
148 
149 CMDFUNC(helpfn);
150 CMDFUNC(focus);			/* focus on inode */
151 CMDFUNC(active);		/* print active inode */
152 CMDFUNC(focusname);		/* focus by name */
153 CMDFUNC(zapi);			/* clear inode */
154 CMDFUNC(uplink);		/* incr link */
155 CMDFUNC(downlink);		/* decr link */
156 CMDFUNC(linkcount);		/* set link count */
157 CMDFUNC(quit);			/* quit */
158 CMDFUNC(ls);			/* list directory */
159 CMDFUNC(blks);			/* list blocks */
160 CMDFUNC(rm);			/* remove name */
161 CMDFUNC(ln);			/* add name */
162 CMDFUNC(newtype);		/* change type */
163 CMDFUNC(chmode);		/* change mode */
164 CMDFUNC(chlen);			/* change length */
165 CMDFUNC(chaflags);		/* change flags */
166 CMDFUNC(chgen);			/* change generation */
167 CMDFUNC(chowner);		/* change owner */
168 CMDFUNC(chgroup);		/* Change group */
169 CMDFUNC(back);			/* pop back to last ino */
170 CMDFUNC(chmtime);		/* Change mtime */
171 CMDFUNC(chctime);		/* Change ctime */
172 CMDFUNC(chatime);		/* Change atime */
173 CMDFUNC(chinum);		/* Change inode # of dirent */
174 CMDFUNC(chname);		/* Change dirname of dirent */
175 
176 static struct cmdtable cmds[] = {
177 	{"help", "Print out help", 1, 1, helpfn},
178 	{"?", "Print out help", 1, 1, helpfn},
179 	{"inode", "Set active inode to INUM", 2, 2, focus},
180 	{"clri", "Clear inode INUM", 2, 2, zapi},
181 	{"lookup", "Set active inode by looking up NAME", 2, 2, focusname},
182 	{"cd", "Set active inode by looking up NAME", 2, 2, focusname},
183 	{"back", "Go to previous active inode", 1, 1, back},
184 	{"active", "Print active inode", 1, 1, active},
185 	{"print", "Print active inode", 1, 1, active},
186 	{"uplink", "Increment link count", 1, 1, uplink},
187 	{"downlink", "Decrement link count", 1, 1, downlink},
188 	{"linkcount", "Set link count to COUNT", 2, 2, linkcount},
189 	{"ls", "List current inode as directory", 1, 1, ls},
190 	{"blks", "List current inode's data blocks", 1, 1, blks},
191 	{"rm", "Remove NAME from current inode directory", 2, 2, rm},
192 	{"del", "Remove NAME from current inode directory", 2, 2, rm},
193 	{"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln},
194 	{"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum},
195 	{"chname", "Change dir entry number INDEX to NAME", 3, 3, chname},
196 	{"chtype", "Change type of current inode to TYPE", 2, 2, newtype},
197 	{"chmod", "Change mode of current inode to MODE", 2, 2, chmode},
198 	{"chown", "Change owner of current inode to OWNER", 2, 2, chowner},
199 	{"chlen", "Change length of current inode to LENGTH", 2, 2, chlen},
200 	{"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup},
201 	{"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags},
202 	{"chgen", "Change generation number of current inode to GEN", 2, 2,
203 		    chgen},
204 	{"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime},
205 	{"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime},
206 	{"atime", "Change atime of current inode to ATIME", 2, 2, chatime},
207 	{"quit", "Exit", 1, 1, quit},
208 	{"q", "Exit", 1, 1, quit},
209 	{"exit", "Exit", 1, 1, quit},
210 	{NULL, 0, 0, 0},
211 };
212 
213 static int
214 helpfn(argc, argv)
215 	int     argc;
216 	char   *argv[];
217 {
218 	struct cmdtable *cmdtp;
219 
220 	printf("Commands are:\n%-10s %5s %5s   %s\n",
221 	    "command", "min argc", "max argc", "what");
222 
223 	for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
224 		printf("%-10s %5u %5u   %s\n",
225 		    cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
226 	return 0;
227 }
228 
229 static char *
230 prompt(el)
231 	EditLine *el;
232 {
233 	static char pstring[64];
234 	snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
235 	return pstring;
236 }
237 
238 
239 static int
240 cmdloop()
241 {
242 	char   *line;
243 	const char *elline;
244 	int     cmd_argc, rval = 0, known;
245 #define scratch known
246 	char  **cmd_argv;
247 	struct cmdtable *cmdp;
248 	History *hist;
249 	HistEvent he;
250 	EditLine *elptr;
251 
252 	curinode = ginode(ROOTINO);
253 	curinum = ROOTINO;
254 	printactive();
255 
256 	hist = history_init();
257 	history(hist, &he, H_SETSIZE, 100);	/* 100 elt history buffer */
258 
259 	elptr = el_init(__progname, stdin, stdout, stderr);
260 	el_set(elptr, EL_EDITOR, "emacs");
261 	el_set(elptr, EL_PROMPT, prompt);
262 	el_set(elptr, EL_HIST, history, hist);
263 	el_source(elptr, NULL);
264 
265 	while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
266 		if (debug)
267 			printf("command `%s'\n", elline);
268 
269 		history(hist, &he, H_ENTER, elline);
270 
271 		line = strdup(elline);
272 		cmd_argv = crack(line, &cmd_argc);
273 		if (cmd_argc) {
274 			/*
275 		         * el_parse returns -1 to signal that it's not been
276 		         * handled internally.
277 		         */
278 			if (el_parse(elptr, cmd_argc, cmd_argv) != -1)
279 				continue;
280 			known = 0;
281 			for (cmdp = cmds; cmdp->cmd; cmdp++) {
282 				if (!strcmp(cmdp->cmd, cmd_argv[0])) {
283 					if (cmd_argc >= cmdp->minargc &&
284 					    cmd_argc <= cmdp->maxargc)
285 						rval =
286 						    (*cmdp->handler)(cmd_argc,
287 							cmd_argv);
288 					else
289 						rval = argcount(cmdp, cmd_argc,
290 						    cmd_argv);
291 					known = 1;
292 					break;
293 				}
294 			}
295 			if (!known)
296 				warnx("unknown command `%s'", cmd_argv[0]),
297 				    rval = 1;
298 		} else
299 			rval = 0;
300 		free(line);
301 		if (rval < 0)
302 			return rval;
303 		if (rval)
304 			warnx("rval was %d", rval);
305 	}
306 	el_end(elptr);
307 	history_end(hist);
308 	return rval;
309 }
310 
311 static ino_t ocurrent;
312 
313 #define GETINUM(ac,inum)    inum = strtoul(argv[ac], &cp, 0); \
314     if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
315 	printf("inode %d out of range; range is [%d,%d]\n", \
316 	       inum, ROOTINO, maxino); \
317 	return 1; \
318     }
319 
320 /*
321  * Focus on given inode number
322  */
323 CMDFUNCSTART(focus)
324 {
325 	ino_t   inum;
326 	char   *cp;
327 
328 	GETINUM(1, inum);
329 	curinode = ginode(inum);
330 	ocurrent = curinum;
331 	curinum = inum;
332 	printactive();
333 	return 0;
334 }
335 
336 CMDFUNCSTART(back)
337 {
338 	curinum = ocurrent;
339 	curinode = ginode(curinum);
340 	printactive();
341 	return 0;
342 }
343 
344 CMDFUNCSTART(zapi)
345 {
346 	ino_t   inum;
347 	struct dinode *dp;
348 	char   *cp;
349 
350 	GETINUM(1, inum);
351 	dp = ginode(inum);
352 	clearinode(dp);
353 	inodirty();
354 	if (curinode)		/* re-set after potential change */
355 		curinode = ginode(curinum);
356 	return 0;
357 }
358 
359 CMDFUNCSTART(active)
360 {
361 	printactive();
362 	return 0;
363 }
364 
365 CMDFUNCSTART(quit)
366 {
367 	return -1;
368 }
369 
370 CMDFUNCSTART(uplink)
371 {
372 	if (!checkactive())
373 		return 1;
374 	curinode->di_nlink = iswap16(iswap16(curinode->di_nlink) + 1);
375 	printf("inode %d link count now %d\n", curinum,
376 		iswap16(curinode->di_nlink));
377 	inodirty();
378 	return 0;
379 }
380 
381 CMDFUNCSTART(downlink)
382 {
383 	if (!checkactive())
384 		return 1;
385 	curinode->di_nlink = iswap16(iswap16(curinode->di_nlink) - 1);
386 	printf("inode %d link count now %d\n", curinum,
387 		iswap16(curinode->di_nlink));
388 	inodirty();
389 	return 0;
390 }
391 
392 static const char *typename[] = {
393 	"unknown",
394 	"fifo",
395 	"char special",
396 	"unregistered #3",
397 	"directory",
398 	"unregistered #5",
399 	"blk special",
400 	"unregistered #7",
401 	"regular",
402 	"unregistered #9",
403 	"symlink",
404 	"unregistered #11",
405 	"socket",
406 	"unregistered #13",
407 	"whiteout",
408 };
409 
410 static int slot;
411 
412 static int
413 scannames(idesc)
414 	struct inodesc *idesc;
415 {
416 	struct direct *dirp = idesc->id_dirp;
417 
418 	printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
419 	    slot++, iswap32(dirp->d_ino), iswap16(dirp->d_reclen),
420 		typename[dirp->d_type],
421 	    dirp->d_namlen, dirp->d_name);
422 	return (KEEPON);
423 }
424 
425 CMDFUNCSTART(ls)
426 {
427 	struct inodesc idesc;
428 	checkactivedir();	/* let it go on anyway */
429 
430 	slot = 0;
431 	idesc.id_number = curinum;
432 	idesc.id_func = scannames;
433 	idesc.id_type = DATA;
434 	idesc.id_fix = IGNORE;
435 	ckinode(curinode, &idesc);
436 	curinode = ginode(curinum);
437 
438 	return 0;
439 }
440 
441 CMDFUNCSTART(blks)
442 {
443 	int blkno = 0;
444 	int i, type;
445 	if (!curinode) {
446 		warnx("no current inode\n");
447 		return 0;
448 	}
449 	type = iswap16(curinode->di_mode) & IFMT;
450 	if (type != IFDIR && type != IFREG) {
451 		warnx("inode %d not a file or directory", curinum);
452 		return 0;
453 	}
454 	printf("I=%d %d block%c\n", curinum,
455 		(iswap32(curinode->di_blocks) + NSPB(sblock) -1) /
456 		    NSPB(sblock),
457 		(iswap32(curinode->di_blocks) > NSPB(sblock)) ? 's' : ' ');
458 	printf("Direct blocks:\n");
459 	print_blks(curinode->di_db, NDADDR, &blkno);
460 	for (i = 0; i < NIADDR; i++) {
461 		if (curinode->di_ib[i] != 0)
462 			print_indirblks(iswap32(curinode->di_ib[i]), i,
463 			    &blkno);
464 	}
465 	return 0;
466 }
467 
468 static void
469 print_blks(buf, size, blknum)
470 	ufs_daddr_t *buf;
471 	int size;
472 	int *blknum;
473 {
474 #define CHARS_PER_LINES 70
475 	int chars;
476 	char prbuf[CHARS_PER_LINES+1];
477 	int blk;
478 
479 	chars = 0;
480 	for(blk = 0; blk < size; blk++, (*blknum)++) {
481 		if (buf[blk] == 0)
482 			continue;
483 		snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk]));
484 		if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
485 			printf("\n");
486 			chars = 0;
487 		}
488 		if (chars == 0)
489 			printf("%d: ", *blknum);
490 		printf(prbuf);
491 		chars += strlen(prbuf);
492 	}
493 	printf("\n");
494 #undef CHARS_PER_LINES
495 }
496 
497 static void
498 print_indirblks(blk,ind_level, blknum)
499 	daddr_t blk;
500 	int ind_level;
501 	int *blknum;
502 {
503 #define MAXNINDIR	(MAXBSIZE / sizeof(daddr_t))
504 	daddr_t idblk[MAXNINDIR];
505 	int i;
506 
507 	printf("Indirect block %d (level %d):\n", blk, ind_level+1);
508 	bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk),
509 	    (int)sblock->fs_bsize);
510 	if (ind_level <= 0) {
511 		print_blks(idblk, sblock->fs_bsize / sizeof(daddr_t), blknum);
512 	} else {
513 		ind_level--;
514 		for (i = 0; i < sblock->fs_bsize / sizeof(daddr_t); i++) {
515 			if(idblk[i] != 0)
516 				print_indirblks(iswap32(idblk[i]),
517 				    ind_level, blknum);
518 		}
519 	}
520 #undef MAXNINDIR
521 }
522 
523 static int
524 dolookup(name)
525 	char   *name;
526 {
527 	struct inodesc idesc;
528 
529 	if (!checkactivedir())
530 		return 0;
531 	idesc.id_number = curinum;
532 	idesc.id_func = findino;
533 	idesc.id_name = name;
534 	idesc.id_type = DATA;
535 	idesc.id_fix = IGNORE;
536 	if (ckinode(curinode, &idesc) & FOUND) {
537 		curinum = idesc.id_parent;
538 		curinode = ginode(curinum);
539 		printactive();
540 		return 1;
541 	} else {
542 		warnx("name `%s' not found in current inode directory", name);
543 		return 0;
544 	}
545 }
546 
547 CMDFUNCSTART(focusname)
548 {
549 	char   *p, *val;
550 
551 	if (!checkactive())
552 		return 1;
553 
554 	ocurrent = curinum;
555 
556 	if (argv[1][0] == '/') {
557 		curinum = ROOTINO;
558 		curinode = ginode(ROOTINO);
559 	} else {
560 		if (!checkactivedir())
561 			return 1;
562 	}
563 	for (p = argv[1]; p != NULL;) {
564 		while ((val = strsep(&p, "/")) != NULL && *val == '\0');
565 		if (val) {
566 			printf("component `%s': ", val);
567 			fflush(stdout);
568 			if (!dolookup(val)) {
569 				curinode = ginode(curinum);
570 				return (1);
571 			}
572 		}
573 	}
574 	return 0;
575 }
576 
577 CMDFUNCSTART(ln)
578 {
579 	ino_t   inum;
580 	int     rval;
581 	char   *cp;
582 
583 	GETINUM(1, inum);
584 
585 	if (!checkactivedir())
586 		return 1;
587 	rval = makeentry(curinum, inum, argv[2]);
588 	if (rval)
589 		printf("Ino %d entered as `%s'\n", inum, argv[2]);
590 	else
591 		printf("could not enter name? weird.\n");
592 	curinode = ginode(curinum);
593 	return rval;
594 }
595 
596 CMDFUNCSTART(rm)
597 {
598 	int     rval;
599 
600 	if (!checkactivedir())
601 		return 1;
602 	rval = changeino(curinum, argv[1], 0);
603 	if (rval & ALTERED) {
604 		printf("Name `%s' removed\n", argv[1]);
605 		return 0;
606 	} else {
607 		printf("could not remove name? weird.\n");
608 		return 1;
609 	}
610 }
611 
612 static long slotcount, desired;
613 
614 static int
615 chinumfunc(idesc)
616 	struct inodesc *idesc;
617 {
618 	struct direct *dirp = idesc->id_dirp;
619 
620 	if (slotcount++ == desired) {
621 		dirp->d_ino = iswap32(idesc->id_parent);
622 		return STOP | ALTERED | FOUND;
623 	}
624 	return KEEPON;
625 }
626 
627 CMDFUNCSTART(chinum)
628 {
629 	char   *cp;
630 	ino_t   inum;
631 	struct inodesc idesc;
632 
633 	slotcount = 0;
634 	if (!checkactivedir())
635 		return 1;
636 	GETINUM(2, inum);
637 
638 	desired = strtol(argv[1], &cp, 0);
639 	if (cp == argv[1] || *cp != '\0' || desired < 0) {
640 		printf("invalid slot number `%s'\n", argv[1]);
641 		return 1;
642 	}
643 	idesc.id_number = curinum;
644 	idesc.id_func = chinumfunc;
645 	idesc.id_fix = IGNORE;
646 	idesc.id_type = DATA;
647 	idesc.id_parent = inum;	/* XXX convenient hiding place */
648 
649 	if (ckinode(curinode, &idesc) & FOUND)
650 		return 0;
651 	else {
652 		warnx("no %sth slot in current directory", argv[1]);
653 		return 1;
654 	}
655 }
656 
657 static int
658 chnamefunc(idesc)
659 	struct inodesc *idesc;
660 {
661 	struct direct *dirp = idesc->id_dirp;
662 	struct direct testdir;
663 
664 	if (slotcount++ == desired) {
665 		/* will name fit? */
666 		testdir.d_namlen = strlen(idesc->id_name);
667 		if (DIRSIZ(NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) {
668 			dirp->d_namlen = testdir.d_namlen;
669 			strcpy(dirp->d_name, idesc->id_name);
670 			return STOP | ALTERED | FOUND;
671 		} else
672 			return STOP | FOUND;	/* won't fit, so give up */
673 	}
674 	return KEEPON;
675 }
676 
677 CMDFUNCSTART(chname)
678 {
679 	int     rval;
680 	char   *cp;
681 	struct inodesc idesc;
682 
683 	slotcount = 0;
684 	if (!checkactivedir())
685 		return 1;
686 
687 	desired = strtoul(argv[1], &cp, 0);
688 	if (cp == argv[1] || *cp != '\0') {
689 		printf("invalid slot number `%s'\n", argv[1]);
690 		return 1;
691 	}
692 	idesc.id_number = curinum;
693 	idesc.id_func = chnamefunc;
694 	idesc.id_fix = IGNORE;
695 	idesc.id_type = DATA;
696 	idesc.id_name = argv[2];
697 
698 	rval = ckinode(curinode, &idesc);
699 	if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED))
700 		return 0;
701 	else
702 		if (rval & FOUND) {
703 			warnx("new name `%s' does not fit in slot %s\n",
704 			    argv[2], argv[1]);
705 			return 1;
706 		} else {
707 			warnx("no %sth slot in current directory", argv[1]);
708 			return 1;
709 		}
710 }
711 
712 static struct typemap {
713 	const char *typename;
714 	int     typebits;
715 }       typenamemap[] = {
716 	{ "file", IFREG },
717 	{ "dir", IFDIR },
718 	{ "socket", IFSOCK },
719 	{ "fifo", IFIFO },
720 };
721 
722 CMDFUNCSTART(newtype)
723 {
724 	int     type;
725 	struct typemap *tp;
726 
727 	if (!checkactive())
728 		return 1;
729 	type = iswap16(curinode->di_mode) & IFMT;
730 	for (tp = typenamemap;
731 	    tp < &typenamemap[sizeof(typemap) / sizeof(*typemap)];
732 	    tp++) {
733 		if (!strcmp(argv[1], tp->typename)) {
734 			printf("setting type to %s\n", tp->typename);
735 			type = tp->typebits;
736 			break;
737 		}
738 	}
739 	if (tp == &typenamemap[sizeof(typemap) / sizeof(*typemap)]) {
740 		warnx("type `%s' not known", argv[1]);
741 		warnx("try one of `file', `dir', `socket', `fifo'");
742 		return 1;
743 	}
744 	curinode->di_mode  = iswap16((iswap16(curinode->di_mode) & ~IFMT) | type);
745 	inodirty();
746 	printactive();
747 	return 0;
748 }
749 
750 CMDFUNCSTART(chmode)
751 {
752 	long    modebits;
753 	char   *cp;
754 
755 	if (!checkactive())
756 		return 1;
757 
758 	modebits = strtol(argv[1], &cp, 8);
759 	if (cp == argv[1] || *cp != '\0') {
760 		warnx("bad modebits `%s'", argv[1]);
761 		return 1;
762 	}
763 	curinode->di_mode =
764 		iswap16((iswap16(curinode->di_mode) & ~07777) | modebits);
765 	inodirty();
766 	printactive();
767 	return 0;
768 }
769 
770 CMDFUNCSTART(chlen)
771 {
772 	long    len;
773 	char   *cp;
774 
775 	if (!checkactive())
776 		return 1;
777 
778 	len = strtol(argv[1], &cp, 0);
779 	if (cp == argv[1] || *cp != '\0' || len < 0) {
780 		warnx("bad length '%s'", argv[1]);
781 		return 1;
782 	}
783 	curinode->di_size = iswap64(len);
784 	inodirty();
785 	printactive();
786 	return 0;
787 }
788 
789 CMDFUNCSTART(chaflags)
790 {
791 	u_long  flags;
792 	char   *cp;
793 
794 	if (!checkactive())
795 		return 1;
796 
797 	flags = strtoul(argv[1], &cp, 0);
798 	if (cp == argv[1] || *cp != '\0') {
799 		warnx("bad flags `%s'", argv[1]);
800 		return 1;
801 	}
802 	if (flags > UINT_MAX) {
803 		warnx("flags set beyond 32-bit range of field (0x%lx)\n",
804 		    flags);
805 		return (1);
806 	}
807 	curinode->di_flags = iswap32(flags);
808 	inodirty();
809 	printactive();
810 	return 0;
811 }
812 
813 CMDFUNCSTART(chgen)
814 {
815 	long    gen;
816 	char   *cp;
817 
818 	if (!checkactive())
819 		return 1;
820 
821 	gen = strtol(argv[1], &cp, 0);
822 	if (cp == argv[1] || *cp != '\0') {
823 		warnx("bad gen `%s'", argv[1]);
824 		return 1;
825 	}
826 	if (gen > INT_MAX || gen < INT_MIN) {
827 		warnx("gen set beyond 32-bit range of field (0x%lx)\n", gen);
828 		return (1);
829 	}
830 	curinode->di_gen = iswap32(gen);
831 	inodirty();
832 	printactive();
833 	return 0;
834 }
835 
836 CMDFUNCSTART(linkcount)
837 {
838 	int     lcnt;
839 	char   *cp;
840 
841 	if (!checkactive())
842 		return 1;
843 
844 	lcnt = strtol(argv[1], &cp, 0);
845 	if (cp == argv[1] || *cp != '\0') {
846 		warnx("bad link count `%s'", argv[1]);
847 		return 1;
848 	}
849 	if (lcnt > USHRT_MAX || lcnt < 0) {
850 		warnx("max link count is %d\n", USHRT_MAX);
851 		return 1;
852 	}
853 	curinode->di_nlink = iswap16(lcnt);
854 	inodirty();
855 	printactive();
856 	return 0;
857 }
858 
859 CMDFUNCSTART(chowner)
860 {
861 	unsigned long uid;
862 	char   *cp;
863 	struct passwd *pwd;
864 
865 	if (!checkactive())
866 		return 1;
867 
868 	uid = strtoul(argv[1], &cp, 0);
869 	if (cp == argv[1] || *cp != '\0') {
870 		/* try looking up name */
871 		if ((pwd = getpwnam(argv[1])) != 0) {
872 			uid = pwd->pw_uid;
873 		} else {
874 			warnx("bad uid `%s'", argv[1]);
875 			return 1;
876 		}
877 	}
878 	curinode->di_uid = iswap32(uid);
879 	inodirty();
880 	printactive();
881 	return 0;
882 }
883 
884 CMDFUNCSTART(chgroup)
885 {
886 	unsigned long gid;
887 	char   *cp;
888 	struct group *grp;
889 
890 	if (!checkactive())
891 		return 1;
892 
893 	gid = strtoul(argv[1], &cp, 0);
894 	if (cp == argv[1] || *cp != '\0') {
895 		if ((grp = getgrnam(argv[1])) != 0) {
896 			gid = grp->gr_gid;
897 		} else {
898 			warnx("bad gid `%s'", argv[1]);
899 			return 1;
900 		}
901 	}
902 	curinode->di_gid = iswap32(gid);
903 	inodirty();
904 	printactive();
905 	return 0;
906 }
907 
908 static int
909 dotime(name, rsec, rnsec)
910 	char   *name;
911 	int32_t *rsec, *rnsec;
912 {
913 	char   *p, *val;
914 	struct tm t;
915 	int32_t sec;
916 	int32_t nsec;
917 	p = strchr(name, '.');
918 	if (p) {
919 		*p = '\0';
920 		nsec = strtoul(++p, &val, 0);
921 		if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
922 			warnx("invalid nanoseconds");
923 			goto badformat;
924 		}
925 	} else
926 		nsec = 0;
927 	if (strlen(name) != 14) {
928 badformat:
929 		warnx("date format: YYYYMMDDHHMMSS[.nsec]");
930 		return 1;
931 	}
932 	for (p = name; *p; p++)
933 		if (*p < '0' || *p > '9')
934 			goto badformat;
935 
936 	p = name;
937 #define VAL() ((*p++) - '0')
938 	t.tm_year = VAL();
939 	t.tm_year = VAL() + t.tm_year * 10;
940 	t.tm_year = VAL() + t.tm_year * 10;
941 	t.tm_year = VAL() + t.tm_year * 10 - 1900;
942 	t.tm_mon = VAL();
943 	t.tm_mon = VAL() + t.tm_mon * 10 - 1;
944 	t.tm_mday = VAL();
945 	t.tm_mday = VAL() + t.tm_mday * 10;
946 	t.tm_hour = VAL();
947 	t.tm_hour = VAL() + t.tm_hour * 10;
948 	t.tm_min = VAL();
949 	t.tm_min = VAL() + t.tm_min * 10;
950 	t.tm_sec = VAL();
951 	t.tm_sec = VAL() + t.tm_sec * 10;
952 	t.tm_isdst = -1;
953 
954 	sec = mktime(&t);
955 	if (sec == -1) {
956 		warnx("date/time out of range");
957 		return 1;
958 	}
959 	*rsec = iswap32(sec);
960 	*rnsec = iswap32(nsec);
961 	return 0;
962 }
963 
964 CMDFUNCSTART(chmtime)
965 {
966 	if (dotime(argv[1], &curinode->di_ctime, &curinode->di_ctimensec))
967 		return 1;
968 	inodirty();
969 	printactive();
970 	return 0;
971 }
972 
973 CMDFUNCSTART(chatime)
974 {
975 	if (dotime(argv[1], &curinode->di_ctime, &curinode->di_ctimensec))
976 		return 1;
977 	inodirty();
978 	printactive();
979 	return 0;
980 }
981 
982 CMDFUNCSTART(chctime)
983 {
984 	if (dotime(argv[1], &curinode->di_ctime, &curinode->di_ctimensec))
985 		return 1;
986 	inodirty();
987 	printactive();
988 	return 0;
989 }
990