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: releng/10.2/sbin/fsdb/fsdb.c 248658 2013-03-23 20:00:02Z mckusick $";
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>
42241013Smdf#include <stdint.h>
4312048Speter#include <string.h>
44217769Smckusick#include <time.h>
45122621Sjohan#include <timeconv.h>
4612048Speter
4712048Speter#include <ufs/ufs/dinode.h>
4812048Speter#include <ufs/ufs/dir.h>
4912048Speter#include <ufs/ffs/fs.h>
5012048Speter
5112048Speter#include "fsdb.h"
5212048Speter#include "fsck.h"
5312048Speter
5492881Simpstatic void usage(void) __dead2;
5592881Simpint cmdloop(void);
56159169Smaximstatic int compare_blk32(uint32_t *wantedblk, uint32_t curblk);
57159169Smaximstatic int compare_blk64(uint64_t *wantedblk, uint64_t curblk);
58159169Smaximstatic int founddatablk(uint64_t blk);
59159169Smaximstatic int find_blks32(uint32_t *buf, int size, uint32_t *blknum);
60159169Smaximstatic int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
61159169Smaximstatic int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum);
62159169Smaximstatic int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum);
6312048Speter
6426557Scharnierstatic void
6592881Simpusage(void)
6612048Speter{
6726557Scharnier	fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
6826557Scharnier	exit(1);
6912048Speter}
7012048Speter
7189826Sjoergint returntosingle;
7289826Sjoergchar nflag;
7312048Speter
7412048Speter/*
7512048Speter * We suck in lots of fsck code, and just pick & choose the stuff we want.
7612048Speter *
77102231Strhodes * fsreadfd is set up to read from the file system, fswritefd to write to
78102231Strhodes * the file system.
7912048Speter */
8046080Simpint
8192881Simpmain(int argc, char *argv[])
8212048Speter{
8312048Speter	int ch, rval;
8412048Speter	char *fsys = NULL;
8512048Speter
8626557Scharnier	while (-1 != (ch = getopt(argc, argv, "fdr"))) {
8712048Speter		switch (ch) {
8812048Speter		case 'f':
8926557Scharnier			/* The -f option is left for historical
9026557Scharnier			 * reasons and has no meaning.
9126557Scharnier			 */
9212048Speter			break;
9312048Speter		case 'd':
9412048Speter			debug++;
9512048Speter			break;
9624956Sjoerg		case 'r':
9724956Sjoerg			nflag++; /* "no" in fsck, readonly for us */
9824956Sjoerg			break;
9912048Speter		default:
10012048Speter			usage();
10112048Speter		}
10212048Speter	}
10318585Sguido	argc -= optind;
10418585Sguido	argv += optind;
10526557Scharnier	if (argc != 1)
10626557Scharnier		usage();
10726557Scharnier	else
10826557Scharnier		fsys = argv[0];
10918585Sguido
11075884Siedowse	sblock_init();
11112048Speter	if (!setup(fsys))
112102231Strhodes		errx(1, "cannot set up file system `%s'", fsys);
113102231Strhodes	printf("%s file system `%s'\nLast Mounted on %s\n",
11424956Sjoerg	       nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
11512048Speter	rval = cmdloop();
11624956Sjoerg	if (!nflag) {
11724956Sjoerg		sblock.fs_clean = 0;	/* mark it dirty */
11824956Sjoerg		sbdirty();
11924956Sjoerg		ckfini(0);
12024956Sjoerg		printf("*** FILE SYSTEM MARKED DIRTY\n");
12124956Sjoerg		printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
12224956Sjoerg		printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
12324956Sjoerg	}
12412048Speter	exit(rval);
12512048Speter}
12612048Speter
12792881Simp#define CMDFUNC(func) int func(int argc, char *argv[])
12892881Simp#define CMDFUNCSTART(func) int func(int argc, char *argv[])
12912048Speter
13012048SpeterCMDFUNC(helpfn);
13112048SpeterCMDFUNC(focus);				/* focus on inode */
13212048SpeterCMDFUNC(active);			/* print active inode */
13389827SjoergCMDFUNC(blocks);			/* print blocks for active inode */
13412048SpeterCMDFUNC(focusname);			/* focus by name */
13512048SpeterCMDFUNC(zapi);				/* clear inode */
13612048SpeterCMDFUNC(uplink);			/* incr link */
13712048SpeterCMDFUNC(downlink);			/* decr link */
13812048SpeterCMDFUNC(linkcount);			/* set link count */
13912048SpeterCMDFUNC(quit);				/* quit */
140159169SmaximCMDFUNC(findblk);			/* find block */
14112048SpeterCMDFUNC(ls);				/* list directory */
14212048SpeterCMDFUNC(rm);				/* remove name */
14312048SpeterCMDFUNC(ln);				/* add name */
14412048SpeterCMDFUNC(newtype);			/* change type */
14512048SpeterCMDFUNC(chmode);			/* change mode */
14618498SguidoCMDFUNC(chlen);				/* change length */
14712048SpeterCMDFUNC(chaflags);			/* change flags */
14812048SpeterCMDFUNC(chgen);				/* change generation */
14912048SpeterCMDFUNC(chowner);			/* change owner */
15012048SpeterCMDFUNC(chgroup);			/* Change group */
15112048SpeterCMDFUNC(back);				/* pop back to last ino */
152161558SceriCMDFUNC(chbtime);			/* Change btime */
15312048SpeterCMDFUNC(chmtime);			/* Change mtime */
15412048SpeterCMDFUNC(chctime);			/* Change ctime */
15512048SpeterCMDFUNC(chatime);			/* Change atime */
15612048SpeterCMDFUNC(chinum);			/* Change inode # of dirent */
15712048SpeterCMDFUNC(chname);			/* Change dirname of dirent */
15812048Speter
15912048Speterstruct cmdtable cmds[] = {
16024956Sjoerg	{ "help", "Print out help", 1, 1, FL_RO, helpfn },
16124956Sjoerg	{ "?", "Print out help", 1, 1, FL_RO, helpfn },
16224956Sjoerg	{ "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
16324956Sjoerg	{ "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
164157950Smaxim	{ "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
165157950Smaxim	{ "cd", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
16624956Sjoerg	{ "back", "Go to previous active inode", 1, 1, FL_RO, back },
16724956Sjoerg	{ "active", "Print active inode", 1, 1, FL_RO, active },
16824956Sjoerg	{ "print", "Print active inode", 1, 1, FL_RO, active },
16989827Sjoerg	{ "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
17024956Sjoerg	{ "uplink", "Increment link count", 1, 1, FL_WR, uplink },
17124956Sjoerg	{ "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
17224956Sjoerg	{ "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
173159169Smaxim	{ "findblk", "Find inode owning disk block(s)", 2, 33, FL_RO, findblk},
17424956Sjoerg	{ "ls", "List current inode as directory", 1, 1, FL_RO, ls },
175157950Smaxim	{ "rm", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
176157950Smaxim	{ "del", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
177157950Smaxim	{ "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR | FL_ST, ln },
17824956Sjoerg	{ "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
179157950Smaxim	{ "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR | FL_ST, chname },
18024956Sjoerg	{ "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
18124956Sjoerg	{ "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
18224956Sjoerg	{ "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
18324956Sjoerg	{ "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
18424956Sjoerg	{ "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
18524956Sjoerg	{ "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
18624956Sjoerg	{ "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
187161558Sceri	{ "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime },
18824956Sjoerg	{ "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
18924956Sjoerg	{ "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
19024956Sjoerg	{ "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
19124956Sjoerg	{ "quit", "Exit", 1, 1, FL_RO, quit },
19224956Sjoerg	{ "q", "Exit", 1, 1, FL_RO, quit },
19324956Sjoerg	{ "exit", "Exit", 1, 1, FL_RO, quit },
194136322Sle	{ NULL, 0, 0, 0, 0, NULL },
19512048Speter};
19612048Speter
19712048Speterint
19892881Simphelpfn(int argc, char *argv[])
19912048Speter{
20092806Sobrien    struct cmdtable *cmdtp;
20112048Speter
20212048Speter    printf("Commands are:\n%-10s %5s %5s   %s\n",
203157950Smaxim	   "command", "min args", "max args", "what");
20412048Speter
20512048Speter    for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
20612048Speter	printf("%-10s %5u %5u   %s\n",
207157950Smaxim		cmdtp->cmd, cmdtp->minargc-1, cmdtp->maxargc-1, cmdtp->helptxt);
20812048Speter    return 0;
20912048Speter}
21012048Speter
21112048Speterchar *
21292881Simpprompt(EditLine *el)
21312048Speter{
21412048Speter    static char pstring[64];
215241013Smdf    snprintf(pstring, sizeof(pstring), "fsdb (inum: %ju)> ",
216241013Smdf	(uintmax_t)curinum);
21712048Speter    return pstring;
21812048Speter}
21912048Speter
22012048Speter
22112048Speterint
22292881Simpcmdloop(void)
22312048Speter{
22412048Speter    char *line;
22512048Speter    const char *elline;
22612048Speter    int cmd_argc, rval = 0, known;
22712048Speter#define scratch known
22812048Speter    char **cmd_argv;
22912048Speter    struct cmdtable *cmdp;
23012048Speter    History *hist;
23112048Speter    EditLine *elptr;
23284261Sobrien    HistEvent he;
23312048Speter
23412048Speter    curinode = ginode(ROOTINO);
23512048Speter    curinum = ROOTINO;
23689827Sjoerg    printactive(0);
23712048Speter
23812048Speter    hist = history_init();
239151471Sstefanf    history(hist, &he, H_SETSIZE, 100);	/* 100 elt history buffer */
24012048Speter
24184261Sobrien    elptr = el_init("fsdb", stdin, stdout, stderr);
24212048Speter    el_set(elptr, EL_EDITOR, "emacs");
24312048Speter    el_set(elptr, EL_PROMPT, prompt);
24412048Speter    el_set(elptr, EL_HIST, history, hist);
24512048Speter    el_source(elptr, NULL);
24612048Speter
24712048Speter    while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
24812048Speter	if (debug)
24937001Scharnier	    printf("command `%s'\n", elline);
25012048Speter
25184261Sobrien	history(hist, &he, H_ENTER, elline);
25212048Speter
25312048Speter	line = strdup(elline);
25412048Speter	cmd_argv = crack(line, &cmd_argc);
25512048Speter	/*
25612048Speter	 * el_parse returns -1 to signal that it's not been handled
25712048Speter	 * internally.
25812048Speter	 */
259148833Sstefanf	if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1)
26012048Speter	    continue;
26112048Speter	if (cmd_argc) {
26212048Speter	    known = 0;
26312048Speter	    for (cmdp = cmds; cmdp->cmd; cmdp++) {
26412048Speter		if (!strcmp(cmdp->cmd, cmd_argv[0])) {
26524956Sjoerg		    if ((cmdp->flags & FL_WR) == FL_WR && nflag)
26624956Sjoerg			warnx("`%s' requires write access", cmd_argv[0]),
26724956Sjoerg			    rval = 1;
26824956Sjoerg		    else if (cmd_argc >= cmdp->minargc &&
26912048Speter			cmd_argc <= cmdp->maxargc)
27012048Speter			rval = (*cmdp->handler)(cmd_argc, cmd_argv);
271157950Smaxim		    else if (cmd_argc >= cmdp->minargc &&
272157950Smaxim			(cmdp->flags & FL_ST) == FL_ST) {
27389791Sgreen			strcpy(line, elline);
27489791Sgreen			cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
27589791Sgreen			rval = (*cmdp->handler)(cmd_argc, cmd_argv);
27689791Sgreen		    } else
27712048Speter			rval = argcount(cmdp, cmd_argc, cmd_argv);
27812048Speter		    known = 1;
27912048Speter		    break;
28012048Speter		}
28112048Speter	    }
28212048Speter	    if (!known)
28312048Speter		warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
28412048Speter	} else
28512048Speter	    rval = 0;
28612048Speter	free(line);
28712048Speter	if (rval < 0)
28889810Sjoerg	    /* user typed "quit" */
28989810Sjoerg	    return 0;
29012048Speter	if (rval)
29112048Speter	    warnx("rval was %d", rval);
29212048Speter    }
29312048Speter    el_end(elptr);
29412048Speter    history_end(hist);
29512048Speter    return rval;
29612048Speter}
29712048Speter
29898542Smckusickunion dinode *curinode;
29912048Speterino_t curinum, ocurrent;
30012048Speter
30112048Speter#define GETINUM(ac,inum)    inum = strtoul(argv[ac], &cp, 0); \
30212048Speter    if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
303241013Smdf	printf("inode %ju out of range; range is [%ju,%ju]\n",		\
304241013Smdf	    (uintmax_t)inum, (uintmax_t)ROOTINO, (uintmax_t)maxino);	\
30512048Speter	return 1; \
30612048Speter    }
30712048Speter
30812048Speter/*
30912048Speter * Focus on given inode number
31012048Speter */
31112048SpeterCMDFUNCSTART(focus)
31212048Speter{
31312048Speter    ino_t inum;
31412048Speter    char *cp;
31512048Speter
31612048Speter    GETINUM(1,inum);
31712048Speter    curinode = ginode(inum);
31812048Speter    ocurrent = curinum;
31912048Speter    curinum = inum;
32089827Sjoerg    printactive(0);
32112048Speter    return 0;
32212048Speter}
32312048Speter
32412048SpeterCMDFUNCSTART(back)
32512048Speter{
32612048Speter    curinum = ocurrent;
32712048Speter    curinode = ginode(curinum);
32889827Sjoerg    printactive(0);
32912048Speter    return 0;
33012048Speter}
33112048Speter
33212048SpeterCMDFUNCSTART(zapi)
33312048Speter{
33412048Speter    ino_t inum;
33598542Smckusick    union dinode *dp;
33612048Speter    char *cp;
33712048Speter
33812048Speter    GETINUM(1,inum);
33912048Speter    dp = ginode(inum);
34012048Speter    clearinode(dp);
34112048Speter    inodirty();
34212048Speter    if (curinode)			/* re-set after potential change */
34312048Speter	curinode = ginode(curinum);
34412048Speter    return 0;
34512048Speter}
34612048Speter
34712048SpeterCMDFUNCSTART(active)
34812048Speter{
34989827Sjoerg    printactive(0);
35012048Speter    return 0;
35112048Speter}
35212048Speter
35389827SjoergCMDFUNCSTART(blocks)
35489827Sjoerg{
35589827Sjoerg    printactive(1);
35689827Sjoerg    return 0;
35789827Sjoerg}
35812048Speter
35912048SpeterCMDFUNCSTART(quit)
36012048Speter{
36112048Speter    return -1;
36212048Speter}
36312048Speter
36412048SpeterCMDFUNCSTART(uplink)
36512048Speter{
36612048Speter    if (!checkactive())
36712048Speter	return 1;
368136322Sle    DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
369241013Smdf    printf("inode %ju link count now %d\n",
370241013Smdf	(uintmax_t)curinum, DIP(curinode, di_nlink));
37112048Speter    inodirty();
37212048Speter    return 0;
37312048Speter}
37412048Speter
37512048SpeterCMDFUNCSTART(downlink)
37612048Speter{
37712048Speter    if (!checkactive())
37812048Speter	return 1;
379136322Sle    DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
380241013Smdf    printf("inode %ju link count now %d\n",
381241013Smdf	(uintmax_t)curinum, DIP(curinode, di_nlink));
38212048Speter    inodirty();
38312048Speter    return 0;
38412048Speter}
38512048Speter
38612048Speterconst char *typename[] = {
38712048Speter    "unknown",
38812048Speter    "fifo",
38912048Speter    "char special",
39012048Speter    "unregistered #3",
39112048Speter    "directory",
39212048Speter    "unregistered #5",
39312048Speter    "blk special",
39412048Speter    "unregistered #7",
39512048Speter    "regular",
39612048Speter    "unregistered #9",
39712048Speter    "symlink",
39812048Speter    "unregistered #11",
39912048Speter    "socket",
40012048Speter    "unregistered #13",
40112048Speter    "whiteout",
40212048Speter};
403207141Sjeff
404207141Sjeffint diroff;
40512048Speterint slot;
40612048Speter
40712048Speterint
40892881Simpscannames(struct inodesc *idesc)
40912048Speter{
41092806Sobrien	struct direct *dirp = idesc->id_dirp;
41112048Speter
412207141Sjeff	printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n",
413207141Sjeff	       slot++, diroff, dirp->d_ino, dirp->d_reclen,
414207141Sjeff	       typename[dirp->d_type], dirp->d_namlen, dirp->d_name);
415207141Sjeff	diroff += dirp->d_reclen;
41612048Speter	return (KEEPON);
41712048Speter}
41812048Speter
41912048SpeterCMDFUNCSTART(ls)
42012048Speter{
42112048Speter    struct inodesc idesc;
42212048Speter    checkactivedir();			/* let it go on anyway */
42312048Speter
42412048Speter    slot = 0;
425207141Sjeff    diroff = 0;
42612048Speter    idesc.id_number = curinum;
42712048Speter    idesc.id_func = scannames;
42812048Speter    idesc.id_type = DATA;
42912048Speter    idesc.id_fix = IGNORE;
43012048Speter    ckinode(curinode, &idesc);
43112048Speter    curinode = ginode(curinum);
43212048Speter
43312048Speter    return 0;
43412048Speter}
43512048Speter
436159169Smaximstatic int findblk_numtofind;
437159169Smaximstatic int wantedblksize;
438159169Smaxim
439159169SmaximCMDFUNCSTART(findblk)
440159169Smaxim{
441159169Smaxim    ino_t inum, inosused;
442159169Smaxim    uint32_t *wantedblk32;
443159169Smaxim    uint64_t *wantedblk64;
444248658Smckusick    struct bufarea *cgbp;
445248658Smckusick    struct cg *cgp;
446159169Smaxim    int c, i, is_ufs2;
447159169Smaxim
448159169Smaxim    wantedblksize = (argc - 1);
449159169Smaxim    is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC;
450159169Smaxim    ocurrent = curinum;
451159169Smaxim
452159169Smaxim    if (is_ufs2) {
453159169Smaxim	wantedblk64 = calloc(wantedblksize, sizeof(uint64_t));
454159169Smaxim	if (wantedblk64 == NULL)
455159169Smaxim	    err(1, "malloc");
456159169Smaxim	for (i = 1; i < argc; i++)
457159169Smaxim	    wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
458159169Smaxim    } else {
459159169Smaxim	wantedblk32 = calloc(wantedblksize, sizeof(uint32_t));
460159169Smaxim	if (wantedblk32 == NULL)
461159169Smaxim	    err(1, "malloc");
462159169Smaxim	for (i = 1; i < argc; i++)
463159169Smaxim	    wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
464159169Smaxim    }
465159169Smaxim    findblk_numtofind = wantedblksize;
466159169Smaxim    /*
467159169Smaxim     * sblock.fs_ncg holds a number of cylinder groups.
468159169Smaxim     * Iterate over all cylinder groups.
469159169Smaxim     */
470159169Smaxim    for (c = 0; c < sblock.fs_ncg; c++) {
471159169Smaxim	/*
472159169Smaxim	 * sblock.fs_ipg holds a number of inodes per cylinder group.
473159169Smaxim	 * Calculate a highest inode number for a given cylinder group.
474159169Smaxim	 */
475159169Smaxim	inum = c * sblock.fs_ipg;
476159169Smaxim	/* Read cylinder group. */
477248658Smckusick	cgbp = cgget(c);
478248658Smckusick	cgp = cgbp->b_un.b_cg;
479159169Smaxim	/*
480159169Smaxim	 * Get a highest used inode number for a given cylinder group.
481159169Smaxim	 * For UFS1 all inodes initialized at the newfs stage.
482159169Smaxim	 */
483159169Smaxim	if (is_ufs2)
484159169Smaxim	    inosused = cgp->cg_initediblk;
485159169Smaxim	else
486159169Smaxim	    inosused = sblock.fs_ipg;
487159169Smaxim
488159169Smaxim	for (; inosused > 0; inum++, inosused--) {
489159169Smaxim	    /* Skip magic inodes: 0, WINO, ROOTINO. */
490159169Smaxim	    if (inum < ROOTINO)
491159169Smaxim		continue;
492159169Smaxim	    /*
493159169Smaxim	     * Check if the block we are looking for is just an inode block.
494159169Smaxim	     *
495159169Smaxim	     * ino_to_fsba() - get block containing inode from its number.
496159169Smaxim	     * INOPB() - get a number of inodes in one disk block.
497159169Smaxim	     */
498159169Smaxim	    if (is_ufs2 ?
499159169Smaxim		compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) :
500159169Smaxim		compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) {
501241013Smdf		printf("block %llu: inode block (%ju-%ju)\n",
502159169Smaxim		    (unsigned long long)fsbtodb(&sblock,
503159169Smaxim			ino_to_fsba(&sblock, inum)),
504241013Smdf		    (uintmax_t)(inum / INOPB(&sblock)) * INOPB(&sblock),
505241013Smdf		    (uintmax_t)(inum / INOPB(&sblock) + 1) * INOPB(&sblock));
506159169Smaxim		findblk_numtofind--;
507159169Smaxim		if (findblk_numtofind == 0)
508159169Smaxim		    goto end;
509159169Smaxim	    }
510159169Smaxim	    /* Get on-disk inode aka dinode. */
511159169Smaxim	    curinum = inum;
512159169Smaxim	    curinode = ginode(inum);
513159169Smaxim	    /* Find IFLNK dinode with allocated data blocks. */
514159169Smaxim	    switch (DIP(curinode, di_mode) & IFMT) {
515159169Smaxim	    case IFDIR:
516159169Smaxim	    case IFREG:
517159169Smaxim		if (DIP(curinode, di_blocks) == 0)
518159169Smaxim		    continue;
519159169Smaxim		break;
520159169Smaxim	    case IFLNK:
521159169Smaxim		{
522159169Smaxim		    uint64_t size = DIP(curinode, di_size);
523159169Smaxim		    if (size > 0 && size < sblock.fs_maxsymlinklen &&
524159169Smaxim			DIP(curinode, di_blocks) == 0)
525159169Smaxim			continue;
526159169Smaxim		    else
527159169Smaxim			break;
528159169Smaxim		}
529159169Smaxim	    default:
530159169Smaxim		continue;
531159169Smaxim	    }
532159169Smaxim	    /* Look through direct data blocks. */
533159169Smaxim	    if (is_ufs2 ?
534159169Smaxim		find_blks64(curinode->dp2.di_db, NDADDR, wantedblk64) :
535159169Smaxim		find_blks32(curinode->dp1.di_db, NDADDR, wantedblk32))
536159169Smaxim		goto end;
537159169Smaxim	    for (i = 0; i < NIADDR; i++) {
538159169Smaxim		/*
539159169Smaxim		 * Does the block we are looking for belongs to the
540159169Smaxim		 * indirect blocks?
541159169Smaxim		 */
542159169Smaxim		if (is_ufs2 ?
543159169Smaxim		    compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) :
544159169Smaxim		    compare_blk32(wantedblk32, curinode->dp1.di_ib[i]))
545159169Smaxim		    if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] :
546159169Smaxim			curinode->dp1.di_ib[i]))
547159169Smaxim			goto end;
548159169Smaxim		/*
549159169Smaxim		 * Search through indirect, double and triple indirect
550159169Smaxim		 * data blocks.
551159169Smaxim		 */
552159169Smaxim		if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) :
553159169Smaxim		    (curinode->dp1.di_ib[i] != 0))
554159169Smaxim		    if (is_ufs2 ?
555159169Smaxim			find_indirblks64(curinode->dp2.di_ib[i], i,
556159169Smaxim			    wantedblk64) :
557159169Smaxim			find_indirblks32(curinode->dp1.di_ib[i], i,
558159169Smaxim			    wantedblk32))
559159169Smaxim			goto end;
560159169Smaxim	    }
561159169Smaxim	}
562159169Smaxim    }
563159169Smaximend:
564159169Smaxim    curinum = ocurrent;
565159169Smaxim    curinode = ginode(curinum);
566159169Smaxim    return 0;
567159169Smaxim}
568159169Smaxim
569159169Smaximstatic int
570159169Smaximcompare_blk32(uint32_t *wantedblk, uint32_t curblk)
571159169Smaxim{
572159169Smaxim    int i;
573159169Smaxim
574159169Smaxim    for (i = 0; i < wantedblksize; i++) {
575159169Smaxim	if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
576159169Smaxim	    wantedblk[i] = 0;
577159169Smaxim	    return 1;
578159169Smaxim	}
579159169Smaxim    }
580159169Smaxim    return 0;
581159169Smaxim}
582159169Smaxim
583159169Smaximstatic int
584159169Smaximcompare_blk64(uint64_t *wantedblk, uint64_t curblk)
585159169Smaxim{
586159169Smaxim    int i;
587159169Smaxim
588159169Smaxim    for (i = 0; i < wantedblksize; i++) {
589159169Smaxim	if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
590159169Smaxim	    wantedblk[i] = 0;
591159169Smaxim	    return 1;
592159169Smaxim	}
593159169Smaxim    }
594159169Smaxim    return 0;
595159169Smaxim}
596159169Smaxim
597159169Smaximstatic int
598159169Smaximfounddatablk(uint64_t blk)
599159169Smaxim{
600159169Smaxim
601241013Smdf    printf("%llu: data block of inode %ju\n",
602241013Smdf	(unsigned long long)fsbtodb(&sblock, blk), (uintmax_t)curinum);
603159169Smaxim    findblk_numtofind--;
604159169Smaxim    if (findblk_numtofind == 0)
605159169Smaxim	return 1;
606159169Smaxim    return 0;
607159169Smaxim}
608159169Smaxim
609159169Smaximstatic int
610159169Smaximfind_blks32(uint32_t *buf, int size, uint32_t *wantedblk)
611159169Smaxim{
612159169Smaxim    int blk;
613159169Smaxim    for (blk = 0; blk < size; blk++) {
614159169Smaxim	if (buf[blk] == 0)
615159169Smaxim	    continue;
616159169Smaxim	if (compare_blk32(wantedblk, buf[blk])) {
617159169Smaxim	    if (founddatablk(buf[blk]))
618159169Smaxim		return 1;
619159169Smaxim	}
620159169Smaxim    }
621159169Smaxim    return 0;
622159169Smaxim}
623159169Smaxim
624159169Smaximstatic int
625159169Smaximfind_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk)
626159169Smaxim{
627159169Smaxim#define MAXNINDIR      (MAXBSIZE / sizeof(uint32_t))
628159169Smaxim    uint32_t idblk[MAXNINDIR];
629159169Smaxim    int i;
630159169Smaxim
631163846Spjd    blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
632159169Smaxim    if (ind_level <= 0) {
633159169Smaxim	if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk))
634159169Smaxim	    return 1;
635159169Smaxim    } else {
636159169Smaxim	ind_level--;
637159169Smaxim	for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) {
638159169Smaxim	    if (compare_blk32(wantedblk, idblk[i])) {
639159169Smaxim		if (founddatablk(idblk[i]))
640159169Smaxim		    return 1;
641159169Smaxim	    }
642159169Smaxim	    if (idblk[i] != 0)
643159169Smaxim		if (find_indirblks32(idblk[i], ind_level, wantedblk))
644159169Smaxim		    return 1;
645159169Smaxim	}
646159169Smaxim    }
647159169Smaxim#undef MAXNINDIR
648159169Smaxim    return 0;
649159169Smaxim}
650159169Smaxim
651159169Smaximstatic int
652159169Smaximfind_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
653159169Smaxim{
654159169Smaxim    int blk;
655159169Smaxim    for (blk = 0; blk < size; blk++) {
656159169Smaxim	if (buf[blk] == 0)
657159169Smaxim	    continue;
658159169Smaxim	if (compare_blk64(wantedblk, buf[blk])) {
659159169Smaxim	    if (founddatablk(buf[blk]))
660159169Smaxim		return 1;
661159169Smaxim	}
662159169Smaxim    }
663159169Smaxim    return 0;
664159169Smaxim}
665159169Smaxim
666159169Smaximstatic int
667159169Smaximfind_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
668159169Smaxim{
669159169Smaxim#define MAXNINDIR      (MAXBSIZE / sizeof(uint64_t))
670159169Smaxim    uint64_t idblk[MAXNINDIR];
671159169Smaxim    int i;
672159169Smaxim
673163846Spjd    blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
674159169Smaxim    if (ind_level <= 0) {
675159169Smaxim	if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk))
676159169Smaxim	    return 1;
677159169Smaxim    } else {
678159169Smaxim	ind_level--;
679159169Smaxim	for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) {
680159169Smaxim	    if (compare_blk64(wantedblk, idblk[i])) {
681159169Smaxim		if (founddatablk(idblk[i]))
682159169Smaxim		    return 1;
683159169Smaxim	    }
684159169Smaxim	    if (idblk[i] != 0)
685159169Smaxim		if (find_indirblks64(idblk[i], ind_level, wantedblk))
686159169Smaxim		    return 1;
687159169Smaxim	}
688159169Smaxim    }
689159169Smaxim#undef MAXNINDIR
690159169Smaxim    return 0;
691159169Smaxim}
692159169Smaxim
69392881Simpint findino(struct inodesc *idesc); /* from fsck */
69492881Simpstatic int dolookup(char *name);
69512048Speter
69612048Speterstatic int
69792881Simpdolookup(char *name)
69812048Speter{
69912048Speter    struct inodesc idesc;
70012048Speter
70112048Speter    if (!checkactivedir())
70212048Speter	    return 0;
70312048Speter    idesc.id_number = curinum;
70412048Speter    idesc.id_func = findino;
70512048Speter    idesc.id_name = name;
70612048Speter    idesc.id_type = DATA;
70712048Speter    idesc.id_fix = IGNORE;
70812048Speter    if (ckinode(curinode, &idesc) & FOUND) {
70912048Speter	curinum = idesc.id_parent;
71012048Speter	curinode = ginode(curinum);
71189827Sjoerg	printactive(0);
71212048Speter	return 1;
71312048Speter    } else {
71412048Speter	warnx("name `%s' not found in current inode directory", name);
71512048Speter	return 0;
71612048Speter    }
71712048Speter}
71812048Speter
71912048SpeterCMDFUNCSTART(focusname)
72012048Speter{
72112048Speter    char *p, *val;
72212048Speter
72312048Speter    if (!checkactive())
72412048Speter	return 1;
72512048Speter
72612048Speter    ocurrent = curinum;
72712048Speter
72812048Speter    if (argv[1][0] == '/') {
72912048Speter	curinum = ROOTINO;
73012048Speter	curinode = ginode(ROOTINO);
73112048Speter    } else {
73212048Speter	if (!checkactivedir())
73312048Speter	    return 1;
73412048Speter    }
73512048Speter    for (p = argv[1]; p != NULL;) {
73612048Speter	while ((val = strsep(&p, "/")) != NULL && *val == '\0');
73712048Speter	if (val) {
73812048Speter	    printf("component `%s': ", val);
73912048Speter	    fflush(stdout);
74012048Speter	    if (!dolookup(val)) {
74112048Speter		curinode = ginode(curinum);
74212048Speter		return(1);
74312048Speter	    }
74412048Speter	}
74512048Speter    }
74612048Speter    return 0;
74712048Speter}
74812048Speter
74912048SpeterCMDFUNCSTART(ln)
75012048Speter{
75112048Speter    ino_t inum;
75212048Speter    int rval;
75312048Speter    char *cp;
75412048Speter
75512048Speter    GETINUM(1,inum);
75612048Speter
75712048Speter    if (!checkactivedir())
75812048Speter	return 1;
75912048Speter    rval = makeentry(curinum, inum, argv[2]);
76012048Speter    if (rval)
761241013Smdf	    printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]);
76212048Speter    else
76312048Speter	printf("could not enter name? weird.\n");
76412048Speter    curinode = ginode(curinum);
76512048Speter    return rval;
76612048Speter}
76712048Speter
76812048SpeterCMDFUNCSTART(rm)
76912048Speter{
77012048Speter    int rval;
77112048Speter
77212048Speter    if (!checkactivedir())
77312048Speter	return 1;
77412048Speter    rval = changeino(curinum, argv[1], 0);
77512048Speter    if (rval & ALTERED) {
77612048Speter	printf("Name `%s' removed\n", argv[1]);
77712048Speter	return 0;
77812048Speter    } else {
77989791Sgreen	printf("could not remove name ('%s')? weird.\n", argv[1]);
78012048Speter	return 1;
78112048Speter    }
78212048Speter}
78312048Speter
78412048Speterlong slotcount, desired;
78512048Speter
78612048Speterint
78792881Simpchinumfunc(struct inodesc *idesc)
78812048Speter{
78992806Sobrien	struct direct *dirp = idesc->id_dirp;
79012048Speter
79112048Speter	if (slotcount++ == desired) {
79212048Speter	    dirp->d_ino = idesc->id_parent;
79312048Speter	    return STOP|ALTERED|FOUND;
79412048Speter	}
79512048Speter	return KEEPON;
79612048Speter}
79712048Speter
79812048SpeterCMDFUNCSTART(chinum)
79912048Speter{
80012048Speter    char *cp;
80112048Speter    ino_t inum;
80212048Speter    struct inodesc idesc;
80312048Speter
80412048Speter    slotcount = 0;
80512048Speter    if (!checkactivedir())
80612048Speter	return 1;
80712048Speter    GETINUM(2,inum);
80812048Speter
80912048Speter    desired = strtol(argv[1], &cp, 0);
81012048Speter    if (cp == argv[1] || *cp != '\0' || desired < 0) {
81112048Speter	printf("invalid slot number `%s'\n", argv[1]);
81212048Speter	return 1;
81312048Speter    }
81412048Speter
81512048Speter    idesc.id_number = curinum;
81612048Speter    idesc.id_func = chinumfunc;
81712048Speter    idesc.id_fix = IGNORE;
81812048Speter    idesc.id_type = DATA;
81912048Speter    idesc.id_parent = inum;		/* XXX convenient hiding place */
82012048Speter
82112048Speter    if (ckinode(curinode, &idesc) & FOUND)
82212048Speter	return 0;
82312048Speter    else {
82412048Speter	warnx("no %sth slot in current directory", argv[1]);
82512048Speter	return 1;
82612048Speter    }
82712048Speter}
82812048Speter
82912048Speterint
83092881Simpchnamefunc(struct inodesc *idesc)
83112048Speter{
83292806Sobrien	struct direct *dirp = idesc->id_dirp;
83312048Speter	struct direct testdir;
83412048Speter
83512048Speter	if (slotcount++ == desired) {
83612048Speter	    /* will name fit? */
83712048Speter	    testdir.d_namlen = strlen(idesc->id_name);
83812048Speter	    if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
83912048Speter		dirp->d_namlen = testdir.d_namlen;
84012048Speter		strcpy(dirp->d_name, idesc->id_name);
84112048Speter		return STOP|ALTERED|FOUND;
84212048Speter	    } else
84312048Speter		return STOP|FOUND;	/* won't fit, so give up */
84412048Speter	}
84512048Speter	return KEEPON;
84612048Speter}
84712048Speter
84812048SpeterCMDFUNCSTART(chname)
84912048Speter{
85012048Speter    int rval;
85112048Speter    char *cp;
85212048Speter    struct inodesc idesc;
85312048Speter
85412048Speter    slotcount = 0;
85512048Speter    if (!checkactivedir())
85612048Speter	return 1;
85712048Speter
85812048Speter    desired = strtoul(argv[1], &cp, 0);
85912048Speter    if (cp == argv[1] || *cp != '\0') {
86012048Speter	printf("invalid slot number `%s'\n", argv[1]);
86112048Speter	return 1;
86212048Speter    }
86312048Speter
86412048Speter    idesc.id_number = curinum;
86512048Speter    idesc.id_func = chnamefunc;
86612048Speter    idesc.id_fix = IGNORE;
86712048Speter    idesc.id_type = DATA;
86812048Speter    idesc.id_name = argv[2];
86912048Speter
87012048Speter    rval = ckinode(curinode, &idesc);
87112048Speter    if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
87212048Speter	return 0;
87312048Speter    else if (rval & FOUND) {
87412048Speter	warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
87512048Speter	return 1;
87612048Speter    } else {
87712048Speter	warnx("no %sth slot in current directory", argv[1]);
87812048Speter	return 1;
87912048Speter    }
88012048Speter}
88112048Speter
88212048Speterstruct typemap {
88312048Speter    const char *typename;
88412048Speter    int typebits;
88512048Speter} typenamemap[]  = {
88612048Speter    {"file", IFREG},
88712048Speter    {"dir", IFDIR},
88812048Speter    {"socket", IFSOCK},
88912048Speter    {"fifo", IFIFO},
89012048Speter};
89112048Speter
89212048SpeterCMDFUNCSTART(newtype)
89312048Speter{
89412048Speter    int type;
89512048Speter    struct typemap *tp;
89612048Speter
89712048Speter    if (!checkactive())
89812048Speter	return 1;
89998542Smckusick    type = DIP(curinode, di_mode) & IFMT;
90012048Speter    for (tp = typenamemap;
90141023Struckman	 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
90212048Speter	 tp++) {
90312048Speter	if (!strcmp(argv[1], tp->typename)) {
90412048Speter	    printf("setting type to %s\n", tp->typename);
90512048Speter	    type = tp->typebits;
90612048Speter	    break;
90712048Speter	}
90812048Speter    }
90941023Struckman    if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
91012048Speter	warnx("type `%s' not known", argv[1]);
91112048Speter	warnx("try one of `file', `dir', `socket', `fifo'");
91212048Speter	return 1;
91312048Speter    }
914136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
915136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
91612048Speter    inodirty();
91789827Sjoerg    printactive(0);
91812048Speter    return 0;
91912048Speter}
92012048Speter
92118498SguidoCMDFUNCSTART(chlen)
92218498Sguido{
92318498Sguido    int rval = 1;
92418498Sguido    long len;
92518498Sguido    char *cp;
92618498Sguido
92718498Sguido    if (!checkactive())
92818498Sguido	return 1;
92918498Sguido
93018498Sguido    len = strtol(argv[1], &cp, 0);
93118498Sguido    if (cp == argv[1] || *cp != '\0' || len < 0) {
93218498Sguido	warnx("bad length `%s'", argv[1]);
93318498Sguido	return 1;
93418498Sguido    }
93518498Sguido
936136322Sle    DIP_SET(curinode, di_size, len);
93718498Sguido    inodirty();
93889827Sjoerg    printactive(0);
93918498Sguido    return rval;
94018498Sguido}
94118498Sguido
94212048SpeterCMDFUNCSTART(chmode)
94312048Speter{
94412048Speter    int rval = 1;
94512048Speter    long modebits;
94612048Speter    char *cp;
94712048Speter
94812048Speter    if (!checkactive())
94912048Speter	return 1;
95012048Speter
95112048Speter    modebits = strtol(argv[1], &cp, 8);
95286258Siedowse    if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
95312048Speter	warnx("bad modebits `%s'", argv[1]);
95412048Speter	return 1;
95512048Speter    }
95612048Speter
957136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
958136322Sle    DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
95912048Speter    inodirty();
96089827Sjoerg    printactive(0);
96112048Speter    return rval;
96212048Speter}
96312048Speter
96412048SpeterCMDFUNCSTART(chaflags)
96512048Speter{
96612048Speter    int rval = 1;
96712048Speter    u_long flags;
96812048Speter    char *cp;
96912048Speter
97012048Speter    if (!checkactive())
97112048Speter	return 1;
97212048Speter
97312048Speter    flags = strtoul(argv[1], &cp, 0);
97412048Speter    if (cp == argv[1] || *cp != '\0' ) {
97512048Speter	warnx("bad flags `%s'", argv[1]);
97612048Speter	return 1;
97712048Speter    }
97812048Speter
97912048Speter    if (flags > UINT_MAX) {
98012048Speter	warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
98112048Speter	return(1);
98212048Speter    }
983136322Sle    DIP_SET(curinode, di_flags, flags);
98412048Speter    inodirty();
98589827Sjoerg    printactive(0);
98612048Speter    return rval;
98712048Speter}
98812048Speter
98912048SpeterCMDFUNCSTART(chgen)
99012048Speter{
99112048Speter    int rval = 1;
99212048Speter    long gen;
99312048Speter    char *cp;
99412048Speter
99512048Speter    if (!checkactive())
99612048Speter	return 1;
99712048Speter
99812048Speter    gen = strtol(argv[1], &cp, 0);
99912048Speter    if (cp == argv[1] || *cp != '\0' ) {
100012048Speter	warnx("bad gen `%s'", argv[1]);
100112048Speter	return 1;
100212048Speter    }
100312048Speter
100412048Speter    if (gen > INT_MAX || gen < INT_MIN) {
100512048Speter	warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
100612048Speter	return(1);
100712048Speter    }
1008136322Sle    DIP_SET(curinode, di_gen, gen);
100912048Speter    inodirty();
101089827Sjoerg    printactive(0);
101112048Speter    return rval;
101212048Speter}
101312048Speter
101412048SpeterCMDFUNCSTART(linkcount)
101512048Speter{
101612048Speter    int rval = 1;
101712048Speter    int lcnt;
101812048Speter    char *cp;
101912048Speter
102012048Speter    if (!checkactive())
102112048Speter	return 1;
102212048Speter
102312048Speter    lcnt = strtol(argv[1], &cp, 0);
102412048Speter    if (cp == argv[1] || *cp != '\0' ) {
102512048Speter	warnx("bad link count `%s'", argv[1]);
102612048Speter	return 1;
102712048Speter    }
102812048Speter    if (lcnt > USHRT_MAX || lcnt < 0) {
102912048Speter	warnx("max link count is %d\n", USHRT_MAX);
103012048Speter	return 1;
103112048Speter    }
103212048Speter
1033136322Sle    DIP_SET(curinode, di_nlink, lcnt);
103412048Speter    inodirty();
103589827Sjoerg    printactive(0);
103612048Speter    return rval;
103712048Speter}
103812048Speter
103912048SpeterCMDFUNCSTART(chowner)
104012048Speter{
104112048Speter    int rval = 1;
104212048Speter    unsigned long uid;
104312048Speter    char *cp;
104412048Speter    struct passwd *pwd;
104512048Speter
104612048Speter    if (!checkactive())
104712048Speter	return 1;
104812048Speter
104912048Speter    uid = strtoul(argv[1], &cp, 0);
105012048Speter    if (cp == argv[1] || *cp != '\0' ) {
105112048Speter	/* try looking up name */
105237001Scharnier	if ((pwd = getpwnam(argv[1]))) {
105312048Speter	    uid = pwd->pw_uid;
105412048Speter	} else {
105512048Speter	    warnx("bad uid `%s'", argv[1]);
105612048Speter	    return 1;
105712048Speter	}
105812048Speter    }
105912048Speter
1060136322Sle    DIP_SET(curinode, di_uid, uid);
106112048Speter    inodirty();
106289827Sjoerg    printactive(0);
106312048Speter    return rval;
106412048Speter}
106512048Speter
106612048SpeterCMDFUNCSTART(chgroup)
106712048Speter{
106812048Speter    int rval = 1;
106912048Speter    unsigned long gid;
107012048Speter    char *cp;
107112048Speter    struct group *grp;
107212048Speter
107312048Speter    if (!checkactive())
107412048Speter	return 1;
107512048Speter
107612048Speter    gid = strtoul(argv[1], &cp, 0);
107712048Speter    if (cp == argv[1] || *cp != '\0' ) {
107837001Scharnier	if ((grp = getgrnam(argv[1]))) {
107912048Speter	    gid = grp->gr_gid;
108012048Speter	} else {
108112048Speter	    warnx("bad gid `%s'", argv[1]);
108212048Speter	    return 1;
108312048Speter	}
108412048Speter    }
108512048Speter
1086136322Sle    DIP_SET(curinode, di_gid, gid);
108712048Speter    inodirty();
108889827Sjoerg    printactive(0);
108912048Speter    return rval;
109012048Speter}
109112048Speter
109212048Speterint
109398542Smckusickdotime(char *name, time_t *secp, int32_t *nsecp)
109412048Speter{
109512048Speter    char *p, *val;
109612048Speter    struct tm t;
109712048Speter    int32_t nsec;
109812048Speter    p = strchr(name, '.');
109912048Speter    if (p) {
110012048Speter	*p = '\0';
110112048Speter	nsec = strtoul(++p, &val, 0);
110212048Speter	if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
110312048Speter		warnx("invalid nanoseconds");
110412048Speter		goto badformat;
110512048Speter	}
110612048Speter    } else
110712048Speter	nsec = 0;
110812048Speter    if (strlen(name) != 14) {
110912048Speterbadformat:
111012048Speter	warnx("date format: YYYYMMDDHHMMSS[.nsec]");
111112048Speter	return 1;
111212048Speter    }
111398542Smckusick    *nsecp = nsec;
111412048Speter
111512048Speter    for (p = name; *p; p++)
111612048Speter	if (*p < '0' || *p > '9')
111712048Speter	    goto badformat;
111812048Speter
111912048Speter    p = name;
112012048Speter#define VAL() ((*p++) - '0')
112112048Speter    t.tm_year = VAL();
112212048Speter    t.tm_year = VAL() + t.tm_year * 10;
112312048Speter    t.tm_year = VAL() + t.tm_year * 10;
112412048Speter    t.tm_year = VAL() + t.tm_year * 10 - 1900;
112512048Speter    t.tm_mon = VAL();
112612048Speter    t.tm_mon = VAL() + t.tm_mon * 10 - 1;
112712048Speter    t.tm_mday = VAL();
112812048Speter    t.tm_mday = VAL() + t.tm_mday * 10;
112912048Speter    t.tm_hour = VAL();
113012048Speter    t.tm_hour = VAL() + t.tm_hour * 10;
113112048Speter    t.tm_min = VAL();
113212048Speter    t.tm_min = VAL() + t.tm_min * 10;
113312048Speter    t.tm_sec = VAL();
113412048Speter    t.tm_sec = VAL() + t.tm_sec * 10;
113512048Speter    t.tm_isdst = -1;
113612048Speter
113798542Smckusick    *secp = mktime(&t);
113898542Smckusick    if (*secp == -1) {
113912048Speter	warnx("date/time out of range");
114012048Speter	return 1;
114112048Speter    }
114212048Speter    return 0;
114312048Speter}
114412048Speter
1145161558SceriCMDFUNCSTART(chbtime)
1146161558Sceri{
1147161558Sceri    time_t secs;
1148161558Sceri    int32_t nsecs;
1149161558Sceri
1150161558Sceri    if (dotime(argv[1], &secs, &nsecs))
1151161558Sceri	return 1;
1152161558Sceri    if (sblock.fs_magic == FS_UFS1_MAGIC)
1153161558Sceri	return 1;
1154161558Sceri    curinode->dp2.di_birthtime = _time_to_time64(secs);
1155161558Sceri    curinode->dp2.di_birthnsec = nsecs;
1156161558Sceri    inodirty();
1157161558Sceri    printactive(0);
1158161558Sceri    return 0;
1159161558Sceri}
1160161558Sceri
116112048SpeterCMDFUNCSTART(chmtime)
116212048Speter{
116398542Smckusick    time_t secs;
116498542Smckusick    int32_t nsecs;
116598542Smckusick
116698542Smckusick    if (dotime(argv[1], &secs, &nsecs))
116712048Speter	return 1;
116898542Smckusick    if (sblock.fs_magic == FS_UFS1_MAGIC)
116998542Smckusick	curinode->dp1.di_mtime = _time_to_time32(secs);
117098542Smckusick    else
117198542Smckusick	curinode->dp2.di_mtime = _time_to_time64(secs);
1172136322Sle    DIP_SET(curinode, di_mtimensec, nsecs);
117312048Speter    inodirty();
117489827Sjoerg    printactive(0);
117512048Speter    return 0;
117612048Speter}
117712048Speter
117812048SpeterCMDFUNCSTART(chatime)
117912048Speter{
118098542Smckusick    time_t secs;
118198542Smckusick    int32_t nsecs;
118298542Smckusick
118398542Smckusick    if (dotime(argv[1], &secs, &nsecs))
118412048Speter	return 1;
118598542Smckusick    if (sblock.fs_magic == FS_UFS1_MAGIC)
118698542Smckusick	curinode->dp1.di_atime = _time_to_time32(secs);
118798542Smckusick    else
118898542Smckusick	curinode->dp2.di_atime = _time_to_time64(secs);
1189136322Sle    DIP_SET(curinode, di_atimensec, nsecs);
119012048Speter    inodirty();
119189827Sjoerg    printactive(0);
119212048Speter    return 0;
119312048Speter}
119412048Speter
119512048SpeterCMDFUNCSTART(chctime)
119612048Speter{
119798542Smckusick    time_t secs;
119898542Smckusick    int32_t nsecs;
119998542Smckusick
120098542Smckusick    if (dotime(argv[1], &secs, &nsecs))
120112048Speter	return 1;
120298542Smckusick    if (sblock.fs_magic == FS_UFS1_MAGIC)
120398542Smckusick	curinode->dp1.di_ctime = _time_to_time32(secs);
120498542Smckusick    else
120598542Smckusick	curinode->dp2.di_ctime = _time_to_time64(secs);
1206136322Sle    DIP_SET(curinode, di_ctimensec, nsecs);
120712048Speter    inodirty();
120889827Sjoerg    printactive(0);
120912048Speter    return 0;
121012048Speter}
1211