112048Speter/*	$NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $	*/
212048Speter
312048Speter/*
412048Speter *  Copyright (c) 1995 John T. Kohl
512048Speter *  All rights reserved.
612048Speter *
712048Speter *  Redistribution and use in source and binary forms, with or without
812048Speter *  modification, are permitted provided that the following conditions
912048Speter *  are met:
1012048Speter *  1. Redistributions of source code must retain the above copyright
1112048Speter *     notice, this list of conditions and the following disclaimer.
1212048Speter *  2. Redistributions in binary form must reproduce the above copyright
1312048Speter *     notice, this list of conditions and the following disclaimer in the
1412048Speter *     documentation and/or other materials provided with the distribution.
1512048Speter *  3. The name of the author may not be used to endorse or promote products
1612048Speter *     derived from this software without specific prior written permission.
1712048Speter *
1812048Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1912048Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2012048Speter * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2112048Speter * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2212048Speter * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2312048Speter * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2412048Speter * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2512048Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2612048Speter * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2712048Speter * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2812048Speter * POSSIBILITY OF SUCH DAMAGE.
2912048Speter */
3012048Speter
3112048Speter#ifndef lint
3237001Scharnierstatic const char rcsid[] =
3350476Speter  "$FreeBSD$";
3412048Speter#endif /* not lint */
3512048Speter
3612048Speter#include <sys/param.h>
3712048Speter#include <ctype.h>
3837001Scharnier#include <err.h>
3912048Speter#include <grp.h>
4012048Speter#include <histedit.h>
4112048Speter#include <pwd.h>
4212048Speter#include <string.h>
43217769Smckusick#include <time.h>
44122621Sjohan#include <timeconv.h>
4512048Speter
4612048Speter#include <ufs/ufs/dinode.h>
4712048Speter#include <ufs/ufs/dir.h>
4812048Speter#include <ufs/ffs/fs.h>
4912048Speter
5012048Speter#include "fsdb.h"
5112048Speter#include "fsck.h"
5212048Speter
5392881Simpstatic void usage(void) __dead2;
5492881Simpint cmdloop(void);
55159169Smaximstatic int compare_blk32(uint32_t *wantedblk, uint32_t curblk);
56159169Smaximstatic int compare_blk64(uint64_t *wantedblk, uint64_t curblk);
57159169Smaximstatic int founddatablk(uint64_t blk);
58159169Smaximstatic int find_blks32(uint32_t *buf, int size, uint32_t *blknum);
59159169Smaximstatic int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
60159169Smaximstatic int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum);
61159169Smaximstatic int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum);
6212048Speter
6326557Scharnierstatic void
6492881Simpusage(void)
6512048Speter{
6626557Scharnier	fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
6726557Scharnier	exit(1);
6812048Speter}
6912048Speter
7089826Sjoergint returntosingle;
7189826Sjoergchar nflag;
7212048Speter
7312048Speter/*
7412048Speter * We suck in lots of fsck code, and just pick & choose the stuff we want.
7512048Speter *
76102231Strhodes * fsreadfd is set up to read from the file system, fswritefd to write to
77102231Strhodes * the file system.
7812048Speter */
7946080Simpint
8092881Simpmain(int argc, char *argv[])
8112048Speter{
8212048Speter	int ch, rval;
8312048Speter	char *fsys = NULL;
8412048Speter
8526557Scharnier	while (-1 != (ch = getopt(argc, argv, "fdr"))) {
8612048Speter		switch (ch) {
8712048Speter		case 'f':
8826557Scharnier			/* The -f option is left for historical
8926557Scharnier			 * reasons and has no meaning.
9026557Scharnier			 */
9112048Speter			break;
9212048Speter		case 'd':
9312048Speter			debug++;
9412048Speter			break;
9524956Sjoerg		case 'r':
9624956Sjoerg			nflag++; /* "no" in fsck, readonly for us */
9724956Sjoerg			break;
9812048Speter		default:
9912048Speter			usage();
10012048Speter		}
10112048Speter	}
10218585Sguido	argc -= optind;
10318585Sguido	argv += optind;
10426557Scharnier	if (argc != 1)
10526557Scharnier		usage();
10626557Scharnier	else
10726557Scharnier		fsys = argv[0];
10818585Sguido
10975884Siedowse	sblock_init();
11012048Speter	if (!setup(fsys))
111102231Strhodes		errx(1, "cannot set up file system `%s'", fsys);
112102231Strhodes	printf("%s file system `%s'\nLast Mounted on %s\n",
11324956Sjoerg	       nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
11412048Speter	rval = cmdloop();
11524956Sjoerg	if (!nflag) {
11624956Sjoerg		sblock.fs_clean = 0;	/* mark it dirty */
11724956Sjoerg		sbdirty();
11824956Sjoerg		ckfini(0);
11924956Sjoerg		printf("*** FILE SYSTEM MARKED DIRTY\n");
12024956Sjoerg		printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
12124956Sjoerg		printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
12224956Sjoerg	}
12312048Speter	exit(rval);
12412048Speter}
12512048Speter
12692881Simp#define CMDFUNC(func) int func(int argc, char *argv[])
12792881Simp#define CMDFUNCSTART(func) int func(int argc, char *argv[])
12812048Speter
12912048SpeterCMDFUNC(helpfn);
13012048SpeterCMDFUNC(focus);				/* focus on inode */
13112048SpeterCMDFUNC(active);			/* print active inode */
13289827SjoergCMDFUNC(blocks);			/* print blocks for active inode */
13312048SpeterCMDFUNC(focusname);			/* focus by name */
13412048SpeterCMDFUNC(zapi);				/* clear inode */
13512048SpeterCMDFUNC(uplink);			/* incr link */
13612048SpeterCMDFUNC(downlink);			/* decr link */
13712048SpeterCMDFUNC(linkcount);			/* set link count */
13812048SpeterCMDFUNC(quit);				/* quit */
139159169SmaximCMDFUNC(findblk);			/* find block */
14012048SpeterCMDFUNC(ls);				/* list directory */
14112048SpeterCMDFUNC(rm);				/* remove name */
14212048SpeterCMDFUNC(ln);				/* add name */
14312048SpeterCMDFUNC(newtype);			/* change type */
14412048SpeterCMDFUNC(chmode);			/* change mode */
14518498SguidoCMDFUNC(chlen);				/* change length */
14612048SpeterCMDFUNC(chaflags);			/* change flags */
14712048SpeterCMDFUNC(chgen);				/* change generation */
14812048SpeterCMDFUNC(chowner);			/* change owner */
14912048SpeterCMDFUNC(chgroup);			/* Change group */
15012048SpeterCMDFUNC(back);				/* pop back to last ino */
151161558SceriCMDFUNC(chbtime);			/* Change btime */
15212048SpeterCMDFUNC(chmtime);			/* Change mtime */
15312048SpeterCMDFUNC(chctime);			/* Change ctime */
15412048SpeterCMDFUNC(chatime);			/* Change atime */
15512048SpeterCMDFUNC(chinum);			/* Change inode # of dirent */
15612048SpeterCMDFUNC(chname);			/* Change dirname of dirent */
15712048Speter
15812048Speterstruct cmdtable cmds[] = {
15924956Sjoerg	{ "help", "Print out help", 1, 1, FL_RO, helpfn },
16024956Sjoerg	{ "?", "Print out help", 1, 1, FL_RO, helpfn },
16124956Sjoerg	{ "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
16224956Sjoerg	{ "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
163157950Smaxim	{ "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
164157950Smaxim	{ "cd", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
16524956Sjoerg	{ "back", "Go to previous active inode", 1, 1, FL_RO, back },
16624956Sjoerg	{ "active", "Print active inode", 1, 1, FL_RO, active },
16724956Sjoerg	{ "print", "Print active inode", 1, 1, FL_RO, active },
16889827Sjoerg	{ "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
16924956Sjoerg	{ "uplink", "Increment link count", 1, 1, FL_WR, uplink },
17024956Sjoerg	{ "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
17124956Sjoerg	{ "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
172159169Smaxim	{ "findblk", "Find inode owning disk block(s)", 2, 33, FL_RO, findblk},
17324956Sjoerg	{ "ls", "List current inode as directory", 1, 1, FL_RO, ls },
174157950Smaxim	{ "rm", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
175157950Smaxim	{ "del", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
176157950Smaxim	{ "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR | FL_ST, ln },
17724956Sjoerg	{ "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
178157950Smaxim	{ "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR | FL_ST, chname },
17924956Sjoerg	{ "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
18024956Sjoerg	{ "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
18124956Sjoerg	{ "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
18224956Sjoerg	{ "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
18324956Sjoerg	{ "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
18424956Sjoerg	{ "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
18524956Sjoerg	{ "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
186161558Sceri	{ "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime },
18724956Sjoerg	{ "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
18824956Sjoerg	{ "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
18924956Sjoerg	{ "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
19024956Sjoerg	{ "quit", "Exit", 1, 1, FL_RO, quit },
19124956Sjoerg	{ "q", "Exit", 1, 1, FL_RO, quit },
19224956Sjoerg	{ "exit", "Exit", 1, 1, FL_RO, quit },
193136322Sle	{ NULL, 0, 0, 0, 0, NULL },
19412048Speter};
19512048Speter
19612048Speterint
19792881Simphelpfn(int argc, char *argv[])
19812048Speter{
19992806Sobrien    struct cmdtable *cmdtp;
20012048Speter
20112048Speter    printf("Commands are:\n%-10s %5s %5s   %s\n",
202157950Smaxim	   "command", "min args", "max args", "what");
20312048Speter
20412048Speter    for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
20512048Speter	printf("%-10s %5u %5u   %s\n",
206157950Smaxim		cmdtp->cmd, cmdtp->minargc-1, cmdtp->maxargc-1, cmdtp->helptxt);
20712048Speter    return 0;
20812048Speter}
20912048Speter
21012048Speterchar *
21192881Simpprompt(EditLine *el)
21212048Speter{
21312048Speter    static char pstring[64];
21412048Speter    snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
21512048Speter    return pstring;
21612048Speter}
21712048Speter
21812048Speter
21912048Speterint
22092881Simpcmdloop(void)
22112048Speter{
22212048Speter    char *line;
22312048Speter    const char *elline;
22412048Speter    int cmd_argc, rval = 0, known;
22512048Speter#define scratch known
22612048Speter    char **cmd_argv;
22712048Speter    struct cmdtable *cmdp;
22812048Speter    History *hist;
22912048Speter    EditLine *elptr;
23084261Sobrien    HistEvent he;
23112048Speter
23212048Speter    curinode = ginode(ROOTINO);
23312048Speter    curinum = ROOTINO;
23489827Sjoerg    printactive(0);
23512048Speter
23612048Speter    hist = history_init();
237151471Sstefanf    history(hist, &he, H_SETSIZE, 100);	/* 100 elt history buffer */
23812048Speter
23984261Sobrien    elptr = el_init("fsdb", stdin, stdout, stderr);
24012048Speter    el_set(elptr, EL_EDITOR, "emacs");
24112048Speter    el_set(elptr, EL_PROMPT, prompt);
24212048Speter    el_set(elptr, EL_HIST, history, hist);
24312048Speter    el_source(elptr, NULL);
24412048Speter
24512048Speter    while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
24612048Speter	if (debug)
24737001Scharnier	    printf("command `%s'\n", elline);
24812048Speter
24984261Sobrien	history(hist, &he, H_ENTER, elline);
25012048Speter
25112048Speter	line = strdup(elline);
25212048Speter	cmd_argv = crack(line, &cmd_argc);
25312048Speter	/*
25412048Speter	 * el_parse returns -1 to signal that it's not been handled
25512048Speter	 * internally.
25612048Speter	 */
257148833Sstefanf	if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1)
25812048Speter	    continue;
25912048Speter	if (cmd_argc) {
26012048Speter	    known = 0;
26112048Speter	    for (cmdp = cmds; cmdp->cmd; cmdp++) {
26212048Speter		if (!strcmp(cmdp->cmd, cmd_argv[0])) {
26324956Sjoerg		    if ((cmdp->flags & FL_WR) == FL_WR && nflag)
26424956Sjoerg			warnx("`%s' requires write access", cmd_argv[0]),
26524956Sjoerg			    rval = 1;
26624956Sjoerg		    else if (cmd_argc >= cmdp->minargc &&
26712048Speter			cmd_argc <= cmdp->maxargc)
26812048Speter			rval = (*cmdp->handler)(cmd_argc, cmd_argv);
269157950Smaxim		    else if (cmd_argc >= cmdp->minargc &&
270157950Smaxim			(cmdp->flags & FL_ST) == FL_ST) {
27189791Sgreen			strcpy(line, elline);
27289791Sgreen			cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
27389791Sgreen			rval = (*cmdp->handler)(cmd_argc, cmd_argv);
27489791Sgreen		    } else
27512048Speter			rval = argcount(cmdp, cmd_argc, cmd_argv);
27612048Speter		    known = 1;
27712048Speter		    break;
27812048Speter		}
27912048Speter	    }
28012048Speter	    if (!known)
28112048Speter		warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
28212048Speter	} else
28312048Speter	    rval = 0;
28412048Speter	free(line);
28512048Speter	if (rval < 0)
28689810Sjoerg	    /* user typed "quit" */
28789810Sjoerg	    return 0;
28812048Speter	if (rval)
28912048Speter	    warnx("rval was %d", rval);
29012048Speter    }
29112048Speter    el_end(elptr);
29212048Speter    history_end(hist);
29312048Speter    return rval;
29412048Speter}
29512048Speter
29698542Smckusickunion dinode *curinode;
29712048Speterino_t curinum, ocurrent;
29812048Speter
29912048Speter#define GETINUM(ac,inum)    inum = strtoul(argv[ac], &cp, 0); \
30012048Speter    if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
30112048Speter	printf("inode %d out of range; range is [%d,%d]\n", \
30212048Speter	       inum, ROOTINO, maxino); \
30312048Speter	return 1; \
30412048Speter    }
30512048Speter
30612048Speter/*
30712048Speter * Focus on given inode number
30812048Speter */
30912048SpeterCMDFUNCSTART(focus)
31012048Speter{
31112048Speter    ino_t inum;
31212048Speter    char *cp;
31312048Speter
31412048Speter    GETINUM(1,inum);
31512048Speter    curinode = ginode(inum);
31612048Speter    ocurrent = curinum;
31712048Speter    curinum = inum;
31889827Sjoerg    printactive(0);
31912048Speter    return 0;
32012048Speter}
32112048Speter
32212048SpeterCMDFUNCSTART(back)
32312048Speter{
32412048Speter    curinum = ocurrent;
32512048Speter    curinode = ginode(curinum);
32689827Sjoerg    printactive(0);
32712048Speter    return 0;
32812048Speter}
32912048Speter
33012048SpeterCMDFUNCSTART(zapi)
33112048Speter{
33212048Speter    ino_t inum;
33398542Smckusick    union dinode *dp;
33412048Speter    char *cp;
33512048Speter
33612048Speter    GETINUM(1,inum);
33712048Speter    dp = ginode(inum);
33812048Speter    clearinode(dp);
33912048Speter    inodirty();
34012048Speter    if (curinode)			/* re-set after potential change */
34112048Speter	curinode = ginode(curinum);
34212048Speter    return 0;
34312048Speter}
34412048Speter
34512048SpeterCMDFUNCSTART(active)
34612048Speter{
34789827Sjoerg    printactive(0);
34812048Speter    return 0;
34912048Speter}
35012048Speter
35189827SjoergCMDFUNCSTART(blocks)
35289827Sjoerg{
35389827Sjoerg    printactive(1);
35489827Sjoerg    return 0;
35589827Sjoerg}
35612048Speter
35712048SpeterCMDFUNCSTART(quit)
35812048Speter{
35912048Speter    return -1;
36012048Speter}
36112048Speter
36212048SpeterCMDFUNCSTART(uplink)
36312048Speter{
36412048Speter    if (!checkactive())
36512048Speter	return 1;
366136322Sle    DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
36798542Smckusick    printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
36812048Speter    inodirty();
36912048Speter    return 0;
37012048Speter}
37112048Speter
37212048SpeterCMDFUNCSTART(downlink)
37312048Speter{
37412048Speter    if (!checkactive())
37512048Speter	return 1;
376136322Sle    DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
37798542Smckusick    printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
37812048Speter    inodirty();
37912048Speter    return 0;
38012048Speter}
38112048Speter
38212048Speterconst char *typename[] = {
38312048Speter    "unknown",
38412048Speter    "fifo",
38512048Speter    "char special",
38612048Speter    "unregistered #3",
38712048Speter    "directory",
38812048Speter    "unregistered #5",
38912048Speter    "blk special",
39012048Speter    "unregistered #7",
39112048Speter    "regular",
39212048Speter    "unregistered #9",
39312048Speter    "symlink",
39412048Speter    "unregistered #11",
39512048Speter    "socket",
39612048Speter    "unregistered #13",
39712048Speter    "whiteout",
39812048Speter};
399207141Sjeff
400207141Sjeffint diroff;
40112048Speterint slot;
40212048Speter
40312048Speterint
40492881Simpscannames(struct inodesc *idesc)
40512048Speter{
40692806Sobrien	struct direct *dirp = idesc->id_dirp;
40712048Speter
408207141Sjeff	printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n",
409207141Sjeff	       slot++, diroff, dirp->d_ino, dirp->d_reclen,
410207141Sjeff	       typename[dirp->d_type], dirp->d_namlen, dirp->d_name);
411207141Sjeff	diroff += dirp->d_reclen;
41212048Speter	return (KEEPON);
41312048Speter}
41412048Speter
41512048SpeterCMDFUNCSTART(ls)
41612048Speter{
41712048Speter    struct inodesc idesc;
41812048Speter    checkactivedir();			/* let it go on anyway */
41912048Speter
42012048Speter    slot = 0;
421207141Sjeff    diroff = 0;
42212048Speter    idesc.id_number = curinum;
42312048Speter    idesc.id_func = scannames;
42412048Speter    idesc.id_type = DATA;
42512048Speter    idesc.id_fix = IGNORE;
42612048Speter    ckinode(curinode, &idesc);
42712048Speter    curinode = ginode(curinum);
42812048Speter
42912048Speter    return 0;
43012048Speter}
43112048Speter
432159169Smaximstatic int findblk_numtofind;
433159169Smaximstatic int wantedblksize;
434159169Smaxim
435159169SmaximCMDFUNCSTART(findblk)
436159169Smaxim{
437159169Smaxim    ino_t inum, inosused;
438159169Smaxim    uint32_t *wantedblk32;
439159169Smaxim    uint64_t *wantedblk64;
440249788Smckusick    struct bufarea *cgbp;
441249788Smckusick    struct cg *cgp;
442159169Smaxim    int c, i, is_ufs2;
443159169Smaxim
444159169Smaxim    wantedblksize = (argc - 1);
445159169Smaxim    is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC;
446159169Smaxim    ocurrent = curinum;
447159169Smaxim
448159169Smaxim    if (is_ufs2) {
449159169Smaxim	wantedblk64 = calloc(wantedblksize, sizeof(uint64_t));
450159169Smaxim	if (wantedblk64 == NULL)
451159169Smaxim	    err(1, "malloc");
452159169Smaxim	for (i = 1; i < argc; i++)
453159169Smaxim	    wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
454159169Smaxim    } else {
455159169Smaxim	wantedblk32 = calloc(wantedblksize, sizeof(uint32_t));
456159169Smaxim	if (wantedblk32 == NULL)
457159169Smaxim	    err(1, "malloc");
458159169Smaxim	for (i = 1; i < argc; i++)
459159169Smaxim	    wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
460159169Smaxim    }
461159169Smaxim    findblk_numtofind = wantedblksize;
462159169Smaxim    /*
463159169Smaxim     * sblock.fs_ncg holds a number of cylinder groups.
464159169Smaxim     * Iterate over all cylinder groups.
465159169Smaxim     */
466159169Smaxim    for (c = 0; c < sblock.fs_ncg; c++) {
467159169Smaxim	/*
468159169Smaxim	 * sblock.fs_ipg holds a number of inodes per cylinder group.
469159169Smaxim	 * Calculate a highest inode number for a given cylinder group.
470159169Smaxim	 */
471159169Smaxim	inum = c * sblock.fs_ipg;
472159169Smaxim	/* Read cylinder group. */
473249788Smckusick	cgbp = cgget(c);
474249788Smckusick	cgp = cgbp->b_un.b_cg;
475159169Smaxim	/*
476159169Smaxim	 * Get a highest used inode number for a given cylinder group.
477159169Smaxim	 * For UFS1 all inodes initialized at the newfs stage.
478159169Smaxim	 */
479159169Smaxim	if (is_ufs2)
480159169Smaxim	    inosused = cgp->cg_initediblk;
481159169Smaxim	else
482159169Smaxim	    inosused = sblock.fs_ipg;
483159169Smaxim
484159169Smaxim	for (; inosused > 0; inum++, inosused--) {
485159169Smaxim	    /* Skip magic inodes: 0, WINO, ROOTINO. */
486159169Smaxim	    if (inum < ROOTINO)
487159169Smaxim		continue;
488159169Smaxim	    /*
489159169Smaxim	     * Check if the block we are looking for is just an inode block.
490159169Smaxim	     *
491159169Smaxim	     * ino_to_fsba() - get block containing inode from its number.
492159169Smaxim	     * INOPB() - get a number of inodes in one disk block.
493159169Smaxim	     */
494159169Smaxim	    if (is_ufs2 ?
495159169Smaxim		compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) :
496159169Smaxim		compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) {
497159169Smaxim		printf("block %llu: inode block (%d-%d)\n",
498159169Smaxim		    (unsigned long long)fsbtodb(&sblock,
499159169Smaxim			ino_to_fsba(&sblock, inum)),
500159169Smaxim		    (inum / INOPB(&sblock)) * INOPB(&sblock),
501159169Smaxim		    (inum / INOPB(&sblock) + 1) * INOPB(&sblock));
502159169Smaxim		findblk_numtofind--;
503159169Smaxim		if (findblk_numtofind == 0)
504159169Smaxim		    goto end;
505159169Smaxim	    }
506159169Smaxim	    /* Get on-disk inode aka dinode. */
507159169Smaxim	    curinum = inum;
508159169Smaxim	    curinode = ginode(inum);
509159169Smaxim	    /* Find IFLNK dinode with allocated data blocks. */
510159169Smaxim	    switch (DIP(curinode, di_mode) & IFMT) {
511159169Smaxim	    case IFDIR:
512159169Smaxim	    case IFREG:
513159169Smaxim		if (DIP(curinode, di_blocks) == 0)
514159169Smaxim		    continue;
515159169Smaxim		break;
516159169Smaxim	    case IFLNK:
517159169Smaxim		{
518159169Smaxim		    uint64_t size = DIP(curinode, di_size);
519159169Smaxim		    if (size > 0 && size < sblock.fs_maxsymlinklen &&
520159169Smaxim			DIP(curinode, di_blocks) == 0)
521159169Smaxim			continue;
522159169Smaxim		    else
523159169Smaxim			break;
524159169Smaxim		}
525159169Smaxim	    default:
526159169Smaxim		continue;
527159169Smaxim	    }
528159169Smaxim	    /* Look through direct data blocks. */
529159169Smaxim	    if (is_ufs2 ?
530159169Smaxim		find_blks64(curinode->dp2.di_db, NDADDR, wantedblk64) :
531159169Smaxim		find_blks32(curinode->dp1.di_db, NDADDR, wantedblk32))
532159169Smaxim		goto end;
533159169Smaxim	    for (i = 0; i < NIADDR; i++) {
534159169Smaxim		/*
535159169Smaxim		 * Does the block we are looking for belongs to the
536159169Smaxim		 * indirect blocks?
537159169Smaxim		 */
538159169Smaxim		if (is_ufs2 ?
539159169Smaxim		    compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) :
540159169Smaxim		    compare_blk32(wantedblk32, curinode->dp1.di_ib[i]))
541159169Smaxim		    if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] :
542159169Smaxim			curinode->dp1.di_ib[i]))
543159169Smaxim			goto end;
544159169Smaxim		/*
545159169Smaxim		 * Search through indirect, double and triple indirect
546159169Smaxim		 * data blocks.
547159169Smaxim		 */
548159169Smaxim		if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) :
549159169Smaxim		    (curinode->dp1.di_ib[i] != 0))
550159169Smaxim		    if (is_ufs2 ?
551159169Smaxim			find_indirblks64(curinode->dp2.di_ib[i], i,
552159169Smaxim			    wantedblk64) :
553159169Smaxim			find_indirblks32(curinode->dp1.di_ib[i], i,
554159169Smaxim			    wantedblk32))
555159169Smaxim			goto end;
556159169Smaxim	    }
557159169Smaxim	}
558159169Smaxim    }
559159169Smaximend:
560159169Smaxim    curinum = ocurrent;
561159169Smaxim    curinode = ginode(curinum);
562159169Smaxim    return 0;
563159169Smaxim}
564159169Smaxim
565159169Smaximstatic int
566159169Smaximcompare_blk32(uint32_t *wantedblk, uint32_t curblk)
567159169Smaxim{
568159169Smaxim    int i;
569159169Smaxim
570159169Smaxim    for (i = 0; i < wantedblksize; i++) {
571159169Smaxim	if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
572159169Smaxim	    wantedblk[i] = 0;
573159169Smaxim	    return 1;
574159169Smaxim	}
575159169Smaxim    }
576159169Smaxim    return 0;
577159169Smaxim}
578159169Smaxim
579159169Smaximstatic int
580159169Smaximcompare_blk64(uint64_t *wantedblk, uint64_t curblk)
581159169Smaxim{
582159169Smaxim    int i;
583159169Smaxim
584159169Smaxim    for (i = 0; i < wantedblksize; i++) {
585159169Smaxim	if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
586159169Smaxim	    wantedblk[i] = 0;
587159169Smaxim	    return 1;
588159169Smaxim	}
589159169Smaxim    }
590159169Smaxim    return 0;
591159169Smaxim}
592159169Smaxim
593159169Smaximstatic int
594159169Smaximfounddatablk(uint64_t blk)
595159169Smaxim{
596159169Smaxim
597159169Smaxim    printf("%llu: data block of inode %d\n",
598159169Smaxim	(unsigned long long)fsbtodb(&sblock, blk), curinum);
599159169Smaxim    findblk_numtofind--;
600159169Smaxim    if (findblk_numtofind == 0)
601159169Smaxim	return 1;
602159169Smaxim    return 0;
603159169Smaxim}
604159169Smaxim
605159169Smaximstatic int
606159169Smaximfind_blks32(uint32_t *buf, int size, uint32_t *wantedblk)
607159169Smaxim{
608159169Smaxim    int blk;
609159169Smaxim    for (blk = 0; blk < size; blk++) {
610159169Smaxim	if (buf[blk] == 0)
611159169Smaxim	    continue;
612159169Smaxim	if (compare_blk32(wantedblk, buf[blk])) {
613159169Smaxim	    if (founddatablk(buf[blk]))
614159169Smaxim		return 1;
615159169Smaxim	}
616159169Smaxim    }
617159169Smaxim    return 0;
618159169Smaxim}
619159169Smaxim
620159169Smaximstatic int
621159169Smaximfind_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk)
622159169Smaxim{
623159169Smaxim#define MAXNINDIR      (MAXBSIZE / sizeof(uint32_t))
624159169Smaxim    uint32_t idblk[MAXNINDIR];
625159169Smaxim    int i;
626159169Smaxim
627163846Spjd    blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
628159169Smaxim    if (ind_level <= 0) {
629159169Smaxim	if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk))
630159169Smaxim	    return 1;
631159169Smaxim    } else {
632159169Smaxim	ind_level--;
633159169Smaxim	for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) {
634159169Smaxim	    if (compare_blk32(wantedblk, idblk[i])) {
635159169Smaxim		if (founddatablk(idblk[i]))
636159169Smaxim		    return 1;
637159169Smaxim	    }
638159169Smaxim	    if (idblk[i] != 0)
639159169Smaxim		if (find_indirblks32(idblk[i], ind_level, wantedblk))
640159169Smaxim		    return 1;
641159169Smaxim	}
642159169Smaxim    }
643159169Smaxim#undef MAXNINDIR
644159169Smaxim    return 0;
645159169Smaxim}
646159169Smaxim
647159169Smaximstatic int
648159169Smaximfind_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
649159169Smaxim{
650159169Smaxim    int blk;
651159169Smaxim    for (blk = 0; blk < size; blk++) {
652159169Smaxim	if (buf[blk] == 0)
653159169Smaxim	    continue;
654159169Smaxim	if (compare_blk64(wantedblk, buf[blk])) {
655159169Smaxim	    if (founddatablk(buf[blk]))
656159169Smaxim		return 1;
657159169Smaxim	}
658159169Smaxim    }
659159169Smaxim    return 0;
660159169Smaxim}
661159169Smaxim
662159169Smaximstatic int
663159169Smaximfind_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
664159169Smaxim{
665159169Smaxim#define MAXNINDIR      (MAXBSIZE / sizeof(uint64_t))
666159169Smaxim    uint64_t idblk[MAXNINDIR];
667159169Smaxim    int i;
668159169Smaxim
669163846Spjd    blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
670159169Smaxim    if (ind_level <= 0) {
671159169Smaxim	if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk))
672159169Smaxim	    return 1;
673159169Smaxim    } else {
674159169Smaxim	ind_level--;
675159169Smaxim	for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) {
676159169Smaxim	    if (compare_blk64(wantedblk, idblk[i])) {
677159169Smaxim		if (founddatablk(idblk[i]))
678159169Smaxim		    return 1;
679159169Smaxim	    }
680159169Smaxim	    if (idblk[i] != 0)
681159169Smaxim		if (find_indirblks64(idblk[i], ind_level, wantedblk))
682159169Smaxim		    return 1;
683159169Smaxim	}
684159169Smaxim    }
685159169Smaxim#undef MAXNINDIR
686159169Smaxim    return 0;
687159169Smaxim}
688159169Smaxim
68992881Simpint findino(struct inodesc *idesc); /* from fsck */
69092881Simpstatic int dolookup(char *name);
69112048Speter
69212048Speterstatic int
69392881Simpdolookup(char *name)
69412048Speter{
69512048Speter    struct inodesc idesc;
69612048Speter
69712048Speter    if (!checkactivedir())
69812048Speter	    return 0;
69912048Speter    idesc.id_number = curinum;
70012048Speter    idesc.id_func = findino;
70112048Speter    idesc.id_name = name;
70212048Speter    idesc.id_type = DATA;
70312048Speter    idesc.id_fix = IGNORE;
70412048Speter    if (ckinode(curinode, &idesc) & FOUND) {
70512048Speter	curinum = idesc.id_parent;
70612048Speter	curinode = ginode(curinum);
70789827Sjoerg	printactive(0);
70812048Speter	return 1;
70912048Speter    } else {
71012048Speter	warnx("name `%s' not found in current inode directory", name);
71112048Speter	return 0;
71212048Speter    }
71312048Speter}
71412048Speter
71512048SpeterCMDFUNCSTART(focusname)
71612048Speter{
71712048Speter    char *p, *val;
71812048Speter
71912048Speter    if (!checkactive())
72012048Speter	return 1;
72112048Speter
72212048Speter    ocurrent = curinum;
72312048Speter
72412048Speter    if (argv[1][0] == '/') {
72512048Speter	curinum = ROOTINO;
72612048Speter	curinode = ginode(ROOTINO);
72712048Speter    } else {
72812048Speter	if (!checkactivedir())
72912048Speter	    return 1;
73012048Speter    }
73112048Speter    for (p = argv[1]; p != NULL;) {
73212048Speter	while ((val = strsep(&p, "/")) != NULL && *val == '\0');
73312048Speter	if (val) {
73412048Speter	    printf("component `%s': ", val);
73512048Speter	    fflush(stdout);
73612048Speter	    if (!dolookup(val)) {
73712048Speter		curinode = ginode(curinum);
73812048Speter		return(1);
73912048Speter	    }
74012048Speter	}
74112048Speter    }
74212048Speter    return 0;
74312048Speter}
74412048Speter
74512048SpeterCMDFUNCSTART(ln)
74612048Speter{
74712048Speter    ino_t inum;
74812048Speter    int rval;
74912048Speter    char *cp;
75012048Speter
75112048Speter    GETINUM(1,inum);
75212048Speter
75312048Speter    if (!checkactivedir())
75412048Speter	return 1;
75512048Speter    rval = makeentry(curinum, inum, argv[2]);
75612048Speter    if (rval)
75712048Speter	printf("Ino %d entered as `%s'\n", inum, argv[2]);
75812048Speter    else
75912048Speter	printf("could not enter name? weird.\n");
76012048Speter    curinode = ginode(curinum);
76112048Speter    return rval;
76212048Speter}
76312048Speter
76412048SpeterCMDFUNCSTART(rm)
76512048Speter{
76612048Speter    int rval;
76712048Speter
76812048Speter    if (!checkactivedir())
76912048Speter	return 1;
77012048Speter    rval = changeino(curinum, argv[1], 0);
77112048Speter    if (rval & ALTERED) {
77212048Speter	printf("Name `%s' removed\n", argv[1]);
77312048Speter	return 0;
77412048Speter    } else {
77589791Sgreen	printf("could not remove name ('%s')? weird.\n", argv[1]);
77612048Speter	return 1;
77712048Speter    }
77812048Speter}
77912048Speter
78012048Speterlong slotcount, desired;
78112048Speter
78212048Speterint
78392881Simpchinumfunc(struct inodesc *idesc)
78412048Speter{
78592806Sobrien	struct direct *dirp = idesc->id_dirp;
78612048Speter
78712048Speter	if (slotcount++ == desired) {
78812048Speter	    dirp->d_ino = idesc->id_parent;
78912048Speter	    return STOP|ALTERED|FOUND;
79012048Speter	}
79112048Speter	return KEEPON;
79212048Speter}
79312048Speter
79412048SpeterCMDFUNCSTART(chinum)
79512048Speter{
79612048Speter    char *cp;
79712048Speter    ino_t inum;
79812048Speter    struct inodesc idesc;
79912048Speter
80012048Speter    slotcount = 0;
80112048Speter    if (!checkactivedir())
80212048Speter	return 1;
80312048Speter    GETINUM(2,inum);
80412048Speter
80512048Speter    desired = strtol(argv[1], &cp, 0);
80612048Speter    if (cp == argv[1] || *cp != '\0' || desired < 0) {
80712048Speter	printf("invalid slot number `%s'\n", argv[1]);
80812048Speter	return 1;
80912048Speter    }
81012048Speter
81112048Speter    idesc.id_number = curinum;
81212048Speter    idesc.id_func = chinumfunc;
81312048Speter    idesc.id_fix = IGNORE;
81412048Speter    idesc.id_type = DATA;
81512048Speter    idesc.id_parent = inum;		/* XXX convenient hiding place */
81612048Speter
81712048Speter    if (ckinode(curinode, &idesc) & FOUND)
81812048Speter	return 0;
81912048Speter    else {
82012048Speter	warnx("no %sth slot in current directory", argv[1]);
82112048Speter	return 1;
82212048Speter    }
82312048Speter}
82412048Speter
82512048Speterint
82692881Simpchnamefunc(struct inodesc *idesc)
82712048Speter{
82892806Sobrien	struct direct *dirp = idesc->id_dirp;
82912048Speter	struct direct testdir;
83012048Speter
83112048Speter	if (slotcount++ == desired) {
83212048Speter	    /* will name fit? */
83312048Speter	    testdir.d_namlen = strlen(idesc->id_name);
83412048Speter	    if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
83512048Speter		dirp->d_namlen = testdir.d_namlen;
83612048Speter		strcpy(dirp->d_name, idesc->id_name);
83712048Speter		return STOP|ALTERED|FOUND;
83812048Speter	    } else
83912048Speter		return STOP|FOUND;	/* won't fit, so give up */
84012048Speter	}
84112048Speter	return KEEPON;
84212048Speter}
84312048Speter
84412048SpeterCMDFUNCSTART(chname)
84512048Speter{
84612048Speter    int rval;
84712048Speter    char *cp;
84812048Speter    struct inodesc idesc;
84912048Speter
85012048Speter    slotcount = 0;
85112048Speter    if (!checkactivedir())
85212048Speter	return 1;
85312048Speter
85412048Speter    desired = strtoul(argv[1], &cp, 0);
85512048Speter    if (cp == argv[1] || *cp != '\0') {
85612048Speter	printf("invalid slot number `%s'\n", argv[1]);
85712048Speter	return 1;
85812048Speter    }
85912048Speter
86012048Speter    idesc.id_number = curinum;
86112048Speter    idesc.id_func = chnamefunc;
86212048Speter    idesc.id_fix = IGNORE;
86312048Speter    idesc.id_type = DATA;
86412048Speter    idesc.id_name = argv[2];
86512048Speter
86612048Speter    rval = ckinode(curinode, &idesc);
86712048Speter    if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
86812048Speter	return 0;
86912048Speter    else if (rval & FOUND) {
87012048Speter	warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
87112048Speter	return 1;
87212048Speter    } else {
87312048Speter	warnx("no %sth slot in current directory", argv[1]);
87412048Speter	return 1;
87512048Speter    }
87612048Speter}
87712048Speter
87812048Speterstruct typemap {
87912048Speter    const char *typename;
88012048Speter    int typebits;
88112048Speter} typenamemap[]  = {
88212048Speter    {"file", IFREG},
88312048Speter    {"dir", IFDIR},
88412048Speter    {"socket", IFSOCK},
88512048Speter    {"fifo", IFIFO},
88612048Speter};
88712048Speter
88812048SpeterCMDFUNCSTART(newtype)
88912048Speter{
89012048Speter    int type;
89112048Speter    struct typemap *tp;
89212048Speter
89312048Speter    if (!checkactive())
89412048Speter	return 1;
89598542Smckusick    type = DIP(curinode, di_mode) & IFMT;
89612048Speter    for (tp = typenamemap;
89741023Struckman	 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
89812048Speter	 tp++) {
89912048Speter	if (!strcmp(argv[1], tp->typename)) {
90012048Speter	    printf("setting type to %s\n", tp->typename);
90112048Speter	    type = tp->typebits;
90212048Speter	    break;
90312048Speter	}
90412048Speter    }
90541023Struckman    if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
90612048Speter	warnx("type `%s' not known", argv[1]);
90712048Speter	warnx("try one of `file', `dir', `socket', `fifo'");
90812048Speter	return 1;
90912048Speter    }
910136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
911136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
91212048Speter    inodirty();
91389827Sjoerg    printactive(0);
91412048Speter    return 0;
91512048Speter}
91612048Speter
91718498SguidoCMDFUNCSTART(chlen)
91818498Sguido{
91918498Sguido    int rval = 1;
92018498Sguido    long len;
92118498Sguido    char *cp;
92218498Sguido
92318498Sguido    if (!checkactive())
92418498Sguido	return 1;
92518498Sguido
92618498Sguido    len = strtol(argv[1], &cp, 0);
92718498Sguido    if (cp == argv[1] || *cp != '\0' || len < 0) {
92818498Sguido	warnx("bad length `%s'", argv[1]);
92918498Sguido	return 1;
93018498Sguido    }
93118498Sguido
932136322Sle    DIP_SET(curinode, di_size, len);
93318498Sguido    inodirty();
93489827Sjoerg    printactive(0);
93518498Sguido    return rval;
93618498Sguido}
93718498Sguido
93812048SpeterCMDFUNCSTART(chmode)
93912048Speter{
94012048Speter    int rval = 1;
94112048Speter    long modebits;
94212048Speter    char *cp;
94312048Speter
94412048Speter    if (!checkactive())
94512048Speter	return 1;
94612048Speter
94712048Speter    modebits = strtol(argv[1], &cp, 8);
94886258Siedowse    if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
94912048Speter	warnx("bad modebits `%s'", argv[1]);
95012048Speter	return 1;
95112048Speter    }
95212048Speter
953136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
954136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
95512048Speter    inodirty();
95689827Sjoerg    printactive(0);
95712048Speter    return rval;
95812048Speter}
95912048Speter
96012048SpeterCMDFUNCSTART(chaflags)
96112048Speter{
96212048Speter    int rval = 1;
96312048Speter    u_long flags;
96412048Speter    char *cp;
96512048Speter
96612048Speter    if (!checkactive())
96712048Speter	return 1;
96812048Speter
96912048Speter    flags = strtoul(argv[1], &cp, 0);
97012048Speter    if (cp == argv[1] || *cp != '\0' ) {
97112048Speter	warnx("bad flags `%s'", argv[1]);
97212048Speter	return 1;
97312048Speter    }
97412048Speter
97512048Speter    if (flags > UINT_MAX) {
97612048Speter	warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
97712048Speter	return(1);
97812048Speter    }
979136322Sle    DIP_SET(curinode, di_flags, flags);
98012048Speter    inodirty();
98189827Sjoerg    printactive(0);
98212048Speter    return rval;
98312048Speter}
98412048Speter
98512048SpeterCMDFUNCSTART(chgen)
98612048Speter{
98712048Speter    int rval = 1;
98812048Speter    long gen;
98912048Speter    char *cp;
99012048Speter
99112048Speter    if (!checkactive())
99212048Speter	return 1;
99312048Speter
99412048Speter    gen = strtol(argv[1], &cp, 0);
99512048Speter    if (cp == argv[1] || *cp != '\0' ) {
99612048Speter	warnx("bad gen `%s'", argv[1]);
99712048Speter	return 1;
99812048Speter    }
99912048Speter
100012048Speter    if (gen > INT_MAX || gen < INT_MIN) {
100112048Speter	warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
100212048Speter	return(1);
100312048Speter    }
1004136322Sle    DIP_SET(curinode, di_gen, gen);
100512048Speter    inodirty();
100689827Sjoerg    printactive(0);
100712048Speter    return rval;
100812048Speter}
100912048Speter
101012048SpeterCMDFUNCSTART(linkcount)
101112048Speter{
101212048Speter    int rval = 1;
101312048Speter    int lcnt;
101412048Speter    char *cp;
101512048Speter
101612048Speter    if (!checkactive())
101712048Speter	return 1;
101812048Speter
101912048Speter    lcnt = strtol(argv[1], &cp, 0);
102012048Speter    if (cp == argv[1] || *cp != '\0' ) {
102112048Speter	warnx("bad link count `%s'", argv[1]);
102212048Speter	return 1;
102312048Speter    }
102412048Speter    if (lcnt > USHRT_MAX || lcnt < 0) {
102512048Speter	warnx("max link count is %d\n", USHRT_MAX);
102612048Speter	return 1;
102712048Speter    }
102812048Speter
1029136322Sle    DIP_SET(curinode, di_nlink, lcnt);
103012048Speter    inodirty();
103189827Sjoerg    printactive(0);
103212048Speter    return rval;
103312048Speter}
103412048Speter
103512048SpeterCMDFUNCSTART(chowner)
103612048Speter{
103712048Speter    int rval = 1;
103812048Speter    unsigned long uid;
103912048Speter    char *cp;
104012048Speter    struct passwd *pwd;
104112048Speter
104212048Speter    if (!checkactive())
104312048Speter	return 1;
104412048Speter
104512048Speter    uid = strtoul(argv[1], &cp, 0);
104612048Speter    if (cp == argv[1] || *cp != '\0' ) {
104712048Speter	/* try looking up name */
104837001Scharnier	if ((pwd = getpwnam(argv[1]))) {
104912048Speter	    uid = pwd->pw_uid;
105012048Speter	} else {
105112048Speter	    warnx("bad uid `%s'", argv[1]);
105212048Speter	    return 1;
105312048Speter	}
105412048Speter    }
105512048Speter
1056136322Sle    DIP_SET(curinode, di_uid, uid);
105712048Speter    inodirty();
105889827Sjoerg    printactive(0);
105912048Speter    return rval;
106012048Speter}
106112048Speter
106212048SpeterCMDFUNCSTART(chgroup)
106312048Speter{
106412048Speter    int rval = 1;
106512048Speter    unsigned long gid;
106612048Speter    char *cp;
106712048Speter    struct group *grp;
106812048Speter
106912048Speter    if (!checkactive())
107012048Speter	return 1;
107112048Speter
107212048Speter    gid = strtoul(argv[1], &cp, 0);
107312048Speter    if (cp == argv[1] || *cp != '\0' ) {
107437001Scharnier	if ((grp = getgrnam(argv[1]))) {
107512048Speter	    gid = grp->gr_gid;
107612048Speter	} else {
107712048Speter	    warnx("bad gid `%s'", argv[1]);
107812048Speter	    return 1;
107912048Speter	}
108012048Speter    }
108112048Speter
1082136322Sle    DIP_SET(curinode, di_gid, gid);
108312048Speter    inodirty();
108489827Sjoerg    printactive(0);
108512048Speter    return rval;
108612048Speter}
108712048Speter
108812048Speterint
108998542Smckusickdotime(char *name, time_t *secp, int32_t *nsecp)
109012048Speter{
109112048Speter    char *p, *val;
109212048Speter    struct tm t;
109312048Speter    int32_t nsec;
109412048Speter    p = strchr(name, '.');
109512048Speter    if (p) {
109612048Speter	*p = '\0';
109712048Speter	nsec = strtoul(++p, &val, 0);
109812048Speter	if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
109912048Speter		warnx("invalid nanoseconds");
110012048Speter		goto badformat;
110112048Speter	}
110212048Speter    } else
110312048Speter	nsec = 0;
110412048Speter    if (strlen(name) != 14) {
110512048Speterbadformat:
110612048Speter	warnx("date format: YYYYMMDDHHMMSS[.nsec]");
110712048Speter	return 1;
110812048Speter    }
110998542Smckusick    *nsecp = nsec;
111012048Speter
111112048Speter    for (p = name; *p; p++)
111212048Speter	if (*p < '0' || *p > '9')
111312048Speter	    goto badformat;
111412048Speter
111512048Speter    p = name;
111612048Speter#define VAL() ((*p++) - '0')
111712048Speter    t.tm_year = VAL();
111812048Speter    t.tm_year = VAL() + t.tm_year * 10;
111912048Speter    t.tm_year = VAL() + t.tm_year * 10;
112012048Speter    t.tm_year = VAL() + t.tm_year * 10 - 1900;
112112048Speter    t.tm_mon = VAL();
112212048Speter    t.tm_mon = VAL() + t.tm_mon * 10 - 1;
112312048Speter    t.tm_mday = VAL();
112412048Speter    t.tm_mday = VAL() + t.tm_mday * 10;
112512048Speter    t.tm_hour = VAL();
112612048Speter    t.tm_hour = VAL() + t.tm_hour * 10;
112712048Speter    t.tm_min = VAL();
112812048Speter    t.tm_min = VAL() + t.tm_min * 10;
112912048Speter    t.tm_sec = VAL();
113012048Speter    t.tm_sec = VAL() + t.tm_sec * 10;
113112048Speter    t.tm_isdst = -1;
113212048Speter
113398542Smckusick    *secp = mktime(&t);
113498542Smckusick    if (*secp == -1) {
113512048Speter	warnx("date/time out of range");
113612048Speter	return 1;
113712048Speter    }
113812048Speter    return 0;
113912048Speter}
114012048Speter
1141161558SceriCMDFUNCSTART(chbtime)
1142161558Sceri{
1143161558Sceri    time_t secs;
1144161558Sceri    int32_t nsecs;
1145161558Sceri
1146161558Sceri    if (dotime(argv[1], &secs, &nsecs))
1147161558Sceri	return 1;
1148161558Sceri    if (sblock.fs_magic == FS_UFS1_MAGIC)
1149161558Sceri	return 1;
1150161558Sceri    curinode->dp2.di_birthtime = _time_to_time64(secs);
1151161558Sceri    curinode->dp2.di_birthnsec = nsecs;
1152161558Sceri    inodirty();
1153161558Sceri    printactive(0);
1154161558Sceri    return 0;
1155161558Sceri}
1156161558Sceri
115712048SpeterCMDFUNCSTART(chmtime)
115812048Speter{
115998542Smckusick    time_t secs;
116098542Smckusick    int32_t nsecs;
116198542Smckusick
116298542Smckusick    if (dotime(argv[1], &secs, &nsecs))
116312048Speter	return 1;
116498542Smckusick    if (sblock.fs_magic == FS_UFS1_MAGIC)
116598542Smckusick	curinode->dp1.di_mtime = _time_to_time32(secs);
116698542Smckusick    else
116798542Smckusick	curinode->dp2.di_mtime = _time_to_time64(secs);
1168136322Sle    DIP_SET(curinode, di_mtimensec, nsecs);
116912048Speter    inodirty();
117089827Sjoerg    printactive(0);
117112048Speter    return 0;
117212048Speter}
117312048Speter
117412048SpeterCMDFUNCSTART(chatime)
117512048Speter{
117698542Smckusick    time_t secs;
117798542Smckusick    int32_t nsecs;
117898542Smckusick
117998542Smckusick    if (dotime(argv[1], &secs, &nsecs))
118012048Speter	return 1;
118198542Smckusick    if (sblock.fs_magic == FS_UFS1_MAGIC)
118298542Smckusick	curinode->dp1.di_atime = _time_to_time32(secs);
118398542Smckusick    else
118498542Smckusick	curinode->dp2.di_atime = _time_to_time64(secs);
1185136322Sle    DIP_SET(curinode, di_atimensec, nsecs);
118612048Speter    inodirty();
118789827Sjoerg    printactive(0);
118812048Speter    return 0;
118912048Speter}
119012048Speter
119112048SpeterCMDFUNCSTART(chctime)
119212048Speter{
119398542Smckusick    time_t secs;
119498542Smckusick    int32_t nsecs;
119598542Smckusick
119698542Smckusick    if (dotime(argv[1], &secs, &nsecs))
119712048Speter	return 1;
119898542Smckusick    if (sblock.fs_magic == FS_UFS1_MAGIC)
119998542Smckusick	curinode->dp1.di_ctime = _time_to_time32(secs);
120098542Smckusick    else
120198542Smckusick	curinode->dp2.di_ctime = _time_to_time64(secs);
1202136322Sle    DIP_SET(curinode, di_ctimensec, nsecs);
120312048Speter    inodirty();
120489827Sjoerg    printactive(0);
120512048Speter    return 0;
120612048Speter}
1207