Deleted Added
full compact
fsdb.c (163846) fsdb.c (207141)
1/* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */
2
3/*
4 * Copyright (c) 1995 John T. Kohl
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifndef lint
32static const char rcsid[] =
1/* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */
2
3/*
4 * Copyright (c) 1995 John T. Kohl
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifndef lint
32static const char rcsid[] =
33 "$FreeBSD: head/sbin/fsdb/fsdb.c 163846 2006-10-31 22:07:29Z pjd $";
33 "$FreeBSD: head/sbin/fsdb/fsdb.c 207141 2010-04-24 07:05:35Z jeff $";
34#endif /* not lint */
35
36#include <sys/param.h>
37#include <sys/time.h>
38#include <ctype.h>
39#include <err.h>
40#include <grp.h>
41#include <histedit.h>
42#include <pwd.h>
43#include <string.h>
44#include <timeconv.h>
45
46#include <ufs/ufs/dinode.h>
47#include <ufs/ufs/dir.h>
48#include <ufs/ffs/fs.h>
49
50#include "fsdb.h"
51#include "fsck.h"
52
53static void usage(void) __dead2;
54int cmdloop(void);
55static int compare_blk32(uint32_t *wantedblk, uint32_t curblk);
56static int compare_blk64(uint64_t *wantedblk, uint64_t curblk);
57static int founddatablk(uint64_t blk);
58static int find_blks32(uint32_t *buf, int size, uint32_t *blknum);
59static int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
60static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum);
61static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum);
62
63static void
64usage(void)
65{
66 fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
67 exit(1);
68}
69
70int returntosingle;
71char nflag;
72
73/*
74 * We suck in lots of fsck code, and just pick & choose the stuff we want.
75 *
76 * fsreadfd is set up to read from the file system, fswritefd to write to
77 * the file system.
78 */
79int
80main(int argc, char *argv[])
81{
82 int ch, rval;
83 char *fsys = NULL;
84
85 while (-1 != (ch = getopt(argc, argv, "fdr"))) {
86 switch (ch) {
87 case 'f':
88 /* The -f option is left for historical
89 * reasons and has no meaning.
90 */
91 break;
92 case 'd':
93 debug++;
94 break;
95 case 'r':
96 nflag++; /* "no" in fsck, readonly for us */
97 break;
98 default:
99 usage();
100 }
101 }
102 argc -= optind;
103 argv += optind;
104 if (argc != 1)
105 usage();
106 else
107 fsys = argv[0];
108
109 sblock_init();
110 if (!setup(fsys))
111 errx(1, "cannot set up file system `%s'", fsys);
112 printf("%s file system `%s'\nLast Mounted on %s\n",
113 nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
114 rval = cmdloop();
115 if (!nflag) {
116 sblock.fs_clean = 0; /* mark it dirty */
117 sbdirty();
118 ckfini(0);
119 printf("*** FILE SYSTEM MARKED DIRTY\n");
120 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
121 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
122 }
123 exit(rval);
124}
125
126#define CMDFUNC(func) int func(int argc, char *argv[])
127#define CMDFUNCSTART(func) int func(int argc, char *argv[])
128
129CMDFUNC(helpfn);
130CMDFUNC(focus); /* focus on inode */
131CMDFUNC(active); /* print active inode */
132CMDFUNC(blocks); /* print blocks for active inode */
133CMDFUNC(focusname); /* focus by name */
134CMDFUNC(zapi); /* clear inode */
135CMDFUNC(uplink); /* incr link */
136CMDFUNC(downlink); /* decr link */
137CMDFUNC(linkcount); /* set link count */
138CMDFUNC(quit); /* quit */
139CMDFUNC(findblk); /* find block */
140CMDFUNC(ls); /* list directory */
141CMDFUNC(rm); /* remove name */
142CMDFUNC(ln); /* add name */
143CMDFUNC(newtype); /* change type */
144CMDFUNC(chmode); /* change mode */
145CMDFUNC(chlen); /* change length */
146CMDFUNC(chaflags); /* change flags */
147CMDFUNC(chgen); /* change generation */
148CMDFUNC(chowner); /* change owner */
149CMDFUNC(chgroup); /* Change group */
150CMDFUNC(back); /* pop back to last ino */
151CMDFUNC(chbtime); /* Change btime */
152CMDFUNC(chmtime); /* Change mtime */
153CMDFUNC(chctime); /* Change ctime */
154CMDFUNC(chatime); /* Change atime */
155CMDFUNC(chinum); /* Change inode # of dirent */
156CMDFUNC(chname); /* Change dirname of dirent */
157
158struct cmdtable cmds[] = {
159 { "help", "Print out help", 1, 1, FL_RO, helpfn },
160 { "?", "Print out help", 1, 1, FL_RO, helpfn },
161 { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
162 { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
163 { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
164 { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
165 { "back", "Go to previous active inode", 1, 1, FL_RO, back },
166 { "active", "Print active inode", 1, 1, FL_RO, active },
167 { "print", "Print active inode", 1, 1, FL_RO, active },
168 { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
169 { "uplink", "Increment link count", 1, 1, FL_WR, uplink },
170 { "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
171 { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
172 { "findblk", "Find inode owning disk block(s)", 2, 33, FL_RO, findblk},
173 { "ls", "List current inode as directory", 1, 1, FL_RO, ls },
174 { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
175 { "del", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
176 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR | FL_ST, ln },
177 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
178 { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR | FL_ST, chname },
179 { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
180 { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
181 { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
182 { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
183 { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
184 { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
185 { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
186 { "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime },
187 { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
188 { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
189 { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
190 { "quit", "Exit", 1, 1, FL_RO, quit },
191 { "q", "Exit", 1, 1, FL_RO, quit },
192 { "exit", "Exit", 1, 1, FL_RO, quit },
193 { NULL, 0, 0, 0, 0, NULL },
194};
195
196int
197helpfn(int argc, char *argv[])
198{
199 struct cmdtable *cmdtp;
200
201 printf("Commands are:\n%-10s %5s %5s %s\n",
202 "command", "min args", "max args", "what");
203
204 for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
205 printf("%-10s %5u %5u %s\n",
206 cmdtp->cmd, cmdtp->minargc-1, cmdtp->maxargc-1, cmdtp->helptxt);
207 return 0;
208}
209
210char *
211prompt(EditLine *el)
212{
213 static char pstring[64];
214 snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
215 return pstring;
216}
217
218
219int
220cmdloop(void)
221{
222 char *line;
223 const char *elline;
224 int cmd_argc, rval = 0, known;
225#define scratch known
226 char **cmd_argv;
227 struct cmdtable *cmdp;
228 History *hist;
229 EditLine *elptr;
230 HistEvent he;
231
232 curinode = ginode(ROOTINO);
233 curinum = ROOTINO;
234 printactive(0);
235
236 hist = history_init();
237 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */
238
239 elptr = el_init("fsdb", stdin, stdout, stderr);
240 el_set(elptr, EL_EDITOR, "emacs");
241 el_set(elptr, EL_PROMPT, prompt);
242 el_set(elptr, EL_HIST, history, hist);
243 el_source(elptr, NULL);
244
245 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
246 if (debug)
247 printf("command `%s'\n", elline);
248
249 history(hist, &he, H_ENTER, elline);
250
251 line = strdup(elline);
252 cmd_argv = crack(line, &cmd_argc);
253 /*
254 * el_parse returns -1 to signal that it's not been handled
255 * internally.
256 */
257 if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1)
258 continue;
259 if (cmd_argc) {
260 known = 0;
261 for (cmdp = cmds; cmdp->cmd; cmdp++) {
262 if (!strcmp(cmdp->cmd, cmd_argv[0])) {
263 if ((cmdp->flags & FL_WR) == FL_WR && nflag)
264 warnx("`%s' requires write access", cmd_argv[0]),
265 rval = 1;
266 else if (cmd_argc >= cmdp->minargc &&
267 cmd_argc <= cmdp->maxargc)
268 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
269 else if (cmd_argc >= cmdp->minargc &&
270 (cmdp->flags & FL_ST) == FL_ST) {
271 strcpy(line, elline);
272 cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
273 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
274 } else
275 rval = argcount(cmdp, cmd_argc, cmd_argv);
276 known = 1;
277 break;
278 }
279 }
280 if (!known)
281 warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
282 } else
283 rval = 0;
284 free(line);
285 if (rval < 0)
286 /* user typed "quit" */
287 return 0;
288 if (rval)
289 warnx("rval was %d", rval);
290 }
291 el_end(elptr);
292 history_end(hist);
293 return rval;
294}
295
296union dinode *curinode;
297ino_t curinum, ocurrent;
298
299#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
300 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
301 printf("inode %d out of range; range is [%d,%d]\n", \
302 inum, ROOTINO, maxino); \
303 return 1; \
304 }
305
306/*
307 * Focus on given inode number
308 */
309CMDFUNCSTART(focus)
310{
311 ino_t inum;
312 char *cp;
313
314 GETINUM(1,inum);
315 curinode = ginode(inum);
316 ocurrent = curinum;
317 curinum = inum;
318 printactive(0);
319 return 0;
320}
321
322CMDFUNCSTART(back)
323{
324 curinum = ocurrent;
325 curinode = ginode(curinum);
326 printactive(0);
327 return 0;
328}
329
330CMDFUNCSTART(zapi)
331{
332 ino_t inum;
333 union dinode *dp;
334 char *cp;
335
336 GETINUM(1,inum);
337 dp = ginode(inum);
338 clearinode(dp);
339 inodirty();
340 if (curinode) /* re-set after potential change */
341 curinode = ginode(curinum);
342 return 0;
343}
344
345CMDFUNCSTART(active)
346{
347 printactive(0);
348 return 0;
349}
350
351CMDFUNCSTART(blocks)
352{
353 printactive(1);
354 return 0;
355}
356
357CMDFUNCSTART(quit)
358{
359 return -1;
360}
361
362CMDFUNCSTART(uplink)
363{
364 if (!checkactive())
365 return 1;
366 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
367 printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
368 inodirty();
369 return 0;
370}
371
372CMDFUNCSTART(downlink)
373{
374 if (!checkactive())
375 return 1;
376 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
377 printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
378 inodirty();
379 return 0;
380}
381
382const char *typename[] = {
383 "unknown",
384 "fifo",
385 "char special",
386 "unregistered #3",
387 "directory",
388 "unregistered #5",
389 "blk special",
390 "unregistered #7",
391 "regular",
392 "unregistered #9",
393 "symlink",
394 "unregistered #11",
395 "socket",
396 "unregistered #13",
397 "whiteout",
398};
34#endif /* not lint */
35
36#include <sys/param.h>
37#include <sys/time.h>
38#include <ctype.h>
39#include <err.h>
40#include <grp.h>
41#include <histedit.h>
42#include <pwd.h>
43#include <string.h>
44#include <timeconv.h>
45
46#include <ufs/ufs/dinode.h>
47#include <ufs/ufs/dir.h>
48#include <ufs/ffs/fs.h>
49
50#include "fsdb.h"
51#include "fsck.h"
52
53static void usage(void) __dead2;
54int cmdloop(void);
55static int compare_blk32(uint32_t *wantedblk, uint32_t curblk);
56static int compare_blk64(uint64_t *wantedblk, uint64_t curblk);
57static int founddatablk(uint64_t blk);
58static int find_blks32(uint32_t *buf, int size, uint32_t *blknum);
59static int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
60static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum);
61static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum);
62
63static void
64usage(void)
65{
66 fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
67 exit(1);
68}
69
70int returntosingle;
71char nflag;
72
73/*
74 * We suck in lots of fsck code, and just pick & choose the stuff we want.
75 *
76 * fsreadfd is set up to read from the file system, fswritefd to write to
77 * the file system.
78 */
79int
80main(int argc, char *argv[])
81{
82 int ch, rval;
83 char *fsys = NULL;
84
85 while (-1 != (ch = getopt(argc, argv, "fdr"))) {
86 switch (ch) {
87 case 'f':
88 /* The -f option is left for historical
89 * reasons and has no meaning.
90 */
91 break;
92 case 'd':
93 debug++;
94 break;
95 case 'r':
96 nflag++; /* "no" in fsck, readonly for us */
97 break;
98 default:
99 usage();
100 }
101 }
102 argc -= optind;
103 argv += optind;
104 if (argc != 1)
105 usage();
106 else
107 fsys = argv[0];
108
109 sblock_init();
110 if (!setup(fsys))
111 errx(1, "cannot set up file system `%s'", fsys);
112 printf("%s file system `%s'\nLast Mounted on %s\n",
113 nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
114 rval = cmdloop();
115 if (!nflag) {
116 sblock.fs_clean = 0; /* mark it dirty */
117 sbdirty();
118 ckfini(0);
119 printf("*** FILE SYSTEM MARKED DIRTY\n");
120 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
121 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
122 }
123 exit(rval);
124}
125
126#define CMDFUNC(func) int func(int argc, char *argv[])
127#define CMDFUNCSTART(func) int func(int argc, char *argv[])
128
129CMDFUNC(helpfn);
130CMDFUNC(focus); /* focus on inode */
131CMDFUNC(active); /* print active inode */
132CMDFUNC(blocks); /* print blocks for active inode */
133CMDFUNC(focusname); /* focus by name */
134CMDFUNC(zapi); /* clear inode */
135CMDFUNC(uplink); /* incr link */
136CMDFUNC(downlink); /* decr link */
137CMDFUNC(linkcount); /* set link count */
138CMDFUNC(quit); /* quit */
139CMDFUNC(findblk); /* find block */
140CMDFUNC(ls); /* list directory */
141CMDFUNC(rm); /* remove name */
142CMDFUNC(ln); /* add name */
143CMDFUNC(newtype); /* change type */
144CMDFUNC(chmode); /* change mode */
145CMDFUNC(chlen); /* change length */
146CMDFUNC(chaflags); /* change flags */
147CMDFUNC(chgen); /* change generation */
148CMDFUNC(chowner); /* change owner */
149CMDFUNC(chgroup); /* Change group */
150CMDFUNC(back); /* pop back to last ino */
151CMDFUNC(chbtime); /* Change btime */
152CMDFUNC(chmtime); /* Change mtime */
153CMDFUNC(chctime); /* Change ctime */
154CMDFUNC(chatime); /* Change atime */
155CMDFUNC(chinum); /* Change inode # of dirent */
156CMDFUNC(chname); /* Change dirname of dirent */
157
158struct cmdtable cmds[] = {
159 { "help", "Print out help", 1, 1, FL_RO, helpfn },
160 { "?", "Print out help", 1, 1, FL_RO, helpfn },
161 { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
162 { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
163 { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
164 { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname },
165 { "back", "Go to previous active inode", 1, 1, FL_RO, back },
166 { "active", "Print active inode", 1, 1, FL_RO, active },
167 { "print", "Print active inode", 1, 1, FL_RO, active },
168 { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
169 { "uplink", "Increment link count", 1, 1, FL_WR, uplink },
170 { "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
171 { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
172 { "findblk", "Find inode owning disk block(s)", 2, 33, FL_RO, findblk},
173 { "ls", "List current inode as directory", 1, 1, FL_RO, ls },
174 { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
175 { "del", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm },
176 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR | FL_ST, ln },
177 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
178 { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR | FL_ST, chname },
179 { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
180 { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
181 { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
182 { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
183 { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
184 { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
185 { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
186 { "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime },
187 { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
188 { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
189 { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
190 { "quit", "Exit", 1, 1, FL_RO, quit },
191 { "q", "Exit", 1, 1, FL_RO, quit },
192 { "exit", "Exit", 1, 1, FL_RO, quit },
193 { NULL, 0, 0, 0, 0, NULL },
194};
195
196int
197helpfn(int argc, char *argv[])
198{
199 struct cmdtable *cmdtp;
200
201 printf("Commands are:\n%-10s %5s %5s %s\n",
202 "command", "min args", "max args", "what");
203
204 for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
205 printf("%-10s %5u %5u %s\n",
206 cmdtp->cmd, cmdtp->minargc-1, cmdtp->maxargc-1, cmdtp->helptxt);
207 return 0;
208}
209
210char *
211prompt(EditLine *el)
212{
213 static char pstring[64];
214 snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
215 return pstring;
216}
217
218
219int
220cmdloop(void)
221{
222 char *line;
223 const char *elline;
224 int cmd_argc, rval = 0, known;
225#define scratch known
226 char **cmd_argv;
227 struct cmdtable *cmdp;
228 History *hist;
229 EditLine *elptr;
230 HistEvent he;
231
232 curinode = ginode(ROOTINO);
233 curinum = ROOTINO;
234 printactive(0);
235
236 hist = history_init();
237 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */
238
239 elptr = el_init("fsdb", stdin, stdout, stderr);
240 el_set(elptr, EL_EDITOR, "emacs");
241 el_set(elptr, EL_PROMPT, prompt);
242 el_set(elptr, EL_HIST, history, hist);
243 el_source(elptr, NULL);
244
245 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
246 if (debug)
247 printf("command `%s'\n", elline);
248
249 history(hist, &he, H_ENTER, elline);
250
251 line = strdup(elline);
252 cmd_argv = crack(line, &cmd_argc);
253 /*
254 * el_parse returns -1 to signal that it's not been handled
255 * internally.
256 */
257 if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1)
258 continue;
259 if (cmd_argc) {
260 known = 0;
261 for (cmdp = cmds; cmdp->cmd; cmdp++) {
262 if (!strcmp(cmdp->cmd, cmd_argv[0])) {
263 if ((cmdp->flags & FL_WR) == FL_WR && nflag)
264 warnx("`%s' requires write access", cmd_argv[0]),
265 rval = 1;
266 else if (cmd_argc >= cmdp->minargc &&
267 cmd_argc <= cmdp->maxargc)
268 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
269 else if (cmd_argc >= cmdp->minargc &&
270 (cmdp->flags & FL_ST) == FL_ST) {
271 strcpy(line, elline);
272 cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
273 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
274 } else
275 rval = argcount(cmdp, cmd_argc, cmd_argv);
276 known = 1;
277 break;
278 }
279 }
280 if (!known)
281 warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
282 } else
283 rval = 0;
284 free(line);
285 if (rval < 0)
286 /* user typed "quit" */
287 return 0;
288 if (rval)
289 warnx("rval was %d", rval);
290 }
291 el_end(elptr);
292 history_end(hist);
293 return rval;
294}
295
296union dinode *curinode;
297ino_t curinum, ocurrent;
298
299#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
300 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
301 printf("inode %d out of range; range is [%d,%d]\n", \
302 inum, ROOTINO, maxino); \
303 return 1; \
304 }
305
306/*
307 * Focus on given inode number
308 */
309CMDFUNCSTART(focus)
310{
311 ino_t inum;
312 char *cp;
313
314 GETINUM(1,inum);
315 curinode = ginode(inum);
316 ocurrent = curinum;
317 curinum = inum;
318 printactive(0);
319 return 0;
320}
321
322CMDFUNCSTART(back)
323{
324 curinum = ocurrent;
325 curinode = ginode(curinum);
326 printactive(0);
327 return 0;
328}
329
330CMDFUNCSTART(zapi)
331{
332 ino_t inum;
333 union dinode *dp;
334 char *cp;
335
336 GETINUM(1,inum);
337 dp = ginode(inum);
338 clearinode(dp);
339 inodirty();
340 if (curinode) /* re-set after potential change */
341 curinode = ginode(curinum);
342 return 0;
343}
344
345CMDFUNCSTART(active)
346{
347 printactive(0);
348 return 0;
349}
350
351CMDFUNCSTART(blocks)
352{
353 printactive(1);
354 return 0;
355}
356
357CMDFUNCSTART(quit)
358{
359 return -1;
360}
361
362CMDFUNCSTART(uplink)
363{
364 if (!checkactive())
365 return 1;
366 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
367 printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
368 inodirty();
369 return 0;
370}
371
372CMDFUNCSTART(downlink)
373{
374 if (!checkactive())
375 return 1;
376 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
377 printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
378 inodirty();
379 return 0;
380}
381
382const char *typename[] = {
383 "unknown",
384 "fifo",
385 "char special",
386 "unregistered #3",
387 "directory",
388 "unregistered #5",
389 "blk special",
390 "unregistered #7",
391 "regular",
392 "unregistered #9",
393 "symlink",
394 "unregistered #11",
395 "socket",
396 "unregistered #13",
397 "whiteout",
398};
399
399
400int diroff;
400int slot;
401
402int
403scannames(struct inodesc *idesc)
404{
405 struct direct *dirp = idesc->id_dirp;
406
401int slot;
402
403int
404scannames(struct inodesc *idesc)
405{
406 struct direct *dirp = idesc->id_dirp;
407
407 printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
408 slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type],
409 dirp->d_namlen, dirp->d_name);
408 printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n",
409 slot++, diroff, dirp->d_ino, dirp->d_reclen,
410 typename[dirp->d_type], dirp->d_namlen, dirp->d_name);
411 diroff += dirp->d_reclen;
410 return (KEEPON);
411}
412
413CMDFUNCSTART(ls)
414{
415 struct inodesc idesc;
416 checkactivedir(); /* let it go on anyway */
417
418 slot = 0;
412 return (KEEPON);
413}
414
415CMDFUNCSTART(ls)
416{
417 struct inodesc idesc;
418 checkactivedir(); /* let it go on anyway */
419
420 slot = 0;
421 diroff = 0;
419 idesc.id_number = curinum;
420 idesc.id_func = scannames;
421 idesc.id_type = DATA;
422 idesc.id_fix = IGNORE;
423 ckinode(curinode, &idesc);
424 curinode = ginode(curinum);
425
426 return 0;
427}
428
429static int findblk_numtofind;
430static int wantedblksize;
431
432CMDFUNCSTART(findblk)
433{
434 ino_t inum, inosused;
435 uint32_t *wantedblk32;
436 uint64_t *wantedblk64;
437 struct cg *cgp = &cgrp;
438 int c, i, is_ufs2;
439
440 wantedblksize = (argc - 1);
441 is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC;
442 ocurrent = curinum;
443
444 if (is_ufs2) {
445 wantedblk64 = calloc(wantedblksize, sizeof(uint64_t));
446 if (wantedblk64 == NULL)
447 err(1, "malloc");
448 for (i = 1; i < argc; i++)
449 wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
450 } else {
451 wantedblk32 = calloc(wantedblksize, sizeof(uint32_t));
452 if (wantedblk32 == NULL)
453 err(1, "malloc");
454 for (i = 1; i < argc; i++)
455 wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
456 }
457 findblk_numtofind = wantedblksize;
458 /*
459 * sblock.fs_ncg holds a number of cylinder groups.
460 * Iterate over all cylinder groups.
461 */
462 for (c = 0; c < sblock.fs_ncg; c++) {
463 /*
464 * sblock.fs_ipg holds a number of inodes per cylinder group.
465 * Calculate a highest inode number for a given cylinder group.
466 */
467 inum = c * sblock.fs_ipg;
468 /* Read cylinder group. */
469 getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize);
470 memcpy(cgp, cgblk.b_un.b_cg, sblock.fs_cgsize);
471 /*
472 * Get a highest used inode number for a given cylinder group.
473 * For UFS1 all inodes initialized at the newfs stage.
474 */
475 if (is_ufs2)
476 inosused = cgp->cg_initediblk;
477 else
478 inosused = sblock.fs_ipg;
479
480 for (; inosused > 0; inum++, inosused--) {
481 /* Skip magic inodes: 0, WINO, ROOTINO. */
482 if (inum < ROOTINO)
483 continue;
484 /*
485 * Check if the block we are looking for is just an inode block.
486 *
487 * ino_to_fsba() - get block containing inode from its number.
488 * INOPB() - get a number of inodes in one disk block.
489 */
490 if (is_ufs2 ?
491 compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) :
492 compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) {
493 printf("block %llu: inode block (%d-%d)\n",
494 (unsigned long long)fsbtodb(&sblock,
495 ino_to_fsba(&sblock, inum)),
496 (inum / INOPB(&sblock)) * INOPB(&sblock),
497 (inum / INOPB(&sblock) + 1) * INOPB(&sblock));
498 findblk_numtofind--;
499 if (findblk_numtofind == 0)
500 goto end;
501 }
502 /* Get on-disk inode aka dinode. */
503 curinum = inum;
504 curinode = ginode(inum);
505 /* Find IFLNK dinode with allocated data blocks. */
506 switch (DIP(curinode, di_mode) & IFMT) {
507 case IFDIR:
508 case IFREG:
509 if (DIP(curinode, di_blocks) == 0)
510 continue;
511 break;
512 case IFLNK:
513 {
514 uint64_t size = DIP(curinode, di_size);
515 if (size > 0 && size < sblock.fs_maxsymlinklen &&
516 DIP(curinode, di_blocks) == 0)
517 continue;
518 else
519 break;
520 }
521 default:
522 continue;
523 }
524 /* Look through direct data blocks. */
525 if (is_ufs2 ?
526 find_blks64(curinode->dp2.di_db, NDADDR, wantedblk64) :
527 find_blks32(curinode->dp1.di_db, NDADDR, wantedblk32))
528 goto end;
529 for (i = 0; i < NIADDR; i++) {
530 /*
531 * Does the block we are looking for belongs to the
532 * indirect blocks?
533 */
534 if (is_ufs2 ?
535 compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) :
536 compare_blk32(wantedblk32, curinode->dp1.di_ib[i]))
537 if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] :
538 curinode->dp1.di_ib[i]))
539 goto end;
540 /*
541 * Search through indirect, double and triple indirect
542 * data blocks.
543 */
544 if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) :
545 (curinode->dp1.di_ib[i] != 0))
546 if (is_ufs2 ?
547 find_indirblks64(curinode->dp2.di_ib[i], i,
548 wantedblk64) :
549 find_indirblks32(curinode->dp1.di_ib[i], i,
550 wantedblk32))
551 goto end;
552 }
553 }
554 }
555end:
556 curinum = ocurrent;
557 curinode = ginode(curinum);
558 return 0;
559}
560
561static int
562compare_blk32(uint32_t *wantedblk, uint32_t curblk)
563{
564 int i;
565
566 for (i = 0; i < wantedblksize; i++) {
567 if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
568 wantedblk[i] = 0;
569 return 1;
570 }
571 }
572 return 0;
573}
574
575static int
576compare_blk64(uint64_t *wantedblk, uint64_t curblk)
577{
578 int i;
579
580 for (i = 0; i < wantedblksize; i++) {
581 if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
582 wantedblk[i] = 0;
583 return 1;
584 }
585 }
586 return 0;
587}
588
589static int
590founddatablk(uint64_t blk)
591{
592
593 printf("%llu: data block of inode %d\n",
594 (unsigned long long)fsbtodb(&sblock, blk), curinum);
595 findblk_numtofind--;
596 if (findblk_numtofind == 0)
597 return 1;
598 return 0;
599}
600
601static int
602find_blks32(uint32_t *buf, int size, uint32_t *wantedblk)
603{
604 int blk;
605 for (blk = 0; blk < size; blk++) {
606 if (buf[blk] == 0)
607 continue;
608 if (compare_blk32(wantedblk, buf[blk])) {
609 if (founddatablk(buf[blk]))
610 return 1;
611 }
612 }
613 return 0;
614}
615
616static int
617find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk)
618{
619#define MAXNINDIR (MAXBSIZE / sizeof(uint32_t))
620 uint32_t idblk[MAXNINDIR];
621 int i;
622
623 blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
624 if (ind_level <= 0) {
625 if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk))
626 return 1;
627 } else {
628 ind_level--;
629 for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) {
630 if (compare_blk32(wantedblk, idblk[i])) {
631 if (founddatablk(idblk[i]))
632 return 1;
633 }
634 if (idblk[i] != 0)
635 if (find_indirblks32(idblk[i], ind_level, wantedblk))
636 return 1;
637 }
638 }
639#undef MAXNINDIR
640 return 0;
641}
642
643static int
644find_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
645{
646 int blk;
647 for (blk = 0; blk < size; blk++) {
648 if (buf[blk] == 0)
649 continue;
650 if (compare_blk64(wantedblk, buf[blk])) {
651 if (founddatablk(buf[blk]))
652 return 1;
653 }
654 }
655 return 0;
656}
657
658static int
659find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
660{
661#define MAXNINDIR (MAXBSIZE / sizeof(uint64_t))
662 uint64_t idblk[MAXNINDIR];
663 int i;
664
665 blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
666 if (ind_level <= 0) {
667 if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk))
668 return 1;
669 } else {
670 ind_level--;
671 for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) {
672 if (compare_blk64(wantedblk, idblk[i])) {
673 if (founddatablk(idblk[i]))
674 return 1;
675 }
676 if (idblk[i] != 0)
677 if (find_indirblks64(idblk[i], ind_level, wantedblk))
678 return 1;
679 }
680 }
681#undef MAXNINDIR
682 return 0;
683}
684
685int findino(struct inodesc *idesc); /* from fsck */
686static int dolookup(char *name);
687
688static int
689dolookup(char *name)
690{
691 struct inodesc idesc;
692
693 if (!checkactivedir())
694 return 0;
695 idesc.id_number = curinum;
696 idesc.id_func = findino;
697 idesc.id_name = name;
698 idesc.id_type = DATA;
699 idesc.id_fix = IGNORE;
700 if (ckinode(curinode, &idesc) & FOUND) {
701 curinum = idesc.id_parent;
702 curinode = ginode(curinum);
703 printactive(0);
704 return 1;
705 } else {
706 warnx("name `%s' not found in current inode directory", name);
707 return 0;
708 }
709}
710
711CMDFUNCSTART(focusname)
712{
713 char *p, *val;
714
715 if (!checkactive())
716 return 1;
717
718 ocurrent = curinum;
719
720 if (argv[1][0] == '/') {
721 curinum = ROOTINO;
722 curinode = ginode(ROOTINO);
723 } else {
724 if (!checkactivedir())
725 return 1;
726 }
727 for (p = argv[1]; p != NULL;) {
728 while ((val = strsep(&p, "/")) != NULL && *val == '\0');
729 if (val) {
730 printf("component `%s': ", val);
731 fflush(stdout);
732 if (!dolookup(val)) {
733 curinode = ginode(curinum);
734 return(1);
735 }
736 }
737 }
738 return 0;
739}
740
741CMDFUNCSTART(ln)
742{
743 ino_t inum;
744 int rval;
745 char *cp;
746
747 GETINUM(1,inum);
748
749 if (!checkactivedir())
750 return 1;
751 rval = makeentry(curinum, inum, argv[2]);
752 if (rval)
753 printf("Ino %d entered as `%s'\n", inum, argv[2]);
754 else
755 printf("could not enter name? weird.\n");
756 curinode = ginode(curinum);
757 return rval;
758}
759
760CMDFUNCSTART(rm)
761{
762 int rval;
763
764 if (!checkactivedir())
765 return 1;
766 rval = changeino(curinum, argv[1], 0);
767 if (rval & ALTERED) {
768 printf("Name `%s' removed\n", argv[1]);
769 return 0;
770 } else {
771 printf("could not remove name ('%s')? weird.\n", argv[1]);
772 return 1;
773 }
774}
775
776long slotcount, desired;
777
778int
779chinumfunc(struct inodesc *idesc)
780{
781 struct direct *dirp = idesc->id_dirp;
782
783 if (slotcount++ == desired) {
784 dirp->d_ino = idesc->id_parent;
785 return STOP|ALTERED|FOUND;
786 }
787 return KEEPON;
788}
789
790CMDFUNCSTART(chinum)
791{
792 char *cp;
793 ino_t inum;
794 struct inodesc idesc;
795
796 slotcount = 0;
797 if (!checkactivedir())
798 return 1;
799 GETINUM(2,inum);
800
801 desired = strtol(argv[1], &cp, 0);
802 if (cp == argv[1] || *cp != '\0' || desired < 0) {
803 printf("invalid slot number `%s'\n", argv[1]);
804 return 1;
805 }
806
807 idesc.id_number = curinum;
808 idesc.id_func = chinumfunc;
809 idesc.id_fix = IGNORE;
810 idesc.id_type = DATA;
811 idesc.id_parent = inum; /* XXX convenient hiding place */
812
813 if (ckinode(curinode, &idesc) & FOUND)
814 return 0;
815 else {
816 warnx("no %sth slot in current directory", argv[1]);
817 return 1;
818 }
819}
820
821int
822chnamefunc(struct inodesc *idesc)
823{
824 struct direct *dirp = idesc->id_dirp;
825 struct direct testdir;
826
827 if (slotcount++ == desired) {
828 /* will name fit? */
829 testdir.d_namlen = strlen(idesc->id_name);
830 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
831 dirp->d_namlen = testdir.d_namlen;
832 strcpy(dirp->d_name, idesc->id_name);
833 return STOP|ALTERED|FOUND;
834 } else
835 return STOP|FOUND; /* won't fit, so give up */
836 }
837 return KEEPON;
838}
839
840CMDFUNCSTART(chname)
841{
842 int rval;
843 char *cp;
844 struct inodesc idesc;
845
846 slotcount = 0;
847 if (!checkactivedir())
848 return 1;
849
850 desired = strtoul(argv[1], &cp, 0);
851 if (cp == argv[1] || *cp != '\0') {
852 printf("invalid slot number `%s'\n", argv[1]);
853 return 1;
854 }
855
856 idesc.id_number = curinum;
857 idesc.id_func = chnamefunc;
858 idesc.id_fix = IGNORE;
859 idesc.id_type = DATA;
860 idesc.id_name = argv[2];
861
862 rval = ckinode(curinode, &idesc);
863 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
864 return 0;
865 else if (rval & FOUND) {
866 warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
867 return 1;
868 } else {
869 warnx("no %sth slot in current directory", argv[1]);
870 return 1;
871 }
872}
873
874struct typemap {
875 const char *typename;
876 int typebits;
877} typenamemap[] = {
878 {"file", IFREG},
879 {"dir", IFDIR},
880 {"socket", IFSOCK},
881 {"fifo", IFIFO},
882};
883
884CMDFUNCSTART(newtype)
885{
886 int type;
887 struct typemap *tp;
888
889 if (!checkactive())
890 return 1;
891 type = DIP(curinode, di_mode) & IFMT;
892 for (tp = typenamemap;
893 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
894 tp++) {
895 if (!strcmp(argv[1], tp->typename)) {
896 printf("setting type to %s\n", tp->typename);
897 type = tp->typebits;
898 break;
899 }
900 }
901 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
902 warnx("type `%s' not known", argv[1]);
903 warnx("try one of `file', `dir', `socket', `fifo'");
904 return 1;
905 }
906 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
907 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
908 inodirty();
909 printactive(0);
910 return 0;
911}
912
913CMDFUNCSTART(chlen)
914{
915 int rval = 1;
916 long len;
917 char *cp;
918
919 if (!checkactive())
920 return 1;
921
922 len = strtol(argv[1], &cp, 0);
923 if (cp == argv[1] || *cp != '\0' || len < 0) {
924 warnx("bad length `%s'", argv[1]);
925 return 1;
926 }
927
928 DIP_SET(curinode, di_size, len);
929 inodirty();
930 printactive(0);
931 return rval;
932}
933
934CMDFUNCSTART(chmode)
935{
936 int rval = 1;
937 long modebits;
938 char *cp;
939
940 if (!checkactive())
941 return 1;
942
943 modebits = strtol(argv[1], &cp, 8);
944 if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
945 warnx("bad modebits `%s'", argv[1]);
946 return 1;
947 }
948
949 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
950 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
951 inodirty();
952 printactive(0);
953 return rval;
954}
955
956CMDFUNCSTART(chaflags)
957{
958 int rval = 1;
959 u_long flags;
960 char *cp;
961
962 if (!checkactive())
963 return 1;
964
965 flags = strtoul(argv[1], &cp, 0);
966 if (cp == argv[1] || *cp != '\0' ) {
967 warnx("bad flags `%s'", argv[1]);
968 return 1;
969 }
970
971 if (flags > UINT_MAX) {
972 warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
973 return(1);
974 }
975 DIP_SET(curinode, di_flags, flags);
976 inodirty();
977 printactive(0);
978 return rval;
979}
980
981CMDFUNCSTART(chgen)
982{
983 int rval = 1;
984 long gen;
985 char *cp;
986
987 if (!checkactive())
988 return 1;
989
990 gen = strtol(argv[1], &cp, 0);
991 if (cp == argv[1] || *cp != '\0' ) {
992 warnx("bad gen `%s'", argv[1]);
993 return 1;
994 }
995
996 if (gen > INT_MAX || gen < INT_MIN) {
997 warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
998 return(1);
999 }
1000 DIP_SET(curinode, di_gen, gen);
1001 inodirty();
1002 printactive(0);
1003 return rval;
1004}
1005
1006CMDFUNCSTART(linkcount)
1007{
1008 int rval = 1;
1009 int lcnt;
1010 char *cp;
1011
1012 if (!checkactive())
1013 return 1;
1014
1015 lcnt = strtol(argv[1], &cp, 0);
1016 if (cp == argv[1] || *cp != '\0' ) {
1017 warnx("bad link count `%s'", argv[1]);
1018 return 1;
1019 }
1020 if (lcnt > USHRT_MAX || lcnt < 0) {
1021 warnx("max link count is %d\n", USHRT_MAX);
1022 return 1;
1023 }
1024
1025 DIP_SET(curinode, di_nlink, lcnt);
1026 inodirty();
1027 printactive(0);
1028 return rval;
1029}
1030
1031CMDFUNCSTART(chowner)
1032{
1033 int rval = 1;
1034 unsigned long uid;
1035 char *cp;
1036 struct passwd *pwd;
1037
1038 if (!checkactive())
1039 return 1;
1040
1041 uid = strtoul(argv[1], &cp, 0);
1042 if (cp == argv[1] || *cp != '\0' ) {
1043 /* try looking up name */
1044 if ((pwd = getpwnam(argv[1]))) {
1045 uid = pwd->pw_uid;
1046 } else {
1047 warnx("bad uid `%s'", argv[1]);
1048 return 1;
1049 }
1050 }
1051
1052 DIP_SET(curinode, di_uid, uid);
1053 inodirty();
1054 printactive(0);
1055 return rval;
1056}
1057
1058CMDFUNCSTART(chgroup)
1059{
1060 int rval = 1;
1061 unsigned long gid;
1062 char *cp;
1063 struct group *grp;
1064
1065 if (!checkactive())
1066 return 1;
1067
1068 gid = strtoul(argv[1], &cp, 0);
1069 if (cp == argv[1] || *cp != '\0' ) {
1070 if ((grp = getgrnam(argv[1]))) {
1071 gid = grp->gr_gid;
1072 } else {
1073 warnx("bad gid `%s'", argv[1]);
1074 return 1;
1075 }
1076 }
1077
1078 DIP_SET(curinode, di_gid, gid);
1079 inodirty();
1080 printactive(0);
1081 return rval;
1082}
1083
1084int
1085dotime(char *name, time_t *secp, int32_t *nsecp)
1086{
1087 char *p, *val;
1088 struct tm t;
1089 int32_t nsec;
1090 p = strchr(name, '.');
1091 if (p) {
1092 *p = '\0';
1093 nsec = strtoul(++p, &val, 0);
1094 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
1095 warnx("invalid nanoseconds");
1096 goto badformat;
1097 }
1098 } else
1099 nsec = 0;
1100 if (strlen(name) != 14) {
1101badformat:
1102 warnx("date format: YYYYMMDDHHMMSS[.nsec]");
1103 return 1;
1104 }
1105 *nsecp = nsec;
1106
1107 for (p = name; *p; p++)
1108 if (*p < '0' || *p > '9')
1109 goto badformat;
1110
1111 p = name;
1112#define VAL() ((*p++) - '0')
1113 t.tm_year = VAL();
1114 t.tm_year = VAL() + t.tm_year * 10;
1115 t.tm_year = VAL() + t.tm_year * 10;
1116 t.tm_year = VAL() + t.tm_year * 10 - 1900;
1117 t.tm_mon = VAL();
1118 t.tm_mon = VAL() + t.tm_mon * 10 - 1;
1119 t.tm_mday = VAL();
1120 t.tm_mday = VAL() + t.tm_mday * 10;
1121 t.tm_hour = VAL();
1122 t.tm_hour = VAL() + t.tm_hour * 10;
1123 t.tm_min = VAL();
1124 t.tm_min = VAL() + t.tm_min * 10;
1125 t.tm_sec = VAL();
1126 t.tm_sec = VAL() + t.tm_sec * 10;
1127 t.tm_isdst = -1;
1128
1129 *secp = mktime(&t);
1130 if (*secp == -1) {
1131 warnx("date/time out of range");
1132 return 1;
1133 }
1134 return 0;
1135}
1136
1137CMDFUNCSTART(chbtime)
1138{
1139 time_t secs;
1140 int32_t nsecs;
1141
1142 if (dotime(argv[1], &secs, &nsecs))
1143 return 1;
1144 if (sblock.fs_magic == FS_UFS1_MAGIC)
1145 return 1;
1146 curinode->dp2.di_birthtime = _time_to_time64(secs);
1147 curinode->dp2.di_birthnsec = nsecs;
1148 inodirty();
1149 printactive(0);
1150 return 0;
1151}
1152
1153CMDFUNCSTART(chmtime)
1154{
1155 time_t secs;
1156 int32_t nsecs;
1157
1158 if (dotime(argv[1], &secs, &nsecs))
1159 return 1;
1160 if (sblock.fs_magic == FS_UFS1_MAGIC)
1161 curinode->dp1.di_mtime = _time_to_time32(secs);
1162 else
1163 curinode->dp2.di_mtime = _time_to_time64(secs);
1164 DIP_SET(curinode, di_mtimensec, nsecs);
1165 inodirty();
1166 printactive(0);
1167 return 0;
1168}
1169
1170CMDFUNCSTART(chatime)
1171{
1172 time_t secs;
1173 int32_t nsecs;
1174
1175 if (dotime(argv[1], &secs, &nsecs))
1176 return 1;
1177 if (sblock.fs_magic == FS_UFS1_MAGIC)
1178 curinode->dp1.di_atime = _time_to_time32(secs);
1179 else
1180 curinode->dp2.di_atime = _time_to_time64(secs);
1181 DIP_SET(curinode, di_atimensec, nsecs);
1182 inodirty();
1183 printactive(0);
1184 return 0;
1185}
1186
1187CMDFUNCSTART(chctime)
1188{
1189 time_t secs;
1190 int32_t nsecs;
1191
1192 if (dotime(argv[1], &secs, &nsecs))
1193 return 1;
1194 if (sblock.fs_magic == FS_UFS1_MAGIC)
1195 curinode->dp1.di_ctime = _time_to_time32(secs);
1196 else
1197 curinode->dp2.di_ctime = _time_to_time64(secs);
1198 DIP_SET(curinode, di_ctimensec, nsecs);
1199 inodirty();
1200 printactive(0);
1201 return 0;
1202}
422 idesc.id_number = curinum;
423 idesc.id_func = scannames;
424 idesc.id_type = DATA;
425 idesc.id_fix = IGNORE;
426 ckinode(curinode, &idesc);
427 curinode = ginode(curinum);
428
429 return 0;
430}
431
432static int findblk_numtofind;
433static int wantedblksize;
434
435CMDFUNCSTART(findblk)
436{
437 ino_t inum, inosused;
438 uint32_t *wantedblk32;
439 uint64_t *wantedblk64;
440 struct cg *cgp = &cgrp;
441 int c, i, is_ufs2;
442
443 wantedblksize = (argc - 1);
444 is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC;
445 ocurrent = curinum;
446
447 if (is_ufs2) {
448 wantedblk64 = calloc(wantedblksize, sizeof(uint64_t));
449 if (wantedblk64 == NULL)
450 err(1, "malloc");
451 for (i = 1; i < argc; i++)
452 wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
453 } else {
454 wantedblk32 = calloc(wantedblksize, sizeof(uint32_t));
455 if (wantedblk32 == NULL)
456 err(1, "malloc");
457 for (i = 1; i < argc; i++)
458 wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0));
459 }
460 findblk_numtofind = wantedblksize;
461 /*
462 * sblock.fs_ncg holds a number of cylinder groups.
463 * Iterate over all cylinder groups.
464 */
465 for (c = 0; c < sblock.fs_ncg; c++) {
466 /*
467 * sblock.fs_ipg holds a number of inodes per cylinder group.
468 * Calculate a highest inode number for a given cylinder group.
469 */
470 inum = c * sblock.fs_ipg;
471 /* Read cylinder group. */
472 getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize);
473 memcpy(cgp, cgblk.b_un.b_cg, sblock.fs_cgsize);
474 /*
475 * Get a highest used inode number for a given cylinder group.
476 * For UFS1 all inodes initialized at the newfs stage.
477 */
478 if (is_ufs2)
479 inosused = cgp->cg_initediblk;
480 else
481 inosused = sblock.fs_ipg;
482
483 for (; inosused > 0; inum++, inosused--) {
484 /* Skip magic inodes: 0, WINO, ROOTINO. */
485 if (inum < ROOTINO)
486 continue;
487 /*
488 * Check if the block we are looking for is just an inode block.
489 *
490 * ino_to_fsba() - get block containing inode from its number.
491 * INOPB() - get a number of inodes in one disk block.
492 */
493 if (is_ufs2 ?
494 compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) :
495 compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) {
496 printf("block %llu: inode block (%d-%d)\n",
497 (unsigned long long)fsbtodb(&sblock,
498 ino_to_fsba(&sblock, inum)),
499 (inum / INOPB(&sblock)) * INOPB(&sblock),
500 (inum / INOPB(&sblock) + 1) * INOPB(&sblock));
501 findblk_numtofind--;
502 if (findblk_numtofind == 0)
503 goto end;
504 }
505 /* Get on-disk inode aka dinode. */
506 curinum = inum;
507 curinode = ginode(inum);
508 /* Find IFLNK dinode with allocated data blocks. */
509 switch (DIP(curinode, di_mode) & IFMT) {
510 case IFDIR:
511 case IFREG:
512 if (DIP(curinode, di_blocks) == 0)
513 continue;
514 break;
515 case IFLNK:
516 {
517 uint64_t size = DIP(curinode, di_size);
518 if (size > 0 && size < sblock.fs_maxsymlinklen &&
519 DIP(curinode, di_blocks) == 0)
520 continue;
521 else
522 break;
523 }
524 default:
525 continue;
526 }
527 /* Look through direct data blocks. */
528 if (is_ufs2 ?
529 find_blks64(curinode->dp2.di_db, NDADDR, wantedblk64) :
530 find_blks32(curinode->dp1.di_db, NDADDR, wantedblk32))
531 goto end;
532 for (i = 0; i < NIADDR; i++) {
533 /*
534 * Does the block we are looking for belongs to the
535 * indirect blocks?
536 */
537 if (is_ufs2 ?
538 compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) :
539 compare_blk32(wantedblk32, curinode->dp1.di_ib[i]))
540 if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] :
541 curinode->dp1.di_ib[i]))
542 goto end;
543 /*
544 * Search through indirect, double and triple indirect
545 * data blocks.
546 */
547 if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) :
548 (curinode->dp1.di_ib[i] != 0))
549 if (is_ufs2 ?
550 find_indirblks64(curinode->dp2.di_ib[i], i,
551 wantedblk64) :
552 find_indirblks32(curinode->dp1.di_ib[i], i,
553 wantedblk32))
554 goto end;
555 }
556 }
557 }
558end:
559 curinum = ocurrent;
560 curinode = ginode(curinum);
561 return 0;
562}
563
564static int
565compare_blk32(uint32_t *wantedblk, uint32_t curblk)
566{
567 int i;
568
569 for (i = 0; i < wantedblksize; i++) {
570 if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
571 wantedblk[i] = 0;
572 return 1;
573 }
574 }
575 return 0;
576}
577
578static int
579compare_blk64(uint64_t *wantedblk, uint64_t curblk)
580{
581 int i;
582
583 for (i = 0; i < wantedblksize; i++) {
584 if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
585 wantedblk[i] = 0;
586 return 1;
587 }
588 }
589 return 0;
590}
591
592static int
593founddatablk(uint64_t blk)
594{
595
596 printf("%llu: data block of inode %d\n",
597 (unsigned long long)fsbtodb(&sblock, blk), curinum);
598 findblk_numtofind--;
599 if (findblk_numtofind == 0)
600 return 1;
601 return 0;
602}
603
604static int
605find_blks32(uint32_t *buf, int size, uint32_t *wantedblk)
606{
607 int blk;
608 for (blk = 0; blk < size; blk++) {
609 if (buf[blk] == 0)
610 continue;
611 if (compare_blk32(wantedblk, buf[blk])) {
612 if (founddatablk(buf[blk]))
613 return 1;
614 }
615 }
616 return 0;
617}
618
619static int
620find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk)
621{
622#define MAXNINDIR (MAXBSIZE / sizeof(uint32_t))
623 uint32_t idblk[MAXNINDIR];
624 int i;
625
626 blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
627 if (ind_level <= 0) {
628 if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk))
629 return 1;
630 } else {
631 ind_level--;
632 for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) {
633 if (compare_blk32(wantedblk, idblk[i])) {
634 if (founddatablk(idblk[i]))
635 return 1;
636 }
637 if (idblk[i] != 0)
638 if (find_indirblks32(idblk[i], ind_level, wantedblk))
639 return 1;
640 }
641 }
642#undef MAXNINDIR
643 return 0;
644}
645
646static int
647find_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
648{
649 int blk;
650 for (blk = 0; blk < size; blk++) {
651 if (buf[blk] == 0)
652 continue;
653 if (compare_blk64(wantedblk, buf[blk])) {
654 if (founddatablk(buf[blk]))
655 return 1;
656 }
657 }
658 return 0;
659}
660
661static int
662find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
663{
664#define MAXNINDIR (MAXBSIZE / sizeof(uint64_t))
665 uint64_t idblk[MAXNINDIR];
666 int i;
667
668 blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize);
669 if (ind_level <= 0) {
670 if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk))
671 return 1;
672 } else {
673 ind_level--;
674 for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) {
675 if (compare_blk64(wantedblk, idblk[i])) {
676 if (founddatablk(idblk[i]))
677 return 1;
678 }
679 if (idblk[i] != 0)
680 if (find_indirblks64(idblk[i], ind_level, wantedblk))
681 return 1;
682 }
683 }
684#undef MAXNINDIR
685 return 0;
686}
687
688int findino(struct inodesc *idesc); /* from fsck */
689static int dolookup(char *name);
690
691static int
692dolookup(char *name)
693{
694 struct inodesc idesc;
695
696 if (!checkactivedir())
697 return 0;
698 idesc.id_number = curinum;
699 idesc.id_func = findino;
700 idesc.id_name = name;
701 idesc.id_type = DATA;
702 idesc.id_fix = IGNORE;
703 if (ckinode(curinode, &idesc) & FOUND) {
704 curinum = idesc.id_parent;
705 curinode = ginode(curinum);
706 printactive(0);
707 return 1;
708 } else {
709 warnx("name `%s' not found in current inode directory", name);
710 return 0;
711 }
712}
713
714CMDFUNCSTART(focusname)
715{
716 char *p, *val;
717
718 if (!checkactive())
719 return 1;
720
721 ocurrent = curinum;
722
723 if (argv[1][0] == '/') {
724 curinum = ROOTINO;
725 curinode = ginode(ROOTINO);
726 } else {
727 if (!checkactivedir())
728 return 1;
729 }
730 for (p = argv[1]; p != NULL;) {
731 while ((val = strsep(&p, "/")) != NULL && *val == '\0');
732 if (val) {
733 printf("component `%s': ", val);
734 fflush(stdout);
735 if (!dolookup(val)) {
736 curinode = ginode(curinum);
737 return(1);
738 }
739 }
740 }
741 return 0;
742}
743
744CMDFUNCSTART(ln)
745{
746 ino_t inum;
747 int rval;
748 char *cp;
749
750 GETINUM(1,inum);
751
752 if (!checkactivedir())
753 return 1;
754 rval = makeentry(curinum, inum, argv[2]);
755 if (rval)
756 printf("Ino %d entered as `%s'\n", inum, argv[2]);
757 else
758 printf("could not enter name? weird.\n");
759 curinode = ginode(curinum);
760 return rval;
761}
762
763CMDFUNCSTART(rm)
764{
765 int rval;
766
767 if (!checkactivedir())
768 return 1;
769 rval = changeino(curinum, argv[1], 0);
770 if (rval & ALTERED) {
771 printf("Name `%s' removed\n", argv[1]);
772 return 0;
773 } else {
774 printf("could not remove name ('%s')? weird.\n", argv[1]);
775 return 1;
776 }
777}
778
779long slotcount, desired;
780
781int
782chinumfunc(struct inodesc *idesc)
783{
784 struct direct *dirp = idesc->id_dirp;
785
786 if (slotcount++ == desired) {
787 dirp->d_ino = idesc->id_parent;
788 return STOP|ALTERED|FOUND;
789 }
790 return KEEPON;
791}
792
793CMDFUNCSTART(chinum)
794{
795 char *cp;
796 ino_t inum;
797 struct inodesc idesc;
798
799 slotcount = 0;
800 if (!checkactivedir())
801 return 1;
802 GETINUM(2,inum);
803
804 desired = strtol(argv[1], &cp, 0);
805 if (cp == argv[1] || *cp != '\0' || desired < 0) {
806 printf("invalid slot number `%s'\n", argv[1]);
807 return 1;
808 }
809
810 idesc.id_number = curinum;
811 idesc.id_func = chinumfunc;
812 idesc.id_fix = IGNORE;
813 idesc.id_type = DATA;
814 idesc.id_parent = inum; /* XXX convenient hiding place */
815
816 if (ckinode(curinode, &idesc) & FOUND)
817 return 0;
818 else {
819 warnx("no %sth slot in current directory", argv[1]);
820 return 1;
821 }
822}
823
824int
825chnamefunc(struct inodesc *idesc)
826{
827 struct direct *dirp = idesc->id_dirp;
828 struct direct testdir;
829
830 if (slotcount++ == desired) {
831 /* will name fit? */
832 testdir.d_namlen = strlen(idesc->id_name);
833 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
834 dirp->d_namlen = testdir.d_namlen;
835 strcpy(dirp->d_name, idesc->id_name);
836 return STOP|ALTERED|FOUND;
837 } else
838 return STOP|FOUND; /* won't fit, so give up */
839 }
840 return KEEPON;
841}
842
843CMDFUNCSTART(chname)
844{
845 int rval;
846 char *cp;
847 struct inodesc idesc;
848
849 slotcount = 0;
850 if (!checkactivedir())
851 return 1;
852
853 desired = strtoul(argv[1], &cp, 0);
854 if (cp == argv[1] || *cp != '\0') {
855 printf("invalid slot number `%s'\n", argv[1]);
856 return 1;
857 }
858
859 idesc.id_number = curinum;
860 idesc.id_func = chnamefunc;
861 idesc.id_fix = IGNORE;
862 idesc.id_type = DATA;
863 idesc.id_name = argv[2];
864
865 rval = ckinode(curinode, &idesc);
866 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
867 return 0;
868 else if (rval & FOUND) {
869 warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
870 return 1;
871 } else {
872 warnx("no %sth slot in current directory", argv[1]);
873 return 1;
874 }
875}
876
877struct typemap {
878 const char *typename;
879 int typebits;
880} typenamemap[] = {
881 {"file", IFREG},
882 {"dir", IFDIR},
883 {"socket", IFSOCK},
884 {"fifo", IFIFO},
885};
886
887CMDFUNCSTART(newtype)
888{
889 int type;
890 struct typemap *tp;
891
892 if (!checkactive())
893 return 1;
894 type = DIP(curinode, di_mode) & IFMT;
895 for (tp = typenamemap;
896 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
897 tp++) {
898 if (!strcmp(argv[1], tp->typename)) {
899 printf("setting type to %s\n", tp->typename);
900 type = tp->typebits;
901 break;
902 }
903 }
904 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
905 warnx("type `%s' not known", argv[1]);
906 warnx("try one of `file', `dir', `socket', `fifo'");
907 return 1;
908 }
909 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
910 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
911 inodirty();
912 printactive(0);
913 return 0;
914}
915
916CMDFUNCSTART(chlen)
917{
918 int rval = 1;
919 long len;
920 char *cp;
921
922 if (!checkactive())
923 return 1;
924
925 len = strtol(argv[1], &cp, 0);
926 if (cp == argv[1] || *cp != '\0' || len < 0) {
927 warnx("bad length `%s'", argv[1]);
928 return 1;
929 }
930
931 DIP_SET(curinode, di_size, len);
932 inodirty();
933 printactive(0);
934 return rval;
935}
936
937CMDFUNCSTART(chmode)
938{
939 int rval = 1;
940 long modebits;
941 char *cp;
942
943 if (!checkactive())
944 return 1;
945
946 modebits = strtol(argv[1], &cp, 8);
947 if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
948 warnx("bad modebits `%s'", argv[1]);
949 return 1;
950 }
951
952 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
953 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
954 inodirty();
955 printactive(0);
956 return rval;
957}
958
959CMDFUNCSTART(chaflags)
960{
961 int rval = 1;
962 u_long flags;
963 char *cp;
964
965 if (!checkactive())
966 return 1;
967
968 flags = strtoul(argv[1], &cp, 0);
969 if (cp == argv[1] || *cp != '\0' ) {
970 warnx("bad flags `%s'", argv[1]);
971 return 1;
972 }
973
974 if (flags > UINT_MAX) {
975 warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
976 return(1);
977 }
978 DIP_SET(curinode, di_flags, flags);
979 inodirty();
980 printactive(0);
981 return rval;
982}
983
984CMDFUNCSTART(chgen)
985{
986 int rval = 1;
987 long gen;
988 char *cp;
989
990 if (!checkactive())
991 return 1;
992
993 gen = strtol(argv[1], &cp, 0);
994 if (cp == argv[1] || *cp != '\0' ) {
995 warnx("bad gen `%s'", argv[1]);
996 return 1;
997 }
998
999 if (gen > INT_MAX || gen < INT_MIN) {
1000 warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
1001 return(1);
1002 }
1003 DIP_SET(curinode, di_gen, gen);
1004 inodirty();
1005 printactive(0);
1006 return rval;
1007}
1008
1009CMDFUNCSTART(linkcount)
1010{
1011 int rval = 1;
1012 int lcnt;
1013 char *cp;
1014
1015 if (!checkactive())
1016 return 1;
1017
1018 lcnt = strtol(argv[1], &cp, 0);
1019 if (cp == argv[1] || *cp != '\0' ) {
1020 warnx("bad link count `%s'", argv[1]);
1021 return 1;
1022 }
1023 if (lcnt > USHRT_MAX || lcnt < 0) {
1024 warnx("max link count is %d\n", USHRT_MAX);
1025 return 1;
1026 }
1027
1028 DIP_SET(curinode, di_nlink, lcnt);
1029 inodirty();
1030 printactive(0);
1031 return rval;
1032}
1033
1034CMDFUNCSTART(chowner)
1035{
1036 int rval = 1;
1037 unsigned long uid;
1038 char *cp;
1039 struct passwd *pwd;
1040
1041 if (!checkactive())
1042 return 1;
1043
1044 uid = strtoul(argv[1], &cp, 0);
1045 if (cp == argv[1] || *cp != '\0' ) {
1046 /* try looking up name */
1047 if ((pwd = getpwnam(argv[1]))) {
1048 uid = pwd->pw_uid;
1049 } else {
1050 warnx("bad uid `%s'", argv[1]);
1051 return 1;
1052 }
1053 }
1054
1055 DIP_SET(curinode, di_uid, uid);
1056 inodirty();
1057 printactive(0);
1058 return rval;
1059}
1060
1061CMDFUNCSTART(chgroup)
1062{
1063 int rval = 1;
1064 unsigned long gid;
1065 char *cp;
1066 struct group *grp;
1067
1068 if (!checkactive())
1069 return 1;
1070
1071 gid = strtoul(argv[1], &cp, 0);
1072 if (cp == argv[1] || *cp != '\0' ) {
1073 if ((grp = getgrnam(argv[1]))) {
1074 gid = grp->gr_gid;
1075 } else {
1076 warnx("bad gid `%s'", argv[1]);
1077 return 1;
1078 }
1079 }
1080
1081 DIP_SET(curinode, di_gid, gid);
1082 inodirty();
1083 printactive(0);
1084 return rval;
1085}
1086
1087int
1088dotime(char *name, time_t *secp, int32_t *nsecp)
1089{
1090 char *p, *val;
1091 struct tm t;
1092 int32_t nsec;
1093 p = strchr(name, '.');
1094 if (p) {
1095 *p = '\0';
1096 nsec = strtoul(++p, &val, 0);
1097 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
1098 warnx("invalid nanoseconds");
1099 goto badformat;
1100 }
1101 } else
1102 nsec = 0;
1103 if (strlen(name) != 14) {
1104badformat:
1105 warnx("date format: YYYYMMDDHHMMSS[.nsec]");
1106 return 1;
1107 }
1108 *nsecp = nsec;
1109
1110 for (p = name; *p; p++)
1111 if (*p < '0' || *p > '9')
1112 goto badformat;
1113
1114 p = name;
1115#define VAL() ((*p++) - '0')
1116 t.tm_year = VAL();
1117 t.tm_year = VAL() + t.tm_year * 10;
1118 t.tm_year = VAL() + t.tm_year * 10;
1119 t.tm_year = VAL() + t.tm_year * 10 - 1900;
1120 t.tm_mon = VAL();
1121 t.tm_mon = VAL() + t.tm_mon * 10 - 1;
1122 t.tm_mday = VAL();
1123 t.tm_mday = VAL() + t.tm_mday * 10;
1124 t.tm_hour = VAL();
1125 t.tm_hour = VAL() + t.tm_hour * 10;
1126 t.tm_min = VAL();
1127 t.tm_min = VAL() + t.tm_min * 10;
1128 t.tm_sec = VAL();
1129 t.tm_sec = VAL() + t.tm_sec * 10;
1130 t.tm_isdst = -1;
1131
1132 *secp = mktime(&t);
1133 if (*secp == -1) {
1134 warnx("date/time out of range");
1135 return 1;
1136 }
1137 return 0;
1138}
1139
1140CMDFUNCSTART(chbtime)
1141{
1142 time_t secs;
1143 int32_t nsecs;
1144
1145 if (dotime(argv[1], &secs, &nsecs))
1146 return 1;
1147 if (sblock.fs_magic == FS_UFS1_MAGIC)
1148 return 1;
1149 curinode->dp2.di_birthtime = _time_to_time64(secs);
1150 curinode->dp2.di_birthnsec = nsecs;
1151 inodirty();
1152 printactive(0);
1153 return 0;
1154}
1155
1156CMDFUNCSTART(chmtime)
1157{
1158 time_t secs;
1159 int32_t nsecs;
1160
1161 if (dotime(argv[1], &secs, &nsecs))
1162 return 1;
1163 if (sblock.fs_magic == FS_UFS1_MAGIC)
1164 curinode->dp1.di_mtime = _time_to_time32(secs);
1165 else
1166 curinode->dp2.di_mtime = _time_to_time64(secs);
1167 DIP_SET(curinode, di_mtimensec, nsecs);
1168 inodirty();
1169 printactive(0);
1170 return 0;
1171}
1172
1173CMDFUNCSTART(chatime)
1174{
1175 time_t secs;
1176 int32_t nsecs;
1177
1178 if (dotime(argv[1], &secs, &nsecs))
1179 return 1;
1180 if (sblock.fs_magic == FS_UFS1_MAGIC)
1181 curinode->dp1.di_atime = _time_to_time32(secs);
1182 else
1183 curinode->dp2.di_atime = _time_to_time64(secs);
1184 DIP_SET(curinode, di_atimensec, nsecs);
1185 inodirty();
1186 printactive(0);
1187 return 0;
1188}
1189
1190CMDFUNCSTART(chctime)
1191{
1192 time_t secs;
1193 int32_t nsecs;
1194
1195 if (dotime(argv[1], &secs, &nsecs))
1196 return 1;
1197 if (sblock.fs_magic == FS_UFS1_MAGIC)
1198 curinode->dp1.di_ctime = _time_to_time32(secs);
1199 else
1200 curinode->dp2.di_ctime = _time_to_time64(secs);
1201 DIP_SET(curinode, di_ctimensec, nsecs);
1202 inodirty();
1203 printactive(0);
1204 return 0;
1205}