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