fsdb.c revision 50476
1241675Suqs/*	$NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $	*/
2241675Suqs
3241675Suqs/*
4241675Suqs *  Copyright (c) 1995 John T. Kohl
5241675Suqs *  All rights reserved.
6241675Suqs *
7241675Suqs *  Redistribution and use in source and binary forms, with or without
8241675Suqs *  modification, are permitted provided that the following conditions
9241675Suqs *  are met:
10241675Suqs *  1. Redistributions of source code must retain the above copyright
11241675Suqs *     notice, this list of conditions and the following disclaimer.
12241675Suqs *  2. Redistributions in binary form must reproduce the above copyright
13241675Suqs *     notice, this list of conditions and the following disclaimer in the
14241675Suqs *     documentation and/or other materials provided with the distribution.
15241675Suqs *  3. The name of the author may not be used to endorse or promote products
16241675Suqs *     derived from this software without specific prior written permission.
17241675Suqs *
18241675Suqs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19241675Suqs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20241675Suqs * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21241675Suqs * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22241675Suqs * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23241675Suqs * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24241675Suqs * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25241675Suqs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26241675Suqs * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27241675Suqs * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28241675Suqs * POSSIBILITY OF SUCH DAMAGE.
29241675Suqs */
30241675Suqs
31241675Suqs#ifndef lint
32241675Suqsstatic const char rcsid[] =
33241675Suqs  "$FreeBSD: head/sbin/fsdb/fsdb.c 50476 1999-08-28 00:22:10Z peter $";
34241675Suqs#endif /* not lint */
35241675Suqs
36241675Suqs#include <sys/types.h>
37241675Suqs#include <sys/param.h>
38241675Suqs#include <sys/time.h>
39241675Suqs#include <ctype.h>
40241675Suqs#include <err.h>
41241675Suqs#include <grp.h>
42241675Suqs#include <histedit.h>
43241675Suqs#include <pwd.h>
44241675Suqs#include <string.h>
45241675Suqs
46241675Suqs#include <ufs/ufs/dinode.h>
47241675Suqs#include <ufs/ufs/dir.h>
48241675Suqs#include <ufs/ffs/fs.h>
49241675Suqs
50241675Suqs#include "fsdb.h"
51241675Suqs#include "fsck.h"
52241675Suqs
53241675Suqsstatic void usage __P((void));
54241675Suqsint cmdloop __P((void));
55241675Suqs
56241675Suqsstatic void
57241675Suqsusage()
58241675Suqs{
59241675Suqs	fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
60241675Suqs	exit(1);
61241675Suqs}
62241675Suqs
63241675Suqsint returntosingle = 0;
64241675Suqschar nflag = 0;
65241675Suqs
66241675Suqs/*
67241675Suqs * We suck in lots of fsck code, and just pick & choose the stuff we want.
68241675Suqs *
69241675Suqs * fsreadfd is set up to read from the file system, fswritefd to write to
70241675Suqs * the file system.
71241675Suqs */
72241675Suqsint
73241675Suqsmain(argc, argv)
74241675Suqs	int argc;
75241675Suqs	char *argv[];
76241675Suqs{
77241675Suqs	int ch, rval;
78241675Suqs	char *fsys = NULL;
79241675Suqs
80241675Suqs	while (-1 != (ch = getopt(argc, argv, "fdr"))) {
81241675Suqs		switch (ch) {
82241675Suqs		case 'f':
83241675Suqs			/* The -f option is left for historical
84241675Suqs			 * reasons and has no meaning.
85241675Suqs			 */
86241675Suqs			break;
87241675Suqs		case 'd':
88241675Suqs			debug++;
89241675Suqs			break;
90241675Suqs		case 'r':
91241675Suqs			nflag++; /* "no" in fsck, readonly for us */
92241675Suqs			break;
93241675Suqs		default:
94241675Suqs			usage();
95241675Suqs		}
96241675Suqs	}
97241675Suqs	argc -= optind;
98241675Suqs	argv += optind;
99241675Suqs	if (argc != 1)
100241675Suqs		usage();
101241675Suqs	else
102241675Suqs		fsys = argv[0];
103241675Suqs
104241675Suqs	if (!setup(fsys))
105241675Suqs		errx(1, "cannot set up file system `%s'", fsys);
106241675Suqs	printf("%s file system `%s'\nLast Mounted on %s\n",
107241675Suqs	       nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
108241675Suqs	rval = cmdloop();
109241675Suqs	if (!nflag) {
110241675Suqs		sblock.fs_clean = 0;	/* mark it dirty */
111241675Suqs		sbdirty();
112241675Suqs		ckfini(0);
113241675Suqs		printf("*** FILE SYSTEM MARKED DIRTY\n");
114241675Suqs		printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
115241675Suqs		printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
116241675Suqs	}
117241675Suqs	exit(rval);
118241675Suqs}
119241675Suqs
120241675Suqs#define CMDFUNC(func) int func __P((int argc, char *argv[]))
121241675Suqs#define CMDFUNCSTART(func) int func(argc, argv)		\
122241675Suqs				int argc;		\
123241675Suqs				char *argv[];
124241675Suqs
125241675SuqsCMDFUNC(helpfn);
126241675SuqsCMDFUNC(focus);				/* focus on inode */
127241675SuqsCMDFUNC(active);			/* print active inode */
128241675SuqsCMDFUNC(focusname);			/* focus by name */
129241675SuqsCMDFUNC(zapi);				/* clear inode */
130241675SuqsCMDFUNC(uplink);			/* incr link */
131241675SuqsCMDFUNC(downlink);			/* decr link */
132241675SuqsCMDFUNC(linkcount);			/* set link count */
133241675SuqsCMDFUNC(quit);				/* quit */
134241675SuqsCMDFUNC(ls);				/* list directory */
135241675SuqsCMDFUNC(rm);				/* remove name */
136241675SuqsCMDFUNC(ln);				/* add name */
137241675SuqsCMDFUNC(newtype);			/* change type */
138241675SuqsCMDFUNC(chmode);			/* change mode */
139241675SuqsCMDFUNC(chlen);				/* change length */
140241675SuqsCMDFUNC(chaflags);			/* change flags */
141241675SuqsCMDFUNC(chgen);				/* change generation */
142241675SuqsCMDFUNC(chowner);			/* change owner */
143241675SuqsCMDFUNC(chgroup);			/* Change group */
144241675SuqsCMDFUNC(back);				/* pop back to last ino */
145241675SuqsCMDFUNC(chmtime);			/* Change mtime */
146241675SuqsCMDFUNC(chctime);			/* Change ctime */
147241675SuqsCMDFUNC(chatime);			/* Change atime */
148241675SuqsCMDFUNC(chinum);			/* Change inode # of dirent */
149241675SuqsCMDFUNC(chname);			/* Change dirname of dirent */
150241675Suqs
151241675Suqsstruct cmdtable cmds[] = {
152241675Suqs	{ "help", "Print out help", 1, 1, FL_RO, helpfn },
153241675Suqs	{ "?", "Print out help", 1, 1, FL_RO, helpfn },
154241675Suqs	{ "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
155241675Suqs	{ "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
156241675Suqs	{ "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
157241675Suqs	{ "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
158241675Suqs	{ "back", "Go to previous active inode", 1, 1, FL_RO, back },
159241675Suqs	{ "active", "Print active inode", 1, 1, FL_RO, active },
160241675Suqs	{ "print", "Print active inode", 1, 1, FL_RO, active },
161241675Suqs	{ "uplink", "Increment link count", 1, 1, FL_WR, uplink },
162241675Suqs	{ "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
163241675Suqs	{ "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
164241675Suqs	{ "ls", "List current inode as directory", 1, 1, FL_RO, ls },
165241675Suqs	{ "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
166241675Suqs	{ "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
167241675Suqs	{ "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln },
168241675Suqs	{ "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
169241675Suqs	{ "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname },
170241675Suqs	{ "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
171241675Suqs	{ "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
172241675Suqs	{ "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
173241675Suqs	{ "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
174241675Suqs	{ "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
175241675Suqs	{ "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
176241675Suqs	{ "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
177241675Suqs	{ "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
178241675Suqs	{ "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
179241675Suqs	{ "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
180241675Suqs	{ "quit", "Exit", 1, 1, FL_RO, quit },
181241675Suqs	{ "q", "Exit", 1, 1, FL_RO, quit },
182241675Suqs	{ "exit", "Exit", 1, 1, FL_RO, quit },
183241675Suqs	{ NULL, 0, 0, 0 },
184241675Suqs};
185241675Suqs
186241675Suqsint
187241675Suqshelpfn(argc, argv)
188241675Suqs	int argc;
189241675Suqs	char *argv[];
190241675Suqs{
191241675Suqs    register struct cmdtable *cmdtp;
192241675Suqs
193241675Suqs    printf("Commands are:\n%-10s %5s %5s   %s\n",
194241675Suqs	   "command", "min argc", "max argc", "what");
195241675Suqs
196241675Suqs    for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
197241675Suqs	printf("%-10s %5u %5u   %s\n",
198241675Suqs	       cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
199241675Suqs    return 0;
200241675Suqs}
201241675Suqs
202241675Suqschar *
203241675Suqsprompt(el)
204241675Suqs	EditLine *el;
205241675Suqs{
206241675Suqs    static char pstring[64];
207241675Suqs    snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
208241675Suqs    return pstring;
209241675Suqs}
210241675Suqs
211241675Suqs
212241675Suqsint
213241675Suqscmdloop()
214241675Suqs{
215241675Suqs    char *line;
216241675Suqs    const char *elline;
217241675Suqs    int cmd_argc, rval = 0, known;
218241675Suqs#define scratch known
219241675Suqs    char **cmd_argv;
220241675Suqs    struct cmdtable *cmdp;
221241675Suqs    History *hist;
222241675Suqs    EditLine *elptr;
223241675Suqs
224241675Suqs    curinode = ginode(ROOTINO);
225241675Suqs    curinum = ROOTINO;
226241675Suqs    printactive();
227241675Suqs
228241675Suqs    hist = history_init();
229241675Suqs    history(hist, H_EVENT, 100);	/* 100 elt history buffer */
230241675Suqs
231241675Suqs    elptr = el_init("fsdb", stdin, stdout);
232241675Suqs    el_set(elptr, EL_EDITOR, "emacs");
233241675Suqs    el_set(elptr, EL_PROMPT, prompt);
234241675Suqs    el_set(elptr, EL_HIST, history, hist);
235241675Suqs    el_source(elptr, NULL);
236241675Suqs
237241675Suqs    while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
238241675Suqs	if (debug)
239241675Suqs	    printf("command `%s'\n", elline);
240241675Suqs
241241675Suqs	history(hist, H_ENTER, elline);
242241675Suqs
243241675Suqs	line = strdup(elline);
244241675Suqs	cmd_argv = crack(line, &cmd_argc);
245241675Suqs	/*
246241675Suqs	 * el_parse returns -1 to signal that it's not been handled
247241675Suqs	 * internally.
248241675Suqs	 */
249241675Suqs	if (el_parse(elptr, cmd_argc, cmd_argv) != -1)
250241675Suqs	    continue;
251241675Suqs	if (cmd_argc) {
252241675Suqs	    known = 0;
253241675Suqs	    for (cmdp = cmds; cmdp->cmd; cmdp++) {
254241675Suqs		if (!strcmp(cmdp->cmd, cmd_argv[0])) {
255241675Suqs		    if ((cmdp->flags & FL_WR) == FL_WR && nflag)
256241675Suqs			warnx("`%s' requires write access", cmd_argv[0]),
257241675Suqs			    rval = 1;
258241675Suqs		    else if (cmd_argc >= cmdp->minargc &&
259241675Suqs			cmd_argc <= cmdp->maxargc)
260241675Suqs			rval = (*cmdp->handler)(cmd_argc, cmd_argv);
261241675Suqs		    else
262241675Suqs			rval = argcount(cmdp, cmd_argc, cmd_argv);
263241675Suqs		    known = 1;
264241675Suqs		    break;
265241675Suqs		}
266241675Suqs	    }
267241675Suqs	    if (!known)
268241675Suqs		warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
269241675Suqs	} else
270241675Suqs	    rval = 0;
271241675Suqs	free(line);
272241675Suqs	if (rval < 0)
273241675Suqs	    return rval;
274241675Suqs	if (rval)
275241675Suqs	    warnx("rval was %d", rval);
276241675Suqs    }
277241675Suqs    el_end(elptr);
278241675Suqs    history_end(hist);
279241675Suqs    return rval;
280241675Suqs}
281241675Suqs
282241675Suqsstruct dinode *curinode;
283241675Suqsino_t curinum, ocurrent;
284241675Suqs
285241675Suqs#define GETINUM(ac,inum)    inum = strtoul(argv[ac], &cp, 0); \
286241675Suqs    if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
287241675Suqs	printf("inode %d out of range; range is [%d,%d]\n", \
288241675Suqs	       inum, ROOTINO, maxino); \
289241675Suqs	return 1; \
290241675Suqs    }
291241675Suqs
292241675Suqs/*
293241675Suqs * Focus on given inode number
294241675Suqs */
295241675SuqsCMDFUNCSTART(focus)
296241675Suqs{
297241675Suqs    ino_t inum;
298241675Suqs    char *cp;
299241675Suqs
300241675Suqs    GETINUM(1,inum);
301241675Suqs    curinode = ginode(inum);
302241675Suqs    ocurrent = curinum;
303241675Suqs    curinum = inum;
304241675Suqs    printactive();
305241675Suqs    return 0;
306241675Suqs}
307241675Suqs
308241675SuqsCMDFUNCSTART(back)
309241675Suqs{
310241675Suqs    curinum = ocurrent;
311241675Suqs    curinode = ginode(curinum);
312241675Suqs    printactive();
313241675Suqs    return 0;
314241675Suqs}
315241675Suqs
316241675SuqsCMDFUNCSTART(zapi)
317241675Suqs{
318241675Suqs    ino_t inum;
319241675Suqs    struct dinode *dp;
320241675Suqs    char *cp;
321241675Suqs
322241675Suqs    GETINUM(1,inum);
323241675Suqs    dp = ginode(inum);
324241675Suqs    clearinode(dp);
325241675Suqs    inodirty();
326241675Suqs    if (curinode)			/* re-set after potential change */
327241675Suqs	curinode = ginode(curinum);
328241675Suqs    return 0;
329241675Suqs}
330241675Suqs
331241675SuqsCMDFUNCSTART(active)
332241675Suqs{
333241675Suqs    printactive();
334241675Suqs    return 0;
335241675Suqs}
336241675Suqs
337241675Suqs
338241675SuqsCMDFUNCSTART(quit)
339241675Suqs{
340241675Suqs    return -1;
341241675Suqs}
342241675Suqs
343241675SuqsCMDFUNCSTART(uplink)
344241675Suqs{
345241675Suqs    if (!checkactive())
346241675Suqs	return 1;
347241675Suqs    printf("inode %d link count now %d\n", curinum, ++curinode->di_nlink);
348241675Suqs    inodirty();
349241675Suqs    return 0;
350241675Suqs}
351241675Suqs
352241675SuqsCMDFUNCSTART(downlink)
353241675Suqs{
354241675Suqs    if (!checkactive())
355241675Suqs	return 1;
356241675Suqs    printf("inode %d link count now %d\n", curinum, --curinode->di_nlink);
357241675Suqs    inodirty();
358241675Suqs    return 0;
359241675Suqs}
360241675Suqs
361241675Suqsconst char *typename[] = {
362241675Suqs    "unknown",
363241675Suqs    "fifo",
364241675Suqs    "char special",
365241675Suqs    "unregistered #3",
366241675Suqs    "directory",
367241675Suqs    "unregistered #5",
368241675Suqs    "blk special",
369241675Suqs    "unregistered #7",
370241675Suqs    "regular",
371241675Suqs    "unregistered #9",
372241675Suqs    "symlink",
373241675Suqs    "unregistered #11",
374241675Suqs    "socket",
375241675Suqs    "unregistered #13",
376241675Suqs    "whiteout",
377241675Suqs};
378241675Suqs
379241675Suqsint slot;
380241675Suqs
381241675Suqsint
382241675Suqsscannames(idesc)
383241675Suqs	struct inodesc *idesc;
384241675Suqs{
385241675Suqs	register struct direct *dirp = idesc->id_dirp;
386241675Suqs
387241675Suqs	printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
388241675Suqs	       slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type],
389241675Suqs	       dirp->d_namlen, dirp->d_name);
390241675Suqs	return (KEEPON);
391241675Suqs}
392241675Suqs
393241675SuqsCMDFUNCSTART(ls)
394241675Suqs{
395241675Suqs    struct inodesc idesc;
396241675Suqs    checkactivedir();			/* let it go on anyway */
397241675Suqs
398241675Suqs    slot = 0;
399241675Suqs    idesc.id_number = curinum;
400241675Suqs    idesc.id_func = scannames;
401241675Suqs    idesc.id_type = DATA;
402241675Suqs    idesc.id_fix = IGNORE;
403241675Suqs    ckinode(curinode, &idesc);
404241675Suqs    curinode = ginode(curinum);
405241675Suqs
406241675Suqs    return 0;
407241675Suqs}
408241675Suqs
409241675Suqsint findino __P((struct inodesc *idesc)); /* from fsck */
410241675Suqsstatic int dolookup __P((char *name));
411241675Suqs
412241675Suqsstatic int
413241675Suqsdolookup(name)
414241675Suqs	char *name;
415241675Suqs{
416241675Suqs    struct inodesc idesc;
417241675Suqs
418241675Suqs    if (!checkactivedir())
419241675Suqs	    return 0;
420241675Suqs    idesc.id_number = curinum;
421241675Suqs    idesc.id_func = findino;
422241675Suqs    idesc.id_name = name;
423241675Suqs    idesc.id_type = DATA;
424241675Suqs    idesc.id_fix = IGNORE;
425241675Suqs    if (ckinode(curinode, &idesc) & FOUND) {
426241675Suqs	curinum = idesc.id_parent;
427241675Suqs	curinode = ginode(curinum);
428241675Suqs	printactive();
429241675Suqs	return 1;
430241675Suqs    } else {
431241675Suqs	warnx("name `%s' not found in current inode directory", name);
432241675Suqs	return 0;
433241675Suqs    }
434241675Suqs}
435241675Suqs
436241675SuqsCMDFUNCSTART(focusname)
437241675Suqs{
438241675Suqs    char *p, *val;
439241675Suqs
440241675Suqs    if (!checkactive())
441241675Suqs	return 1;
442241675Suqs
443241675Suqs    ocurrent = curinum;
444241675Suqs
445241675Suqs    if (argv[1][0] == '/') {
446241675Suqs	curinum = ROOTINO;
447241675Suqs	curinode = ginode(ROOTINO);
448241675Suqs    } else {
449241675Suqs	if (!checkactivedir())
450241675Suqs	    return 1;
451241675Suqs    }
452241675Suqs    for (p = argv[1]; p != NULL;) {
453241675Suqs	while ((val = strsep(&p, "/")) != NULL && *val == '\0');
454241675Suqs	if (val) {
455241675Suqs	    printf("component `%s': ", val);
456241675Suqs	    fflush(stdout);
457241675Suqs	    if (!dolookup(val)) {
458241675Suqs		curinode = ginode(curinum);
459241675Suqs		return(1);
460241675Suqs	    }
461241675Suqs	}
462241675Suqs    }
463241675Suqs    return 0;
464241675Suqs}
465241675Suqs
466241675SuqsCMDFUNCSTART(ln)
467241675Suqs{
468241675Suqs    ino_t inum;
469241675Suqs    int rval;
470241675Suqs    char *cp;
471241675Suqs
472241675Suqs    GETINUM(1,inum);
473241675Suqs
474241675Suqs    if (!checkactivedir())
475241675Suqs	return 1;
476241675Suqs    rval = makeentry(curinum, inum, argv[2]);
477241675Suqs    if (rval)
478241675Suqs	printf("Ino %d entered as `%s'\n", inum, argv[2]);
479241675Suqs    else
480241675Suqs	printf("could not enter name? weird.\n");
481241675Suqs    curinode = ginode(curinum);
482241675Suqs    return rval;
483241675Suqs}
484241675Suqs
485241675SuqsCMDFUNCSTART(rm)
486241675Suqs{
487241675Suqs    int rval;
488241675Suqs
489241675Suqs    if (!checkactivedir())
490241675Suqs	return 1;
491241675Suqs    rval = changeino(curinum, argv[1], 0);
492241675Suqs    if (rval & ALTERED) {
493241675Suqs	printf("Name `%s' removed\n", argv[1]);
494241675Suqs	return 0;
495241675Suqs    } else {
496241675Suqs	printf("could not remove name? weird.\n");
497241675Suqs	return 1;
498241675Suqs    }
499241675Suqs}
500241675Suqs
501241675Suqslong slotcount, desired;
502241675Suqs
503241675Suqsint
504241675Suqschinumfunc(idesc)
505241675Suqs	struct inodesc *idesc;
506241675Suqs{
507241675Suqs	register struct direct *dirp = idesc->id_dirp;
508241675Suqs
509241675Suqs	if (slotcount++ == desired) {
510241675Suqs	    dirp->d_ino = idesc->id_parent;
511241675Suqs	    return STOP|ALTERED|FOUND;
512241675Suqs	}
513241675Suqs	return KEEPON;
514241675Suqs}
515241675Suqs
516241675SuqsCMDFUNCSTART(chinum)
517241675Suqs{
518241675Suqs    char *cp;
519241675Suqs    ino_t inum;
520241675Suqs    struct inodesc idesc;
521241675Suqs
522241675Suqs    slotcount = 0;
523241675Suqs    if (!checkactivedir())
524241675Suqs	return 1;
525241675Suqs    GETINUM(2,inum);
526241675Suqs
527241675Suqs    desired = strtol(argv[1], &cp, 0);
528241675Suqs    if (cp == argv[1] || *cp != '\0' || desired < 0) {
529241675Suqs	printf("invalid slot number `%s'\n", argv[1]);
530241675Suqs	return 1;
531241675Suqs    }
532241675Suqs
533241675Suqs    idesc.id_number = curinum;
534241675Suqs    idesc.id_func = chinumfunc;
535241675Suqs    idesc.id_fix = IGNORE;
536241675Suqs    idesc.id_type = DATA;
537241675Suqs    idesc.id_parent = inum;		/* XXX convenient hiding place */
538241675Suqs
539241675Suqs    if (ckinode(curinode, &idesc) & FOUND)
540241675Suqs	return 0;
541241675Suqs    else {
542241675Suqs	warnx("no %sth slot in current directory", argv[1]);
543241675Suqs	return 1;
544241675Suqs    }
545241675Suqs}
546241675Suqs
547241675Suqsint
548241675Suqschnamefunc(idesc)
549241675Suqs	struct inodesc *idesc;
550241675Suqs{
551241675Suqs	register struct direct *dirp = idesc->id_dirp;
552241675Suqs	struct direct testdir;
553241675Suqs
554241675Suqs	if (slotcount++ == desired) {
555241675Suqs	    /* will name fit? */
556241675Suqs	    testdir.d_namlen = strlen(idesc->id_name);
557241675Suqs	    if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
558241675Suqs		dirp->d_namlen = testdir.d_namlen;
559241675Suqs		strcpy(dirp->d_name, idesc->id_name);
560241675Suqs		return STOP|ALTERED|FOUND;
561241675Suqs	    } else
562241675Suqs		return STOP|FOUND;	/* won't fit, so give up */
563241675Suqs	}
564241675Suqs	return KEEPON;
565241675Suqs}
566241675Suqs
567241675SuqsCMDFUNCSTART(chname)
568241675Suqs{
569241675Suqs    int rval;
570241675Suqs    char *cp;
571241675Suqs    struct inodesc idesc;
572241675Suqs
573241675Suqs    slotcount = 0;
574241675Suqs    if (!checkactivedir())
575241675Suqs	return 1;
576241675Suqs
577241675Suqs    desired = strtoul(argv[1], &cp, 0);
578241675Suqs    if (cp == argv[1] || *cp != '\0') {
579241675Suqs	printf("invalid slot number `%s'\n", argv[1]);
580241675Suqs	return 1;
581241675Suqs    }
582241675Suqs
583241675Suqs    idesc.id_number = curinum;
584241675Suqs    idesc.id_func = chnamefunc;
585241675Suqs    idesc.id_fix = IGNORE;
586241675Suqs    idesc.id_type = DATA;
587241675Suqs    idesc.id_name = argv[2];
588241675Suqs
589241675Suqs    rval = ckinode(curinode, &idesc);
590241675Suqs    if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
591241675Suqs	return 0;
592241675Suqs    else if (rval & FOUND) {
593241675Suqs	warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
594241675Suqs	return 1;
595241675Suqs    } else {
596241675Suqs	warnx("no %sth slot in current directory", argv[1]);
597241675Suqs	return 1;
598241675Suqs    }
599241675Suqs}
600241675Suqs
601241675Suqsstruct typemap {
602241675Suqs    const char *typename;
603241675Suqs    int typebits;
604241675Suqs} typenamemap[]  = {
605241675Suqs    {"file", IFREG},
606241675Suqs    {"dir", IFDIR},
607241675Suqs    {"socket", IFSOCK},
608241675Suqs    {"fifo", IFIFO},
609241675Suqs};
610241675Suqs
611241675SuqsCMDFUNCSTART(newtype)
612241675Suqs{
613241675Suqs    int type;
614241675Suqs    struct typemap *tp;
615241675Suqs
616241675Suqs    if (!checkactive())
617241675Suqs	return 1;
618241675Suqs    type = curinode->di_mode & IFMT;
619241675Suqs    for (tp = typenamemap;
620241675Suqs	 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
621241675Suqs	 tp++) {
622241675Suqs	if (!strcmp(argv[1], tp->typename)) {
623241675Suqs	    printf("setting type to %s\n", tp->typename);
624241675Suqs	    type = tp->typebits;
625241675Suqs	    break;
626241675Suqs	}
627241675Suqs    }
628241675Suqs    if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
629241675Suqs	warnx("type `%s' not known", argv[1]);
630241675Suqs	warnx("try one of `file', `dir', `socket', `fifo'");
631241675Suqs	return 1;
632241675Suqs    }
633241675Suqs    curinode->di_mode &= ~IFMT;
634241675Suqs    curinode->di_mode |= type;
635241675Suqs    inodirty();
636241675Suqs    printactive();
637241675Suqs    return 0;
638241675Suqs}
639241675Suqs
640241675SuqsCMDFUNCSTART(chlen)
641241675Suqs{
642241675Suqs    int rval = 1;
643241675Suqs    long len;
644241675Suqs    char *cp;
645241675Suqs
646241675Suqs    if (!checkactive())
647241675Suqs	return 1;
648241675Suqs
649241675Suqs    len = strtol(argv[1], &cp, 0);
650241675Suqs    if (cp == argv[1] || *cp != '\0' || len < 0) {
651241675Suqs	warnx("bad length `%s'", argv[1]);
652241675Suqs	return 1;
653241675Suqs    }
654241675Suqs
655241675Suqs    curinode->di_size = len;
656241675Suqs    inodirty();
657241675Suqs    printactive();
658241675Suqs    return rval;
659241675Suqs}
660241675Suqs
661241675SuqsCMDFUNCSTART(chmode)
662241675Suqs{
663241675Suqs    int rval = 1;
664241675Suqs    long modebits;
665241675Suqs    char *cp;
666241675Suqs
667241675Suqs    if (!checkactive())
668241675Suqs	return 1;
669241675Suqs
670241675Suqs    modebits = strtol(argv[1], &cp, 8);
671241675Suqs    if (cp == argv[1] || *cp != '\0' ) {
672241675Suqs	warnx("bad modebits `%s'", argv[1]);
673241675Suqs	return 1;
674241675Suqs    }
675241675Suqs
676241675Suqs    curinode->di_mode &= ~07777;
677241675Suqs    curinode->di_mode |= modebits;
678241675Suqs    inodirty();
679241675Suqs    printactive();
680241675Suqs    return rval;
681241675Suqs}
682241675Suqs
683241675SuqsCMDFUNCSTART(chaflags)
684241675Suqs{
685241675Suqs    int rval = 1;
686241675Suqs    u_long flags;
687241675Suqs    char *cp;
688241675Suqs
689241675Suqs    if (!checkactive())
690241675Suqs	return 1;
691241675Suqs
692241675Suqs    flags = strtoul(argv[1], &cp, 0);
693241675Suqs    if (cp == argv[1] || *cp != '\0' ) {
694241675Suqs	warnx("bad flags `%s'", argv[1]);
695241675Suqs	return 1;
696241675Suqs    }
697241675Suqs
698241675Suqs    if (flags > UINT_MAX) {
699241675Suqs	warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
700241675Suqs	return(1);
701241675Suqs    }
702241675Suqs    curinode->di_flags = flags;
703241675Suqs    inodirty();
704241675Suqs    printactive();
705241675Suqs    return rval;
706241675Suqs}
707241675Suqs
708241675SuqsCMDFUNCSTART(chgen)
709241675Suqs{
710241675Suqs    int rval = 1;
711241675Suqs    long gen;
712241675Suqs    char *cp;
713241675Suqs
714241675Suqs    if (!checkactive())
715241675Suqs	return 1;
716241675Suqs
717241675Suqs    gen = strtol(argv[1], &cp, 0);
718241675Suqs    if (cp == argv[1] || *cp != '\0' ) {
719241675Suqs	warnx("bad gen `%s'", argv[1]);
720241675Suqs	return 1;
721241675Suqs    }
722241675Suqs
723241675Suqs    if (gen > INT_MAX || gen < INT_MIN) {
724241675Suqs	warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
725241675Suqs	return(1);
726241675Suqs    }
727241675Suqs    curinode->di_gen = gen;
728241675Suqs    inodirty();
729241675Suqs    printactive();
730241675Suqs    return rval;
731241675Suqs}
732241675Suqs
733241675SuqsCMDFUNCSTART(linkcount)
734241675Suqs{
735241675Suqs    int rval = 1;
736241675Suqs    int lcnt;
737241675Suqs    char *cp;
738241675Suqs
739241675Suqs    if (!checkactive())
740241675Suqs	return 1;
741241675Suqs
742241675Suqs    lcnt = strtol(argv[1], &cp, 0);
743241675Suqs    if (cp == argv[1] || *cp != '\0' ) {
744241675Suqs	warnx("bad link count `%s'", argv[1]);
745241675Suqs	return 1;
746241675Suqs    }
747241675Suqs    if (lcnt > USHRT_MAX || lcnt < 0) {
748241675Suqs	warnx("max link count is %d\n", USHRT_MAX);
749241675Suqs	return 1;
750241675Suqs    }
751241675Suqs
752241675Suqs    curinode->di_nlink = lcnt;
753241675Suqs    inodirty();
754241675Suqs    printactive();
755241675Suqs    return rval;
756241675Suqs}
757241675Suqs
758241675SuqsCMDFUNCSTART(chowner)
759241675Suqs{
760241675Suqs    int rval = 1;
761241675Suqs    unsigned long uid;
762241675Suqs    char *cp;
763241675Suqs    struct passwd *pwd;
764241675Suqs
765241675Suqs    if (!checkactive())
766241675Suqs	return 1;
767241675Suqs
768241675Suqs    uid = strtoul(argv[1], &cp, 0);
769241675Suqs    if (cp == argv[1] || *cp != '\0' ) {
770241675Suqs	/* try looking up name */
771241675Suqs	if ((pwd = getpwnam(argv[1]))) {
772241675Suqs	    uid = pwd->pw_uid;
773241675Suqs	} else {
774241675Suqs	    warnx("bad uid `%s'", argv[1]);
775241675Suqs	    return 1;
776241675Suqs	}
777241675Suqs    }
778241675Suqs
779241675Suqs    curinode->di_uid = uid;
780241675Suqs    inodirty();
781241675Suqs    printactive();
782241675Suqs    return rval;
783241675Suqs}
784241675Suqs
785241675SuqsCMDFUNCSTART(chgroup)
786241675Suqs{
787241675Suqs    int rval = 1;
788241675Suqs    unsigned long gid;
789241675Suqs    char *cp;
790241675Suqs    struct group *grp;
791241675Suqs
792241675Suqs    if (!checkactive())
793241675Suqs	return 1;
794241675Suqs
795241675Suqs    gid = strtoul(argv[1], &cp, 0);
796241675Suqs    if (cp == argv[1] || *cp != '\0' ) {
797241675Suqs	if ((grp = getgrnam(argv[1]))) {
798241675Suqs	    gid = grp->gr_gid;
799241675Suqs	} else {
800241675Suqs	    warnx("bad gid `%s'", argv[1]);
801241675Suqs	    return 1;
802241675Suqs	}
803241675Suqs    }
804241675Suqs
805241675Suqs    curinode->di_gid = gid;
806241675Suqs    inodirty();
807241675Suqs    printactive();
808241675Suqs    return rval;
809241675Suqs}
810241675Suqs
811241675Suqsint
812241675Suqsdotime(name, rts)
813241675Suqs	char *name;
814241675Suqs	struct timespec *rts;
815241675Suqs{
816241675Suqs    char *p, *val;
817241675Suqs    struct tm t;
818241675Suqs    int32_t sec;
819241675Suqs    int32_t nsec;
820241675Suqs    p = strchr(name, '.');
821241675Suqs    if (p) {
822241675Suqs	*p = '\0';
823241675Suqs	nsec = strtoul(++p, &val, 0);
824241675Suqs	if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
825241675Suqs		warnx("invalid nanoseconds");
826241675Suqs		goto badformat;
827241675Suqs	}
828241675Suqs    } else
829241675Suqs	nsec = 0;
830241675Suqs    if (strlen(name) != 14) {
831241675Suqsbadformat:
832241675Suqs	warnx("date format: YYYYMMDDHHMMSS[.nsec]");
833241675Suqs	return 1;
834241675Suqs    }
835241675Suqs
836241675Suqs    for (p = name; *p; p++)
837241675Suqs	if (*p < '0' || *p > '9')
838241675Suqs	    goto badformat;
839241675Suqs
840241675Suqs    p = name;
841241675Suqs#define VAL() ((*p++) - '0')
842241675Suqs    t.tm_year = VAL();
843241675Suqs    t.tm_year = VAL() + t.tm_year * 10;
844241675Suqs    t.tm_year = VAL() + t.tm_year * 10;
845241675Suqs    t.tm_year = VAL() + t.tm_year * 10 - 1900;
846241675Suqs    t.tm_mon = VAL();
847241675Suqs    t.tm_mon = VAL() + t.tm_mon * 10 - 1;
848241675Suqs    t.tm_mday = VAL();
849241675Suqs    t.tm_mday = VAL() + t.tm_mday * 10;
850241675Suqs    t.tm_hour = VAL();
851241675Suqs    t.tm_hour = VAL() + t.tm_hour * 10;
852241675Suqs    t.tm_min = VAL();
853241675Suqs    t.tm_min = VAL() + t.tm_min * 10;
854241675Suqs    t.tm_sec = VAL();
855241675Suqs    t.tm_sec = VAL() + t.tm_sec * 10;
856241675Suqs    t.tm_isdst = -1;
857241675Suqs
858241675Suqs    sec = mktime(&t);
859241675Suqs    if (sec == -1) {
860241675Suqs	warnx("date/time out of range");
861241675Suqs	return 1;
862241675Suqs    }
863241675Suqs    rts->tv_sec = sec;
864241675Suqs    rts->tv_nsec = nsec;
865241675Suqs    return 0;
866241675Suqs}
867241675Suqs
868241675SuqsCMDFUNCSTART(chmtime)
869241675Suqs{
870241675Suqs    if (dotime(argv[1], &curinode->di_ctime))
871241675Suqs	return 1;
872241675Suqs    inodirty();
873241675Suqs    printactive();
874241675Suqs    return 0;
875241675Suqs}
876241675Suqs
877241675SuqsCMDFUNCSTART(chatime)
878241675Suqs{
879241675Suqs    if (dotime(argv[1], &curinode->di_ctime))
880241675Suqs	return 1;
881241675Suqs    inodirty();
882241675Suqs    printactive();
883241675Suqs    return 0;
884241675Suqs}
885241675Suqs
886241675SuqsCMDFUNCSTART(chctime)
887241675Suqs{
888241675Suqs    if (dotime(argv[1], &curinode->di_ctime))
889241675Suqs	return 1;
890241675Suqs    inodirty();
891241675Suqs    printactive();
892241675Suqs    return 0;
893241675Suqs}
894241675Suqs