1 /* $OpenBSD: fsdb.c,v 1.36 2024/01/09 03:16:00 guenther Exp $ */
2 /* $NetBSD: fsdb.c,v 1.7 1997/01/11 06:50:53 lukem Exp $ */
3
4 /*-
5 * Copyright (c) 1996 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by John T. Kohl.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/stat.h>
34 #include <sys/time.h>
35 #include <sys/mount.h>
36 #include <ctype.h>
37 #include <err.h>
38 #include <fcntl.h>
39 #include <grp.h>
40 #include <histedit.h>
41 #include <limits.h>
42 #include <pwd.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 #include <ufs/ufs/dinode.h>
49 #include <ufs/ufs/dir.h>
50 #include <ufs/ffs/fs.h>
51
52 #include "fsdb.h"
53 #include "fsck.h"
54 #include "extern.h"
55
56 extern char *__progname; /* from crt0.o */
57
58 int main(int, char *[]);
59 static void usage(void);
60 static int cmdloop(void);
61 static int helpfn(int, char *[]);
62 static char *prompt(EditLine *);
63 static int scannames(struct inodesc *);
64 static int dolookup(char *);
65 static int chinumfunc(struct inodesc *);
66 static int chnamefunc(struct inodesc *);
67 static int dotime(char *, time_t *, int32_t *);
68
69 int returntosingle = 0;
70 union dinode *curinode;
71 ino_t curinum;
72
73 struct inostatlist *inostathead;
74
75 struct bufarea bufhead; /* head of list of other blks in filesys */
76 struct bufarea sblk; /* file system superblock */
77 struct bufarea asblk; /* alternate file system superblock */
78 struct bufarea *pdirbp; /* current directory contents */
79 struct bufarea *pbp; /* current inode block */
80
81 struct dups *duplist; /* head of dup list */
82 struct dups *muldup; /* end of unique duplicate dup block numbers */
83
84 struct zlncnt *zlnhead; /* head of zero link count list */
85
86 struct inoinfo **inphead, **inpsort;
87
88 extern long numdirs, listmax, inplast;
89
90 long secsize; /* actual disk sector size */
91 char nflag; /* assume a no response */
92 char yflag; /* assume a yes response */
93 daddr_t bflag; /* location of alternate super block */
94 int debug; /* output debugging info */
95 int cvtlevel; /* convert to newer file system format */
96 char usedsoftdep; /* just fix soft dependency inconsistencies */
97 int preen; /* just fix normal inconsistencies */
98 char resolved; /* cleared if unresolved changes => not clean */
99 char havesb; /* superblock has been read */
100 char skipclean; /* skip clean file systems if preening */
101 int fsmodified; /* 1 => write done to file system */
102 int fsreadfd; /* file descriptor for reading file system */
103 int fswritefd; /* file descriptor for writing file system */
104 int rerun; /* rerun fsck. Only used in non-preen mode */
105
106 daddr_t maxfsblock; /* number of blocks in the file system */
107 char *blockmap; /* ptr to primary blk allocation map */
108 ino_t maxino; /* number of inodes in file system */
109 ino_t lastino; /* last inode in use */
110
111 ino_t lfdir; /* lost & found directory inode number */
112
113 daddr_t n_blks; /* number of blocks in use */
114 int64_t n_files; /* number of files in use */
115
116 struct ufs1_dinode ufs1_zino;
117 struct ufs2_dinode ufs2_zino;
118
119
120 static void
usage(void)121 usage(void)
122 {
123 fprintf(stderr, "usage: %s [-d] -f fsname\n", __progname);
124 exit(1);
125 }
126
127 /*
128 * We suck in lots of fsck code, and just pick & choose the stuff we want.
129 *
130 * fsreadfd is set up to read from the file system, fswritefd to write to
131 * the file system.
132 */
133 int
main(int argc,char * argv[])134 main(int argc, char *argv[])
135 {
136 int ch, rval;
137 char *fsys = NULL;
138
139 while (-1 != (ch = getopt(argc, argv, "f:d"))) {
140 switch (ch) {
141 case 'f':
142 fsys = optarg;
143 break;
144 case 'd':
145 debug++;
146 break;
147 default:
148 usage();
149 }
150 }
151 if (fsys == NULL)
152 usage();
153 if (!setup(fsys, 1))
154 errx(1, "cannot set up file system `%s'", fsys);
155 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys,
156 sblock.fs_fsmnt);
157 rval = cmdloop();
158 sblock.fs_clean = 0; /* mark it dirty */
159 sbdirty();
160 ckfini(0);
161 printf("*** FILE SYSTEM MARKED DIRTY\n");
162 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
163 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
164 exit(rval);
165 }
166
167 #define CMDFUNC(func) static int func(int argc, char *argv[])
168 #define CMDFUNCSTART(func) static int func(int argc, char *argv[])
169
170 CMDFUNC(helpfn);
171 CMDFUNC(focus); /* focus on inode */
172 CMDFUNC(active); /* print active inode */
173 CMDFUNC(focusname); /* focus by name */
174 CMDFUNC(zapi); /* clear inode */
175 CMDFUNC(uplink); /* incr link */
176 CMDFUNC(downlink); /* decr link */
177 CMDFUNC(linkcount); /* set link count */
178 CMDFUNC(quit); /* quit */
179 CMDFUNC(ls); /* list directory */
180 CMDFUNC(rm); /* remove name */
181 CMDFUNC(ln); /* add name */
182 CMDFUNC(newtype); /* change type */
183 CMDFUNC(chmode); /* change mode */
184 CMDFUNC(chlen); /* change length */
185 CMDFUNC(chaflags); /* change flags */
186 CMDFUNC(chgen); /* change generation */
187 CMDFUNC(chowner); /* change owner */
188 CMDFUNC(chgroup); /* Change group */
189 CMDFUNC(back); /* pop back to last ino */
190 CMDFUNC(chmtime); /* Change mtime */
191 CMDFUNC(chctime); /* Change ctime */
192 CMDFUNC(chatime); /* Change atime */
193 CMDFUNC(chinum); /* Change inode # of dirent */
194 CMDFUNC(chname); /* Change dirname of dirent */
195
196 static struct cmdtable cmds[] = {
197 { "help", "Print out help", 1, 1, helpfn },
198 { "?", "Print out help", 1, 1, helpfn },
199 { "inode", "Set active inode to INUM", 2, 2, focus },
200 { "clri", "Clear inode INUM", 2, 2, zapi },
201 { "lookup", "Set active inode by looking up NAME", 2, 2, focusname },
202 { "cd", "Set active inode by looking up NAME", 2, 2, focusname },
203 { "back", "Go to previous active inode", 1, 1, back },
204 { "active", "Print active inode", 1, 1, active },
205 { "print", "Print active inode", 1, 1, active },
206 { "uplink", "Increment link count", 1, 1, uplink },
207 { "downlink", "Decrement link count", 1, 1, downlink },
208 { "linkcount", "Set link count to COUNT", 2, 2, linkcount },
209 { "ls", "List current inode as directory", 1, 1, ls },
210 { "rm", "Remove NAME from current inode directory", 2, 2, rm },
211 { "del", "Remove NAME from current inode directory", 2, 2, rm },
212 { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln },
213 { "chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum },
214 { "chname", "Change dir entry number INDEX to NAME", 3, 3, chname },
215 { "chtype", "Change type of current inode to TYPE", 2, 2, newtype },
216 { "chmod", "Change mode of current inode to MODE", 2, 2, chmode },
217 { "chown", "Change owner of current inode to OWNER", 2, 2, chowner },
218 { "chlen", "Change length of current inode to LENGTH", 2, 2, chlen },
219 { "chgrp", "Change group of current inode to GROUP", 2, 2, chgroup },
220 { "chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags },
221 { "chgen", "Change generation number of current inode to GEN", 2, 2, chgen },
222 { "mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime },
223 { "ctime", "Change ctime of current inode to CTIME", 2, 2, chctime },
224 { "atime", "Change atime of current inode to ATIME", 2, 2, chatime },
225 { "quit", "Exit", 1, 1, quit },
226 { "q", "Exit", 1, 1, quit },
227 { "exit", "Exit", 1, 1, quit },
228 { NULL, 0, 0, 0 },
229 };
230
231 static int
helpfn(int argc,char * argv[])232 helpfn(int argc, char *argv[])
233 {
234 struct cmdtable *cmdtp;
235
236 printf("Commands are:\n%-10s %5s %5s %s\n",
237 "command", "min argc", "max argc", "what");
238
239 for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
240 printf("%-10s %5u %5u %s\n",
241 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
242 return 0;
243 }
244
245 static char *
prompt(EditLine * el)246 prompt(EditLine *el)
247 {
248 static char pstring[64];
249
250 snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ",
251 (unsigned long long)curinum);
252 return pstring;
253 }
254
255
256 static int
cmdloop(void)257 cmdloop(void)
258 {
259 char *line = NULL;
260 const char *elline;
261 int cmd_argc, rval = 0, known;
262 #define scratch known
263 char **cmd_argv;
264 struct cmdtable *cmdp;
265 History *hist;
266 EditLine *elptr;
267 HistEvent hev;
268
269 curinode = ginode(ROOTINO);
270 curinum = ROOTINO;
271 printactive();
272
273 hist = history_init();
274 history(hist, &hev, H_SETSIZE, 100); /* 100 elt history buffer */
275
276 elptr = el_init(__progname, stdin, stdout, stderr);
277 el_set(elptr, EL_EDITOR, "emacs");
278 el_set(elptr, EL_PROMPT, prompt);
279 el_set(elptr, EL_HIST, history, hist);
280 el_source(elptr, NULL);
281
282 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
283 if (debug)
284 printf("command `%s'\n", elline);
285
286 history(hist, &hev, H_ENTER, elline);
287
288 line = strdup(elline);
289 if (line == NULL)
290 errx(1, "out of memory");
291 cmd_argv = crack(line, &cmd_argc);
292 if (cmd_argc) {
293 /*
294 * el_parse returns -1 to signal that it's not been handled
295 * internally.
296 */
297 if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1)
298 continue;
299 known = 0;
300 for (cmdp = cmds; cmdp->cmd; cmdp++) {
301 if (!strcmp(cmdp->cmd, cmd_argv[0])) {
302 if (cmd_argc >= cmdp->minargc &&
303 cmd_argc <= cmdp->maxargc)
304 rval = (*cmdp->handler)(cmd_argc,
305 cmd_argv);
306 else
307 rval = argcount(cmdp,
308 cmd_argc, cmd_argv);
309 known = 1;
310 break;
311 }
312 }
313 if (!known) {
314 warnx("unknown command `%s'", cmd_argv[0]);
315 rval = 1;
316 }
317 } else
318 rval = 0;
319 free(line);
320 if (rval < 0)
321 return rval;
322 if (rval)
323 warnx("rval was %d", rval);
324 }
325 el_end(elptr);
326 history_end(hist);
327 return rval;
328 }
329
330 static ino_t ocurrent;
331
332 #define GETINUM(ac,inum) inum = strtoull(argv[ac], &cp, 0); \
333 if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
334 printf("inode %llu out of range; range is [%llu,%llu]\n", \
335 (unsigned long long)inum, (unsigned long long)ROOTINO, \
336 (unsigned long long)maxino); \
337 return 1; \
338 }
339
340 /*
341 * Focus on given inode number
342 */
CMDFUNCSTART(focus)343 CMDFUNCSTART(focus)
344 {
345 ino_t inum;
346 char *cp;
347
348 GETINUM(1,inum);
349 curinode = ginode(inum);
350 ocurrent = curinum;
351 curinum = inum;
352 printactive();
353 return 0;
354 }
355
CMDFUNCSTART(back)356 CMDFUNCSTART(back)
357 {
358 curinum = ocurrent;
359 curinode = ginode(curinum);
360 printactive();
361 return 0;
362 }
363
CMDFUNCSTART(zapi)364 CMDFUNCSTART(zapi)
365 {
366 ino_t inum;
367 union dinode *dp;
368 char *cp;
369
370 GETINUM(1,inum);
371 dp = ginode(inum);
372 clearinode(dp);
373 inodirty();
374 if (curinode) /* re-set after potential change */
375 curinode = ginode(curinum);
376 return 0;
377 }
378
CMDFUNCSTART(active)379 CMDFUNCSTART(active)
380 {
381 printactive();
382 return 0;
383 }
384
385
CMDFUNCSTART(quit)386 CMDFUNCSTART(quit)
387 {
388 return -1;
389 }
390
CMDFUNCSTART(uplink)391 CMDFUNCSTART(uplink)
392 {
393 if (!checkactive())
394 return 1;
395 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
396 printf("inode %llu link count now %d\n",
397 (unsigned long long)curinum, DIP(curinode, di_nlink));
398 inodirty();
399 return 0;
400 }
401
CMDFUNCSTART(downlink)402 CMDFUNCSTART(downlink)
403 {
404 if (!checkactive())
405 return 1;
406 DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
407 printf("inode %llu link count now %d\n",
408 (unsigned long long)curinum, DIP(curinode, di_nlink));
409 inodirty();
410 return 0;
411 }
412
413 static const char *typename[] = {
414 "unknown",
415 "fifo",
416 "char special",
417 "unregistered #3",
418 "directory",
419 "unregistered #5",
420 "blk special",
421 "unregistered #7",
422 "regular",
423 "unregistered #9",
424 "symlink",
425 "unregistered #11",
426 "socket",
427 "unregistered #13",
428 "whiteout",
429 };
430
431 static int slot;
432
433 static int
scannames(struct inodesc * idesc)434 scannames(struct inodesc *idesc)
435 {
436 struct direct *dirp = idesc->id_dirp;
437
438 printf("slot %d ino %llu reclen %d: %s, `%.*s'\n",
439 slot++, (unsigned long long)dirp->d_ino, dirp->d_reclen,
440 typename[dirp->d_type], dirp->d_namlen, dirp->d_name);
441 return (KEEPON);
442 }
443
CMDFUNCSTART(ls)444 CMDFUNCSTART(ls)
445 {
446 struct inodesc idesc;
447 checkactivedir(); /* let it go on anyway */
448
449 slot = 0;
450 idesc.id_number = curinum;
451 idesc.id_func = scannames;
452 idesc.id_type = DATA;
453 idesc.id_fix = IGNORE;
454 ckinode(curinode, &idesc);
455 curinode = ginode(curinum);
456
457 return 0;
458 }
459
460 static int
dolookup(char * name)461 dolookup(char *name)
462 {
463 struct inodesc idesc;
464
465 if (!checkactivedir())
466 return 0;
467 idesc.id_number = curinum;
468 idesc.id_func = findino;
469 idesc.id_name = name;
470 idesc.id_type = DATA;
471 idesc.id_fix = IGNORE;
472 if (ckinode(curinode, &idesc) & FOUND) {
473 curinum = idesc.id_parent;
474 curinode = ginode(curinum);
475 printactive();
476 return 1;
477 } else {
478 warnx("name `%s' not found in current inode directory", name);
479 return 0;
480 }
481 }
482
CMDFUNCSTART(focusname)483 CMDFUNCSTART(focusname)
484 {
485 char *p, *val;
486
487 if (!checkactive())
488 return 1;
489
490 ocurrent = curinum;
491
492 if (argv[1][0] == '/') {
493 curinum = ROOTINO;
494 curinode = ginode(ROOTINO);
495 } else {
496 if (!checkactivedir())
497 return 1;
498 }
499 for (p = argv[1]; p != NULL;) {
500 while ((val = strsep(&p, "/")) != NULL && *val == '\0')
501 continue;
502 if (val) {
503 printf("component `%s': ", val);
504 fflush(stdout);
505 if (!dolookup(val)) {
506 curinode = ginode(curinum);
507 return(1);
508 }
509 }
510 }
511 return 0;
512 }
513
CMDFUNCSTART(ln)514 CMDFUNCSTART(ln)
515 {
516 ino_t inum;
517 int rval;
518 char *cp;
519
520 GETINUM(1,inum);
521
522 if (!checkactivedir())
523 return 1;
524 rval = makeentry(curinum, inum, argv[2]);
525 if (rval)
526 printf("Ino %llu entered as `%s'\n",
527 (unsigned long long)inum, argv[2]);
528 else
529 printf("could not enter name? weird.\n");
530 curinode = ginode(curinum);
531 return rval;
532 }
533
CMDFUNCSTART(rm)534 CMDFUNCSTART(rm)
535 {
536 int rval;
537
538 if (!checkactivedir())
539 return 1;
540 rval = changeino(curinum, argv[1], 0);
541 if (rval & ALTERED) {
542 printf("Name `%s' removed\n", argv[1]);
543 return 0;
544 } else {
545 printf("could not remove name? weird.\n");
546 return 1;
547 }
548 }
549
550 static long slotcount, desired;
551
552 static int
chinumfunc(struct inodesc * idesc)553 chinumfunc(struct inodesc *idesc)
554 {
555 struct direct *dirp = idesc->id_dirp;
556
557 if (slotcount++ == desired) {
558 dirp->d_ino = idesc->id_parent;
559 return STOP|ALTERED|FOUND;
560 }
561 return KEEPON;
562 }
563
CMDFUNCSTART(chinum)564 CMDFUNCSTART(chinum)
565 {
566 char *cp;
567 ino_t inum;
568 struct inodesc idesc;
569
570 slotcount = 0;
571 if (!checkactivedir())
572 return 1;
573 GETINUM(2,inum);
574
575 desired = strtol(argv[1], &cp, 0);
576 if (cp == argv[1] || *cp != '\0' || desired < 0) {
577 printf("invalid slot number `%s'\n", argv[1]);
578 return 1;
579 }
580
581 idesc.id_number = curinum;
582 idesc.id_func = chinumfunc;
583 idesc.id_fix = IGNORE;
584 idesc.id_type = DATA;
585 idesc.id_parent = inum; /* XXX convenient hiding place */
586
587 if (ckinode(curinode, &idesc) & FOUND)
588 return 0;
589 else {
590 warnx("no %sth slot in current directory", argv[1]);
591 return 1;
592 }
593 }
594
595 static int
chnamefunc(struct inodesc * idesc)596 chnamefunc(struct inodesc *idesc)
597 {
598 struct direct *dirp = idesc->id_dirp;
599 struct direct testdir;
600
601 if (slotcount++ == desired) {
602 /* will name fit? */
603 testdir.d_namlen = strlen(idesc->id_name);
604 if (DIRSIZ(&testdir) <= dirp->d_reclen) {
605 dirp->d_namlen = testdir.d_namlen;
606 strlcpy(dirp->d_name, idesc->id_name, sizeof dirp->d_name);
607 return STOP|ALTERED|FOUND;
608 } else
609 return STOP|FOUND; /* won't fit, so give up */
610 }
611 return KEEPON;
612 }
613
CMDFUNCSTART(chname)614 CMDFUNCSTART(chname)
615 {
616 int rval;
617 char *cp;
618 struct inodesc idesc;
619
620 slotcount = 0;
621 if (!checkactivedir())
622 return 1;
623
624 desired = strtoul(argv[1], &cp, 0);
625 if (cp == argv[1] || *cp != '\0') {
626 printf("invalid slot number `%s'\n", argv[1]);
627 return 1;
628 }
629
630 idesc.id_number = curinum;
631 idesc.id_func = chnamefunc;
632 idesc.id_fix = IGNORE;
633 idesc.id_type = DATA;
634 idesc.id_name = argv[2];
635
636 rval = ckinode(curinode, &idesc);
637 if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED))
638 return 0;
639 else if (rval & FOUND) {
640 warnx("new name `%s' does not fit in slot %s", argv[2], argv[1]);
641 return 1;
642 } else {
643 warnx("no %sth slot in current directory", argv[1]);
644 return 1;
645 }
646 }
647
648 static struct typemap {
649 const char *typename;
650 int typebits;
651 } typenamemap[] = {
652 {"file", IFREG},
653 {"dir", IFDIR},
654 {"socket", IFSOCK},
655 {"fifo", IFIFO},
656 };
657
CMDFUNCSTART(newtype)658 CMDFUNCSTART(newtype)
659 {
660 int type;
661 struct typemap *tp;
662
663 if (!checkactive())
664 return 1;
665 type = DIP(curinode, di_mode) & IFMT;
666 for (tp = typenamemap;
667 tp < &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)];
668 tp++) {
669 if (!strcmp(argv[1], tp->typename)) {
670 printf("setting type to %s\n", tp->typename);
671 type = tp->typebits;
672 break;
673 }
674 }
675 if (tp == &typenamemap[sizeof(typenamemap)/sizeof(*typenamemap)]) {
676 warnx("type `%s' not known", argv[1]);
677 warnx("try one of `file', `dir', `socket', `fifo'");
678 return 1;
679 }
680 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT);
681 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type);
682 inodirty();
683 printactive();
684 return 0;
685 }
686
CMDFUNCSTART(chmode)687 CMDFUNCSTART(chmode)
688 {
689 int rval = 1;
690 long modebits;
691 char *cp;
692
693 if (!checkactive())
694 return 1;
695
696 modebits = strtol(argv[1], &cp, 8);
697 if (cp == argv[1] || *cp != '\0' ) {
698 warnx("bad modebits `%s'", argv[1]);
699 return 1;
700 }
701
702 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777);
703 DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits);
704 inodirty();
705 printactive();
706 return rval;
707 }
708
CMDFUNCSTART(chlen)709 CMDFUNCSTART(chlen)
710 {
711 int rval = 1;
712 long len;
713 char *cp;
714
715 if (!checkactive())
716 return 1;
717
718 len = strtol(argv[1], &cp, 0);
719 if (cp == argv[1] || *cp != '\0' || len < 0) {
720 warnx("bad length '%s'", argv[1]);
721 return 1;
722 }
723
724 DIP_SET(curinode, di_size, len);
725 inodirty();
726 printactive();
727 return rval;
728 }
729
CMDFUNCSTART(chaflags)730 CMDFUNCSTART(chaflags)
731 {
732 int rval = 1;
733 u_long flags;
734 char *cp;
735
736 if (!checkactive())
737 return 1;
738
739 flags = strtoul(argv[1], &cp, 0);
740 if (cp == argv[1] || *cp != '\0' ) {
741 warnx("bad flags `%s'", argv[1]);
742 return 1;
743 }
744
745 if (flags > UINT_MAX) {
746 warnx("flags set beyond 32-bit range of field (%lx)", flags);
747 return(1);
748 }
749 DIP_SET(curinode, di_flags, flags);
750 inodirty();
751 printactive();
752 return rval;
753 }
754
CMDFUNCSTART(chgen)755 CMDFUNCSTART(chgen)
756 {
757 int rval = 1;
758 long long gen;
759 char *cp;
760
761 if (!checkactive())
762 return 1;
763
764 gen = strtoll(argv[1], &cp, 0);
765 if (cp == argv[1] || *cp != '\0' ) {
766 warnx("bad gen `%s'", argv[1]);
767 return 1;
768 }
769
770 if (gen > UINT_MAX || gen < 0) {
771 warnx("gen set beyond 32-bit range of field (%llx)", gen);
772 return(1);
773 }
774 DIP_SET(curinode, di_gen, gen);
775 inodirty();
776 printactive();
777 return rval;
778 }
779
CMDFUNCSTART(linkcount)780 CMDFUNCSTART(linkcount)
781 {
782 int rval = 1;
783 int lcnt;
784 char *cp;
785
786 if (!checkactive())
787 return 1;
788
789 lcnt = strtol(argv[1], &cp, 0);
790 if (cp == argv[1] || *cp != '\0' ) {
791 warnx("bad link count `%s'", argv[1]);
792 return 1;
793 }
794 if (lcnt > USHRT_MAX || lcnt < 0) {
795 warnx("max link count is %d", USHRT_MAX);
796 return 1;
797 }
798
799 DIP_SET(curinode, di_nlink, lcnt);
800 inodirty();
801 printactive();
802 return rval;
803 }
804
CMDFUNCSTART(chowner)805 CMDFUNCSTART(chowner)
806 {
807 int rval = 1;
808 uid_t uid;
809 char *cp;
810
811 if (!checkactive())
812 return 1;
813
814 uid = strtoul(argv[1], &cp, 0);
815 if (cp == argv[1] || *cp != '\0' ) {
816 /* try looking up name */
817 if (uid_from_user(argv[1], &uid) == -1) {
818 warnx("bad uid `%s'", argv[1]);
819 return 1;
820 }
821 }
822
823 DIP_SET(curinode, di_uid, uid);
824 inodirty();
825 printactive();
826 return rval;
827 }
828
CMDFUNCSTART(chgroup)829 CMDFUNCSTART(chgroup)
830 {
831 int rval = 1;
832 gid_t gid;
833 char *cp;
834 struct group *grp;
835
836 if (!checkactive())
837 return 1;
838
839 gid = strtoul(argv[1], &cp, 0);
840 if (cp == argv[1] || *cp != '\0' ) {
841 if ((grp = getgrnam(argv[1]))) {
842 gid = grp->gr_gid;
843 } else {
844 warnx("bad gid `%s'", argv[1]);
845 return 1;
846 }
847 }
848
849 DIP_SET(curinode, di_gid, gid);
850 inodirty();
851 printactive();
852 return rval;
853 }
854
855 static int
dotime(char * name,time_t * rsec,int32_t * rnsec)856 dotime(char *name, time_t *rsec, int32_t *rnsec)
857 {
858 char *p, *val;
859 struct tm t;
860 time_t sec;
861 int32_t nsec;
862
863 p = strchr(name, '.');
864 if (p) {
865 *p = '\0';
866 nsec = strtoul(++p, &val, 0);
867 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
868 warnx("invalid nanoseconds");
869 goto badformat;
870 }
871 } else
872 nsec = 0;
873
874 if (strlen(name) != 14) {
875 badformat:
876 warnx("date format: YYYYMMDDHHMMSS[.nsec]");
877 return 1;
878 }
879
880 for (p = name; *p; p++)
881 if (*p < '0' || *p > '9')
882 goto badformat;
883
884 p = name;
885 #define VAL() ((*p++) - '0')
886 bzero(&t, sizeof t);
887 t.tm_year = VAL();
888 t.tm_year = VAL() + t.tm_year * 10;
889 t.tm_year = VAL() + t.tm_year * 10;
890 t.tm_year = VAL() + t.tm_year * 10 - 1900;
891 t.tm_mon = VAL();
892 t.tm_mon = VAL() + t.tm_mon * 10 - 1;
893 t.tm_mday = VAL();
894 t.tm_mday = VAL() + t.tm_mday * 10;
895 t.tm_hour = VAL();
896 t.tm_hour = VAL() + t.tm_hour * 10;
897 t.tm_min = VAL();
898 t.tm_min = VAL() + t.tm_min * 10;
899 t.tm_sec = VAL();
900 t.tm_sec = VAL() + t.tm_sec * 10;
901 t.tm_isdst = -1;
902
903 sec = mktime(&t);
904 if (sec == -1) {
905 warnx("date/time out of range");
906 return 1;
907 }
908 *rsec = sec;
909 *rnsec = nsec;
910 return 0;
911 }
912
CMDFUNCSTART(chmtime)913 CMDFUNCSTART(chmtime)
914 {
915 time_t rsec;
916 int32_t nsec;
917
918 if (dotime(argv[1], &rsec, &nsec))
919 return 1;
920 DIP_SET(curinode, di_mtime, rsec);
921 DIP_SET(curinode, di_mtimensec, nsec);
922 inodirty();
923 printactive();
924 return 0;
925 }
926
CMDFUNCSTART(chatime)927 CMDFUNCSTART(chatime)
928 {
929 time_t rsec;
930 int32_t nsec;
931
932 if (dotime(argv[1], &rsec, &nsec))
933 return 1;
934 DIP_SET(curinode, di_atime, rsec);
935 DIP_SET(curinode, di_atimensec, nsec);
936 inodirty();
937 printactive();
938 return 0;
939 }
940
CMDFUNCSTART(chctime)941 CMDFUNCSTART(chctime)
942 {
943 time_t rsec;
944 int32_t nsec;
945
946 if (dotime(argv[1], &rsec, &nsec))
947 return 1;
948 DIP_SET(curinode, di_ctime, rsec);
949 DIP_SET(curinode, di_ctimensec, nsec);
950 inodirty();
951 printactive();
952 return 0;
953 }
954