Deleted Added
full compact
fsdb.c (89827) fsdb.c (92806)
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 89827 2002-01-26 15:53:23Z joerg $";
33 "$FreeBSD: head/sbin/fsdb/fsdb.c 92806 2002-03-20 17:55:10Z obrien $";
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
45#include <ufs/ufs/dinode.h>
46#include <ufs/ufs/dir.h>
47#include <ufs/ffs/fs.h>
48
49#include "fsdb.h"
50#include "fsck.h"
51
52static void usage __P((void));
53int cmdloop __P((void));
54
55static void
56usage()
57{
58 fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
59 exit(1);
60}
61
62int returntosingle;
63char nflag;
64
65/*
66 * We suck in lots of fsck code, and just pick & choose the stuff we want.
67 *
68 * fsreadfd is set up to read from the file system, fswritefd to write to
69 * the file system.
70 */
71int
72main(argc, argv)
73 int argc;
74 char *argv[];
75{
76 int ch, rval;
77 char *fsys = NULL;
78
79 while (-1 != (ch = getopt(argc, argv, "fdr"))) {
80 switch (ch) {
81 case 'f':
82 /* The -f option is left for historical
83 * reasons and has no meaning.
84 */
85 break;
86 case 'd':
87 debug++;
88 break;
89 case 'r':
90 nflag++; /* "no" in fsck, readonly for us */
91 break;
92 default:
93 usage();
94 }
95 }
96 argc -= optind;
97 argv += optind;
98 if (argc != 1)
99 usage();
100 else
101 fsys = argv[0];
102
103 sblock_init();
104 if (!setup(fsys))
105 errx(1, "cannot set up file system `%s'", fsys);
106 printf("%s file system `%s'\nLast Mounted on %s\n",
107 nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
108 rval = cmdloop();
109 if (!nflag) {
110 sblock.fs_clean = 0; /* mark it dirty */
111 sbdirty();
112 ckfini(0);
113 printf("*** FILE SYSTEM MARKED DIRTY\n");
114 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
115 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
116 }
117 exit(rval);
118}
119
120#define CMDFUNC(func) int func __P((int argc, char *argv[]))
121#define CMDFUNCSTART(func) int func(argc, argv) \
122 int argc; \
123 char *argv[];
124
125CMDFUNC(helpfn);
126CMDFUNC(focus); /* focus on inode */
127CMDFUNC(active); /* print active inode */
128CMDFUNC(blocks); /* print blocks for active inode */
129CMDFUNC(focusname); /* focus by name */
130CMDFUNC(zapi); /* clear inode */
131CMDFUNC(uplink); /* incr link */
132CMDFUNC(downlink); /* decr link */
133CMDFUNC(linkcount); /* set link count */
134CMDFUNC(quit); /* quit */
135CMDFUNC(ls); /* list directory */
136CMDFUNC(rm); /* remove name */
137CMDFUNC(ln); /* add name */
138CMDFUNC(newtype); /* change type */
139CMDFUNC(chmode); /* change mode */
140CMDFUNC(chlen); /* change length */
141CMDFUNC(chaflags); /* change flags */
142CMDFUNC(chgen); /* change generation */
143CMDFUNC(chowner); /* change owner */
144CMDFUNC(chgroup); /* Change group */
145CMDFUNC(back); /* pop back to last ino */
146CMDFUNC(chmtime); /* Change mtime */
147CMDFUNC(chctime); /* Change ctime */
148CMDFUNC(chatime); /* Change atime */
149CMDFUNC(chinum); /* Change inode # of dirent */
150CMDFUNC(chname); /* Change dirname of dirent */
151
152struct cmdtable cmds[] = {
153 { "help", "Print out help", 1, 1, FL_RO, helpfn },
154 { "?", "Print out help", 1, 1, FL_RO, helpfn },
155 { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
156 { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
157 { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
158 { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
159 { "back", "Go to previous active inode", 1, 1, FL_RO, back },
160 { "active", "Print active inode", 1, 1, FL_RO, active },
161 { "print", "Print active inode", 1, 1, FL_RO, active },
162 { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
163 { "uplink", "Increment link count", 1, 1, FL_WR, uplink },
164 { "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
165 { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
166 { "ls", "List current inode as directory", 1, 1, FL_RO, ls },
167 { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
168 { "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
169 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln },
170 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
171 { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname },
172 { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
173 { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
174 { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
175 { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
176 { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
177 { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
178 { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
179 { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
180 { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
181 { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
182 { "quit", "Exit", 1, 1, FL_RO, quit },
183 { "q", "Exit", 1, 1, FL_RO, quit },
184 { "exit", "Exit", 1, 1, FL_RO, quit },
185 { NULL, 0, 0, 0 },
186};
187
188int
189helpfn(argc, argv)
190 int argc;
191 char *argv[];
192{
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
45#include <ufs/ufs/dinode.h>
46#include <ufs/ufs/dir.h>
47#include <ufs/ffs/fs.h>
48
49#include "fsdb.h"
50#include "fsck.h"
51
52static void usage __P((void));
53int cmdloop __P((void));
54
55static void
56usage()
57{
58 fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n");
59 exit(1);
60}
61
62int returntosingle;
63char nflag;
64
65/*
66 * We suck in lots of fsck code, and just pick & choose the stuff we want.
67 *
68 * fsreadfd is set up to read from the file system, fswritefd to write to
69 * the file system.
70 */
71int
72main(argc, argv)
73 int argc;
74 char *argv[];
75{
76 int ch, rval;
77 char *fsys = NULL;
78
79 while (-1 != (ch = getopt(argc, argv, "fdr"))) {
80 switch (ch) {
81 case 'f':
82 /* The -f option is left for historical
83 * reasons and has no meaning.
84 */
85 break;
86 case 'd':
87 debug++;
88 break;
89 case 'r':
90 nflag++; /* "no" in fsck, readonly for us */
91 break;
92 default:
93 usage();
94 }
95 }
96 argc -= optind;
97 argv += optind;
98 if (argc != 1)
99 usage();
100 else
101 fsys = argv[0];
102
103 sblock_init();
104 if (!setup(fsys))
105 errx(1, "cannot set up file system `%s'", fsys);
106 printf("%s file system `%s'\nLast Mounted on %s\n",
107 nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt);
108 rval = cmdloop();
109 if (!nflag) {
110 sblock.fs_clean = 0; /* mark it dirty */
111 sbdirty();
112 ckfini(0);
113 printf("*** FILE SYSTEM MARKED DIRTY\n");
114 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
115 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
116 }
117 exit(rval);
118}
119
120#define CMDFUNC(func) int func __P((int argc, char *argv[]))
121#define CMDFUNCSTART(func) int func(argc, argv) \
122 int argc; \
123 char *argv[];
124
125CMDFUNC(helpfn);
126CMDFUNC(focus); /* focus on inode */
127CMDFUNC(active); /* print active inode */
128CMDFUNC(blocks); /* print blocks for active inode */
129CMDFUNC(focusname); /* focus by name */
130CMDFUNC(zapi); /* clear inode */
131CMDFUNC(uplink); /* incr link */
132CMDFUNC(downlink); /* decr link */
133CMDFUNC(linkcount); /* set link count */
134CMDFUNC(quit); /* quit */
135CMDFUNC(ls); /* list directory */
136CMDFUNC(rm); /* remove name */
137CMDFUNC(ln); /* add name */
138CMDFUNC(newtype); /* change type */
139CMDFUNC(chmode); /* change mode */
140CMDFUNC(chlen); /* change length */
141CMDFUNC(chaflags); /* change flags */
142CMDFUNC(chgen); /* change generation */
143CMDFUNC(chowner); /* change owner */
144CMDFUNC(chgroup); /* Change group */
145CMDFUNC(back); /* pop back to last ino */
146CMDFUNC(chmtime); /* Change mtime */
147CMDFUNC(chctime); /* Change ctime */
148CMDFUNC(chatime); /* Change atime */
149CMDFUNC(chinum); /* Change inode # of dirent */
150CMDFUNC(chname); /* Change dirname of dirent */
151
152struct cmdtable cmds[] = {
153 { "help", "Print out help", 1, 1, FL_RO, helpfn },
154 { "?", "Print out help", 1, 1, FL_RO, helpfn },
155 { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus },
156 { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi },
157 { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
158 { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO, focusname },
159 { "back", "Go to previous active inode", 1, 1, FL_RO, back },
160 { "active", "Print active inode", 1, 1, FL_RO, active },
161 { "print", "Print active inode", 1, 1, FL_RO, active },
162 { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks },
163 { "uplink", "Increment link count", 1, 1, FL_WR, uplink },
164 { "downlink", "Decrement link count", 1, 1, FL_WR, downlink },
165 { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount },
166 { "ls", "List current inode as directory", 1, 1, FL_RO, ls },
167 { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
168 { "del", "Remove NAME from current inode directory", 2, 2, FL_WR, rm },
169 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR, ln },
170 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum },
171 { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR, chname },
172 { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype },
173 { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode },
174 { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen },
175 { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner },
176 { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
177 { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
178 { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
179 { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
180 { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
181 { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime },
182 { "quit", "Exit", 1, 1, FL_RO, quit },
183 { "q", "Exit", 1, 1, FL_RO, quit },
184 { "exit", "Exit", 1, 1, FL_RO, quit },
185 { NULL, 0, 0, 0 },
186};
187
188int
189helpfn(argc, argv)
190 int argc;
191 char *argv[];
192{
193 register struct cmdtable *cmdtp;
193 struct cmdtable *cmdtp;
194
195 printf("Commands are:\n%-10s %5s %5s %s\n",
196 "command", "min argc", "max argc", "what");
197
198 for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
199 printf("%-10s %5u %5u %s\n",
200 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
201 return 0;
202}
203
204char *
205prompt(el)
206 EditLine *el;
207{
208 static char pstring[64];
209 snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
210 return pstring;
211}
212
213
214int
215cmdloop()
216{
217 char *line;
218 const char *elline;
219 int cmd_argc, rval = 0, known;
220#define scratch known
221 char **cmd_argv;
222 struct cmdtable *cmdp;
223 History *hist;
224 EditLine *elptr;
225 HistEvent he;
226
227 curinode = ginode(ROOTINO);
228 curinum = ROOTINO;
229 printactive(0);
230
231 hist = history_init();
232 history(hist, &he, H_EVENT, 100); /* 100 elt history buffer */
233
234 elptr = el_init("fsdb", stdin, stdout, stderr);
235 el_set(elptr, EL_EDITOR, "emacs");
236 el_set(elptr, EL_PROMPT, prompt);
237 el_set(elptr, EL_HIST, history, hist);
238 el_source(elptr, NULL);
239
240 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
241 if (debug)
242 printf("command `%s'\n", elline);
243
244 history(hist, &he, H_ENTER, elline);
245
246 line = strdup(elline);
247 cmd_argv = crack(line, &cmd_argc);
248 /*
249 * el_parse returns -1 to signal that it's not been handled
250 * internally.
251 */
252 if (el_parse(elptr, cmd_argc, cmd_argv) != -1)
253 continue;
254 if (cmd_argc) {
255 known = 0;
256 for (cmdp = cmds; cmdp->cmd; cmdp++) {
257 if (!strcmp(cmdp->cmd, cmd_argv[0])) {
258 if ((cmdp->flags & FL_WR) == FL_WR && nflag)
259 warnx("`%s' requires write access", cmd_argv[0]),
260 rval = 1;
261 else if (cmd_argc >= cmdp->minargc &&
262 cmd_argc <= cmdp->maxargc)
263 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
264 else if (cmd_argc >= cmdp->minargc) {
265 strcpy(line, elline);
266 cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
267 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
268 } else
269 rval = argcount(cmdp, cmd_argc, cmd_argv);
270 known = 1;
271 break;
272 }
273 }
274 if (!known)
275 warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
276 } else
277 rval = 0;
278 free(line);
279 if (rval < 0)
280 /* user typed "quit" */
281 return 0;
282 if (rval)
283 warnx("rval was %d", rval);
284 }
285 el_end(elptr);
286 history_end(hist);
287 return rval;
288}
289
290struct dinode *curinode;
291ino_t curinum, ocurrent;
292
293#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
294 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
295 printf("inode %d out of range; range is [%d,%d]\n", \
296 inum, ROOTINO, maxino); \
297 return 1; \
298 }
299
300/*
301 * Focus on given inode number
302 */
303CMDFUNCSTART(focus)
304{
305 ino_t inum;
306 char *cp;
307
308 GETINUM(1,inum);
309 curinode = ginode(inum);
310 ocurrent = curinum;
311 curinum = inum;
312 printactive(0);
313 return 0;
314}
315
316CMDFUNCSTART(back)
317{
318 curinum = ocurrent;
319 curinode = ginode(curinum);
320 printactive(0);
321 return 0;
322}
323
324CMDFUNCSTART(zapi)
325{
326 ino_t inum;
327 struct dinode *dp;
328 char *cp;
329
330 GETINUM(1,inum);
331 dp = ginode(inum);
332 clearinode(dp);
333 inodirty();
334 if (curinode) /* re-set after potential change */
335 curinode = ginode(curinum);
336 return 0;
337}
338
339CMDFUNCSTART(active)
340{
341 printactive(0);
342 return 0;
343}
344
345CMDFUNCSTART(blocks)
346{
347 printactive(1);
348 return 0;
349}
350
351CMDFUNCSTART(quit)
352{
353 return -1;
354}
355
356CMDFUNCSTART(uplink)
357{
358 if (!checkactive())
359 return 1;
360 printf("inode %d link count now %d\n", curinum, ++curinode->di_nlink);
361 inodirty();
362 return 0;
363}
364
365CMDFUNCSTART(downlink)
366{
367 if (!checkactive())
368 return 1;
369 printf("inode %d link count now %d\n", curinum, --curinode->di_nlink);
370 inodirty();
371 return 0;
372}
373
374const char *typename[] = {
375 "unknown",
376 "fifo",
377 "char special",
378 "unregistered #3",
379 "directory",
380 "unregistered #5",
381 "blk special",
382 "unregistered #7",
383 "regular",
384 "unregistered #9",
385 "symlink",
386 "unregistered #11",
387 "socket",
388 "unregistered #13",
389 "whiteout",
390};
391
392int slot;
393
394int
395scannames(idesc)
396 struct inodesc *idesc;
397{
194
195 printf("Commands are:\n%-10s %5s %5s %s\n",
196 "command", "min argc", "max argc", "what");
197
198 for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
199 printf("%-10s %5u %5u %s\n",
200 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
201 return 0;
202}
203
204char *
205prompt(el)
206 EditLine *el;
207{
208 static char pstring[64];
209 snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
210 return pstring;
211}
212
213
214int
215cmdloop()
216{
217 char *line;
218 const char *elline;
219 int cmd_argc, rval = 0, known;
220#define scratch known
221 char **cmd_argv;
222 struct cmdtable *cmdp;
223 History *hist;
224 EditLine *elptr;
225 HistEvent he;
226
227 curinode = ginode(ROOTINO);
228 curinum = ROOTINO;
229 printactive(0);
230
231 hist = history_init();
232 history(hist, &he, H_EVENT, 100); /* 100 elt history buffer */
233
234 elptr = el_init("fsdb", stdin, stdout, stderr);
235 el_set(elptr, EL_EDITOR, "emacs");
236 el_set(elptr, EL_PROMPT, prompt);
237 el_set(elptr, EL_HIST, history, hist);
238 el_source(elptr, NULL);
239
240 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
241 if (debug)
242 printf("command `%s'\n", elline);
243
244 history(hist, &he, H_ENTER, elline);
245
246 line = strdup(elline);
247 cmd_argv = crack(line, &cmd_argc);
248 /*
249 * el_parse returns -1 to signal that it's not been handled
250 * internally.
251 */
252 if (el_parse(elptr, cmd_argc, cmd_argv) != -1)
253 continue;
254 if (cmd_argc) {
255 known = 0;
256 for (cmdp = cmds; cmdp->cmd; cmdp++) {
257 if (!strcmp(cmdp->cmd, cmd_argv[0])) {
258 if ((cmdp->flags & FL_WR) == FL_WR && nflag)
259 warnx("`%s' requires write access", cmd_argv[0]),
260 rval = 1;
261 else if (cmd_argc >= cmdp->minargc &&
262 cmd_argc <= cmdp->maxargc)
263 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
264 else if (cmd_argc >= cmdp->minargc) {
265 strcpy(line, elline);
266 cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc);
267 rval = (*cmdp->handler)(cmd_argc, cmd_argv);
268 } else
269 rval = argcount(cmdp, cmd_argc, cmd_argv);
270 known = 1;
271 break;
272 }
273 }
274 if (!known)
275 warnx("unknown command `%s'", cmd_argv[0]), rval = 1;
276 } else
277 rval = 0;
278 free(line);
279 if (rval < 0)
280 /* user typed "quit" */
281 return 0;
282 if (rval)
283 warnx("rval was %d", rval);
284 }
285 el_end(elptr);
286 history_end(hist);
287 return rval;
288}
289
290struct dinode *curinode;
291ino_t curinum, ocurrent;
292
293#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
294 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
295 printf("inode %d out of range; range is [%d,%d]\n", \
296 inum, ROOTINO, maxino); \
297 return 1; \
298 }
299
300/*
301 * Focus on given inode number
302 */
303CMDFUNCSTART(focus)
304{
305 ino_t inum;
306 char *cp;
307
308 GETINUM(1,inum);
309 curinode = ginode(inum);
310 ocurrent = curinum;
311 curinum = inum;
312 printactive(0);
313 return 0;
314}
315
316CMDFUNCSTART(back)
317{
318 curinum = ocurrent;
319 curinode = ginode(curinum);
320 printactive(0);
321 return 0;
322}
323
324CMDFUNCSTART(zapi)
325{
326 ino_t inum;
327 struct dinode *dp;
328 char *cp;
329
330 GETINUM(1,inum);
331 dp = ginode(inum);
332 clearinode(dp);
333 inodirty();
334 if (curinode) /* re-set after potential change */
335 curinode = ginode(curinum);
336 return 0;
337}
338
339CMDFUNCSTART(active)
340{
341 printactive(0);
342 return 0;
343}
344
345CMDFUNCSTART(blocks)
346{
347 printactive(1);
348 return 0;
349}
350
351CMDFUNCSTART(quit)
352{
353 return -1;
354}
355
356CMDFUNCSTART(uplink)
357{
358 if (!checkactive())
359 return 1;
360 printf("inode %d link count now %d\n", curinum, ++curinode->di_nlink);
361 inodirty();
362 return 0;
363}
364
365CMDFUNCSTART(downlink)
366{
367 if (!checkactive())
368 return 1;
369 printf("inode %d link count now %d\n", curinum, --curinode->di_nlink);
370 inodirty();
371 return 0;
372}
373
374const char *typename[] = {
375 "unknown",
376 "fifo",
377 "char special",
378 "unregistered #3",
379 "directory",
380 "unregistered #5",
381 "blk special",
382 "unregistered #7",
383 "regular",
384 "unregistered #9",
385 "symlink",
386 "unregistered #11",
387 "socket",
388 "unregistered #13",
389 "whiteout",
390};
391
392int slot;
393
394int
395scannames(idesc)
396 struct inodesc *idesc;
397{
398 register struct direct *dirp = idesc->id_dirp;
398 struct direct *dirp = idesc->id_dirp;
399
400 printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
401 slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type],
402 dirp->d_namlen, dirp->d_name);
403 return (KEEPON);
404}
405
406CMDFUNCSTART(ls)
407{
408 struct inodesc idesc;
409 checkactivedir(); /* let it go on anyway */
410
411 slot = 0;
412 idesc.id_number = curinum;
413 idesc.id_func = scannames;
414 idesc.id_type = DATA;
415 idesc.id_fix = IGNORE;
416 ckinode(curinode, &idesc);
417 curinode = ginode(curinum);
418
419 return 0;
420}
421
422int findino __P((struct inodesc *idesc)); /* from fsck */
423static int dolookup __P((char *name));
424
425static int
426dolookup(name)
427 char *name;
428{
429 struct inodesc idesc;
430
431 if (!checkactivedir())
432 return 0;
433 idesc.id_number = curinum;
434 idesc.id_func = findino;
435 idesc.id_name = name;
436 idesc.id_type = DATA;
437 idesc.id_fix = IGNORE;
438 if (ckinode(curinode, &idesc) & FOUND) {
439 curinum = idesc.id_parent;
440 curinode = ginode(curinum);
441 printactive(0);
442 return 1;
443 } else {
444 warnx("name `%s' not found in current inode directory", name);
445 return 0;
446 }
447}
448
449CMDFUNCSTART(focusname)
450{
451 char *p, *val;
452
453 if (!checkactive())
454 return 1;
455
456 ocurrent = curinum;
457
458 if (argv[1][0] == '/') {
459 curinum = ROOTINO;
460 curinode = ginode(ROOTINO);
461 } else {
462 if (!checkactivedir())
463 return 1;
464 }
465 for (p = argv[1]; p != NULL;) {
466 while ((val = strsep(&p, "/")) != NULL && *val == '\0');
467 if (val) {
468 printf("component `%s': ", val);
469 fflush(stdout);
470 if (!dolookup(val)) {
471 curinode = ginode(curinum);
472 return(1);
473 }
474 }
475 }
476 return 0;
477}
478
479CMDFUNCSTART(ln)
480{
481 ino_t inum;
482 int rval;
483 char *cp;
484
485 GETINUM(1,inum);
486
487 if (!checkactivedir())
488 return 1;
489 rval = makeentry(curinum, inum, argv[2]);
490 if (rval)
491 printf("Ino %d entered as `%s'\n", inum, argv[2]);
492 else
493 printf("could not enter name? weird.\n");
494 curinode = ginode(curinum);
495 return rval;
496}
497
498CMDFUNCSTART(rm)
499{
500 int rval;
501
502 if (!checkactivedir())
503 return 1;
504 rval = changeino(curinum, argv[1], 0);
505 if (rval & ALTERED) {
506 printf("Name `%s' removed\n", argv[1]);
507 return 0;
508 } else {
509 printf("could not remove name ('%s')? weird.\n", argv[1]);
510 return 1;
511 }
512}
513
514long slotcount, desired;
515
516int
517chinumfunc(idesc)
518 struct inodesc *idesc;
519{
399
400 printf("slot %d ino %d reclen %d: %s, `%.*s'\n",
401 slot++, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type],
402 dirp->d_namlen, dirp->d_name);
403 return (KEEPON);
404}
405
406CMDFUNCSTART(ls)
407{
408 struct inodesc idesc;
409 checkactivedir(); /* let it go on anyway */
410
411 slot = 0;
412 idesc.id_number = curinum;
413 idesc.id_func = scannames;
414 idesc.id_type = DATA;
415 idesc.id_fix = IGNORE;
416 ckinode(curinode, &idesc);
417 curinode = ginode(curinum);
418
419 return 0;
420}
421
422int findino __P((struct inodesc *idesc)); /* from fsck */
423static int dolookup __P((char *name));
424
425static int
426dolookup(name)
427 char *name;
428{
429 struct inodesc idesc;
430
431 if (!checkactivedir())
432 return 0;
433 idesc.id_number = curinum;
434 idesc.id_func = findino;
435 idesc.id_name = name;
436 idesc.id_type = DATA;
437 idesc.id_fix = IGNORE;
438 if (ckinode(curinode, &idesc) & FOUND) {
439 curinum = idesc.id_parent;
440 curinode = ginode(curinum);
441 printactive(0);
442 return 1;
443 } else {
444 warnx("name `%s' not found in current inode directory", name);
445 return 0;
446 }
447}
448
449CMDFUNCSTART(focusname)
450{
451 char *p, *val;
452
453 if (!checkactive())
454 return 1;
455
456 ocurrent = curinum;
457
458 if (argv[1][0] == '/') {
459 curinum = ROOTINO;
460 curinode = ginode(ROOTINO);
461 } else {
462 if (!checkactivedir())
463 return 1;
464 }
465 for (p = argv[1]; p != NULL;) {
466 while ((val = strsep(&p, "/")) != NULL && *val == '\0');
467 if (val) {
468 printf("component `%s': ", val);
469 fflush(stdout);
470 if (!dolookup(val)) {
471 curinode = ginode(curinum);
472 return(1);
473 }
474 }
475 }
476 return 0;
477}
478
479CMDFUNCSTART(ln)
480{
481 ino_t inum;
482 int rval;
483 char *cp;
484
485 GETINUM(1,inum);
486
487 if (!checkactivedir())
488 return 1;
489 rval = makeentry(curinum, inum, argv[2]);
490 if (rval)
491 printf("Ino %d entered as `%s'\n", inum, argv[2]);
492 else
493 printf("could not enter name? weird.\n");
494 curinode = ginode(curinum);
495 return rval;
496}
497
498CMDFUNCSTART(rm)
499{
500 int rval;
501
502 if (!checkactivedir())
503 return 1;
504 rval = changeino(curinum, argv[1], 0);
505 if (rval & ALTERED) {
506 printf("Name `%s' removed\n", argv[1]);
507 return 0;
508 } else {
509 printf("could not remove name ('%s')? weird.\n", argv[1]);
510 return 1;
511 }
512}
513
514long slotcount, desired;
515
516int
517chinumfunc(idesc)
518 struct inodesc *idesc;
519{
520 register struct direct *dirp = idesc->id_dirp;
520 struct direct *dirp = idesc->id_dirp;
521
522 if (slotcount++ == desired) {
523 dirp->d_ino = idesc->id_parent;
524 return STOP|ALTERED|FOUND;
525 }
526 return KEEPON;
527}
528
529CMDFUNCSTART(chinum)
530{
531 char *cp;
532 ino_t inum;
533 struct inodesc idesc;
534
535 slotcount = 0;
536 if (!checkactivedir())
537 return 1;
538 GETINUM(2,inum);
539
540 desired = strtol(argv[1], &cp, 0);
541 if (cp == argv[1] || *cp != '\0' || desired < 0) {
542 printf("invalid slot number `%s'\n", argv[1]);
543 return 1;
544 }
545
546 idesc.id_number = curinum;
547 idesc.id_func = chinumfunc;
548 idesc.id_fix = IGNORE;
549 idesc.id_type = DATA;
550 idesc.id_parent = inum; /* XXX convenient hiding place */
551
552 if (ckinode(curinode, &idesc) & FOUND)
553 return 0;
554 else {
555 warnx("no %sth slot in current directory", argv[1]);
556 return 1;
557 }
558}
559
560int
561chnamefunc(idesc)
562 struct inodesc *idesc;
563{
521
522 if (slotcount++ == desired) {
523 dirp->d_ino = idesc->id_parent;
524 return STOP|ALTERED|FOUND;
525 }
526 return KEEPON;
527}
528
529CMDFUNCSTART(chinum)
530{
531 char *cp;
532 ino_t inum;
533 struct inodesc idesc;
534
535 slotcount = 0;
536 if (!checkactivedir())
537 return 1;
538 GETINUM(2,inum);
539
540 desired = strtol(argv[1], &cp, 0);
541 if (cp == argv[1] || *cp != '\0' || desired < 0) {
542 printf("invalid slot number `%s'\n", argv[1]);
543 return 1;
544 }
545
546 idesc.id_number = curinum;
547 idesc.id_func = chinumfunc;
548 idesc.id_fix = IGNORE;
549 idesc.id_type = DATA;
550 idesc.id_parent = inum; /* XXX convenient hiding place */
551
552 if (ckinode(curinode, &idesc) & FOUND)
553 return 0;
554 else {
555 warnx("no %sth slot in current directory", argv[1]);
556 return 1;
557 }
558}
559
560int
561chnamefunc(idesc)
562 struct inodesc *idesc;
563{
564 register struct direct *dirp = idesc->id_dirp;
564 struct direct *dirp = idesc->id_dirp;
565 struct direct testdir;
566
567 if (slotcount++ == desired) {
568 /* will name fit? */
569 testdir.d_namlen = strlen(idesc->id_name);
570 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
571 dirp->d_namlen = testdir.d_namlen;
572 strcpy(dirp->d_name, idesc->id_name);
573 return STOP|ALTERED|FOUND;
574 } else
575 return STOP|FOUND; /* won't fit, so give up */
576 }
577 return KEEPON;
578}
579
580CMDFUNCSTART(chname)
581{
582 int rval;
583 char *cp;
584 struct inodesc idesc;
585
586 slotcount = 0;
587 if (!checkactivedir())
588 return 1;
589
590 desired = strtoul(argv[1], &cp, 0);
591 if (cp == argv[1] || *cp != '\0') {
592 printf("invalid slot number `%s'\n", argv[1]);
593 return 1;
594 }
595
596 idesc.id_number = curinum;
597 idesc.id_func = chnamefunc;
598 idesc.id_fix = IGNORE;
599 idesc.id_type = DATA;
600 idesc.id_name = argv[2];
601
602 rval = ckinode(curinode, &idesc);
603 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
604 return 0;
605 else if (rval & FOUND) {
606 warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
607 return 1;
608 } else {
609 warnx("no %sth slot in current directory", argv[1]);
610 return 1;
611 }
612}
613
614struct typemap {
615 const char *typename;
616 int typebits;
617} typenamemap[] = {
618 {"file", IFREG},
619 {"dir", IFDIR},
620 {"socket", IFSOCK},
621 {"fifo", IFIFO},
622};
623
624CMDFUNCSTART(newtype)
625{
626 int type;
627 struct typemap *tp;
628
629 if (!checkactive())
630 return 1;
631 type = curinode->di_mode & IFMT;
632 for (tp = typenamemap;
633 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
634 tp++) {
635 if (!strcmp(argv[1], tp->typename)) {
636 printf("setting type to %s\n", tp->typename);
637 type = tp->typebits;
638 break;
639 }
640 }
641 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
642 warnx("type `%s' not known", argv[1]);
643 warnx("try one of `file', `dir', `socket', `fifo'");
644 return 1;
645 }
646 curinode->di_mode &= ~IFMT;
647 curinode->di_mode |= type;
648 inodirty();
649 printactive(0);
650 return 0;
651}
652
653CMDFUNCSTART(chlen)
654{
655 int rval = 1;
656 long len;
657 char *cp;
658
659 if (!checkactive())
660 return 1;
661
662 len = strtol(argv[1], &cp, 0);
663 if (cp == argv[1] || *cp != '\0' || len < 0) {
664 warnx("bad length `%s'", argv[1]);
665 return 1;
666 }
667
668 curinode->di_size = len;
669 inodirty();
670 printactive(0);
671 return rval;
672}
673
674CMDFUNCSTART(chmode)
675{
676 int rval = 1;
677 long modebits;
678 char *cp;
679
680 if (!checkactive())
681 return 1;
682
683 modebits = strtol(argv[1], &cp, 8);
684 if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
685 warnx("bad modebits `%s'", argv[1]);
686 return 1;
687 }
688
689 curinode->di_mode &= ~07777;
690 curinode->di_mode |= modebits;
691 inodirty();
692 printactive(0);
693 return rval;
694}
695
696CMDFUNCSTART(chaflags)
697{
698 int rval = 1;
699 u_long flags;
700 char *cp;
701
702 if (!checkactive())
703 return 1;
704
705 flags = strtoul(argv[1], &cp, 0);
706 if (cp == argv[1] || *cp != '\0' ) {
707 warnx("bad flags `%s'", argv[1]);
708 return 1;
709 }
710
711 if (flags > UINT_MAX) {
712 warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
713 return(1);
714 }
715 curinode->di_flags = flags;
716 inodirty();
717 printactive(0);
718 return rval;
719}
720
721CMDFUNCSTART(chgen)
722{
723 int rval = 1;
724 long gen;
725 char *cp;
726
727 if (!checkactive())
728 return 1;
729
730 gen = strtol(argv[1], &cp, 0);
731 if (cp == argv[1] || *cp != '\0' ) {
732 warnx("bad gen `%s'", argv[1]);
733 return 1;
734 }
735
736 if (gen > INT_MAX || gen < INT_MIN) {
737 warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
738 return(1);
739 }
740 curinode->di_gen = gen;
741 inodirty();
742 printactive(0);
743 return rval;
744}
745
746CMDFUNCSTART(linkcount)
747{
748 int rval = 1;
749 int lcnt;
750 char *cp;
751
752 if (!checkactive())
753 return 1;
754
755 lcnt = strtol(argv[1], &cp, 0);
756 if (cp == argv[1] || *cp != '\0' ) {
757 warnx("bad link count `%s'", argv[1]);
758 return 1;
759 }
760 if (lcnt > USHRT_MAX || lcnt < 0) {
761 warnx("max link count is %d\n", USHRT_MAX);
762 return 1;
763 }
764
765 curinode->di_nlink = lcnt;
766 inodirty();
767 printactive(0);
768 return rval;
769}
770
771CMDFUNCSTART(chowner)
772{
773 int rval = 1;
774 unsigned long uid;
775 char *cp;
776 struct passwd *pwd;
777
778 if (!checkactive())
779 return 1;
780
781 uid = strtoul(argv[1], &cp, 0);
782 if (cp == argv[1] || *cp != '\0' ) {
783 /* try looking up name */
784 if ((pwd = getpwnam(argv[1]))) {
785 uid = pwd->pw_uid;
786 } else {
787 warnx("bad uid `%s'", argv[1]);
788 return 1;
789 }
790 }
791
792 curinode->di_uid = uid;
793 inodirty();
794 printactive(0);
795 return rval;
796}
797
798CMDFUNCSTART(chgroup)
799{
800 int rval = 1;
801 unsigned long gid;
802 char *cp;
803 struct group *grp;
804
805 if (!checkactive())
806 return 1;
807
808 gid = strtoul(argv[1], &cp, 0);
809 if (cp == argv[1] || *cp != '\0' ) {
810 if ((grp = getgrnam(argv[1]))) {
811 gid = grp->gr_gid;
812 } else {
813 warnx("bad gid `%s'", argv[1]);
814 return 1;
815 }
816 }
817
818 curinode->di_gid = gid;
819 inodirty();
820 printactive(0);
821 return rval;
822}
823
824int
825dotime(name, rts)
826 char *name;
827 struct timespec *rts;
828{
829 char *p, *val;
830 struct tm t;
831 int32_t sec;
832 int32_t nsec;
833 p = strchr(name, '.');
834 if (p) {
835 *p = '\0';
836 nsec = strtoul(++p, &val, 0);
837 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
838 warnx("invalid nanoseconds");
839 goto badformat;
840 }
841 } else
842 nsec = 0;
843 if (strlen(name) != 14) {
844badformat:
845 warnx("date format: YYYYMMDDHHMMSS[.nsec]");
846 return 1;
847 }
848
849 for (p = name; *p; p++)
850 if (*p < '0' || *p > '9')
851 goto badformat;
852
853 p = name;
854#define VAL() ((*p++) - '0')
855 t.tm_year = VAL();
856 t.tm_year = VAL() + t.tm_year * 10;
857 t.tm_year = VAL() + t.tm_year * 10;
858 t.tm_year = VAL() + t.tm_year * 10 - 1900;
859 t.tm_mon = VAL();
860 t.tm_mon = VAL() + t.tm_mon * 10 - 1;
861 t.tm_mday = VAL();
862 t.tm_mday = VAL() + t.tm_mday * 10;
863 t.tm_hour = VAL();
864 t.tm_hour = VAL() + t.tm_hour * 10;
865 t.tm_min = VAL();
866 t.tm_min = VAL() + t.tm_min * 10;
867 t.tm_sec = VAL();
868 t.tm_sec = VAL() + t.tm_sec * 10;
869 t.tm_isdst = -1;
870
871 sec = mktime(&t);
872 if (sec == -1) {
873 warnx("date/time out of range");
874 return 1;
875 }
876 rts->tv_sec = sec;
877 rts->tv_nsec = nsec;
878 return 0;
879}
880
881CMDFUNCSTART(chmtime)
882{
883 if (dotime(argv[1], &curinode->di_ctime))
884 return 1;
885 inodirty();
886 printactive(0);
887 return 0;
888}
889
890CMDFUNCSTART(chatime)
891{
892 if (dotime(argv[1], &curinode->di_ctime))
893 return 1;
894 inodirty();
895 printactive(0);
896 return 0;
897}
898
899CMDFUNCSTART(chctime)
900{
901 if (dotime(argv[1], &curinode->di_ctime))
902 return 1;
903 inodirty();
904 printactive(0);
905 return 0;
906}
565 struct direct testdir;
566
567 if (slotcount++ == desired) {
568 /* will name fit? */
569 testdir.d_namlen = strlen(idesc->id_name);
570 if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) {
571 dirp->d_namlen = testdir.d_namlen;
572 strcpy(dirp->d_name, idesc->id_name);
573 return STOP|ALTERED|FOUND;
574 } else
575 return STOP|FOUND; /* won't fit, so give up */
576 }
577 return KEEPON;
578}
579
580CMDFUNCSTART(chname)
581{
582 int rval;
583 char *cp;
584 struct inodesc idesc;
585
586 slotcount = 0;
587 if (!checkactivedir())
588 return 1;
589
590 desired = strtoul(argv[1], &cp, 0);
591 if (cp == argv[1] || *cp != '\0') {
592 printf("invalid slot number `%s'\n", argv[1]);
593 return 1;
594 }
595
596 idesc.id_number = curinum;
597 idesc.id_func = chnamefunc;
598 idesc.id_fix = IGNORE;
599 idesc.id_type = DATA;
600 idesc.id_name = argv[2];
601
602 rval = ckinode(curinode, &idesc);
603 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
604 return 0;
605 else if (rval & FOUND) {
606 warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]);
607 return 1;
608 } else {
609 warnx("no %sth slot in current directory", argv[1]);
610 return 1;
611 }
612}
613
614struct typemap {
615 const char *typename;
616 int typebits;
617} typenamemap[] = {
618 {"file", IFREG},
619 {"dir", IFDIR},
620 {"socket", IFSOCK},
621 {"fifo", IFIFO},
622};
623
624CMDFUNCSTART(newtype)
625{
626 int type;
627 struct typemap *tp;
628
629 if (!checkactive())
630 return 1;
631 type = curinode->di_mode & IFMT;
632 for (tp = typenamemap;
633 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
634 tp++) {
635 if (!strcmp(argv[1], tp->typename)) {
636 printf("setting type to %s\n", tp->typename);
637 type = tp->typebits;
638 break;
639 }
640 }
641 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
642 warnx("type `%s' not known", argv[1]);
643 warnx("try one of `file', `dir', `socket', `fifo'");
644 return 1;
645 }
646 curinode->di_mode &= ~IFMT;
647 curinode->di_mode |= type;
648 inodirty();
649 printactive(0);
650 return 0;
651}
652
653CMDFUNCSTART(chlen)
654{
655 int rval = 1;
656 long len;
657 char *cp;
658
659 if (!checkactive())
660 return 1;
661
662 len = strtol(argv[1], &cp, 0);
663 if (cp == argv[1] || *cp != '\0' || len < 0) {
664 warnx("bad length `%s'", argv[1]);
665 return 1;
666 }
667
668 curinode->di_size = len;
669 inodirty();
670 printactive(0);
671 return rval;
672}
673
674CMDFUNCSTART(chmode)
675{
676 int rval = 1;
677 long modebits;
678 char *cp;
679
680 if (!checkactive())
681 return 1;
682
683 modebits = strtol(argv[1], &cp, 8);
684 if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) {
685 warnx("bad modebits `%s'", argv[1]);
686 return 1;
687 }
688
689 curinode->di_mode &= ~07777;
690 curinode->di_mode |= modebits;
691 inodirty();
692 printactive(0);
693 return rval;
694}
695
696CMDFUNCSTART(chaflags)
697{
698 int rval = 1;
699 u_long flags;
700 char *cp;
701
702 if (!checkactive())
703 return 1;
704
705 flags = strtoul(argv[1], &cp, 0);
706 if (cp == argv[1] || *cp != '\0' ) {
707 warnx("bad flags `%s'", argv[1]);
708 return 1;
709 }
710
711 if (flags > UINT_MAX) {
712 warnx("flags set beyond 32-bit range of field (%lx)\n", flags);
713 return(1);
714 }
715 curinode->di_flags = flags;
716 inodirty();
717 printactive(0);
718 return rval;
719}
720
721CMDFUNCSTART(chgen)
722{
723 int rval = 1;
724 long gen;
725 char *cp;
726
727 if (!checkactive())
728 return 1;
729
730 gen = strtol(argv[1], &cp, 0);
731 if (cp == argv[1] || *cp != '\0' ) {
732 warnx("bad gen `%s'", argv[1]);
733 return 1;
734 }
735
736 if (gen > INT_MAX || gen < INT_MIN) {
737 warnx("gen set beyond 32-bit range of field (%lx)\n", gen);
738 return(1);
739 }
740 curinode->di_gen = gen;
741 inodirty();
742 printactive(0);
743 return rval;
744}
745
746CMDFUNCSTART(linkcount)
747{
748 int rval = 1;
749 int lcnt;
750 char *cp;
751
752 if (!checkactive())
753 return 1;
754
755 lcnt = strtol(argv[1], &cp, 0);
756 if (cp == argv[1] || *cp != '\0' ) {
757 warnx("bad link count `%s'", argv[1]);
758 return 1;
759 }
760 if (lcnt > USHRT_MAX || lcnt < 0) {
761 warnx("max link count is %d\n", USHRT_MAX);
762 return 1;
763 }
764
765 curinode->di_nlink = lcnt;
766 inodirty();
767 printactive(0);
768 return rval;
769}
770
771CMDFUNCSTART(chowner)
772{
773 int rval = 1;
774 unsigned long uid;
775 char *cp;
776 struct passwd *pwd;
777
778 if (!checkactive())
779 return 1;
780
781 uid = strtoul(argv[1], &cp, 0);
782 if (cp == argv[1] || *cp != '\0' ) {
783 /* try looking up name */
784 if ((pwd = getpwnam(argv[1]))) {
785 uid = pwd->pw_uid;
786 } else {
787 warnx("bad uid `%s'", argv[1]);
788 return 1;
789 }
790 }
791
792 curinode->di_uid = uid;
793 inodirty();
794 printactive(0);
795 return rval;
796}
797
798CMDFUNCSTART(chgroup)
799{
800 int rval = 1;
801 unsigned long gid;
802 char *cp;
803 struct group *grp;
804
805 if (!checkactive())
806 return 1;
807
808 gid = strtoul(argv[1], &cp, 0);
809 if (cp == argv[1] || *cp != '\0' ) {
810 if ((grp = getgrnam(argv[1]))) {
811 gid = grp->gr_gid;
812 } else {
813 warnx("bad gid `%s'", argv[1]);
814 return 1;
815 }
816 }
817
818 curinode->di_gid = gid;
819 inodirty();
820 printactive(0);
821 return rval;
822}
823
824int
825dotime(name, rts)
826 char *name;
827 struct timespec *rts;
828{
829 char *p, *val;
830 struct tm t;
831 int32_t sec;
832 int32_t nsec;
833 p = strchr(name, '.');
834 if (p) {
835 *p = '\0';
836 nsec = strtoul(++p, &val, 0);
837 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
838 warnx("invalid nanoseconds");
839 goto badformat;
840 }
841 } else
842 nsec = 0;
843 if (strlen(name) != 14) {
844badformat:
845 warnx("date format: YYYYMMDDHHMMSS[.nsec]");
846 return 1;
847 }
848
849 for (p = name; *p; p++)
850 if (*p < '0' || *p > '9')
851 goto badformat;
852
853 p = name;
854#define VAL() ((*p++) - '0')
855 t.tm_year = VAL();
856 t.tm_year = VAL() + t.tm_year * 10;
857 t.tm_year = VAL() + t.tm_year * 10;
858 t.tm_year = VAL() + t.tm_year * 10 - 1900;
859 t.tm_mon = VAL();
860 t.tm_mon = VAL() + t.tm_mon * 10 - 1;
861 t.tm_mday = VAL();
862 t.tm_mday = VAL() + t.tm_mday * 10;
863 t.tm_hour = VAL();
864 t.tm_hour = VAL() + t.tm_hour * 10;
865 t.tm_min = VAL();
866 t.tm_min = VAL() + t.tm_min * 10;
867 t.tm_sec = VAL();
868 t.tm_sec = VAL() + t.tm_sec * 10;
869 t.tm_isdst = -1;
870
871 sec = mktime(&t);
872 if (sec == -1) {
873 warnx("date/time out of range");
874 return 1;
875 }
876 rts->tv_sec = sec;
877 rts->tv_nsec = nsec;
878 return 0;
879}
880
881CMDFUNCSTART(chmtime)
882{
883 if (dotime(argv[1], &curinode->di_ctime))
884 return 1;
885 inodirty();
886 printactive(0);
887 return 0;
888}
889
890CMDFUNCSTART(chatime)
891{
892 if (dotime(argv[1], &curinode->di_ctime))
893 return 1;
894 inodirty();
895 printactive(0);
896 return 0;
897}
898
899CMDFUNCSTART(chctime)
900{
901 if (dotime(argv[1], &curinode->di_ctime))
902 return 1;
903 inodirty();
904 printactive(0);
905 return 0;
906}