fsdb.c revision 148833
1139825Simp/*	$NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $	*/
2110439Sbenno
3110439Sbenno/*
4110439Sbenno *  Copyright (c) 1995 John T. Kohl
5110439Sbenno *  All rights reserved.
6110439Sbenno *
7110439Sbenno *  Redistribution and use in source and binary forms, with or without
8110439Sbenno *  modification, are permitted provided that the following conditions
9110439Sbenno *  are met:
10110439Sbenno *  1. Redistributions of source code must retain the above copyright
11110439Sbenno *     notice, this list of conditions and the following disclaimer.
12110439Sbenno *  2. Redistributions in binary form must reproduce the above copyright
13110439Sbenno *     notice, this list of conditions and the following disclaimer in the
14110439Sbenno *     documentation and/or other materials provided with the distribution.
15110439Sbenno *  3. The name of the author may not be used to endorse or promote products
16110439Sbenno *     derived from this software without specific prior written permission.
17110439Sbenno *
18110439Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19110439Sbenno * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20110439Sbenno * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21110439Sbenno * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22110439Sbenno * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23110439Sbenno * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24110439Sbenno * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25110439Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26110439Sbenno * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27110439Sbenno * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28110441Sbenno * POSSIBILITY OF SUCH DAMAGE.
29110441Sbenno */
30110439Sbenno
31110439Sbenno#ifndef lint
32132519Sgallatinstatic const char rcsid[] =
33110439Sbenno  "$FreeBSD: head/sbin/fsdb/fsdb.c 148833 2005-08-07 20:53:33Z stefanf $";
34132519Sgallatin#endif /* not lint */
35110439Sbenno
36110439Sbenno#include <sys/param.h>
37110439Sbenno#include <sys/time.h>
38110439Sbenno#include <ctype.h>
39110439Sbenno#include <err.h>
40110439Sbenno#include <grp.h>
41110439Sbenno#include <histedit.h>
42110439Sbenno#include <pwd.h>
43110439Sbenno#include <string.h>
44110439Sbenno#include <timeconv.h>
45110439Sbenno
46110439Sbenno#include <ufs/ufs/dinode.h>
47110439Sbenno#include <ufs/ufs/dir.h>
48110439Sbenno#include <ufs/ffs/fs.h>
49110439Sbenno
50110439Sbenno#include "fsdb.h"
51110439Sbenno#include "fsck.h"
52110439Sbenno
53110439Sbennostatic void usage(void) __dead2;
54110439Sbennoint cmdloop(void);
55167170Spiso
56110439Sbennostatic void
57110439Sbennousage(void)
58110439Sbenno{
59110439Sbenno	fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
60110439Sbenno	exit(1);
61110439Sbenno}
62110439Sbenno
63110439Sbennoint returntosingle;
64110439Sbennochar nflag;
65110439Sbenno
66110439Sbenno/*
67110439Sbenno * We suck in lots of fsck code, and just pick & choose the stuff we want.
68110439Sbenno *
69110439Sbenno * fsreadfd is set up to read from the file system, fswritefd to write to
70110439Sbenno * the file system.
71110439Sbenno */
72110439Sbennoint
73110439Sbennomain(int argc, char *argv[])
74110439Sbenno{
75110439Sbenno	int ch, rval;
76110439Sbenno	char *fsys = NULL;
77110439Sbenno
78110439Sbenno	while (-1 != (ch = getopt(argc, argv, "fdr"))) {
79110439Sbenno		switch (ch) {
80110439Sbenno		case 'f':
81110439Sbenno			/* The -f option is left for historical
82110439Sbenno			 * reasons and has no meaning.
83110439Sbenno			 */
84110439Sbenno			break;
85110439Sbenno		case 'd':
86110439Sbenno			debug++;
87110439Sbenno			break;
88110439Sbenno		case 'r':
89110439Sbenno			nflag++; /* "no" in fsck, readonly for us */
90110439Sbenno			break;
91110439Sbenno		default:
92110439Sbenno			usage();
93110439Sbenno		}
94110439Sbenno	}
95110439Sbenno	argc -= optind;
96110439Sbenno	argv += optind;
97110439Sbenno	if (argc != 1)
98110439Sbenno		usage();
99110439Sbenno	else
100110439Sbenno		fsys = argv[0];
101110439Sbenno
102110439Sbenno	sblock_init();
103110439Sbenno	if (!setup(fsys))
104110439Sbenno		errx(1, "cannot set up file system `%s'", fsys);
105110439Sbenno	printf("%s file system `%s'\nLast Mounted on %s\n",
106110439Sbenno	       nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
107110439Sbenno	rval = cmdloop();
108110439Sbenno	if (!nflag) {
109110439Sbenno		sblock.fs_clean = 0;	/* mark it dirty */
110110439Sbenno		sbdirty();
111110439Sbenno		ckfini(0);
112110439Sbenno		printf("*** FILE SYSTEM MARKED DIRTY\n");
113110439Sbenno		printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
114110439Sbenno		printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
115110439Sbenno	}
116110439Sbenno	exit(rval);
117110439Sbenno}
118110439Sbenno
119110439Sbenno#define CMDFUNC(func) int func(int argc, char *argv[])
120110439Sbenno#define CMDFUNCSTART(func) int func(int argc, char *argv[])
121110439Sbenno
122110439SbennoCMDFUNC(helpfn);
123110439SbennoCMDFUNC(focus);				/* focus on inode */
124166978SpisoCMDFUNC(active);			/* print active inode */
125166978SpisoCMDFUNC(blocks);			/* print blocks for active inode */
126110439SbennoCMDFUNC(focusname);			/* focus by name */
127110439SbennoCMDFUNC(zapi);				/* clear inode */
128110439SbennoCMDFUNC(uplink);			/* incr link */
129110439SbennoCMDFUNC(downlink);			/* decr link */
130110439SbennoCMDFUNC(linkcount);			/* set link count */
131110439SbennoCMDFUNC(quit);				/* quit */
132110439SbennoCMDFUNC(ls);				/* list directory */
133110439SbennoCMDFUNC(rm);				/* remove name */
134110439SbennoCMDFUNC(ln);				/* add name */
135167170SpisoCMDFUNC(newtype);			/* change type */
136110439SbennoCMDFUNC(chmode);			/* change mode */
137110439SbennoCMDFUNC(chlen);				/* change length */
138110439SbennoCMDFUNC(chaflags);			/* change flags */
139110439SbennoCMDFUNC(chgen);				/* change generation */
140110439SbennoCMDFUNC(chowner);			/* change owner */
141110439SbennoCMDFUNC(chgroup);			/* Change group */
142174898SrwatsonCMDFUNC(back);				/* pop back to last ino */
143167170SpisoCMDFUNC(chmtime);			/* Change mtime */
144110439SbennoCMDFUNC(chctime);			/* Change ctime */
145CMDFUNC(chatime);			/* Change atime */
146CMDFUNC(chinum);			/* Change inode # of dirent */
147CMDFUNC(chname);			/* Change dirname of dirent */
148
149struct cmdtable cmds[] = {
150	{ "help", "Print out help", 1, 1, FL_RO, helpfn },
151	{ "?", "Print out help", 1, 1, FL_RO, helpfn },
152	{ "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
153	{ "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
154	{ "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
155	{ "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
156	{ "back", "Go to previous active inode", 1, 1, FL_RO, back },
157	{ "active", "Print active inode", 1, 1, FL_RO, active },
158	{ "print", "Print active inode", 1, 1, FL_RO, active },
159	{ "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
160	{ "uplink", "Increment link count", 1, 1, FL_WR, uplink },
161	{ "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
162	{ "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
163	{ "ls", "List current inode as directory", 1, 1, FL_RO, ls },
164	{ "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
165	{ "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
166	{ "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln },
167	{ "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
168	{ "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname },
169	{ "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
170	{ "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
171	{ "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
172	{ "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
173	{ "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
174	{ "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
175	{ "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
176	{ "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
177	{ "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
178	{ "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
179	{ "quit", "Exit", 1, 1, FL_RO, quit },
180	{ "q", "Exit", 1, 1, FL_RO, quit },
181	{ "exit", "Exit", 1, 1, FL_RO, quit },
182	{ NULL, 0, 0, 0, 0, NULL },
183};
184
185int
186helpfn(int argc, char *argv[])
187{
188    struct cmdtable *cmdtp;
189
190    printf("Commands are:\n%-10s %5s %5s   %s\n",
191	   "command", "min argc", "max argc", "what");
192
193    for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
194	printf("%-10s %5u %5u   %s\n",
195	       cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
196    return 0;
197}
198
199char *
200prompt(EditLine *el)
201{
202    static char pstring[64];
203    snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
204    return pstring;
205}
206
207
208int
209cmdloop(void)
210{
211    char *line;
212    const char *elline;
213    int cmd_argc, rval = 0, known;
214#define scratch known
215    char **cmd_argv;
216    struct cmdtable *cmdp;
217    History *hist;
218    EditLine *elptr;
219    HistEvent he;
220
221    curinode = ginode(ROOTINO);
222    curinum = ROOTINO;
223    printactive(0);
224
225    hist = history_init();
226    history(hist, &he, H_EVENT, 100);	/* 100 elt history buffer */
227
228    elptr = el_init("fsdb", stdin, stdout, stderr);
229    el_set(elptr, EL_EDITOR, "emacs");
230    el_set(elptr, EL_PROMPT, prompt);
231    el_set(elptr, EL_HIST, history, hist);
232    el_source(elptr, NULL);
233
234    while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
235	if (debug)
236	    printf("command `%s'\n", elline);
237
238	history(hist, &he, H_ENTER, elline);
239
240	line = strdup(elline);
241	cmd_argv = crack(line, &cmd_argc);
242	/*
243	 * el_parse returns -1 to signal that it's not been handled
244	 * internally.
245	 */
246	if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1)
247	    continue;
248	if (cmd_argc) {
249	    known = 0;
250	    for (cmdp = cmds; cmdp->cmd; cmdp++) {
251		if (!strcmp(cmdp->cmd, cmd_argv[0])) {
252		    if ((cmdp->flags & FL_WR) == FL_WR && nflag)
253			warnx("`%s' requires write access", cmd_argv[0]),
254			    rval = 1;
255		    else if (cmd_argc >= cmdp->minargc &&
256			cmd_argc <= cmdp->maxargc)
257			rval = (*cmdp->handler)(cmd_argc, cmd_argv);
258		    else if (cmd_argc >= cmdp->minargc) {
259			strcpy(line, elline);
260			cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
261			rval = (*cmdp->handler)(cmd_argc, cmd_argv);
262		    } else
263			rval = argcount(cmdp, cmd_argc, cmd_argv);
264		    known = 1;
265		    break;
266		}
267	    }
268	    if (!known)
269		warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
270	} else
271	    rval = 0;
272	free(line);
273	if (rval < 0)
274	    /* user typed "quit" */
275	    return 0;
276	if (rval)
277	    warnx("rval was %d", rval);
278    }
279    el_end(elptr);
280    history_end(hist);
281    return rval;
282}
283
284union dinode *curinode;
285ino_t curinum, ocurrent;
286
287#define GETINUM(ac,inum)    inum = strtoul(argv[ac], &cp, 0); \
288    if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
289	printf("inode %d out of range; range is [%d,%d]\n", \
290	       inum, ROOTINO, maxino); \
291	return 1; \
292    }
293
294/*
295 * Focus on given inode number
296 */
297CMDFUNCSTART(focus)
298{
299    ino_t inum;
300    char *cp;
301
302    GETINUM(1,inum);
303    curinode = ginode(inum);
304    ocurrent = curinum;
305    curinum = inum;
306    printactive(0);
307    return 0;
308}
309
310CMDFUNCSTART(back)
311{
312    curinum = ocurrent;
313    curinode = ginode(curinum);
314    printactive(0);
315    return 0;
316}
317
318CMDFUNCSTART(zapi)
319{
320    ino_t inum;
321    union dinode *dp;
322    char *cp;
323
324    GETINUM(1,inum);
325    dp = ginode(inum);
326    clearinode(dp);
327    inodirty();
328    if (curinode)			/* re-set after potential change */
329	curinode = ginode(curinum);
330    return 0;
331}
332
333CMDFUNCSTART(active)
334{
335    printactive(0);
336    return 0;
337}
338
339CMDFUNCSTART(blocks)
340{
341    printactive(1);
342    return 0;
343}
344
345CMDFUNCSTART(quit)
346{
347    return -1;
348}
349
350CMDFUNCSTART(uplink)
351{
352    if (!checkactive())
353	return 1;
354    DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
355    printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
356    inodirty();
357    return 0;
358}
359
360CMDFUNCSTART(downlink)
361{
362    if (!checkactive())
363	return 1;
364    DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
365    printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
366    inodirty();
367    return 0;
368}
369
370const char *typename[] = {
371    "unknown",
372    "fifo",
373    "char special",
374    "unregistered #3",
375    "directory",
376    "unregistered #5",
377    "blk special",
378    "unregistered #7",
379    "regular",
380    "unregistered #9",
381    "symlink",
382    "unregistered #11",
383    "socket",
384    "unregistered #13",
385    "whiteout",
386};
387
388int slot;
389
390int
391scannames(struct inodesc *idesc)
392{
393	struct direct *dirp = idesc->id_dirp;
394
395	printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
396	       slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type],
397	       dirp->d_namlen, dirp->d_name);
398	return (KEEPON);
399}
400
401CMDFUNCSTART(ls)
402{
403    struct inodesc idesc;
404    checkactivedir();			/* let it go on anyway */
405
406    slot = 0;
407    idesc.id_number = curinum;
408    idesc.id_func = scannames;
409    idesc.id_type = DATA;
410    idesc.id_fix = IGNORE;
411    ckinode(curinode, &idesc);
412    curinode = ginode(curinum);
413
414    return 0;
415}
416
417int findino(struct inodesc *idesc); /* from fsck */
418static int dolookup(char *name);
419
420static int
421dolookup(char *name)
422{
423    struct inodesc idesc;
424
425    if (!checkactivedir())
426	    return 0;
427    idesc.id_number = curinum;
428    idesc.id_func = findino;
429    idesc.id_name = name;
430    idesc.id_type = DATA;
431    idesc.id_fix = IGNORE;
432    if (ckinode(curinode, &idesc) & FOUND) {
433	curinum = idesc.id_parent;
434	curinode = ginode(curinum);
435	printactive(0);
436	return 1;
437    } else {
438	warnx("name `%s' not found in current inode directory", name);
439	return 0;
440    }
441}
442
443CMDFUNCSTART(focusname)
444{
445    char *p, *val;
446
447    if (!checkactive())
448	return 1;
449
450    ocurrent = curinum;
451
452    if (argv[1][0] == '/') {
453	curinum = ROOTINO;
454	curinode = ginode(ROOTINO);
455    } else {
456	if (!checkactivedir())
457	    return 1;
458    }
459    for (p = argv[1]; p != NULL;) {
460	while ((val = strsep(&p, "/")) != NULL && *val == '\0');
461	if (val) {
462	    printf("component `%s': ", val);
463	    fflush(stdout);
464	    if (!dolookup(val)) {
465		curinode = ginode(curinum);
466		return(1);
467	    }
468	}
469    }
470    return 0;
471}
472
473CMDFUNCSTART(ln)
474{
475    ino_t inum;
476    int rval;
477    char *cp;
478
479    GETINUM(1,inum);
480
481    if (!checkactivedir())
482	return 1;
483    rval = makeentry(curinum, inum, argv[2]);
484    if (rval)
485	printf("Ino %d entered as `%s'\n", inum, argv[2]);
486    else
487	printf("could not enter name? weird.\n");
488    curinode = ginode(curinum);
489    return rval;
490}
491
492CMDFUNCSTART(rm)
493{
494    int rval;
495
496    if (!checkactivedir())
497	return 1;
498    rval = changeino(curinum, argv[1], 0);
499    if (rval & ALTERED) {
500	printf("Name `%s' removed\n", argv[1]);
501	return 0;
502    } else {
503	printf("could not remove name ('%s')? weird.\n", argv[1]);
504	return 1;
505    }
506}
507
508long slotcount, desired;
509
510int
511chinumfunc(struct inodesc *idesc)
512{
513	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(struct inodesc *idesc)
555{
556	struct direct *dirp = idesc->id_dirp;
557	struct direct testdir;
558
559	if (slotcount++ == desired) {
560	    /* will name fit? */
561	    testdir.d_namlen = strlen(idesc->id_name);
562	    if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
563		dirp->d_namlen = testdir.d_namlen;
564		strcpy(dirp->d_name, idesc->id_name);
565		return STOP|ALTERED|FOUND;
566	    } else
567		return STOP|FOUND;	/* won't fit, so give up */
568	}
569	return KEEPON;
570}
571
572CMDFUNCSTART(chname)
573{
574    int rval;
575    char *cp;
576    struct inodesc idesc;
577
578    slotcount = 0;
579    if (!checkactivedir())
580	return 1;
581
582    desired = strtoul(argv[1], &cp, 0);
583    if (cp == argv[1] || *cp != '\0') {
584	printf("invalid slot number `%s'\n", argv[1]);
585	return 1;
586    }
587
588    idesc.id_number = curinum;
589    idesc.id_func = chnamefunc;
590    idesc.id_fix = IGNORE;
591    idesc.id_type = DATA;
592    idesc.id_name = argv[2];
593
594    rval = ckinode(curinode, &idesc);
595    if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
596	return 0;
597    else if (rval & FOUND) {
598	warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
599	return 1;
600    } else {
601	warnx("no %sth slot in current directory", argv[1]);
602	return 1;
603    }
604}
605
606struct typemap {
607    const char *typename;
608    int typebits;
609} typenamemap[]  = {
610    {"file", IFREG},
611    {"dir", IFDIR},
612    {"socket", IFSOCK},
613    {"fifo", IFIFO},
614};
615
616CMDFUNCSTART(newtype)
617{
618    int type;
619    struct typemap *tp;
620
621    if (!checkactive())
622	return 1;
623    type = DIP(curinode, di_mode) & IFMT;
624    for (tp = typenamemap;
625	 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
626	 tp++) {
627	if (!strcmp(argv[1], tp->typename)) {
628	    printf("setting type to %s\n", tp->typename);
629	    type = tp->typebits;
630	    break;
631	}
632    }
633    if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
634	warnx("type `%s' not known", argv[1]);
635	warnx("try one of `file', `dir', `socket', `fifo'");
636	return 1;
637    }
638    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
639    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
640    inodirty();
641    printactive(0);
642    return 0;
643}
644
645CMDFUNCSTART(chlen)
646{
647    int rval = 1;
648    long len;
649    char *cp;
650
651    if (!checkactive())
652	return 1;
653
654    len = strtol(argv[1], &cp, 0);
655    if (cp == argv[1] || *cp != '\0' || len < 0) {
656	warnx("bad length `%s'", argv[1]);
657	return 1;
658    }
659
660    DIP_SET(curinode, di_size, len);
661    inodirty();
662    printactive(0);
663    return rval;
664}
665
666CMDFUNCSTART(chmode)
667{
668    int rval = 1;
669    long modebits;
670    char *cp;
671
672    if (!checkactive())
673	return 1;
674
675    modebits = strtol(argv[1], &cp, 8);
676    if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
677	warnx("bad modebits `%s'", argv[1]);
678	return 1;
679    }
680
681    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
682    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
683    inodirty();
684    printactive(0);
685    return rval;
686}
687
688CMDFUNCSTART(chaflags)
689{
690    int rval = 1;
691    u_long flags;
692    char *cp;
693
694    if (!checkactive())
695	return 1;
696
697    flags = strtoul(argv[1], &cp, 0);
698    if (cp == argv[1] || *cp != '\0' ) {
699	warnx("bad flags `%s'", argv[1]);
700	return 1;
701    }
702
703    if (flags > UINT_MAX) {
704	warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
705	return(1);
706    }
707    DIP_SET(curinode, di_flags, flags);
708    inodirty();
709    printactive(0);
710    return rval;
711}
712
713CMDFUNCSTART(chgen)
714{
715    int rval = 1;
716    long gen;
717    char *cp;
718
719    if (!checkactive())
720	return 1;
721
722    gen = strtol(argv[1], &cp, 0);
723    if (cp == argv[1] || *cp != '\0' ) {
724	warnx("bad gen `%s'", argv[1]);
725	return 1;
726    }
727
728    if (gen > INT_MAX || gen < INT_MIN) {
729	warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
730	return(1);
731    }
732    DIP_SET(curinode, di_gen, gen);
733    inodirty();
734    printactive(0);
735    return rval;
736}
737
738CMDFUNCSTART(linkcount)
739{
740    int rval = 1;
741    int lcnt;
742    char *cp;
743
744    if (!checkactive())
745	return 1;
746
747    lcnt = strtol(argv[1], &cp, 0);
748    if (cp == argv[1] || *cp != '\0' ) {
749	warnx("bad link count `%s'", argv[1]);
750	return 1;
751    }
752    if (lcnt > USHRT_MAX || lcnt < 0) {
753	warnx("max link count is %d\n", USHRT_MAX);
754	return 1;
755    }
756
757    DIP_SET(curinode, di_nlink, lcnt);
758    inodirty();
759    printactive(0);
760    return rval;
761}
762
763CMDFUNCSTART(chowner)
764{
765    int rval = 1;
766    unsigned long uid;
767    char *cp;
768    struct passwd *pwd;
769
770    if (!checkactive())
771	return 1;
772
773    uid = strtoul(argv[1], &cp, 0);
774    if (cp == argv[1] || *cp != '\0' ) {
775	/* try looking up name */
776	if ((pwd = getpwnam(argv[1]))) {
777	    uid = pwd->pw_uid;
778	} else {
779	    warnx("bad uid `%s'", argv[1]);
780	    return 1;
781	}
782    }
783
784    DIP_SET(curinode, di_uid, uid);
785    inodirty();
786    printactive(0);
787    return rval;
788}
789
790CMDFUNCSTART(chgroup)
791{
792    int rval = 1;
793    unsigned long gid;
794    char *cp;
795    struct group *grp;
796
797    if (!checkactive())
798	return 1;
799
800    gid = strtoul(argv[1], &cp, 0);
801    if (cp == argv[1] || *cp != '\0' ) {
802	if ((grp = getgrnam(argv[1]))) {
803	    gid = grp->gr_gid;
804	} else {
805	    warnx("bad gid `%s'", argv[1]);
806	    return 1;
807	}
808    }
809
810    DIP_SET(curinode, di_gid, gid);
811    inodirty();
812    printactive(0);
813    return rval;
814}
815
816int
817dotime(char *name, time_t *secp, int32_t *nsecp)
818{
819    char *p, *val;
820    struct tm t;
821    int32_t nsec;
822    p = strchr(name, '.');
823    if (p) {
824	*p = '\0';
825	nsec = strtoul(++p, &val, 0);
826	if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
827		warnx("invalid nanoseconds");
828		goto badformat;
829	}
830    } else
831	nsec = 0;
832    if (strlen(name) != 14) {
833badformat:
834	warnx("date format: YYYYMMDDHHMMSS[.nsec]");
835	return 1;
836    }
837    *nsecp = nsec;
838
839    for (p = name; *p; p++)
840	if (*p < '0' || *p > '9')
841	    goto badformat;
842
843    p = name;
844#define VAL() ((*p++) - '0')
845    t.tm_year = VAL();
846    t.tm_year = VAL() + t.tm_year * 10;
847    t.tm_year = VAL() + t.tm_year * 10;
848    t.tm_year = VAL() + t.tm_year * 10 - 1900;
849    t.tm_mon = VAL();
850    t.tm_mon = VAL() + t.tm_mon * 10 - 1;
851    t.tm_mday = VAL();
852    t.tm_mday = VAL() + t.tm_mday * 10;
853    t.tm_hour = VAL();
854    t.tm_hour = VAL() + t.tm_hour * 10;
855    t.tm_min = VAL();
856    t.tm_min = VAL() + t.tm_min * 10;
857    t.tm_sec = VAL();
858    t.tm_sec = VAL() + t.tm_sec * 10;
859    t.tm_isdst = -1;
860
861    *secp = mktime(&t);
862    if (*secp == -1) {
863	warnx("date/time out of range");
864	return 1;
865    }
866    return 0;
867}
868
869CMDFUNCSTART(chmtime)
870{
871    time_t secs;
872    int32_t nsecs;
873
874    if (dotime(argv[1], &secs, &nsecs))
875	return 1;
876    if (sblock.fs_magic == FS_UFS1_MAGIC)
877	curinode->dp1.di_mtime = _time_to_time32(secs);
878    else
879	curinode->dp2.di_mtime = _time_to_time64(secs);
880    DIP_SET(curinode, di_mtimensec, nsecs);
881    inodirty();
882    printactive(0);
883    return 0;
884}
885
886CMDFUNCSTART(chatime)
887{
888    time_t secs;
889    int32_t nsecs;
890
891    if (dotime(argv[1], &secs, &nsecs))
892	return 1;
893    if (sblock.fs_magic == FS_UFS1_MAGIC)
894	curinode->dp1.di_atime = _time_to_time32(secs);
895    else
896	curinode->dp2.di_atime = _time_to_time64(secs);
897    DIP_SET(curinode, di_atimensec, nsecs);
898    inodirty();
899    printactive(0);
900    return 0;
901}
902
903CMDFUNCSTART(chctime)
904{
905    time_t secs;
906    int32_t nsecs;
907
908    if (dotime(argv[1], &secs, &nsecs))
909	return 1;
910    if (sblock.fs_magic == FS_UFS1_MAGIC)
911	curinode->dp1.di_ctime = _time_to_time32(secs);
912    else
913	curinode->dp2.di_ctime = _time_to_time64(secs);
914    DIP_SET(curinode, di_ctimensec, nsecs);
915    inodirty();
916    printactive(0);
917    return 0;
918}
919