xref: /netbsd-src/sbin/fsdb/fsdb.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: fsdb.c,v 1.43 2011/08/29 14:34:59 joerg Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996 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.43 2011/08/29 14:34:59 joerg 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 
55 #include <ufs/ufs/dinode.h>
56 #include <ufs/ufs/dir.h>
57 #include <ufs/ffs/fs.h>
58 #include <ufs/ffs/ffs_extern.h>
59 
60 #include "fsdb.h"
61 #include "fsck.h"
62 #include "extern.h"
63 
64 __dead static void usage(void);
65 static int cmdloop(void);
66 static char *prompt(EditLine *);
67 static int scannames(struct inodesc *);
68 static int dolookup(char *);
69 static int chinumfunc(struct inodesc *);
70 static int chnamefunc(struct inodesc *);
71 static int dotime(char *, int32_t *, int32_t *);
72 static void print_blks32(int32_t *buf, int size, uint64_t *blknum);
73 static void print_blks64(int64_t *buf, int size, uint64_t *blknum);
74 static void print_indirblks32(uint32_t blk, int ind_level,
75     uint64_t *blknum);
76 static void print_indirblks64(uint64_t blk, int ind_level,
77     uint64_t *blknum);
78 static int compare_blk32(uint32_t *, uint32_t);
79 static int compare_blk64(uint64_t *, uint64_t);
80 static int founddatablk(uint64_t);
81 static int find_blks32(uint32_t *buf, int size, uint32_t *blknum);
82 static int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
83 static int find_indirblks32(uint32_t blk, int ind_level,
84 						uint32_t *blknum);
85 static int find_indirblks64(uint64_t blk, int ind_level,
86 						uint64_t *blknum);
87 
88 union dinode *curinode;
89 ino_t   curinum;
90 
91 static void
92 usage(void)
93 {
94 	errx(1, "usage: %s [-dFn] -f <fsname>", getprogname());
95 }
96 /*
97  * We suck in lots of fsck code, and just pick & choose the stuff we want.
98  *
99  * fsreadfd is set up to read from the file system, fswritefd to write to
100  * the file system.
101  */
102 int
103 main(int argc, char *argv[])
104 {
105 	int     ch, rval;
106 	char   *fsys = NULL;
107 
108 	forceimage = 0;
109 	debug = 0;
110 	isappleufs = 0;
111 	while ((ch = getopt(argc, argv, "dFf:n")) != -1) {
112 		switch (ch) {
113 		case 'd':
114 			debug++;
115 			break;
116 		case 'F':
117 			forceimage = 1;
118 			break;
119 		case 'f':
120 			fsys = optarg;
121 			break;
122 		case 'n':
123 			nflag++;
124 			break;
125 		default:
126 			usage();
127 		}
128 	}
129 	if (fsys == NULL)
130 		usage();
131 	endian = 0;
132 	if (setup(fsys, fsys) <= 0)
133 		errx(1, "cannot set up file system `%s'", fsys);
134 	printf("Editing file system `%s'\nLast Mounted on %s\n", fsys,
135 	    sblock->fs_fsmnt);
136 	rval = cmdloop();
137 	if (nflag)
138 		exit(rval);
139 	sblock->fs_clean = 0;	/* mark it dirty */
140 	sbdirty();
141 	markclean = 0;
142 	ckfini(1);
143 	printf("*** FILE SYSTEM MARKED DIRTY\n");
144 	printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
145 	printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
146 	exit(rval);
147 }
148 
149 #define CMDFUNC(func) static int func (int argc, char *argv[])
150 #define CMDFUNCSTART(func) static int func(argc, argv)		\
151 				int argc;			\
152 				char *argv[];
153 
154 CMDFUNC(helpfn);
155 CMDFUNC(focus);			/* focus on inode */
156 CMDFUNC(active);		/* print active inode */
157 CMDFUNC(focusname);		/* focus by name */
158 CMDFUNC(zapi);			/* clear inode */
159 CMDFUNC(uplink);		/* incr link */
160 CMDFUNC(downlink);		/* decr link */
161 CMDFUNC(linkcount);		/* set link count */
162 CMDFUNC(quit);			/* quit */
163 CMDFUNC(ls);			/* list directory */
164 CMDFUNC(blks);			/* list blocks */
165 CMDFUNC(findblk);		/* find block */
166 CMDFUNC(rm);			/* remove name */
167 CMDFUNC(ln);			/* add name */
168 CMDFUNC(newtype);		/* change type */
169 CMDFUNC(chmode);		/* change mode */
170 CMDFUNC(chlen);			/* change length */
171 CMDFUNC(chaflags);		/* change flags */
172 CMDFUNC(chgen);			/* change generation */
173 CMDFUNC(chowner);		/* change owner */
174 CMDFUNC(chgroup);		/* Change group */
175 CMDFUNC(back);			/* pop back to last ino */
176 CMDFUNC(chmtime);		/* Change mtime */
177 CMDFUNC(chctime);		/* Change ctime */
178 CMDFUNC(chatime);		/* Change atime */
179 CMDFUNC(chinum);		/* Change inode # of dirent */
180 CMDFUNC(chname);		/* Change dirname of dirent */
181 
182 static struct cmdtable cmds[] = {
183 	{"help", "Print out help", 1, 1, helpfn},
184 	{"?", "Print out help", 1, 1, helpfn},
185 	{"inode", "Set active inode to INUM", 2, 2, focus},
186 	{"clri", "Clear inode INUM", 2, 2, zapi},
187 	{"lookup", "Set active inode by looking up NAME", 2, 2, focusname},
188 	{"cd", "Set active inode by looking up NAME", 2, 2, focusname},
189 	{"back", "Go to previous active inode", 1, 1, back},
190 	{"active", "Print active inode", 1, 1, active},
191 	{"print", "Print active inode", 1, 1, active},
192 	{"uplink", "Increment link count", 1, 1, uplink},
193 	{"downlink", "Decrement link count", 1, 1, downlink},
194 	{"linkcount", "Set link count to COUNT", 2, 2, linkcount},
195 	{"ls", "List current inode as directory", 1, 1, ls},
196 	{"blks", "List current inode's data blocks", 1, 1, blks},
197 	{"findblk", "Find inode owning disk block(s)", 2, 33, findblk},
198 	{"rm", "Remove NAME from current inode directory", 2, 2, rm},
199 	{"del", "Remove NAME from current inode directory", 2, 2, rm},
200 	{"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln},
201 	{"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum},
202 	{"chname", "Change dir entry number INDEX to NAME", 3, 3, chname},
203 	{"chtype", "Change type of current inode to TYPE", 2, 2, newtype},
204 	{"chmod", "Change mode of current inode to MODE", 2, 2, chmode},
205 	{"chown", "Change owner of current inode to OWNER", 2, 2, chowner},
206 	{"chlen", "Change length of current inode to LENGTH", 2, 2, chlen},
207 	{"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup},
208 	{"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags},
209 	{"chgen", "Change generation number of current inode to GEN", 2, 2,
210 		    chgen},
211 	{"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime},
212 	{"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime},
213 	{"atime", "Change atime of current inode to ATIME", 2, 2, chatime},
214 	{"quit", "Exit", 1, 1, quit},
215 	{"q", "Exit", 1, 1, quit},
216 	{"exit", "Exit", 1, 1, quit},
217 	{ .cmd = NULL},
218 };
219 
220 static int
221 helpfn(int argc, char *argv[])
222 {
223 	struct cmdtable *cmdtp;
224 
225 	printf("Commands are:\n%-10s %5s %5s   %s\n",
226 	    "command", "min argc", "max argc", "what");
227 
228 	for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
229 		printf("%-10s %5u %5u   %s\n",
230 		    cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
231 	return 0;
232 }
233 
234 static char *
235 prompt(EditLine *el)
236 {
237 	static char pstring[64];
238 	snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ",
239 	    (unsigned long long)curinum);
240 	return pstring;
241 }
242 
243 
244 static int
245 cmdloop(void)
246 {
247 	char   *line;
248 	const char *elline;
249 	int     cmd_argc, rval = 0, known;
250 #define scratch known
251 	char  **cmd_argv;
252 	struct cmdtable *cmdp;
253 	History *hist;
254 	HistEvent he;
255 	EditLine *elptr;
256 
257 	curinode = ginode(ROOTINO);
258 	curinum = ROOTINO;
259 	printactive();
260 
261 	hist = history_init();
262 	history(hist, &he, H_SETSIZE, 100);	/* 100 elt history buffer */
263 
264 	elptr = el_init(getprogname(), stdin, stdout, stderr);
265 	el_set(elptr, EL_EDITOR, "emacs");
266 	el_set(elptr, EL_PROMPT, prompt);
267 	el_set(elptr, EL_HIST, history, hist);
268 	el_source(elptr, NULL);
269 
270 	while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
271 		if (debug)
272 			printf("command `%s'\n", elline);
273 
274 		history(hist, &he, H_ENTER, elline);
275 
276 		line = strdup(elline);
277 		cmd_argv = crack(line, &cmd_argc);
278 		if (cmd_argc) {
279 			/*
280 		         * el_parse returns -1 to signal that it's not been
281 		         * handled internally.
282 		         */
283 			if (el_parse(elptr, cmd_argc, (void *)cmd_argv) != -1)
284 				continue;
285 			known = 0;
286 			for (cmdp = cmds; cmdp->cmd; cmdp++) {
287 				if (!strcmp(cmdp->cmd, cmd_argv[0])) {
288 					if (cmd_argc >= cmdp->minargc &&
289 					    cmd_argc <= cmdp->maxargc)
290 						rval =
291 						    (*cmdp->handler)(cmd_argc,
292 							cmd_argv);
293 					else
294 						rval = argcount(cmdp, cmd_argc,
295 						    cmd_argv);
296 					known = 1;
297 					break;
298 				}
299 			}
300 			if (!known)
301 				warnx("unknown command `%s'", cmd_argv[0]),
302 				    rval = 1;
303 		} else
304 			rval = 0;
305 		free(line);
306 		if (rval < 0)
307 			return rval;
308 		if (rval)
309 			warnx("rval was %d", rval);
310 	}
311 	el_end(elptr);
312 	history_end(hist);
313 	return rval;
314 }
315 
316 static ino_t ocurrent;
317 
318 #define GETINUM(ac,inum)    inum = strtoull(argv[ac], &cp, 0); \
319     if (inum < ROOTINO || inum >= maxino || cp == argv[ac] || *cp != '\0' ) { \
320 	printf("inode %llu out of range; range is [%llu,%llu]\n", \
321 	   (unsigned long long)inum, (unsigned long long)ROOTINO, \
322 	   (unsigned long long)maxino); \
323 	return 1; \
324     }
325 
326 /*
327  * Focus on given inode number
328  */
329 CMDFUNCSTART(focus)
330 {
331 	ino_t   inum;
332 	char   *cp;
333 
334 	GETINUM(1, inum);
335 	curinode = ginode(inum);
336 	ocurrent = curinum;
337 	curinum = inum;
338 	printactive();
339 	return 0;
340 }
341 
342 CMDFUNCSTART(back)
343 {
344 	curinum = ocurrent;
345 	curinode = ginode(curinum);
346 	printactive();
347 	return 0;
348 }
349 
350 CMDFUNCSTART(zapi)
351 {
352 	ino_t   inum;
353 	union dinode *dp;
354 	char   *cp;
355 
356 	GETINUM(1, inum);
357 	dp = ginode(inum);
358 	clearinode(dp);
359 	inodirty();
360 	if (curinode)		/* re-set after potential change */
361 		curinode = ginode(curinum);
362 	return 0;
363 }
364 
365 CMDFUNCSTART(active)
366 {
367 	printactive();
368 	return 0;
369 }
370 
371 CMDFUNCSTART(quit)
372 {
373 	return -1;
374 }
375 
376 CMDFUNCSTART(uplink)
377 {
378 	int16_t nlink;
379 
380 	if (!checkactive())
381 		return 1;
382 	nlink = iswap16(DIP(curinode, nlink));
383 	nlink++;
384 	DIP_SET(curinode, nlink, iswap16(nlink));
385 	printf("inode %llu link count now %d\n", (unsigned long long)curinum,
386 	    nlink);
387 	inodirty();
388 	return 0;
389 }
390 
391 CMDFUNCSTART(downlink)
392 {
393 	int16_t nlink;
394 
395 	if (!checkactive())
396 		return 1;
397 	nlink = iswap16(DIP(curinode, nlink));
398 	nlink--;
399 	DIP_SET(curinode, nlink, iswap16(nlink));
400 	printf("inode %llu link count now %d\n", (unsigned long long)curinum,
401 	    nlink);
402 	inodirty();
403 	return 0;
404 }
405 
406 static const char *typename[] = {
407 	"unknown",
408 	"fifo",
409 	"char special",
410 	"unregistered #3",
411 	"directory",
412 	"unregistered #5",
413 	"blk special",
414 	"unregistered #7",
415 	"regular",
416 	"unregistered #9",
417 	"symlink",
418 	"unregistered #11",
419 	"socket",
420 	"unregistered #13",
421 	"whiteout",
422 };
423 
424 static int slot;
425 
426 static int
427 scannames(struct inodesc *idesc)
428 {
429 	struct direct *dirp = idesc->id_dirp;
430 
431 	printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
432 	    slot++, iswap32(dirp->d_ino), iswap16(dirp->d_reclen),
433 		typename[dirp->d_type],
434 	    dirp->d_namlen, dirp->d_name);
435 	return (KEEPON);
436 }
437 
438 CMDFUNCSTART(ls)
439 {
440 	struct inodesc idesc;
441 	checkactivedir();	/* let it go on anyway */
442 
443 	slot = 0;
444 	idesc.id_number = curinum;
445 	idesc.id_func = scannames;
446 	idesc.id_type = DATA;
447 	idesc.id_fix = IGNORE;
448 	ckinode(curinode, &idesc);
449 	curinode = ginode(curinum);
450 
451 	return 0;
452 }
453 
454 CMDFUNCSTART(blks)
455 {
456 	uint64_t blkno = 0;
457 	int i, type;
458 	if (!curinode) {
459 		warnx("no current inode");
460 		return 0;
461 	}
462 	type = iswap16(DIP(curinode, mode)) & IFMT;
463 	if (type != IFDIR && type != IFREG) {
464 		warnx("inode %llu not a file or directory",
465 		    (unsigned long long)curinum);
466 		return 0;
467 	}
468 	if (is_ufs2) {
469 		printf("I=%llu %lld blocks\n", (unsigned long long)curinum,
470 		    (long long)(iswap64(curinode->dp2.di_blocks)));
471 	} else {
472 		printf("I=%llu %d blocks\n", (unsigned long long)curinum,
473 		    iswap32(curinode->dp1.di_blocks));
474 	}
475 	printf("Direct blocks:\n");
476 	if (is_ufs2)
477 		print_blks64(curinode->dp2.di_db, NDADDR, &blkno);
478 	else
479 		print_blks32(curinode->dp1.di_db, NDADDR, &blkno);
480 
481 	if (is_ufs2) {
482 		for (i = 0; i < NIADDR; i++)
483 			print_indirblks64(iswap64(curinode->dp2.di_ib[i]), i,
484 			    &blkno);
485 	} else {
486 		for (i = 0; i < NIADDR; i++)
487 			print_indirblks32(iswap32(curinode->dp1.di_ib[i]), i,
488 			    &blkno);
489 	}
490 	return 0;
491 }
492 
493 static int findblk_numtofind;
494 static int wantedblksize;
495 CMDFUNCSTART(findblk)
496 {
497 	ino_t   inum, inosused;
498 	uint32_t *wantedblk32 = NULL;
499 	uint64_t *wantedblk64 = NULL;
500 	struct cg *cgp = cgrp;
501 	int i, c;
502 
503 	ocurrent = curinum;
504 	wantedblksize = (argc - 1);
505 	if (is_ufs2) {
506 		wantedblk64 = malloc(sizeof(uint64_t) * wantedblksize);
507 		if (wantedblk64 == NULL) {
508 			perror("malloc");
509 			return 1;
510 		}
511 		memset(wantedblk64, 0, sizeof(uint64_t) * wantedblksize);
512 		for (i = 1; i < argc; i++)
513 			wantedblk64[i - 1] =
514 			    dbtofsb(sblock, strtoull(argv[i], NULL, 0));
515 	} else {
516 		wantedblk32 = malloc(sizeof(uint32_t) * wantedblksize);
517 		if (wantedblk32 == NULL) {
518 			perror("malloc");
519 			return 1;
520 		}
521 		memset(wantedblk32, 0, sizeof(uint32_t) * wantedblksize);
522 		for (i = 1; i < argc; i++)
523 			wantedblk32[i - 1] =
524 			    dbtofsb(sblock, strtoull(argv[i], NULL, 0));
525 	}
526 	findblk_numtofind = wantedblksize;
527 	for (c = 0; c < sblock->fs_ncg; c++) {
528 		inum = c * sblock->fs_ipg;
529 		getblk(&cgblk, cgtod(sblock, c), sblock->fs_cgsize);
530 		memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize);
531 		if (needswap)
532 			ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock);
533 		if (is_ufs2)
534 			inosused = cgp->cg_initediblk;
535 		else
536 			inosused = sblock->fs_ipg;
537 		for (; inosused > 0; inum++, inosused--) {
538 			if (inum < ROOTINO)
539 				continue;
540 			if (is_ufs2 ? compare_blk64(wantedblk64,
541 			        ino_to_fsba(sblock, inum)) :
542 			    compare_blk32(wantedblk32,
543 			        ino_to_fsba(sblock, inum))) {
544 				printf("block %llu: inode block (%llu-%llu)\n",
545 				    (unsigned long long)fsbtodb(sblock,
546 					ino_to_fsba(sblock, inum)),
547 				    (unsigned long long)
548 				    (inum / INOPB(sblock)) * INOPB(sblock),
549 				    (unsigned long long)
550 				    (inum / INOPB(sblock) + 1) * INOPB(sblock));
551 				findblk_numtofind--;
552 				if (findblk_numtofind == 0)
553 					goto end;
554 			}
555 			curinum = inum;
556 			curinode = ginode(inum);
557 			switch (iswap16(DIP(curinode, mode)) & IFMT) {
558 			case IFDIR:
559 			case IFREG:
560 				if (DIP(curinode, blocks) == 0)
561 					continue;
562 				break;
563 			case IFLNK:
564 				{
565 				uint64_t size = iswap64(DIP(curinode, size));
566 				if (size > 0 &&
567 				    size < (uint64_t)sblock->fs_maxsymlinklen &&
568 				    DIP(curinode, blocks) == 0)
569 					continue;
570 				else
571 					break;
572 				}
573 			default:
574 				continue;
575 			}
576 			if (is_ufs2 ?
577 			    find_blks64(curinode->dp2.di_db, NDADDR,
578 				wantedblk64) :
579 			    find_blks32(curinode->dp1.di_db, NDADDR,
580 				wantedblk32))
581 				goto end;
582 			for (i = 0; i < NIADDR; i++) {
583 				if (is_ufs2 ?
584 				    compare_blk64(wantedblk64,
585 					iswap64(curinode->dp2.di_ib[i])) :
586 				    compare_blk32(wantedblk32,
587 					iswap32(curinode->dp1.di_ib[i])))
588 					if (founddatablk(is_ufs2 ?
589 					    iswap64(curinode->dp2.di_ib[i]) :
590 					    iswap32(curinode->dp1.di_ib[i])))
591 						goto end;
592 				if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) :
593 				    (curinode->dp1.di_ib[i] != 0))
594 					if (is_ufs2 ?
595 					    find_indirblks64(
596 						iswap64(curinode->dp2.di_ib[i]),
597 						i, wantedblk64) :
598 					    find_indirblks32(
599 						iswap32(curinode->dp1.di_ib[i]),
600 						i, wantedblk32))
601 						goto end;
602 			}
603 		}
604 	}
605 end:
606 	if (wantedblk32)
607 		free(wantedblk32);
608 	if (wantedblk64)
609 		free(wantedblk64);
610 	curinum = ocurrent;
611 	curinode = ginode(curinum);
612 	return 0;
613 }
614 
615 static int
616 compare_blk32(uint32_t *wantedblk, uint32_t curblk)
617 {
618 	int i;
619 	for (i = 0; i < wantedblksize; i++) {
620 		if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
621 			wantedblk[i] = 0;
622 			return 1;
623 		}
624 	}
625 	return 0;
626 }
627 
628 static int
629 compare_blk64(uint64_t *wantedblk, uint64_t curblk)
630 {
631 	int i;
632 	for (i = 0; i < wantedblksize; i++) {
633 		if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
634 			wantedblk[i] = 0;
635 			return 1;
636 		}
637 	}
638 	return 0;
639 }
640 
641 static int
642 founddatablk(uint64_t blk)
643 {
644 	printf("%llu: data block of inode %llu\n",
645 	    (unsigned long long)fsbtodb(sblock, blk),
646 	    (unsigned long long)curinum);
647 	findblk_numtofind--;
648 	if (findblk_numtofind == 0)
649 		return 1;
650 	return 0;
651 }
652 
653 static int
654 find_blks32(uint32_t *buf, int size, uint32_t *wantedblk)
655 {
656 	int blk;
657 	for(blk = 0; blk < size; blk++) {
658 		if (buf[blk] == 0)
659 			continue;
660 		if (compare_blk32(wantedblk, iswap32(buf[blk]))) {
661 			if (founddatablk(iswap32(buf[blk])))
662 				return 1;
663 		}
664 	}
665 	return 0;
666 }
667 
668 static int
669 find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk)
670 {
671 #define MAXNINDIR	(MAXBSIZE / sizeof(uint32_t))
672 	uint32_t idblk[MAXNINDIR];
673 	size_t i;
674 
675 	bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk),
676 	    (int)sblock->fs_bsize);
677 	if (ind_level <= 0) {
678 		if (find_blks32(idblk,
679 		    sblock->fs_bsize / sizeof(uint32_t), wantedblk))
680 			return 1;
681 	} else {
682 		ind_level--;
683 		for (i = 0; i < sblock->fs_bsize / sizeof(uint32_t); i++) {
684 			if (compare_blk32(wantedblk, iswap32(idblk[i]))) {
685 				if (founddatablk(iswap32(idblk[i])))
686 					return 1;
687 			}
688 			if(idblk[i] != 0)
689 				if (find_indirblks32(iswap32(idblk[i]),
690 				    ind_level, wantedblk))
691 				return 1;
692 		}
693 	}
694 #undef MAXNINDIR
695 	return 0;
696 }
697 
698 
699 static int
700 find_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
701 {
702 	int blk;
703 	for(blk = 0; blk < size; blk++) {
704 		if (buf[blk] == 0)
705 			continue;
706 		if (compare_blk64(wantedblk, iswap64(buf[blk]))) {
707 			if (founddatablk(iswap64(buf[blk])))
708 				return 1;
709 		}
710 	}
711 	return 0;
712 }
713 
714 static int
715 find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
716 {
717 #define MAXNINDIR	(MAXBSIZE / sizeof(uint64_t))
718 	uint64_t idblk[MAXNINDIR];
719 	size_t i;
720 
721 	bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk),
722 	    (int)sblock->fs_bsize);
723 	if (ind_level <= 0) {
724 		if (find_blks64(idblk,
725 		    sblock->fs_bsize / sizeof(uint64_t), wantedblk))
726 			return 1;
727 	} else {
728 		ind_level--;
729 		for (i = 0; i < sblock->fs_bsize / sizeof(uint64_t); i++) {
730 			if (compare_blk64(wantedblk, iswap64(idblk[i]))) {
731 				if (founddatablk(iswap64(idblk[i])))
732 					return 1;
733 			}
734 			if(idblk[i] != 0)
735 				if (find_indirblks64(iswap64(idblk[i]),
736 				    ind_level, wantedblk))
737 				return 1;
738 		}
739 	}
740 #undef MAXNINDIR
741 	return 0;
742 }
743 
744 
745 #define CHARS_PER_LINES 70
746 
747 static void
748 print_blks32(int32_t *buf, int size, uint64_t *blknum)
749 {
750 	int chars;
751 	char prbuf[CHARS_PER_LINES+1];
752 	int blk;
753 
754 	chars = 0;
755 	for(blk = 0; blk < size; blk++, (*blknum)++) {
756 		if (buf[blk] == 0)
757 			continue;
758 		snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk]));
759 		if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
760 			printf("\n");
761 			chars = 0;
762 		}
763 		if (chars == 0)
764 			printf("%" PRIu64 ": ", *blknum);
765 		printf("%s", prbuf);
766 		chars += strlen(prbuf);
767 	}
768 	printf("\n");
769 }
770 
771 static void
772 print_blks64(int64_t *buf, int size, uint64_t *blknum)
773 {
774 	int chars;
775 	char prbuf[CHARS_PER_LINES+1];
776 	int blk;
777 
778 	chars = 0;
779 	for(blk = 0; blk < size; blk++, (*blknum)++) {
780 		if (buf[blk] == 0)
781 			continue;
782 		snprintf(prbuf, CHARS_PER_LINES, "%lld ",
783 		    (long long)iswap64(buf[blk]));
784 		if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
785 			printf("\n");
786 			chars = 0;
787 		}
788 		if (chars == 0)
789 			printf("%" PRIu64 ": ", *blknum);
790 		printf("%s", prbuf);
791 		chars += strlen(prbuf);
792 	}
793 	printf("\n");
794 }
795 
796 #undef CHARS_PER_LINES
797 
798 static void
799 print_indirblks32(uint32_t blk, int ind_level, uint64_t *blknum)
800 {
801 #define MAXNINDIR	(MAXBSIZE / sizeof(int32_t))
802 	const int ptrperblk_shift = sblock->fs_bshift - 2;
803 	const int ptrperblk = 1 << ptrperblk_shift;
804 	int32_t idblk[MAXNINDIR];
805 	int i;
806 
807 	if (blk == 0) {
808 		*blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level);
809 		return;
810 	}
811 
812 	printf("Indirect block %lld (level %d):\n", (long long)blk,
813 	    ind_level+1);
814 	bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk),
815 	    (int)sblock->fs_bsize);
816 	if (ind_level <= 0) {
817 		print_blks32(idblk, ptrperblk, blknum);
818 	} else {
819 		ind_level--;
820 		for (i = 0; i < ptrperblk; i++)
821 			print_indirblks32(iswap32(idblk[i]), ind_level, blknum);
822 	}
823 #undef MAXNINDIR
824 }
825 
826 static void
827 print_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum)
828 {
829 #define MAXNINDIR	(MAXBSIZE / sizeof(int64_t))
830 	const int ptrperblk_shift = sblock->fs_bshift - 3;
831 	const int ptrperblk = 1 << ptrperblk_shift;
832 	int64_t idblk[MAXNINDIR];
833 	int i;
834 
835 	if (blk == 0) {
836 		*blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level);
837 		return;
838 	}
839 
840 	printf("Indirect block %lld (level %d):\n", (long long)blk,
841 	    ind_level+1);
842 	bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk),
843 	    (int)sblock->fs_bsize);
844 	if (ind_level <= 0) {
845 		print_blks64(idblk, ptrperblk, blknum);
846 	} else {
847 		ind_level--;
848 		for (i = 0; i < ptrperblk; i++)
849 			print_indirblks64(iswap64(idblk[i]), ind_level, blknum);
850 	}
851 #undef MAXNINDIR
852 }
853 
854 static int
855 dolookup(char *name)
856 {
857 	struct inodesc idesc;
858 
859 	if (!checkactivedir())
860 		return 0;
861 	idesc.id_number = curinum;
862 	idesc.id_func = findino;
863 	idesc.id_name = name;
864 	idesc.id_type = DATA;
865 	idesc.id_fix = IGNORE;
866 	if (ckinode(curinode, &idesc) & FOUND) {
867 		curinum = idesc.id_parent;
868 		curinode = ginode(curinum);
869 		printactive();
870 		return 1;
871 	} else {
872 		warnx("name `%s' not found in current inode directory", name);
873 		return 0;
874 	}
875 }
876 
877 CMDFUNCSTART(focusname)
878 {
879 	char   *p, *val;
880 
881 	if (!checkactive())
882 		return 1;
883 
884 	ocurrent = curinum;
885 
886 	if (argv[1][0] == '/') {
887 		curinum = ROOTINO;
888 		curinode = ginode(ROOTINO);
889 	} else {
890 		if (!checkactivedir())
891 			return 1;
892 	}
893 	for (p = argv[1]; p != NULL;) {
894 		while ((val = strsep(&p, "/")) != NULL && *val == '\0');
895 		if (val) {
896 			printf("component `%s': ", val);
897 			fflush(stdout);
898 			if (!dolookup(val)) {
899 				curinode = ginode(curinum);
900 				return (1);
901 			}
902 		}
903 	}
904 	return 0;
905 }
906 
907 CMDFUNCSTART(ln)
908 {
909 	ino_t   inum;
910 	int     rval;
911 	char   *cp;
912 
913 	GETINUM(1, inum);
914 
915 	if (!checkactivedir())
916 		return 1;
917 	rval = makeentry(curinum, inum, argv[2]);
918 	if (rval)
919 		printf("Ino %llu entered as `%s'\n", (unsigned long long)inum,
920 		    argv[2]);
921 	else
922 		printf("could not enter name? weird.\n");
923 	curinode = ginode(curinum);
924 	return rval;
925 }
926 
927 CMDFUNCSTART(rm)
928 {
929 	int     rval;
930 
931 	if (!checkactivedir())
932 		return 1;
933 	rval = changeino(curinum, argv[1], 0);
934 	if (rval & ALTERED) {
935 		printf("Name `%s' removed\n", argv[1]);
936 		return 0;
937 	} else {
938 		printf("could not remove name? weird.\n");
939 		return 1;
940 	}
941 }
942 
943 static long slotcount, desired;
944 
945 static int
946 chinumfunc(struct inodesc *idesc)
947 {
948 	struct direct *dirp = idesc->id_dirp;
949 
950 	if (slotcount++ == desired) {
951 		dirp->d_ino = iswap32(idesc->id_parent);
952 		return STOP | ALTERED | FOUND;
953 	}
954 	return KEEPON;
955 }
956 
957 CMDFUNCSTART(chinum)
958 {
959 	char   *cp;
960 	ino_t   inum;
961 	struct inodesc idesc;
962 
963 	slotcount = 0;
964 	if (!checkactivedir())
965 		return 1;
966 	GETINUM(2, inum);
967 
968 	desired = strtol(argv[1], &cp, 0);
969 	if (cp == argv[1] || *cp != '\0' || desired < 0) {
970 		printf("invalid slot number `%s'\n", argv[1]);
971 		return 1;
972 	}
973 	idesc.id_number = curinum;
974 	idesc.id_func = chinumfunc;
975 	idesc.id_fix = IGNORE;
976 	idesc.id_type = DATA;
977 	idesc.id_parent = inum;	/* XXX convenient hiding place */
978 
979 	if (ckinode(curinode, &idesc) & FOUND)
980 		return 0;
981 	else {
982 		warnx("no %sth slot in current directory", argv[1]);
983 		return 1;
984 	}
985 }
986 
987 static int
988 chnamefunc(struct inodesc *idesc)
989 {
990 	struct direct *dirp = idesc->id_dirp;
991 	struct direct testdir;
992 
993 	if (slotcount++ == desired) {
994 		/* will name fit? */
995 		testdir.d_namlen = strlen(idesc->id_name);
996 		if (DIRSIZ(NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) {
997 			dirp->d_namlen = testdir.d_namlen;
998 			strlcpy(dirp->d_name, idesc->id_name,
999 			    sizeof(dirp->d_name));
1000 			return STOP | ALTERED | FOUND;
1001 		} else
1002 			return STOP | FOUND;	/* won't fit, so give up */
1003 	}
1004 	return KEEPON;
1005 }
1006 
1007 CMDFUNCSTART(chname)
1008 {
1009 	int     rval;
1010 	char   *cp;
1011 	struct inodesc idesc;
1012 
1013 	slotcount = 0;
1014 	if (!checkactivedir())
1015 		return 1;
1016 
1017 	desired = strtoul(argv[1], &cp, 0);
1018 	if (cp == argv[1] || *cp != '\0') {
1019 		printf("invalid slot number `%s'\n", argv[1]);
1020 		return 1;
1021 	}
1022 	idesc.id_number = curinum;
1023 	idesc.id_func = chnamefunc;
1024 	idesc.id_fix = IGNORE;
1025 	idesc.id_type = DATA;
1026 	idesc.id_name = argv[2];
1027 
1028 	rval = ckinode(curinode, &idesc);
1029 	if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED))
1030 		return 0;
1031 	else
1032 		if (rval & FOUND) {
1033 			warnx("new name `%s' does not fit in slot %s",
1034 			    argv[2], argv[1]);
1035 			return 1;
1036 		} else {
1037 			warnx("no %sth slot in current directory", argv[1]);
1038 			return 1;
1039 		}
1040 }
1041 
1042 static struct typemap {
1043 	const char *typename;
1044 	int     typebits;
1045 }       typenamemap[] = {
1046 	{ "file", IFREG },
1047 	{ "dir", IFDIR },
1048 	{ "socket", IFSOCK },
1049 	{ "fifo", IFIFO },
1050 };
1051 
1052 CMDFUNCSTART(newtype)
1053 {
1054 	int     type;
1055 	uint16_t mode;
1056 	struct typemap *tp;
1057 
1058 	if (!checkactive())
1059 		return 1;
1060 	mode = iswap16(DIP(curinode, mode));
1061 	type = mode & IFMT;
1062 	for (tp = typenamemap;
1063 	    tp < &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)];
1064 	    tp++) {
1065 		if (!strcmp(argv[1], tp->typename)) {
1066 			printf("setting type to %s\n", tp->typename);
1067 			type = tp->typebits;
1068 			break;
1069 		}
1070 	}
1071 	if (tp == &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]) {
1072 		warnx("type `%s' not known", argv[1]);
1073 		warnx("try one of `file', `dir', `socket', `fifo'");
1074 		return 1;
1075 	}
1076 	DIP_SET(curinode, mode, iswap16((mode & ~IFMT) | type));
1077 	inodirty();
1078 	printactive();
1079 	return 0;
1080 }
1081 
1082 CMDFUNCSTART(chmode)
1083 {
1084 	long    modebits;
1085 	char   *cp;
1086 	uint16_t mode;
1087 
1088 	if (!checkactive())
1089 		return 1;
1090 
1091 	modebits = strtol(argv[1], &cp, 8);
1092 	if (cp == argv[1] || *cp != '\0') {
1093 		warnx("bad modebits `%s'", argv[1]);
1094 		return 1;
1095 	}
1096 	mode = iswap16(DIP(curinode, mode));
1097 	DIP_SET(curinode, mode, iswap16((mode & ~07777) | modebits));
1098 	inodirty();
1099 	printactive();
1100 	return 0;
1101 }
1102 
1103 CMDFUNCSTART(chlen)
1104 {
1105 	long    len;
1106 	char   *cp;
1107 
1108 	if (!checkactive())
1109 		return 1;
1110 
1111 	len = strtol(argv[1], &cp, 0);
1112 	if (cp == argv[1] || *cp != '\0' || len < 0) {
1113 		warnx("bad length '%s'", argv[1]);
1114 		return 1;
1115 	}
1116 	DIP_SET(curinode, size, iswap64(len));
1117 	inodirty();
1118 	printactive();
1119 	return 0;
1120 }
1121 
1122 CMDFUNCSTART(chaflags)
1123 {
1124 	u_long  flags;
1125 	char   *cp;
1126 
1127 	if (!checkactive())
1128 		return 1;
1129 
1130 	flags = strtoul(argv[1], &cp, 0);
1131 	if (cp == argv[1] || *cp != '\0') {
1132 		warnx("bad flags `%s'", argv[1]);
1133 		return 1;
1134 	}
1135 	if (flags > UINT_MAX) {
1136 		warnx("flags set beyond 32-bit range of field (0x%lx)",
1137 		    flags);
1138 		return (1);
1139 	}
1140 	DIP_SET(curinode, flags, iswap32(flags));
1141 	inodirty();
1142 	printactive();
1143 	return 0;
1144 }
1145 
1146 CMDFUNCSTART(chgen)
1147 {
1148 	long    gen;
1149 	char   *cp;
1150 
1151 	if (!checkactive())
1152 		return 1;
1153 
1154 	gen = strtol(argv[1], &cp, 0);
1155 	if (cp == argv[1] || *cp != '\0') {
1156 		warnx("bad gen `%s'", argv[1]);
1157 		return 1;
1158 	}
1159 	if (gen > INT_MAX || gen < INT_MIN) {
1160 		warnx("gen set beyond 32-bit range of field (0x%lx)", gen);
1161 		return (1);
1162 	}
1163 	DIP_SET(curinode, gen, iswap32(gen));
1164 	inodirty();
1165 	printactive();
1166 	return 0;
1167 }
1168 
1169 CMDFUNCSTART(linkcount)
1170 {
1171 	int     lcnt;
1172 	char   *cp;
1173 
1174 	if (!checkactive())
1175 		return 1;
1176 
1177 	lcnt = strtol(argv[1], &cp, 0);
1178 	if (cp == argv[1] || *cp != '\0') {
1179 		warnx("bad link count `%s'", argv[1]);
1180 		return 1;
1181 	}
1182 	if (lcnt > USHRT_MAX || lcnt < 0) {
1183 		warnx("max link count is %d", USHRT_MAX);
1184 		return 1;
1185 	}
1186 	DIP_SET(curinode, nlink, iswap16(lcnt));
1187 	inodirty();
1188 	printactive();
1189 	return 0;
1190 }
1191 
1192 CMDFUNCSTART(chowner)
1193 {
1194 	unsigned long uid;
1195 	char   *cp;
1196 	struct passwd *pwd;
1197 
1198 	if (!checkactive())
1199 		return 1;
1200 
1201 	uid = strtoul(argv[1], &cp, 0);
1202 	if (cp == argv[1] || *cp != '\0') {
1203 		/* try looking up name */
1204 		if ((pwd = getpwnam(argv[1])) != 0) {
1205 			uid = pwd->pw_uid;
1206 		} else {
1207 			warnx("bad uid `%s'", argv[1]);
1208 			return 1;
1209 		}
1210 	}
1211 	if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT)
1212 		curinode->dp1.di_ouid = iswap32(uid);
1213 	else
1214 		DIP_SET(curinode, uid, iswap32(uid));
1215 	inodirty();
1216 	printactive();
1217 	return 0;
1218 }
1219 
1220 CMDFUNCSTART(chgroup)
1221 {
1222 	unsigned long gid;
1223 	char   *cp;
1224 	struct group *grp;
1225 
1226 	if (!checkactive())
1227 		return 1;
1228 
1229 	gid = strtoul(argv[1], &cp, 0);
1230 	if (cp == argv[1] || *cp != '\0') {
1231 		if ((grp = getgrnam(argv[1])) != 0) {
1232 			gid = grp->gr_gid;
1233 		} else {
1234 			warnx("bad gid `%s'", argv[1]);
1235 			return 1;
1236 		}
1237 	}
1238 	if (sblock->fs_old_inodefmt < FS_44INODEFMT)
1239 		curinode->dp1.di_ogid = iswap32(gid);
1240 	else
1241 		DIP_SET(curinode, gid, iswap32(gid));
1242 	inodirty();
1243 	printactive();
1244 	return 0;
1245 }
1246 
1247 static int
1248 dotime(char *name, int32_t *rsec, int32_t *rnsec)
1249 {
1250 	char   *p, *val;
1251 	struct tm t;
1252 	int32_t sec;
1253 	int32_t nsec;
1254 	p = strchr(name, '.');
1255 	if (p) {
1256 		*p = '\0';
1257 		nsec = strtoul(++p, &val, 0);
1258 		if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
1259 			warnx("invalid nanoseconds");
1260 			goto badformat;
1261 		}
1262 	} else
1263 		nsec = 0;
1264 	if (strlen(name) != 14) {
1265 badformat:
1266 		warnx("date format: YYYYMMDDHHMMSS[.nsec]");
1267 		return 1;
1268 	}
1269 	for (p = name; *p; p++)
1270 		if (*p < '0' || *p > '9')
1271 			goto badformat;
1272 
1273 	p = name;
1274 #define VAL() ((*p++) - '0')
1275 	t.tm_year = VAL();
1276 	t.tm_year = VAL() + t.tm_year * 10;
1277 	t.tm_year = VAL() + t.tm_year * 10;
1278 	t.tm_year = VAL() + t.tm_year * 10 - 1900;
1279 	t.tm_mon = VAL();
1280 	t.tm_mon = VAL() + t.tm_mon * 10 - 1;
1281 	t.tm_mday = VAL();
1282 	t.tm_mday = VAL() + t.tm_mday * 10;
1283 	t.tm_hour = VAL();
1284 	t.tm_hour = VAL() + t.tm_hour * 10;
1285 	t.tm_min = VAL();
1286 	t.tm_min = VAL() + t.tm_min * 10;
1287 	t.tm_sec = VAL();
1288 	t.tm_sec = VAL() + t.tm_sec * 10;
1289 	t.tm_isdst = -1;
1290 
1291 	sec = mktime(&t);
1292 	if (sec == -1) {
1293 		warnx("date/time out of range");
1294 		return 1;
1295 	}
1296 	*rsec = iswap32(sec);
1297 	*rnsec = iswap32(nsec);
1298 	return 0;
1299 }
1300 
1301 CMDFUNCSTART(chmtime)
1302 {
1303 	int32_t rsec, nsec;
1304 
1305 	if (dotime(argv[1], &rsec, &nsec))
1306 		return 1;
1307 	DIP_SET(curinode, mtime, rsec);
1308 	DIP_SET(curinode, mtimensec, nsec);
1309 	inodirty();
1310 	printactive();
1311 	return 0;
1312 }
1313 
1314 CMDFUNCSTART(chatime)
1315 {
1316 	int32_t rsec, nsec;
1317 
1318 	if (dotime(argv[1], &rsec, &nsec))
1319 		return 1;
1320 	DIP_SET(curinode, atime, rsec);
1321 	DIP_SET(curinode, atimensec, nsec);
1322 	inodirty();
1323 	printactive();
1324 	return 0;
1325 }
1326 
1327 CMDFUNCSTART(chctime)
1328 {
1329 	int32_t rsec, nsec;
1330 
1331 	if (dotime(argv[1], &rsec, &nsec))
1332 		return 1;
1333 	DIP_SET(curinode, ctime, rsec);
1334 	DIP_SET(curinode, ctimensec, nsec);
1335 	inodirty();
1336 	printactive();
1337 	return 0;
1338 }
1339