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