xref: /netbsd-src/sbin/fsdb/fsdb.c (revision f298a94b738ab130659689d3f04c17f6991b6de5)
1 /*	$NetBSD: fsdb.c,v 1.54 2023/01/07 19:41:29 chs Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996, 2017 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by John T. Kohl.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: fsdb.c,v 1.54 2023/01/07 19:41:29 chs Exp $");
35 #endif /* not lint */
36 
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <sys/mount.h>
42 #include <ctype.h>
43 #include <fcntl.h>
44 #include <grp.h>
45 #include <histedit.h>
46 #include <limits.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <err.h>
54 #include <stdbool.h>
55 
56 #include <ufs/ufs/dinode.h>
57 #include <ufs/ufs/dir.h>
58 #include <ufs/ffs/fs.h>
59 #include <ufs/ffs/ffs_extern.h>
60 
61 #include "fsdb.h"
62 #include "fsck.h"
63 #include "extern.h"
64 
65 struct bufarea bufhead;
66 struct bufarea sblk;
67 struct bufarea asblk;
68 struct bufarea cgblk;
69 struct bufarea appleufsblk;
70 struct bufarea *pdirbp;
71 struct bufarea *pbp;
72 struct fs *sblock;
73 struct fs *altsblock;
74 struct cg *cgrp;
75 struct fs *sblocksave;
76 struct dups *duplist;
77 struct dups *muldup;
78 struct zlncnt *zlnhead;
79 struct inoinfo **inphead, **inpsort;
80 long numdirs, dirhash, listmax, inplast;
81 struct uquot_hash *uquot_user_hash;
82 struct uquot_hash *uquot_group_hash;
83 uint8_t q2h_hash_shift;
84 uint16_t q2h_hash_mask;
85 struct inostatlist *inostathead;
86 long	dev_bsize;
87 long	secsize;
88 char	nflag;
89 char	yflag;
90 int	Uflag;
91 int	bflag;
92 int	debug;
93 int	zflag;
94 int	cvtlevel;
95 int	eaflag;
96 int	doinglevel1;
97 int	doinglevel2;
98 int	doing2ea;
99 int	doing2noea;
100 int	newinofmt;
101 char	usedsoftdep;
102 int	preen;
103 int	forceimage;
104 int	is_ufs2;
105 int	is_ufs2ea;
106 int	markclean;
107 char	havesb;
108 char	skipclean;
109 int	fsmodified;
110 int	fsreadfd;
111 int	fswritefd;
112 int	rerun;
113 char	resolved;
114 int	endian;
115 int	doswap;
116 int	needswap;
117 int	do_blkswap;
118 int	do_dirswap;
119 int	isappleufs;
120 daddr_t maxfsblock;
121 char	*blockmap;
122 ino_t	maxino;
123 int	dirblksiz;
124 daddr_t n_blks;
125 ino_t n_files;
126 long countdirs;
127 int	got_siginfo;
128 struct	ufs1_dinode ufs1_zino;
129 struct	ufs2_dinode ufs2_zino;
130 
131 /* Used to keep state for "saveblks" command.  */
132 struct wrinfo {
133 	off_t size;
134 	off_t written_size;
135 	int fd;
136 };
137 
138 __dead static void usage(void);
139 static int cmdloop(void);
140 static char *prompt(EditLine *);
141 static int scannames(struct inodesc *);
142 static int dolookup(char *);
143 static int chinumfunc(struct inodesc *);
144 static int chnamefunc(struct inodesc *);
145 static int chreclenfunc(struct inodesc *);
146 static int dotime(char *, int64_t *, int32_t *);
147 static void print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp);
148 static void print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp);
149 static void print_indirblks32(uint32_t blk, int ind_level,
150     uint64_t *blknum, struct wrinfo *wrp);
151 static void print_indirblks64(uint64_t blk, int ind_level,
152     uint64_t *blknum, struct wrinfo *wrp);
153 static int compare_blk32(uint32_t *, uint32_t);
154 static int compare_blk64(uint64_t *, uint64_t);
155 static int founddatablk(uint64_t);
156 static int find_blks32(uint32_t *buf, int size, uint32_t *blknum);
157 static int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
158 static int find_indirblks32(uint32_t blk, int ind_level,
159 						uint32_t *blknum);
160 static int find_indirblks64(uint64_t blk, int ind_level,
161 						uint64_t *blknum);
162 
163 union dinode *curinode;
164 ino_t   curinum;
165 
166 static void
usage(void)167 usage(void)
168 {
169 	errx(1, "usage: %s [-dFfNn] <fsname>", getprogname());
170 }
171 /*
172  * We suck in lots of fsck code, and just pick & choose the stuff we want.
173  *
174  * fsreadfd is set up to read from the file system, fswritefd to write to
175  * the file system.
176  */
177 int
main(int argc,char * argv[])178 main(int argc, char *argv[])
179 {
180 	int     ch, rval;
181 	char   *fsys = NULL;
182 	bool	makedirty = true;
183 
184 	forceimage = 0;
185 	debug = 0;
186 	isappleufs = 0;
187 	while ((ch = getopt(argc, argv, "dFf:Nn")) != -1) {
188 		switch (ch) {
189 		case 'd':
190 			debug++;
191 			break;
192 		case 'F':
193 			forceimage = 1;
194 			break;
195 		case 'f':
196 			fsys = optarg;
197 			break;
198 		case 'N':
199 			makedirty = false;
200 			break;
201 		case 'n':
202 			nflag++;
203 			break;
204 		default:
205 			usage();
206 		}
207 	}
208 	argc -= optind;
209 	argv += optind;
210 	if (fsys == NULL)
211 		fsys = argv[0];
212 	if (fsys == NULL)
213 		usage();
214 	endian = 0;
215 	if (setup(fsys, fsys) <= 0)
216 		errx(1, "cannot set up file system `%s'", fsys);
217 	printf("Editing file system `%s'\nLast Mounted on %s\n", fsys,
218 	    sblock->fs_fsmnt);
219 	rval = cmdloop();
220 	if (nflag)
221 		exit(rval);
222 	if (!makedirty) {
223 		ckfini(1);
224 		exit(rval);
225 	}
226 	sblock->fs_clean = 0;	/* mark it dirty */
227 	sbdirty();
228 	markclean = 0;
229 	ckfini(1);
230 	printf("*** FILE SYSTEM MARKED DIRTY\n");
231 	printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
232 	printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
233 	exit(rval);
234 }
235 
236 #define CMDFUNC(func) static int func (int argc, char *argv[])
237 
238 CMDFUNC(helpfn);
239 CMDFUNC(focus);			/* focus on inode */
240 CMDFUNC(active);		/* print active inode */
241 CMDFUNC(focusname);		/* focus by name */
242 CMDFUNC(zapi);			/* clear inode */
243 CMDFUNC(uplink);		/* incr link */
244 CMDFUNC(downlink);		/* decr link */
245 CMDFUNC(linkcount);		/* set link count */
246 CMDFUNC(quit);			/* quit */
247 CMDFUNC(ls);			/* list directory */
248 CMDFUNC(blks);			/* list blocks */
249 CMDFUNC(findblk);		/* find block */
250 CMDFUNC(rm);			/* remove name */
251 CMDFUNC(ln);			/* add name */
252 CMDFUNC(newtype);		/* change type */
253 CMDFUNC(chmode);		/* change mode */
254 CMDFUNC(chlen);			/* change length */
255 CMDFUNC(chaflags);		/* change flags */
256 CMDFUNC(chgen);			/* change generation */
257 CMDFUNC(chowner);		/* change owner */
258 CMDFUNC(chgroup);		/* Change group */
259 CMDFUNC(back);			/* pop back to last ino */
260 CMDFUNC(chmtime);		/* Change mtime */
261 CMDFUNC(chctime);		/* Change ctime */
262 CMDFUNC(chatime);		/* Change atime */
263 CMDFUNC(chbirthtime);		/* Change birthtime */
264 CMDFUNC(chinum);		/* Change inode # of dirent */
265 CMDFUNC(chname);		/* Change dirname of dirent */
266 CMDFUNC(chreclen);		/* Change reclen of dirent */
267 CMDFUNC(chextsize);		/* Change extsize */
268 CMDFUNC(chblocks);		/* Change blocks */
269 CMDFUNC(chdb);			/* Change direct block pointer */
270 CMDFUNC(chib);			/* Change indirect block pointer */
271 CMDFUNC(chextb);		/* Change extattr block pointer */
272 CMDFUNC(chfreelink);		/* Change freelink pointer */
273 CMDFUNC(iptrs);			/* print raw block pointers for active inode */
274 CMDFUNC(saveea);		/* Save extattrs */
275 
276 static struct cmdtable cmds[] = {
277 	{"help", "Print out help", 1, 1, helpfn},
278 	{"?", "Print out help", 1, 1, helpfn},
279 	{"inode", "Set active inode to INUM", 2, 2, focus},
280 	{"clri", "Clear inode INUM", 2, 2, zapi},
281 	{"lookup", "Set active inode by looking up NAME", 2, 2, focusname},
282 	{"cd", "Set active inode by looking up NAME", 2, 2, focusname},
283 	{"back", "Go to previous active inode", 1, 1, back},
284 	{"active", "Print active inode", 1, 1, active},
285 	{"print", "Print active inode", 1, 1, active},
286 	{"uplink", "Increment link count", 1, 1, uplink},
287 	{"downlink", "Decrement link count", 1, 1, downlink},
288 	{"linkcount", "Set link count to COUNT", 2, 2, linkcount},
289 	{"ls", "List current inode as directory", 1, 1, ls},
290 	{"blks", "List current inode's data blocks", 1, 1, blks},
291 	{"saveblks", "Save current inode's data blocks to FILE", 2, 2, blks},
292 	{"findblk", "Find inode owning disk block(s)", 2, 33, findblk},
293 	{"rm", "Remove NAME from current inode directory", 2, 2, rm},
294 	{"del", "Remove NAME from current inode directory", 2, 2, rm},
295 	{"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln},
296 	{"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum},
297 	{"chname", "Change dir entry number INDEX to NAME", 3, 3, chname},
298 	{"chreclen", "Change dir entry number INDEX to RECLEN", 3, 3, chreclen},
299 	{"chtype", "Change type of current inode to TYPE", 2, 2, newtype},
300 	{"chmod", "Change mode of current inode to MODE", 2, 2, chmode},
301 	{"chown", "Change owner of current inode to OWNER", 2, 2, chowner},
302 	{"chlen", "Change length of current inode to LENGTH", 2, 2, chlen},
303 	{"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup},
304 	{"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags},
305 	{"chgen", "Change generation number of current inode to GEN", 2, 2,
306 		    chgen},
307 	{ "chextsize", "Change extsize of current inode to EXTSIZE", 2, 2, chextsize },
308 	{ "chblocks", "Change blocks of current inode to BLOCKS", 2, 2, chblocks },
309 	{ "chdb", "Change db pointer N of current inode to BLKNO", 3, 3, chdb },
310 	{ "chib", "Change ib pointer N of current inode to BLKNO", 3, 3, chib },
311 	{ "chextb", "Change extb pointer N of current inode to BLKNO", 3, 3, chextb },
312 	{ "chfreelink", "Change freelink of current inode to FREELINK", 2, 2, chfreelink },
313 	{ "iptrs", "Print raw block pointers of current inode", 1, 1, iptrs },
314 	{"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime},
315 	{"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime},
316 	{"atime", "Change atime of current inode to ATIME", 2, 2, chatime},
317 	{"birthtime", "Change atime of current inode to BIRTHTIME", 2, 2,
318 	    chbirthtime},
319 	{"saveea", "Save current inode's extattr blocks to FILE", 2, 2, saveea},
320 	{"quit", "Exit", 1, 1, quit},
321 	{"q", "Exit", 1, 1, quit},
322 	{"exit", "Exit", 1, 1, quit},
323 	{ .cmd = NULL},
324 };
325 
326 static int
helpfn(int argc,char * argv[])327 helpfn(int argc, char *argv[])
328 {
329 	struct cmdtable *cmdtp;
330 
331 	printf("Commands are:\n%-10s %5s %5s   %s\n",
332 	    "command", "min argc", "max argc", "what");
333 
334 	for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
335 		printf("%-10s %5u %5u   %s\n",
336 		    cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
337 	return 0;
338 }
339 
340 static char *
prompt(EditLine * el)341 prompt(EditLine *el)
342 {
343 	static char pstring[64];
344 	snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ",
345 	    (unsigned long long)curinum);
346 	return pstring;
347 }
348 
349 
350 static int
cmdloop(void)351 cmdloop(void)
352 {
353 	char   *line;
354 	const char *elline;
355 	int     cmd_argc, rval = 0, known;
356 #define scratch known
357 	char  **cmd_argv;
358 	struct cmdtable *cmdp;
359 	History *hist;
360 	HistEvent he;
361 	EditLine *elptr;
362 
363 	curinode = ginode(UFS_ROOTINO);
364 	curinum = UFS_ROOTINO;
365 	printactive();
366 
367 	hist = history_init();
368 	history(hist, &he, H_SETSIZE, 100);	/* 100 elt history buffer */
369 
370 	elptr = el_init(getprogname(), stdin, stdout, stderr);
371 	el_set(elptr, EL_EDITOR, "emacs");
372 	el_set(elptr, EL_PROMPT, prompt);
373 	el_set(elptr, EL_HIST, history, hist);
374 	el_source(elptr, NULL);
375 
376 	while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
377 		if (debug)
378 			printf("command `%s'\n", elline);
379 
380 		history(hist, &he, H_ENTER, elline);
381 
382 		line = strdup(elline);
383 		cmd_argv = crack(line, &cmd_argc);
384 		if (cmd_argc) {
385 			/*
386 		         * el_parse returns -1 to signal that it's not been
387 		         * handled internally.
388 		         */
389 			if (el_parse(elptr, cmd_argc, (void *)cmd_argv) != -1)
390 				continue;
391 			known = 0;
392 			for (cmdp = cmds; cmdp->cmd; cmdp++) {
393 				if (!strcmp(cmdp->cmd, cmd_argv[0])) {
394 					if (cmd_argc >= cmdp->minargc &&
395 					    cmd_argc <= cmdp->maxargc)
396 						rval =
397 						    (*cmdp->handler)(cmd_argc,
398 							cmd_argv);
399 					else
400 						rval = argcount(cmdp, cmd_argc,
401 						    cmd_argv);
402 					known = 1;
403 					break;
404 				}
405 			}
406 			if (!known)
407 				warnx("unknown command `%s'", cmd_argv[0]),
408 				    rval = 1;
409 		} else
410 			rval = 0;
411 		free(line);
412 		if (rval < 0)
413 			return rval;
414 		if (rval)
415 			warnx("rval was %d", rval);
416 	}
417 	el_end(elptr);
418 	history_end(hist);
419 	return rval;
420 }
421 
422 static ino_t ocurrent;
423 
424 #define GETINUM(ac,inum)    inum = strtoull(argv[ac], &cp, 0); \
425     if (inum < UFS_ROOTINO || inum >= maxino || cp == argv[ac] || *cp != '\0' ) { \
426 	printf("inode %llu out of range; range is [%llu,%llu]\n", \
427 	   (unsigned long long)inum, (unsigned long long)UFS_ROOTINO, \
428 	   (unsigned long long)maxino); \
429 	return 1; \
430     }
431 
432 /*
433  * Focus on given inode number
434  */
CMDFUNC(focus)435 CMDFUNC(focus)
436 {
437 	ino_t   inum;
438 	char   *cp;
439 
440 	GETINUM(1, inum);
441 	curinode = ginode(inum);
442 	ocurrent = curinum;
443 	curinum = inum;
444 	printactive();
445 	return 0;
446 }
447 
CMDFUNC(back)448 CMDFUNC(back)
449 {
450 	curinum = ocurrent;
451 	curinode = ginode(curinum);
452 	printactive();
453 	return 0;
454 }
455 
CMDFUNC(zapi)456 CMDFUNC(zapi)
457 {
458 	ino_t   inum;
459 	union dinode *dp;
460 	char   *cp;
461 
462 	GETINUM(1, inum);
463 	dp = ginode(inum);
464 	clearinode(dp);
465 	inodirty();
466 	if (curinode)		/* re-set after potential change */
467 		curinode = ginode(curinum);
468 	return 0;
469 }
470 
CMDFUNC(active)471 CMDFUNC(active)
472 {
473 	printactive();
474 	return 0;
475 }
476 
CMDFUNC(quit)477 CMDFUNC(quit)
478 {
479 	return -1;
480 }
481 
CMDFUNC(uplink)482 CMDFUNC(uplink)
483 {
484 	int16_t nlink;
485 
486 	if (!checkactive())
487 		return 1;
488 	nlink = iswap16(DIP(curinode, nlink));
489 	nlink++;
490 	DIP_SET(curinode, nlink, iswap16(nlink));
491 	printf("inode %llu link count now %d\n", (unsigned long long)curinum,
492 	    nlink);
493 	inodirty();
494 	return 0;
495 }
496 
CMDFUNC(downlink)497 CMDFUNC(downlink)
498 {
499 	int16_t nlink;
500 
501 	if (!checkactive())
502 		return 1;
503 	nlink = iswap16(DIP(curinode, nlink));
504 	nlink--;
505 	DIP_SET(curinode, nlink, iswap16(nlink));
506 	printf("inode %llu link count now %d\n", (unsigned long long)curinum,
507 	    nlink);
508 	inodirty();
509 	return 0;
510 }
511 
512 static const char *typename[] = {
513 	"unknown",
514 	"fifo",
515 	"char special",
516 	"unregistered #3",
517 	"directory",
518 	"unregistered #5",
519 	"blk special",
520 	"unregistered #7",
521 	"regular",
522 	"unregistered #9",
523 	"symlink",
524 	"unregistered #11",
525 	"socket",
526 	"unregistered #13",
527 	"whiteout",
528 };
529 
530 static int diroff;
531 static int slot;
532 
533 static int
scannames(struct inodesc * idesc)534 scannames(struct inodesc *idesc)
535 {
536 	struct direct *dirp = idesc->id_dirp;
537 
538 	printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n",
539 	    slot++, diroff, iswap32(dirp->d_ino), iswap16(dirp->d_reclen),
540 	    typename[dirp->d_type],
541 	    dirp->d_namlen, dirp->d_name);
542 	diroff += dirp->d_reclen;
543 	return (KEEPON);
544 }
545 
CMDFUNC(ls)546 CMDFUNC(ls)
547 {
548 	struct inodesc idesc;
549 	checkactivedir();	/* let it go on anyway */
550 
551 	slot = 0;
552 	diroff = 0;
553 	idesc.id_number = curinum;
554 	idesc.id_func = scannames;
555 	idesc.id_type = DATA;
556 	idesc.id_fix = IGNORE;
557 	ckinode(curinode, &idesc);
558 	curinode = ginode(curinum);
559 
560 	return 0;
561 }
562 
CMDFUNC(blks)563 CMDFUNC(blks)
564 {
565 	uint64_t blkno = 0;
566 	int i;
567 	struct wrinfo wrinfo, *wrp = NULL;
568 	bool saveblks;
569 
570 	saveblks = strcmp(argv[0], "saveblks") == 0;
571 	if (saveblks) {
572 		wrinfo.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644);
573 		if (wrinfo.fd == -1) {
574 			warn("unable to create file %s", argv[1]);
575 			return 0;
576 		}
577 		wrinfo.size = iswap64(DIP(curinode, size));
578 		wrinfo.written_size = 0;
579 		wrp = &wrinfo;
580 	}
581 	if (!curinode) {
582 		warnx("no current inode");
583 		return 0;
584 	}
585 	if (is_ufs2) {
586 		printf("I=%llu %lld blocks\n", (unsigned long long)curinum,
587 		    (long long)(iswap64(curinode->dp2.di_blocks)));
588 	} else {
589 		printf("I=%llu %d blocks\n", (unsigned long long)curinum,
590 		    iswap32(curinode->dp1.di_blocks));
591 	}
592 	printf("Direct blocks:\n");
593 	if (is_ufs2)
594 		print_blks64(curinode->dp2.di_db, UFS_NDADDR, &blkno, wrp);
595 	else
596 		print_blks32(curinode->dp1.di_db, UFS_NDADDR, &blkno, wrp);
597 
598 	if (is_ufs2) {
599 		for (i = 0; i < UFS_NIADDR; i++)
600 			print_indirblks64(iswap64(curinode->dp2.di_ib[i]), i,
601 			    &blkno, wrp);
602 		printf("Extattr blocks:\n");
603 		blkno = 0;
604 		if (saveblks)
605 			wrinfo.size += iswap32(curinode->dp2.di_extsize);
606 		print_blks64(curinode->dp2.di_extb, UFS_NXADDR, &blkno, wrp);
607 	} else {
608 		for (i = 0; i < UFS_NIADDR; i++)
609 			print_indirblks32(iswap32(curinode->dp1.di_ib[i]), i,
610 			    &blkno, wrp);
611 	}
612 	return 0;
613 }
614 
615 static int findblk_numtofind;
616 static int wantedblksize;
CMDFUNC(findblk)617 CMDFUNC(findblk)
618 {
619 	ino_t   inum, inosused;
620 	uint32_t *wantedblk32 = NULL;
621 	uint64_t *wantedblk64 = NULL;
622 	struct cg *cgp = cgrp;
623 	int i;
624 	uint32_t c;
625 
626 	ocurrent = curinum;
627 	wantedblksize = (argc - 1);
628 	if (is_ufs2) {
629 		wantedblk64 = malloc(sizeof(uint64_t) * wantedblksize);
630 		if (wantedblk64 == NULL) {
631 			perror("malloc");
632 			return 1;
633 		}
634 		memset(wantedblk64, 0, sizeof(uint64_t) * wantedblksize);
635 		for (i = 1; i < argc; i++)
636 			wantedblk64[i - 1] =
637 			    FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0));
638 	} else {
639 		wantedblk32 = malloc(sizeof(uint32_t) * wantedblksize);
640 		if (wantedblk32 == NULL) {
641 			perror("malloc");
642 			return 1;
643 		}
644 		memset(wantedblk32, 0, sizeof(uint32_t) * wantedblksize);
645 		for (i = 1; i < argc; i++)
646 			wantedblk32[i - 1] =
647 			    FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0));
648 	}
649 	findblk_numtofind = wantedblksize;
650 	for (c = 0; c < sblock->fs_ncg; c++) {
651 		inum = c * sblock->fs_ipg;
652 		getblk(&cgblk, cgtod(sblock, c), sblock->fs_cgsize);
653 		memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize);
654 		if (needswap)
655 			ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock);
656 		if (is_ufs2)
657 			inosused = cgp->cg_initediblk;
658 		else
659 			inosused = sblock->fs_ipg;
660 		for (; inosused > 0; inum++, inosused--) {
661 			if (inum < UFS_ROOTINO)
662 				continue;
663 			if (is_ufs2 ? compare_blk64(wantedblk64,
664 			        ino_to_fsba(sblock, inum)) :
665 			    compare_blk32(wantedblk32,
666 			        ino_to_fsba(sblock, inum))) {
667 				printf("block %llu: inode block (%llu-%llu)\n",
668 				    (unsigned long long)FFS_FSBTODB(sblock,
669 					ino_to_fsba(sblock, inum)),
670 				    (unsigned long long)
671 				    (inum / FFS_INOPB(sblock)) * FFS_INOPB(sblock),
672 				    (unsigned long long)
673 				    (inum / FFS_INOPB(sblock) + 1) * FFS_INOPB(sblock));
674 				findblk_numtofind--;
675 				if (findblk_numtofind == 0)
676 					goto end;
677 			}
678 			curinum = inum;
679 			curinode = ginode(inum);
680 			switch (iswap16(DIP(curinode, mode)) & IFMT) {
681 			case IFDIR:
682 			case IFREG:
683 				if (DIP(curinode, blocks) == 0)
684 					continue;
685 				break;
686 			case IFLNK:
687 				{
688 				uint64_t size = iswap64(DIP(curinode, size));
689 				if (size > 0 &&
690 				    size < (uint64_t)sblock->fs_maxsymlinklen &&
691 				    DIP(curinode, blocks) == 0)
692 					continue;
693 				else
694 					break;
695 				}
696 			default:
697 				continue;
698 			}
699 			if (is_ufs2 ?
700 			    find_blks64(curinode->dp2.di_db, UFS_NDADDR,
701 				wantedblk64) :
702 			    find_blks32(curinode->dp1.di_db, UFS_NDADDR,
703 				wantedblk32))
704 				goto end;
705 			for (i = 0; i < UFS_NIADDR; i++) {
706 				if (is_ufs2 ?
707 				    compare_blk64(wantedblk64,
708 					iswap64(curinode->dp2.di_ib[i])) :
709 				    compare_blk32(wantedblk32,
710 					iswap32(curinode->dp1.di_ib[i])))
711 					if (founddatablk(is_ufs2 ?
712 					    iswap64(curinode->dp2.di_ib[i]) :
713 					    iswap32(curinode->dp1.di_ib[i])))
714 						goto end;
715 				if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) :
716 				    (curinode->dp1.di_ib[i] != 0))
717 					if (is_ufs2 ?
718 					    find_indirblks64(
719 						iswap64(curinode->dp2.di_ib[i]),
720 						i, wantedblk64) :
721 					    find_indirblks32(
722 						iswap32(curinode->dp1.di_ib[i]),
723 						i, wantedblk32))
724 						goto end;
725 			}
726 		}
727 	}
728 end:
729 	if (wantedblk32)
730 		free(wantedblk32);
731 	if (wantedblk64)
732 		free(wantedblk64);
733 	curinum = ocurrent;
734 	curinode = ginode(curinum);
735 	return 0;
736 }
737 
738 static int
compare_blk32(uint32_t * wantedblk,uint32_t curblk)739 compare_blk32(uint32_t *wantedblk, uint32_t curblk)
740 {
741 	int i;
742 	for (i = 0; i < wantedblksize; i++) {
743 		if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
744 			wantedblk[i] = 0;
745 			return 1;
746 		}
747 	}
748 	return 0;
749 }
750 
751 static int
compare_blk64(uint64_t * wantedblk,uint64_t curblk)752 compare_blk64(uint64_t *wantedblk, uint64_t curblk)
753 {
754 	int i;
755 	for (i = 0; i < wantedblksize; i++) {
756 		if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
757 			wantedblk[i] = 0;
758 			return 1;
759 		}
760 	}
761 	return 0;
762 }
763 
764 static int
founddatablk(uint64_t blk)765 founddatablk(uint64_t blk)
766 {
767 	printf("%llu: data block of inode %llu\n",
768 	    (unsigned long long)FFS_FSBTODB(sblock, blk),
769 	    (unsigned long long)curinum);
770 	findblk_numtofind--;
771 	if (findblk_numtofind == 0)
772 		return 1;
773 	return 0;
774 }
775 
776 static int
find_blks32(uint32_t * buf,int size,uint32_t * wantedblk)777 find_blks32(uint32_t *buf, int size, uint32_t *wantedblk)
778 {
779 	int blk;
780 	for(blk = 0; blk < size; blk++) {
781 		if (buf[blk] == 0)
782 			continue;
783 		if (compare_blk32(wantedblk, iswap32(buf[blk]))) {
784 			if (founddatablk(iswap32(buf[blk])))
785 				return 1;
786 		}
787 	}
788 	return 0;
789 }
790 
791 static int
find_indirblks32(uint32_t blk,int ind_level,uint32_t * wantedblk)792 find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk)
793 {
794 #define MAXNINDIR	(MAXBSIZE / sizeof(uint32_t))
795 	uint32_t idblk[MAXNINDIR];
796 	size_t i;
797 
798 	bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
799 	    (int)sblock->fs_bsize);
800 	if (ind_level <= 0) {
801 		if (find_blks32(idblk,
802 		    sblock->fs_bsize / sizeof(uint32_t), wantedblk))
803 			return 1;
804 	} else {
805 		ind_level--;
806 		for (i = 0; i < sblock->fs_bsize / sizeof(uint32_t); i++) {
807 			if (compare_blk32(wantedblk, iswap32(idblk[i]))) {
808 				if (founddatablk(iswap32(idblk[i])))
809 					return 1;
810 			}
811 			if(idblk[i] != 0)
812 				if (find_indirblks32(iswap32(idblk[i]),
813 				    ind_level, wantedblk))
814 				return 1;
815 		}
816 	}
817 #undef MAXNINDIR
818 	return 0;
819 }
820 
821 
822 static int
find_blks64(uint64_t * buf,int size,uint64_t * wantedblk)823 find_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
824 {
825 	int blk;
826 	for(blk = 0; blk < size; blk++) {
827 		if (buf[blk] == 0)
828 			continue;
829 		if (compare_blk64(wantedblk, iswap64(buf[blk]))) {
830 			if (founddatablk(iswap64(buf[blk])))
831 				return 1;
832 		}
833 	}
834 	return 0;
835 }
836 
837 static int
find_indirblks64(uint64_t blk,int ind_level,uint64_t * wantedblk)838 find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
839 {
840 #define MAXNINDIR	(MAXBSIZE / sizeof(uint64_t))
841 	uint64_t idblk[MAXNINDIR];
842 	size_t i;
843 
844 	bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
845 	    (int)sblock->fs_bsize);
846 	if (ind_level <= 0) {
847 		if (find_blks64(idblk,
848 		    sblock->fs_bsize / sizeof(uint64_t), wantedblk))
849 			return 1;
850 	} else {
851 		ind_level--;
852 		for (i = 0; i < sblock->fs_bsize / sizeof(uint64_t); i++) {
853 			if (compare_blk64(wantedblk, iswap64(idblk[i]))) {
854 				if (founddatablk(iswap64(idblk[i])))
855 					return 1;
856 			}
857 			if (idblk[i] != 0)
858 				if (find_indirblks64(iswap64(idblk[i]),
859 				    ind_level, wantedblk))
860 				return 1;
861 		}
862 	}
863 #undef MAXNINDIR
864 	return 0;
865 }
866 
867 static int
writefileblk(struct wrinfo * wrp,uint64_t blk)868 writefileblk(struct wrinfo *wrp, uint64_t blk)
869 {
870 	char buf[MAXBSIZE];
871 	long long size, rsize;
872 
873 	size = wrp->size - wrp->written_size;
874 	if (size > sblock->fs_bsize)
875 		size = sblock->fs_bsize;
876 	if (size > (long long)sizeof buf) {
877 		warnx("sblock->fs_bsize > MAX_BSIZE");
878 		return -1;
879 	}
880 
881 	rsize = roundup(size, DEV_BSIZE);
882 	if (bread(fsreadfd, buf, FFS_FSBTODB(sblock, blk), rsize) != 0)
883 		return -1;
884 	if (write(wrp->fd, buf, size) != size)
885 		return -1;
886 	wrp->written_size += size;
887 	return 0;
888 }
889 
890 
891 #define CHARS_PER_LINES 70
892 
893 static void
print_blks32(int32_t * buf,int size,uint64_t * blknum,struct wrinfo * wrp)894 print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp)
895 {
896 	int chars;
897 	char prbuf[CHARS_PER_LINES+1];
898 	int blk;
899 
900 	chars = 0;
901 	for (blk = 0; blk < size; blk++, (*blknum)++) {
902 		if (buf[blk] == 0)
903 			continue;
904 		if (wrp && writefileblk(wrp, iswap32(buf[blk])) != 0) {
905 			warn("unable to write block %d", iswap32(buf[blk]));
906 			return;
907 		}
908 		snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk]));
909 		if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
910 			printf("\n");
911 			chars = 0;
912 		}
913 		if (chars == 0)
914 			printf("%" PRIu64 ": ", *blknum);
915 		printf("%s", prbuf);
916 		chars += strlen(prbuf);
917 	}
918 	printf("\n");
919 }
920 
921 static void
print_blks64(int64_t * buf,int size,uint64_t * blknum,struct wrinfo * wrp)922 print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp)
923 {
924 	int chars;
925 	char prbuf[CHARS_PER_LINES+1];
926 	int blk;
927 
928 	chars = 0;
929 	for (blk = 0; blk < size; blk++, (*blknum)++) {
930 		if (buf[blk] == 0)
931 			continue;
932 		if (wrp && writefileblk(wrp, iswap64(buf[blk])) != 0) {
933 			warn("unable to write block %lld",
934 			     (long long)iswap64(buf[blk]));
935 			return;
936 		}
937 		snprintf(prbuf, CHARS_PER_LINES, "%lld ",
938 		    (long long)iswap64(buf[blk]));
939 		if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
940 			printf("\n");
941 			chars = 0;
942 		}
943 		if (chars == 0)
944 			printf("%" PRIu64 ": ", *blknum);
945 		printf("%s", prbuf);
946 		chars += strlen(prbuf);
947 	}
948 	printf("\n");
949 }
950 
951 #undef CHARS_PER_LINES
952 
953 static void
print_indirblks32(uint32_t blk,int ind_level,uint64_t * blknum,struct wrinfo * wrp)954 print_indirblks32(uint32_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp)
955 {
956 #define MAXNINDIR	(MAXBSIZE / sizeof(int32_t))
957 	const int ptrperblk_shift = sblock->fs_bshift - 2;
958 	const int ptrperblk = 1 << ptrperblk_shift;
959 	int32_t idblk[MAXNINDIR];
960 	int i;
961 
962 	if (blk == 0) {
963 		*blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level);
964 		return;
965 	}
966 
967 	printf("Indirect block %lld (level %d):\n", (long long)blk,
968 	    ind_level+1);
969 	bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
970 	    (int)sblock->fs_bsize);
971 	if (ind_level <= 0) {
972 		print_blks32(idblk, ptrperblk, blknum, wrp);
973 	} else {
974 		ind_level--;
975 		for (i = 0; i < ptrperblk; i++)
976 			print_indirblks32(iswap32(idblk[i]), ind_level, blknum,
977 				wrp);
978 	}
979 #undef MAXNINDIR
980 }
981 
982 static void
print_indirblks64(uint64_t blk,int ind_level,uint64_t * blknum,struct wrinfo * wrp)983 print_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp)
984 {
985 #define MAXNINDIR	(MAXBSIZE / sizeof(int64_t))
986 	const int ptrperblk_shift = sblock->fs_bshift - 3;
987 	const int ptrperblk = 1 << ptrperblk_shift;
988 	int64_t idblk[MAXNINDIR];
989 	int i;
990 
991 	if (blk == 0) {
992 		*blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level);
993 		return;
994 	}
995 
996 	printf("Indirect block %lld (level %d):\n", (long long)blk,
997 	    ind_level+1);
998 	bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
999 	    (int)sblock->fs_bsize);
1000 	if (ind_level <= 0) {
1001 		print_blks64(idblk, ptrperblk, blknum, wrp);
1002 	} else {
1003 		ind_level--;
1004 		for (i = 0; i < ptrperblk; i++)
1005 			print_indirblks64(iswap64(idblk[i]), ind_level, blknum,
1006 				wrp);
1007 	}
1008 #undef MAXNINDIR
1009 }
1010 
1011 static int
dolookup(char * name)1012 dolookup(char *name)
1013 {
1014 	struct inodesc idesc;
1015 
1016 	if (!checkactivedir())
1017 		return 0;
1018 	idesc.id_number = curinum;
1019 	idesc.id_func = findino;
1020 	idesc.id_name = name;
1021 	idesc.id_type = DATA;
1022 	idesc.id_fix = IGNORE;
1023 	if (ckinode(curinode, &idesc) & FOUND) {
1024 		curinum = idesc.id_parent;
1025 		curinode = ginode(curinum);
1026 		printactive();
1027 		return 1;
1028 	} else {
1029 		warnx("name `%s' not found in current inode directory", name);
1030 		return 0;
1031 	}
1032 }
1033 
CMDFUNC(focusname)1034 CMDFUNC(focusname)
1035 {
1036 	char   *p, *val;
1037 
1038 	if (!checkactive())
1039 		return 1;
1040 
1041 	ocurrent = curinum;
1042 
1043 	if (argv[1][0] == '/') {
1044 		curinum = UFS_ROOTINO;
1045 		curinode = ginode(UFS_ROOTINO);
1046 	} else {
1047 		if (!checkactivedir())
1048 			return 1;
1049 	}
1050 	for (p = argv[1]; p != NULL;) {
1051 		while ((val = strsep(&p, "/")) != NULL && *val == '\0');
1052 		if (val) {
1053 			printf("component `%s': ", val);
1054 			fflush(stdout);
1055 			if (!dolookup(val)) {
1056 				curinode = ginode(curinum);
1057 				return (1);
1058 			}
1059 		}
1060 	}
1061 	return 0;
1062 }
1063 
CMDFUNC(ln)1064 CMDFUNC(ln)
1065 {
1066 	ino_t   inum;
1067 	int     rval;
1068 	char   *cp;
1069 
1070 	GETINUM(1, inum);
1071 
1072 	if (!checkactivedir())
1073 		return 1;
1074 	rval = makeentry(curinum, inum, argv[2]);
1075 	if (rval)
1076 		printf("Ino %llu entered as `%s'\n", (unsigned long long)inum,
1077 		    argv[2]);
1078 	else
1079 		printf("could not enter name? weird.\n");
1080 	curinode = ginode(curinum);
1081 	return rval;
1082 }
1083 
CMDFUNC(rm)1084 CMDFUNC(rm)
1085 {
1086 	int     rval;
1087 
1088 	if (!checkactivedir())
1089 		return 1;
1090 	rval = changeino(curinum, argv[1], 0);
1091 	if (rval & ALTERED) {
1092 		printf("Name `%s' removed\n", argv[1]);
1093 		return 0;
1094 	} else {
1095 		printf("could not remove name? weird.\n");
1096 		return 1;
1097 	}
1098 }
1099 
1100 static long slotcount, desired;
1101 
1102 static int
chinumfunc(struct inodesc * idesc)1103 chinumfunc(struct inodesc *idesc)
1104 {
1105 	struct direct *dirp = idesc->id_dirp;
1106 
1107 	if (slotcount++ == desired) {
1108 		dirp->d_ino = iswap32(idesc->id_parent);
1109 		return STOP | ALTERED | FOUND;
1110 	}
1111 	return KEEPON;
1112 }
1113 
CMDFUNC(chinum)1114 CMDFUNC(chinum)
1115 {
1116 	char   *cp;
1117 	ino_t   inum;
1118 	struct inodesc idesc;
1119 
1120 	slotcount = 0;
1121 	if (!checkactivedir())
1122 		return 1;
1123 	GETINUM(2, inum);
1124 
1125 	desired = strtol(argv[1], &cp, 0);
1126 	if (cp == argv[1] || *cp != '\0' || desired < 0) {
1127 		printf("invalid slot number `%s'\n", argv[1]);
1128 		return 1;
1129 	}
1130 	idesc.id_number = curinum;
1131 	idesc.id_func = chinumfunc;
1132 	idesc.id_fix = IGNORE;
1133 	idesc.id_type = DATA;
1134 	idesc.id_parent = inum;	/* XXX convenient hiding place */
1135 
1136 	if (ckinode(curinode, &idesc) & FOUND)
1137 		return 0;
1138 	else {
1139 		warnx("no %sth slot in current directory", argv[1]);
1140 		return 1;
1141 	}
1142 }
1143 
1144 static int
chnamefunc(struct inodesc * idesc)1145 chnamefunc(struct inodesc *idesc)
1146 {
1147 	struct direct *dirp = idesc->id_dirp;
1148 	struct direct testdir;
1149 
1150 	if (slotcount++ == desired) {
1151 		/* will name fit? */
1152 		testdir.d_namlen = strlen(idesc->id_name);
1153 		if (UFS_DIRSIZ(UFS_NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) {
1154 			dirp->d_namlen = testdir.d_namlen;
1155 			strlcpy(dirp->d_name, idesc->id_name,
1156 			    sizeof(dirp->d_name));
1157 			return STOP | ALTERED | FOUND;
1158 		} else
1159 			return STOP | FOUND;	/* won't fit, so give up */
1160 	}
1161 	return KEEPON;
1162 }
1163 
CMDFUNC(chname)1164 CMDFUNC(chname)
1165 {
1166 	int     rval;
1167 	char   *cp;
1168 	struct inodesc idesc;
1169 
1170 	slotcount = 0;
1171 	if (!checkactivedir())
1172 		return 1;
1173 
1174 	desired = strtoul(argv[1], &cp, 0);
1175 	if (cp == argv[1] || *cp != '\0') {
1176 		printf("invalid slot number `%s'\n", argv[1]);
1177 		return 1;
1178 	}
1179 	idesc.id_number = curinum;
1180 	idesc.id_func = chnamefunc;
1181 	idesc.id_fix = IGNORE;
1182 	idesc.id_type = DATA;
1183 	idesc.id_name = argv[2];
1184 
1185 	rval = ckinode(curinode, &idesc);
1186 	if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED))
1187 		return 0;
1188 	else
1189 		if (rval & FOUND) {
1190 			warnx("new name `%s' does not fit in slot %s",
1191 			    argv[2], argv[1]);
1192 			return 1;
1193 		} else {
1194 			warnx("no %sth slot in current directory", argv[1]);
1195 			return 1;
1196 		}
1197 }
1198 
1199 static int
chreclenfunc(struct inodesc * idesc)1200 chreclenfunc(struct inodesc *idesc)
1201 {
1202 	struct direct *dirp = idesc->id_dirp;
1203 
1204 	if (slotcount++ == desired) {
1205 		dirp->d_reclen = iswap16(idesc->id_parent);
1206 		return STOP | ALTERED | FOUND;
1207 	}
1208 	return KEEPON;
1209 }
1210 
CMDFUNC(chreclen)1211 CMDFUNC(chreclen)
1212 {
1213 	char   *cp;
1214 	uint32_t reclen;
1215 	struct inodesc idesc;
1216 
1217 	slotcount = 0;
1218 	if (!checkactivedir())
1219 		return 1;
1220 
1221 	desired = strtoul(argv[1], &cp, 0);
1222 	if (cp == argv[1] || *cp != '\0') {
1223 		printf("invalid slot number `%s'\n", argv[1]);
1224 		return 1;
1225 	}
1226 	reclen = strtoul(argv[2], &cp, 0);
1227 	if (reclen >= UINT16_MAX) {
1228 		printf("invalid reclen `%s'\n", argv[2]);
1229 		return 1;
1230 	}
1231 
1232 	idesc.id_number = curinum;
1233 	idesc.id_func = chreclenfunc;
1234 	idesc.id_fix = IGNORE;
1235 	idesc.id_type = DATA;
1236 	idesc.id_parent = reclen;	/* XXX convenient hiding place */
1237 
1238 	if (ckinode(curinode, &idesc) & FOUND)
1239 		return 0;
1240 	else {
1241 		warnx("no %sth slot in current directory", argv[1]);
1242 		return 1;
1243 	}
1244 }
1245 
1246 static struct typemap {
1247 	const char *typename;
1248 	int     typebits;
1249 }       typenamemap[] = {
1250 	{ "file", IFREG },
1251 	{ "dir", IFDIR },
1252 	{ "socket", IFSOCK },
1253 	{ "fifo", IFIFO },
1254 	{"link", IFLNK},
1255 	{"chr", IFCHR},
1256 	{"blk", IFBLK},
1257 };
1258 
CMDFUNC(newtype)1259 CMDFUNC(newtype)
1260 {
1261 	int     type;
1262 	uint16_t mode;
1263 	struct typemap *tp;
1264 
1265 	if (!checkactive())
1266 		return 1;
1267 	mode = iswap16(DIP(curinode, mode));
1268 	type = mode & IFMT;
1269 	for (tp = typenamemap;
1270 	    tp < &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)];
1271 	    tp++) {
1272 		if (!strcmp(argv[1], tp->typename)) {
1273 			printf("setting type to %s\n", tp->typename);
1274 			type = tp->typebits;
1275 			break;
1276 		}
1277 	}
1278 	if (tp == &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]) {
1279 		warnx("type `%s' not known", argv[1]);
1280 		warnx("try one of `file', `dir', `socket', `fifo'");
1281 		return 1;
1282 	}
1283 	DIP_SET(curinode, mode, iswap16((mode & ~IFMT) | type));
1284 	inodirty();
1285 	printactive();
1286 	return 0;
1287 }
1288 
CMDFUNC(chmode)1289 CMDFUNC(chmode)
1290 {
1291 	long    modebits;
1292 	char   *cp;
1293 	uint16_t mode;
1294 
1295 	if (!checkactive())
1296 		return 1;
1297 
1298 	modebits = strtol(argv[1], &cp, 8);
1299 	if (cp == argv[1] || *cp != '\0') {
1300 		warnx("bad modebits `%s'", argv[1]);
1301 		return 1;
1302 	}
1303 	mode = iswap16(DIP(curinode, mode));
1304 	DIP_SET(curinode, mode, iswap16((mode & ~07777) | modebits));
1305 	inodirty();
1306 	printactive();
1307 	return 0;
1308 }
1309 
CMDFUNC(chlen)1310 CMDFUNC(chlen)
1311 {
1312 	off_t    len;
1313 	char   *cp;
1314 
1315 	if (!checkactive())
1316 		return 1;
1317 
1318 	len = strtoull(argv[1], &cp, 0);
1319 	if (cp == argv[1] || *cp != '\0' || len < 0) {
1320 		warnx("bad length '%s'", argv[1]);
1321 		return 1;
1322 	}
1323 	DIP_SET(curinode, size, iswap64(len));
1324 	inodirty();
1325 	printactive();
1326 	return 0;
1327 }
1328 
CMDFUNC(chaflags)1329 CMDFUNC(chaflags)
1330 {
1331 	u_long  flags;
1332 	char   *cp;
1333 
1334 	if (!checkactive())
1335 		return 1;
1336 
1337 	flags = strtoul(argv[1], &cp, 0);
1338 	if (cp == argv[1] || *cp != '\0') {
1339 		warnx("bad flags `%s'", argv[1]);
1340 		return 1;
1341 	}
1342 	if (flags > UINT_MAX) {
1343 		warnx("flags set beyond 32-bit range of field (0x%lx)",
1344 		    flags);
1345 		return (1);
1346 	}
1347 	DIP_SET(curinode, flags, iswap32(flags));
1348 	inodirty();
1349 	printactive();
1350 	return 0;
1351 }
1352 
CMDFUNC(chgen)1353 CMDFUNC(chgen)
1354 {
1355 	long    gen;
1356 	char   *cp;
1357 
1358 	if (!checkactive())
1359 		return 1;
1360 
1361 	gen = strtol(argv[1], &cp, 0);
1362 	if (cp == argv[1] || *cp != '\0') {
1363 		warnx("bad gen `%s'", argv[1]);
1364 		return 1;
1365 	}
1366 	if (gen > INT_MAX || gen < INT_MIN) {
1367 		warnx("gen set beyond 32-bit range of field (0x%lx)", gen);
1368 		return (1);
1369 	}
1370 	DIP_SET(curinode, gen, iswap32(gen));
1371 	inodirty();
1372 	printactive();
1373 	return 0;
1374 }
1375 
CMDFUNC(chextsize)1376 CMDFUNC(chextsize)
1377 {
1378 	uint32_t extsize;
1379 	char *cp;
1380 
1381 	if (!is_ufs2)
1382 		return 1;
1383 	if (!checkactive())
1384 		return 1;
1385 
1386 	extsize = strtol(argv[1], &cp, 0);
1387 	if (cp == argv[1] || *cp != '\0') {
1388 		warnx("bad extsize `%s'", argv[1]);
1389 		return 1;
1390 	}
1391 
1392 	curinode->dp2.di_extsize = extsize;
1393 	inodirty();
1394 	printactive();
1395 	return 0;
1396 }
1397 
CMDFUNC(chblocks)1398 CMDFUNC(chblocks)
1399 {
1400 	uint64_t blocks;
1401 	char *cp;
1402 
1403 	if (!checkactive())
1404 		return 1;
1405 
1406 	blocks = strtoll(argv[1], &cp, 0);
1407 	if (cp == argv[1] || *cp != '\0') {
1408 		warnx("bad blocks `%s'", argv[1]);
1409 		return 1;
1410 	}
1411 
1412 	DIP_SET(curinode, blocks, blocks);
1413 	inodirty();
1414 	printactive();
1415 	return 0;
1416 }
1417 
CMDFUNC(chdb)1418 CMDFUNC(chdb)
1419 {
1420 	unsigned int idx;
1421 	daddr_t bno;
1422 	char *cp;
1423 
1424 	if (!checkactive())
1425 		return 1;
1426 
1427 	idx = strtoull(argv[1], &cp, 0);
1428 	if (cp == argv[1] || *cp != '\0') {
1429 		warnx("bad pointer idx `%s'", argv[1]);
1430 		return 1;
1431 	}
1432 	bno = strtoll(argv[2], &cp, 0);
1433 	if (cp == argv[2] || *cp != '\0') {
1434 		warnx("bad block number `%s'", argv[2]);
1435 		return 1;
1436 	}
1437 	if (idx >= UFS_NDADDR) {
1438 		warnx("pointer index %d is out of range", idx);
1439 		return 1;
1440 	}
1441 
1442 	DIP_SET(curinode, db[idx], bno);
1443 	inodirty();
1444 	printactive();
1445 	return 0;
1446 }
1447 
CMDFUNC(chib)1448 CMDFUNC(chib)
1449 {
1450 	unsigned int idx;
1451 	daddr_t bno;
1452 	char *cp;
1453 
1454 	if (!checkactive())
1455 		return 1;
1456 
1457 	idx = strtoull(argv[1], &cp, 0);
1458 	if (cp == argv[1] || *cp != '\0') {
1459 		warnx("bad pointer idx `%s'", argv[1]);
1460 		return 1;
1461 	}
1462 	bno = strtoll(argv[2], &cp, 0);
1463 	if (cp == argv[2] || *cp != '\0') {
1464 		warnx("bad block number `%s'", argv[2]);
1465 		return 1;
1466 	}
1467 	if (idx >= UFS_NIADDR) {
1468 		warnx("pointer index %d is out of range", idx);
1469 		return 1;
1470 	}
1471 
1472 	DIP_SET(curinode, ib[idx], bno);
1473 	inodirty();
1474 	printactive();
1475 	return 0;
1476 }
1477 
CMDFUNC(chextb)1478 CMDFUNC(chextb)
1479 {
1480 	unsigned int idx;
1481 	daddr_t bno;
1482 	char *cp;
1483 
1484 	if (!checkactive())
1485 		return 1;
1486 
1487 	idx = strtoull(argv[1], &cp, 0);
1488 	if (cp == argv[1] || *cp != '\0') {
1489 		warnx("bad pointer idx `%s'", argv[1]);
1490 		return 1;
1491 	}
1492 	bno = strtoll(argv[2], &cp, 0);
1493 	if (cp == argv[2] || *cp != '\0') {
1494 		warnx("bad block number `%s'", argv[2]);
1495 		return 1;
1496 	}
1497 	if (idx >= UFS_NXADDR) {
1498 		warnx("pointer index %d is out of range", idx);
1499 		return 1;
1500 	}
1501 
1502 	curinode->dp2.di_extb[idx] = bno;
1503 	inodirty();
1504 	printactive();
1505 	return 0;
1506 }
1507 
CMDFUNC(chfreelink)1508 CMDFUNC(chfreelink)
1509 {
1510 #if 0
1511 	ino_t freelink;
1512 	char *cp;
1513 
1514 	if (!checkactive())
1515 		return 1;
1516 
1517 	freelink = strtoll(argv[1], &cp, 0);
1518 	if (cp == argv[1] || *cp != '\0') {
1519 		warnx("bad freelink `%s'", argv[1]);
1520 		return 1;
1521 	}
1522 
1523 	DIP_SET(curinode, freelink, freelink);
1524 	inodirty();
1525 	printactive();
1526 #endif
1527 	return 0;
1528 }
1529 
CMDFUNC(linkcount)1530 CMDFUNC(linkcount)
1531 {
1532 	int     lcnt;
1533 	char   *cp;
1534 
1535 	if (!checkactive())
1536 		return 1;
1537 
1538 	lcnt = strtol(argv[1], &cp, 0);
1539 	if (cp == argv[1] || *cp != '\0') {
1540 		warnx("bad link count `%s'", argv[1]);
1541 		return 1;
1542 	}
1543 	if (lcnt > USHRT_MAX || lcnt < 0) {
1544 		warnx("max link count is %d", USHRT_MAX);
1545 		return 1;
1546 	}
1547 	DIP_SET(curinode, nlink, iswap16(lcnt));
1548 	inodirty();
1549 	printactive();
1550 	return 0;
1551 }
1552 
CMDFUNC(chowner)1553 CMDFUNC(chowner)
1554 {
1555 	unsigned long uid;
1556 	char   *cp;
1557 	struct passwd *pwd;
1558 
1559 	if (!checkactive())
1560 		return 1;
1561 
1562 	uid = strtoul(argv[1], &cp, 0);
1563 	if (cp == argv[1] || *cp != '\0') {
1564 		/* try looking up name */
1565 		if ((pwd = getpwnam(argv[1])) != 0) {
1566 			uid = pwd->pw_uid;
1567 		} else {
1568 			warnx("bad uid `%s'", argv[1]);
1569 			return 1;
1570 		}
1571 	}
1572 	if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT)
1573 		curinode->dp1.di_ouid = iswap32(uid);
1574 	else
1575 		DIP_SET(curinode, uid, iswap32(uid));
1576 	inodirty();
1577 	printactive();
1578 	return 0;
1579 }
1580 
CMDFUNC(chgroup)1581 CMDFUNC(chgroup)
1582 {
1583 	unsigned long gid;
1584 	char   *cp;
1585 	struct group *grp;
1586 
1587 	if (!checkactive())
1588 		return 1;
1589 
1590 	gid = strtoul(argv[1], &cp, 0);
1591 	if (cp == argv[1] || *cp != '\0') {
1592 		if ((grp = getgrnam(argv[1])) != 0) {
1593 			gid = grp->gr_gid;
1594 		} else {
1595 			warnx("bad gid `%s'", argv[1]);
1596 			return 1;
1597 		}
1598 	}
1599 	if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT)
1600 		curinode->dp1.di_ogid = iswap32(gid);
1601 	else
1602 		DIP_SET(curinode, gid, iswap32(gid));
1603 	inodirty();
1604 	printactive();
1605 	return 0;
1606 }
1607 
1608 static int
dotime(char * name,int64_t * rsec,int32_t * rnsec)1609 dotime(char *name, int64_t *rsec, int32_t *rnsec)
1610 {
1611 	char   *p, *val;
1612 	struct tm t;
1613 	int64_t sec;
1614 	int32_t nsec;
1615 	p = strchr(name, '.');
1616 	if (p) {
1617 		*p = '\0';
1618 		nsec = strtoul(++p, &val, 0);
1619 		if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
1620 			warnx("invalid nanoseconds");
1621 			goto badformat;
1622 		}
1623 	} else
1624 		nsec = 0;
1625 	if (strlen(name) != 14) {
1626 badformat:
1627 		warnx("date format: YYYYMMDDHHMMSS[.nsec]");
1628 		return 1;
1629 	}
1630 	for (p = name; *p; p++)
1631 		if (*p < '0' || *p > '9')
1632 			goto badformat;
1633 
1634 	p = name;
1635 #define VAL() ((*p++) - '0')
1636 	t.tm_year = VAL();
1637 	t.tm_year = VAL() + t.tm_year * 10;
1638 	t.tm_year = VAL() + t.tm_year * 10;
1639 	t.tm_year = VAL() + t.tm_year * 10 - 1900;
1640 	t.tm_mon = VAL();
1641 	t.tm_mon = VAL() + t.tm_mon * 10 - 1;
1642 	t.tm_mday = VAL();
1643 	t.tm_mday = VAL() + t.tm_mday * 10;
1644 	t.tm_hour = VAL();
1645 	t.tm_hour = VAL() + t.tm_hour * 10;
1646 	t.tm_min = VAL();
1647 	t.tm_min = VAL() + t.tm_min * 10;
1648 	t.tm_sec = VAL();
1649 	t.tm_sec = VAL() + t.tm_sec * 10;
1650 	t.tm_isdst = -1;
1651 
1652 	sec = mktime(&t);
1653 	if (sec == -1) {
1654 		warnx("date/time out of range");
1655 		return 1;
1656 	}
1657 	*rsec = iswap64(sec);
1658 	*rnsec = iswap32(nsec);
1659 	return 0;
1660 }
1661 
CMDFUNC(chmtime)1662 CMDFUNC(chmtime)
1663 {
1664 	int64_t rsec;
1665 	int32_t nsec;
1666 
1667 	if (!checkactive())
1668 		return 1;
1669 	if (dotime(argv[1], &rsec, &nsec))
1670 		return 1;
1671 	DIP_SET(curinode, mtime, rsec);
1672 	DIP_SET(curinode, mtimensec, nsec);
1673 	inodirty();
1674 	printactive();
1675 	return 0;
1676 }
1677 
CMDFUNC(chatime)1678 CMDFUNC(chatime)
1679 {
1680 	int64_t rsec;
1681 	int32_t nsec;
1682 
1683 	if (!checkactive())
1684 		return 1;
1685 	if (dotime(argv[1], &rsec, &nsec))
1686 		return 1;
1687 	DIP_SET(curinode, atime, rsec);
1688 	DIP_SET(curinode, atimensec, nsec);
1689 	inodirty();
1690 	printactive();
1691 	return 0;
1692 }
1693 
CMDFUNC(chctime)1694 CMDFUNC(chctime)
1695 {
1696 	int64_t rsec;
1697 	int32_t nsec;
1698 
1699 	if (!checkactive())
1700 		return 1;
1701 	if (dotime(argv[1], &rsec, &nsec))
1702 		return 1;
1703 	DIP_SET(curinode, ctime, rsec);
1704 	DIP_SET(curinode, ctimensec, nsec);
1705 	inodirty();
1706 	printactive();
1707 	return 0;
1708 }
1709 
CMDFUNC(chbirthtime)1710 CMDFUNC(chbirthtime)
1711 {
1712 	int64_t rsec;
1713 	int32_t nsec;
1714 
1715 	if (!is_ufs2) {
1716 		warnx("birthtime can only be set in ufs2");
1717 		return 1;
1718 	}
1719 	if (!checkactive())
1720 		return 1;
1721 
1722 	if (dotime(argv[1], &rsec, &nsec))
1723 		return 1;
1724 	curinode->dp2.di_birthtime = rsec;
1725 	curinode->dp2.di_birthnsec = nsec;
1726 	inodirty();
1727 	printactive();
1728 	return 0;
1729 }
1730 
CMDFUNC(iptrs)1731 CMDFUNC(iptrs)
1732 {
1733 	int i;
1734 
1735 	if (!checkactive())
1736 		return 1;
1737 	for (i = 0; i < UFS_NDADDR; i++)
1738 		printf("di_db %d %ju\n", i, DIP(curinode, db[i]));
1739 	for (i = 0; i < UFS_NIADDR; i++)
1740 		printf("di_ib %d %ju\n", i, DIP(curinode, ib[i]));
1741 	if (is_ufs2)
1742 		for (i = 0; i < UFS_NXADDR; i++)
1743 			printf("di_extb %d %ju\n", i, curinode->dp2.di_extb[i]);
1744 	return 0;
1745 }
1746 
CMDFUNC(saveea)1747 CMDFUNC(saveea)
1748 {
1749 	struct wrinfo wrinfo;
1750 	uint64_t blkno = 0;
1751 
1752 	if (!is_ufs2) {
1753 		warnx("dumping extattrs is only supported for ufs2");
1754 		return 1;
1755 	}
1756 	if (!checkactive())
1757 		return 1;
1758 
1759 	wrinfo.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644);
1760 	if (wrinfo.fd == -1) {
1761 		warn("unable to create file %s", argv[1]);
1762 		return 0;
1763 	}
1764 
1765 	wrinfo.size = iswap32(curinode->dp2.di_extsize);
1766 	wrinfo.written_size = 0;
1767 	print_blks64(curinode->dp2.di_extb, UFS_NXADDR, &blkno, &wrinfo);
1768 	return 0;
1769 }
1770