1 /* $NetBSD: fsdb.c,v 1.54 2023/01/07 19:41:29 chs Exp $ */
2
3 /*-
4 * Copyright (c) 1996, 2017 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by John T. Kohl.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: fsdb.c,v 1.54 2023/01/07 19:41:29 chs Exp $");
35 #endif /* not lint */
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <sys/mount.h>
42 #include <ctype.h>
43 #include <fcntl.h>
44 #include <grp.h>
45 #include <histedit.h>
46 #include <limits.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <err.h>
54 #include <stdbool.h>
55
56 #include <ufs/ufs/dinode.h>
57 #include <ufs/ufs/dir.h>
58 #include <ufs/ffs/fs.h>
59 #include <ufs/ffs/ffs_extern.h>
60
61 #include "fsdb.h"
62 #include "fsck.h"
63 #include "extern.h"
64
65 struct bufarea bufhead;
66 struct bufarea sblk;
67 struct bufarea asblk;
68 struct bufarea cgblk;
69 struct bufarea appleufsblk;
70 struct bufarea *pdirbp;
71 struct bufarea *pbp;
72 struct fs *sblock;
73 struct fs *altsblock;
74 struct cg *cgrp;
75 struct fs *sblocksave;
76 struct dups *duplist;
77 struct dups *muldup;
78 struct zlncnt *zlnhead;
79 struct inoinfo **inphead, **inpsort;
80 long numdirs, dirhash, listmax, inplast;
81 struct uquot_hash *uquot_user_hash;
82 struct uquot_hash *uquot_group_hash;
83 uint8_t q2h_hash_shift;
84 uint16_t q2h_hash_mask;
85 struct inostatlist *inostathead;
86 long dev_bsize;
87 long secsize;
88 char nflag;
89 char yflag;
90 int Uflag;
91 int bflag;
92 int debug;
93 int zflag;
94 int cvtlevel;
95 int eaflag;
96 int doinglevel1;
97 int doinglevel2;
98 int doing2ea;
99 int doing2noea;
100 int newinofmt;
101 char usedsoftdep;
102 int preen;
103 int forceimage;
104 int is_ufs2;
105 int is_ufs2ea;
106 int markclean;
107 char havesb;
108 char skipclean;
109 int fsmodified;
110 int fsreadfd;
111 int fswritefd;
112 int rerun;
113 char resolved;
114 int endian;
115 int doswap;
116 int needswap;
117 int do_blkswap;
118 int do_dirswap;
119 int isappleufs;
120 daddr_t maxfsblock;
121 char *blockmap;
122 ino_t maxino;
123 int dirblksiz;
124 daddr_t n_blks;
125 ino_t n_files;
126 long countdirs;
127 int got_siginfo;
128 struct ufs1_dinode ufs1_zino;
129 struct ufs2_dinode ufs2_zino;
130
131 /* Used to keep state for "saveblks" command. */
132 struct wrinfo {
133 off_t size;
134 off_t written_size;
135 int fd;
136 };
137
138 __dead static void usage(void);
139 static int cmdloop(void);
140 static char *prompt(EditLine *);
141 static int scannames(struct inodesc *);
142 static int dolookup(char *);
143 static int chinumfunc(struct inodesc *);
144 static int chnamefunc(struct inodesc *);
145 static int chreclenfunc(struct inodesc *);
146 static int dotime(char *, int64_t *, int32_t *);
147 static void print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp);
148 static void print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp);
149 static void print_indirblks32(uint32_t blk, int ind_level,
150 uint64_t *blknum, struct wrinfo *wrp);
151 static void print_indirblks64(uint64_t blk, int ind_level,
152 uint64_t *blknum, struct wrinfo *wrp);
153 static int compare_blk32(uint32_t *, uint32_t);
154 static int compare_blk64(uint64_t *, uint64_t);
155 static int founddatablk(uint64_t);
156 static int find_blks32(uint32_t *buf, int size, uint32_t *blknum);
157 static int find_blks64(uint64_t *buf, int size, uint64_t *blknum);
158 static int find_indirblks32(uint32_t blk, int ind_level,
159 uint32_t *blknum);
160 static int find_indirblks64(uint64_t blk, int ind_level,
161 uint64_t *blknum);
162
163 union dinode *curinode;
164 ino_t curinum;
165
166 static void
usage(void)167 usage(void)
168 {
169 errx(1, "usage: %s [-dFfNn] <fsname>", getprogname());
170 }
171 /*
172 * We suck in lots of fsck code, and just pick & choose the stuff we want.
173 *
174 * fsreadfd is set up to read from the file system, fswritefd to write to
175 * the file system.
176 */
177 int
main(int argc,char * argv[])178 main(int argc, char *argv[])
179 {
180 int ch, rval;
181 char *fsys = NULL;
182 bool makedirty = true;
183
184 forceimage = 0;
185 debug = 0;
186 isappleufs = 0;
187 while ((ch = getopt(argc, argv, "dFf:Nn")) != -1) {
188 switch (ch) {
189 case 'd':
190 debug++;
191 break;
192 case 'F':
193 forceimage = 1;
194 break;
195 case 'f':
196 fsys = optarg;
197 break;
198 case 'N':
199 makedirty = false;
200 break;
201 case 'n':
202 nflag++;
203 break;
204 default:
205 usage();
206 }
207 }
208 argc -= optind;
209 argv += optind;
210 if (fsys == NULL)
211 fsys = argv[0];
212 if (fsys == NULL)
213 usage();
214 endian = 0;
215 if (setup(fsys, fsys) <= 0)
216 errx(1, "cannot set up file system `%s'", fsys);
217 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys,
218 sblock->fs_fsmnt);
219 rval = cmdloop();
220 if (nflag)
221 exit(rval);
222 if (!makedirty) {
223 ckfini(1);
224 exit(rval);
225 }
226 sblock->fs_clean = 0; /* mark it dirty */
227 sbdirty();
228 markclean = 0;
229 ckfini(1);
230 printf("*** FILE SYSTEM MARKED DIRTY\n");
231 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n");
232 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n");
233 exit(rval);
234 }
235
236 #define CMDFUNC(func) static int func (int argc, char *argv[])
237
238 CMDFUNC(helpfn);
239 CMDFUNC(focus); /* focus on inode */
240 CMDFUNC(active); /* print active inode */
241 CMDFUNC(focusname); /* focus by name */
242 CMDFUNC(zapi); /* clear inode */
243 CMDFUNC(uplink); /* incr link */
244 CMDFUNC(downlink); /* decr link */
245 CMDFUNC(linkcount); /* set link count */
246 CMDFUNC(quit); /* quit */
247 CMDFUNC(ls); /* list directory */
248 CMDFUNC(blks); /* list blocks */
249 CMDFUNC(findblk); /* find block */
250 CMDFUNC(rm); /* remove name */
251 CMDFUNC(ln); /* add name */
252 CMDFUNC(newtype); /* change type */
253 CMDFUNC(chmode); /* change mode */
254 CMDFUNC(chlen); /* change length */
255 CMDFUNC(chaflags); /* change flags */
256 CMDFUNC(chgen); /* change generation */
257 CMDFUNC(chowner); /* change owner */
258 CMDFUNC(chgroup); /* Change group */
259 CMDFUNC(back); /* pop back to last ino */
260 CMDFUNC(chmtime); /* Change mtime */
261 CMDFUNC(chctime); /* Change ctime */
262 CMDFUNC(chatime); /* Change atime */
263 CMDFUNC(chbirthtime); /* Change birthtime */
264 CMDFUNC(chinum); /* Change inode # of dirent */
265 CMDFUNC(chname); /* Change dirname of dirent */
266 CMDFUNC(chreclen); /* Change reclen of dirent */
267 CMDFUNC(chextsize); /* Change extsize */
268 CMDFUNC(chblocks); /* Change blocks */
269 CMDFUNC(chdb); /* Change direct block pointer */
270 CMDFUNC(chib); /* Change indirect block pointer */
271 CMDFUNC(chextb); /* Change extattr block pointer */
272 CMDFUNC(chfreelink); /* Change freelink pointer */
273 CMDFUNC(iptrs); /* print raw block pointers for active inode */
274 CMDFUNC(saveea); /* Save extattrs */
275
276 static struct cmdtable cmds[] = {
277 {"help", "Print out help", 1, 1, helpfn},
278 {"?", "Print out help", 1, 1, helpfn},
279 {"inode", "Set active inode to INUM", 2, 2, focus},
280 {"clri", "Clear inode INUM", 2, 2, zapi},
281 {"lookup", "Set active inode by looking up NAME", 2, 2, focusname},
282 {"cd", "Set active inode by looking up NAME", 2, 2, focusname},
283 {"back", "Go to previous active inode", 1, 1, back},
284 {"active", "Print active inode", 1, 1, active},
285 {"print", "Print active inode", 1, 1, active},
286 {"uplink", "Increment link count", 1, 1, uplink},
287 {"downlink", "Decrement link count", 1, 1, downlink},
288 {"linkcount", "Set link count to COUNT", 2, 2, linkcount},
289 {"ls", "List current inode as directory", 1, 1, ls},
290 {"blks", "List current inode's data blocks", 1, 1, blks},
291 {"saveblks", "Save current inode's data blocks to FILE", 2, 2, blks},
292 {"findblk", "Find inode owning disk block(s)", 2, 33, findblk},
293 {"rm", "Remove NAME from current inode directory", 2, 2, rm},
294 {"del", "Remove NAME from current inode directory", 2, 2, rm},
295 {"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln},
296 {"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum},
297 {"chname", "Change dir entry number INDEX to NAME", 3, 3, chname},
298 {"chreclen", "Change dir entry number INDEX to RECLEN", 3, 3, chreclen},
299 {"chtype", "Change type of current inode to TYPE", 2, 2, newtype},
300 {"chmod", "Change mode of current inode to MODE", 2, 2, chmode},
301 {"chown", "Change owner of current inode to OWNER", 2, 2, chowner},
302 {"chlen", "Change length of current inode to LENGTH", 2, 2, chlen},
303 {"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup},
304 {"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags},
305 {"chgen", "Change generation number of current inode to GEN", 2, 2,
306 chgen},
307 { "chextsize", "Change extsize of current inode to EXTSIZE", 2, 2, chextsize },
308 { "chblocks", "Change blocks of current inode to BLOCKS", 2, 2, chblocks },
309 { "chdb", "Change db pointer N of current inode to BLKNO", 3, 3, chdb },
310 { "chib", "Change ib pointer N of current inode to BLKNO", 3, 3, chib },
311 { "chextb", "Change extb pointer N of current inode to BLKNO", 3, 3, chextb },
312 { "chfreelink", "Change freelink of current inode to FREELINK", 2, 2, chfreelink },
313 { "iptrs", "Print raw block pointers of current inode", 1, 1, iptrs },
314 {"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime},
315 {"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime},
316 {"atime", "Change atime of current inode to ATIME", 2, 2, chatime},
317 {"birthtime", "Change atime of current inode to BIRTHTIME", 2, 2,
318 chbirthtime},
319 {"saveea", "Save current inode's extattr blocks to FILE", 2, 2, saveea},
320 {"quit", "Exit", 1, 1, quit},
321 {"q", "Exit", 1, 1, quit},
322 {"exit", "Exit", 1, 1, quit},
323 { .cmd = NULL},
324 };
325
326 static int
helpfn(int argc,char * argv[])327 helpfn(int argc, char *argv[])
328 {
329 struct cmdtable *cmdtp;
330
331 printf("Commands are:\n%-10s %5s %5s %s\n",
332 "command", "min argc", "max argc", "what");
333
334 for (cmdtp = cmds; cmdtp->cmd; cmdtp++)
335 printf("%-10s %5u %5u %s\n",
336 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt);
337 return 0;
338 }
339
340 static char *
prompt(EditLine * el)341 prompt(EditLine *el)
342 {
343 static char pstring[64];
344 snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ",
345 (unsigned long long)curinum);
346 return pstring;
347 }
348
349
350 static int
cmdloop(void)351 cmdloop(void)
352 {
353 char *line;
354 const char *elline;
355 int cmd_argc, rval = 0, known;
356 #define scratch known
357 char **cmd_argv;
358 struct cmdtable *cmdp;
359 History *hist;
360 HistEvent he;
361 EditLine *elptr;
362
363 curinode = ginode(UFS_ROOTINO);
364 curinum = UFS_ROOTINO;
365 printactive();
366
367 hist = history_init();
368 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */
369
370 elptr = el_init(getprogname(), stdin, stdout, stderr);
371 el_set(elptr, EL_EDITOR, "emacs");
372 el_set(elptr, EL_PROMPT, prompt);
373 el_set(elptr, EL_HIST, history, hist);
374 el_source(elptr, NULL);
375
376 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) {
377 if (debug)
378 printf("command `%s'\n", elline);
379
380 history(hist, &he, H_ENTER, elline);
381
382 line = strdup(elline);
383 cmd_argv = crack(line, &cmd_argc);
384 if (cmd_argc) {
385 /*
386 * el_parse returns -1 to signal that it's not been
387 * handled internally.
388 */
389 if (el_parse(elptr, cmd_argc, (void *)cmd_argv) != -1)
390 continue;
391 known = 0;
392 for (cmdp = cmds; cmdp->cmd; cmdp++) {
393 if (!strcmp(cmdp->cmd, cmd_argv[0])) {
394 if (cmd_argc >= cmdp->minargc &&
395 cmd_argc <= cmdp->maxargc)
396 rval =
397 (*cmdp->handler)(cmd_argc,
398 cmd_argv);
399 else
400 rval = argcount(cmdp, cmd_argc,
401 cmd_argv);
402 known = 1;
403 break;
404 }
405 }
406 if (!known)
407 warnx("unknown command `%s'", cmd_argv[0]),
408 rval = 1;
409 } else
410 rval = 0;
411 free(line);
412 if (rval < 0)
413 return rval;
414 if (rval)
415 warnx("rval was %d", rval);
416 }
417 el_end(elptr);
418 history_end(hist);
419 return rval;
420 }
421
422 static ino_t ocurrent;
423
424 #define GETINUM(ac,inum) inum = strtoull(argv[ac], &cp, 0); \
425 if (inum < UFS_ROOTINO || inum >= maxino || cp == argv[ac] || *cp != '\0' ) { \
426 printf("inode %llu out of range; range is [%llu,%llu]\n", \
427 (unsigned long long)inum, (unsigned long long)UFS_ROOTINO, \
428 (unsigned long long)maxino); \
429 return 1; \
430 }
431
432 /*
433 * Focus on given inode number
434 */
CMDFUNC(focus)435 CMDFUNC(focus)
436 {
437 ino_t inum;
438 char *cp;
439
440 GETINUM(1, inum);
441 curinode = ginode(inum);
442 ocurrent = curinum;
443 curinum = inum;
444 printactive();
445 return 0;
446 }
447
CMDFUNC(back)448 CMDFUNC(back)
449 {
450 curinum = ocurrent;
451 curinode = ginode(curinum);
452 printactive();
453 return 0;
454 }
455
CMDFUNC(zapi)456 CMDFUNC(zapi)
457 {
458 ino_t inum;
459 union dinode *dp;
460 char *cp;
461
462 GETINUM(1, inum);
463 dp = ginode(inum);
464 clearinode(dp);
465 inodirty();
466 if (curinode) /* re-set after potential change */
467 curinode = ginode(curinum);
468 return 0;
469 }
470
CMDFUNC(active)471 CMDFUNC(active)
472 {
473 printactive();
474 return 0;
475 }
476
CMDFUNC(quit)477 CMDFUNC(quit)
478 {
479 return -1;
480 }
481
CMDFUNC(uplink)482 CMDFUNC(uplink)
483 {
484 int16_t nlink;
485
486 if (!checkactive())
487 return 1;
488 nlink = iswap16(DIP(curinode, nlink));
489 nlink++;
490 DIP_SET(curinode, nlink, iswap16(nlink));
491 printf("inode %llu link count now %d\n", (unsigned long long)curinum,
492 nlink);
493 inodirty();
494 return 0;
495 }
496
CMDFUNC(downlink)497 CMDFUNC(downlink)
498 {
499 int16_t nlink;
500
501 if (!checkactive())
502 return 1;
503 nlink = iswap16(DIP(curinode, nlink));
504 nlink--;
505 DIP_SET(curinode, nlink, iswap16(nlink));
506 printf("inode %llu link count now %d\n", (unsigned long long)curinum,
507 nlink);
508 inodirty();
509 return 0;
510 }
511
512 static const char *typename[] = {
513 "unknown",
514 "fifo",
515 "char special",
516 "unregistered #3",
517 "directory",
518 "unregistered #5",
519 "blk special",
520 "unregistered #7",
521 "regular",
522 "unregistered #9",
523 "symlink",
524 "unregistered #11",
525 "socket",
526 "unregistered #13",
527 "whiteout",
528 };
529
530 static int diroff;
531 static int slot;
532
533 static int
scannames(struct inodesc * idesc)534 scannames(struct inodesc *idesc)
535 {
536 struct direct *dirp = idesc->id_dirp;
537
538 printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n",
539 slot++, diroff, iswap32(dirp->d_ino), iswap16(dirp->d_reclen),
540 typename[dirp->d_type],
541 dirp->d_namlen, dirp->d_name);
542 diroff += dirp->d_reclen;
543 return (KEEPON);
544 }
545
CMDFUNC(ls)546 CMDFUNC(ls)
547 {
548 struct inodesc idesc;
549 checkactivedir(); /* let it go on anyway */
550
551 slot = 0;
552 diroff = 0;
553 idesc.id_number = curinum;
554 idesc.id_func = scannames;
555 idesc.id_type = DATA;
556 idesc.id_fix = IGNORE;
557 ckinode(curinode, &idesc);
558 curinode = ginode(curinum);
559
560 return 0;
561 }
562
CMDFUNC(blks)563 CMDFUNC(blks)
564 {
565 uint64_t blkno = 0;
566 int i;
567 struct wrinfo wrinfo, *wrp = NULL;
568 bool saveblks;
569
570 saveblks = strcmp(argv[0], "saveblks") == 0;
571 if (saveblks) {
572 wrinfo.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644);
573 if (wrinfo.fd == -1) {
574 warn("unable to create file %s", argv[1]);
575 return 0;
576 }
577 wrinfo.size = iswap64(DIP(curinode, size));
578 wrinfo.written_size = 0;
579 wrp = &wrinfo;
580 }
581 if (!curinode) {
582 warnx("no current inode");
583 return 0;
584 }
585 if (is_ufs2) {
586 printf("I=%llu %lld blocks\n", (unsigned long long)curinum,
587 (long long)(iswap64(curinode->dp2.di_blocks)));
588 } else {
589 printf("I=%llu %d blocks\n", (unsigned long long)curinum,
590 iswap32(curinode->dp1.di_blocks));
591 }
592 printf("Direct blocks:\n");
593 if (is_ufs2)
594 print_blks64(curinode->dp2.di_db, UFS_NDADDR, &blkno, wrp);
595 else
596 print_blks32(curinode->dp1.di_db, UFS_NDADDR, &blkno, wrp);
597
598 if (is_ufs2) {
599 for (i = 0; i < UFS_NIADDR; i++)
600 print_indirblks64(iswap64(curinode->dp2.di_ib[i]), i,
601 &blkno, wrp);
602 printf("Extattr blocks:\n");
603 blkno = 0;
604 if (saveblks)
605 wrinfo.size += iswap32(curinode->dp2.di_extsize);
606 print_blks64(curinode->dp2.di_extb, UFS_NXADDR, &blkno, wrp);
607 } else {
608 for (i = 0; i < UFS_NIADDR; i++)
609 print_indirblks32(iswap32(curinode->dp1.di_ib[i]), i,
610 &blkno, wrp);
611 }
612 return 0;
613 }
614
615 static int findblk_numtofind;
616 static int wantedblksize;
CMDFUNC(findblk)617 CMDFUNC(findblk)
618 {
619 ino_t inum, inosused;
620 uint32_t *wantedblk32 = NULL;
621 uint64_t *wantedblk64 = NULL;
622 struct cg *cgp = cgrp;
623 int i;
624 uint32_t c;
625
626 ocurrent = curinum;
627 wantedblksize = (argc - 1);
628 if (is_ufs2) {
629 wantedblk64 = malloc(sizeof(uint64_t) * wantedblksize);
630 if (wantedblk64 == NULL) {
631 perror("malloc");
632 return 1;
633 }
634 memset(wantedblk64, 0, sizeof(uint64_t) * wantedblksize);
635 for (i = 1; i < argc; i++)
636 wantedblk64[i - 1] =
637 FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0));
638 } else {
639 wantedblk32 = malloc(sizeof(uint32_t) * wantedblksize);
640 if (wantedblk32 == NULL) {
641 perror("malloc");
642 return 1;
643 }
644 memset(wantedblk32, 0, sizeof(uint32_t) * wantedblksize);
645 for (i = 1; i < argc; i++)
646 wantedblk32[i - 1] =
647 FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0));
648 }
649 findblk_numtofind = wantedblksize;
650 for (c = 0; c < sblock->fs_ncg; c++) {
651 inum = c * sblock->fs_ipg;
652 getblk(&cgblk, cgtod(sblock, c), sblock->fs_cgsize);
653 memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize);
654 if (needswap)
655 ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock);
656 if (is_ufs2)
657 inosused = cgp->cg_initediblk;
658 else
659 inosused = sblock->fs_ipg;
660 for (; inosused > 0; inum++, inosused--) {
661 if (inum < UFS_ROOTINO)
662 continue;
663 if (is_ufs2 ? compare_blk64(wantedblk64,
664 ino_to_fsba(sblock, inum)) :
665 compare_blk32(wantedblk32,
666 ino_to_fsba(sblock, inum))) {
667 printf("block %llu: inode block (%llu-%llu)\n",
668 (unsigned long long)FFS_FSBTODB(sblock,
669 ino_to_fsba(sblock, inum)),
670 (unsigned long long)
671 (inum / FFS_INOPB(sblock)) * FFS_INOPB(sblock),
672 (unsigned long long)
673 (inum / FFS_INOPB(sblock) + 1) * FFS_INOPB(sblock));
674 findblk_numtofind--;
675 if (findblk_numtofind == 0)
676 goto end;
677 }
678 curinum = inum;
679 curinode = ginode(inum);
680 switch (iswap16(DIP(curinode, mode)) & IFMT) {
681 case IFDIR:
682 case IFREG:
683 if (DIP(curinode, blocks) == 0)
684 continue;
685 break;
686 case IFLNK:
687 {
688 uint64_t size = iswap64(DIP(curinode, size));
689 if (size > 0 &&
690 size < (uint64_t)sblock->fs_maxsymlinklen &&
691 DIP(curinode, blocks) == 0)
692 continue;
693 else
694 break;
695 }
696 default:
697 continue;
698 }
699 if (is_ufs2 ?
700 find_blks64(curinode->dp2.di_db, UFS_NDADDR,
701 wantedblk64) :
702 find_blks32(curinode->dp1.di_db, UFS_NDADDR,
703 wantedblk32))
704 goto end;
705 for (i = 0; i < UFS_NIADDR; i++) {
706 if (is_ufs2 ?
707 compare_blk64(wantedblk64,
708 iswap64(curinode->dp2.di_ib[i])) :
709 compare_blk32(wantedblk32,
710 iswap32(curinode->dp1.di_ib[i])))
711 if (founddatablk(is_ufs2 ?
712 iswap64(curinode->dp2.di_ib[i]) :
713 iswap32(curinode->dp1.di_ib[i])))
714 goto end;
715 if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) :
716 (curinode->dp1.di_ib[i] != 0))
717 if (is_ufs2 ?
718 find_indirblks64(
719 iswap64(curinode->dp2.di_ib[i]),
720 i, wantedblk64) :
721 find_indirblks32(
722 iswap32(curinode->dp1.di_ib[i]),
723 i, wantedblk32))
724 goto end;
725 }
726 }
727 }
728 end:
729 if (wantedblk32)
730 free(wantedblk32);
731 if (wantedblk64)
732 free(wantedblk64);
733 curinum = ocurrent;
734 curinode = ginode(curinum);
735 return 0;
736 }
737
738 static int
compare_blk32(uint32_t * wantedblk,uint32_t curblk)739 compare_blk32(uint32_t *wantedblk, uint32_t curblk)
740 {
741 int i;
742 for (i = 0; i < wantedblksize; i++) {
743 if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
744 wantedblk[i] = 0;
745 return 1;
746 }
747 }
748 return 0;
749 }
750
751 static int
compare_blk64(uint64_t * wantedblk,uint64_t curblk)752 compare_blk64(uint64_t *wantedblk, uint64_t curblk)
753 {
754 int i;
755 for (i = 0; i < wantedblksize; i++) {
756 if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
757 wantedblk[i] = 0;
758 return 1;
759 }
760 }
761 return 0;
762 }
763
764 static int
founddatablk(uint64_t blk)765 founddatablk(uint64_t blk)
766 {
767 printf("%llu: data block of inode %llu\n",
768 (unsigned long long)FFS_FSBTODB(sblock, blk),
769 (unsigned long long)curinum);
770 findblk_numtofind--;
771 if (findblk_numtofind == 0)
772 return 1;
773 return 0;
774 }
775
776 static int
find_blks32(uint32_t * buf,int size,uint32_t * wantedblk)777 find_blks32(uint32_t *buf, int size, uint32_t *wantedblk)
778 {
779 int blk;
780 for(blk = 0; blk < size; blk++) {
781 if (buf[blk] == 0)
782 continue;
783 if (compare_blk32(wantedblk, iswap32(buf[blk]))) {
784 if (founddatablk(iswap32(buf[blk])))
785 return 1;
786 }
787 }
788 return 0;
789 }
790
791 static int
find_indirblks32(uint32_t blk,int ind_level,uint32_t * wantedblk)792 find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk)
793 {
794 #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t))
795 uint32_t idblk[MAXNINDIR];
796 size_t i;
797
798 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
799 (int)sblock->fs_bsize);
800 if (ind_level <= 0) {
801 if (find_blks32(idblk,
802 sblock->fs_bsize / sizeof(uint32_t), wantedblk))
803 return 1;
804 } else {
805 ind_level--;
806 for (i = 0; i < sblock->fs_bsize / sizeof(uint32_t); i++) {
807 if (compare_blk32(wantedblk, iswap32(idblk[i]))) {
808 if (founddatablk(iswap32(idblk[i])))
809 return 1;
810 }
811 if(idblk[i] != 0)
812 if (find_indirblks32(iswap32(idblk[i]),
813 ind_level, wantedblk))
814 return 1;
815 }
816 }
817 #undef MAXNINDIR
818 return 0;
819 }
820
821
822 static int
find_blks64(uint64_t * buf,int size,uint64_t * wantedblk)823 find_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
824 {
825 int blk;
826 for(blk = 0; blk < size; blk++) {
827 if (buf[blk] == 0)
828 continue;
829 if (compare_blk64(wantedblk, iswap64(buf[blk]))) {
830 if (founddatablk(iswap64(buf[blk])))
831 return 1;
832 }
833 }
834 return 0;
835 }
836
837 static int
find_indirblks64(uint64_t blk,int ind_level,uint64_t * wantedblk)838 find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
839 {
840 #define MAXNINDIR (MAXBSIZE / sizeof(uint64_t))
841 uint64_t idblk[MAXNINDIR];
842 size_t i;
843
844 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
845 (int)sblock->fs_bsize);
846 if (ind_level <= 0) {
847 if (find_blks64(idblk,
848 sblock->fs_bsize / sizeof(uint64_t), wantedblk))
849 return 1;
850 } else {
851 ind_level--;
852 for (i = 0; i < sblock->fs_bsize / sizeof(uint64_t); i++) {
853 if (compare_blk64(wantedblk, iswap64(idblk[i]))) {
854 if (founddatablk(iswap64(idblk[i])))
855 return 1;
856 }
857 if (idblk[i] != 0)
858 if (find_indirblks64(iswap64(idblk[i]),
859 ind_level, wantedblk))
860 return 1;
861 }
862 }
863 #undef MAXNINDIR
864 return 0;
865 }
866
867 static int
writefileblk(struct wrinfo * wrp,uint64_t blk)868 writefileblk(struct wrinfo *wrp, uint64_t blk)
869 {
870 char buf[MAXBSIZE];
871 long long size, rsize;
872
873 size = wrp->size - wrp->written_size;
874 if (size > sblock->fs_bsize)
875 size = sblock->fs_bsize;
876 if (size > (long long)sizeof buf) {
877 warnx("sblock->fs_bsize > MAX_BSIZE");
878 return -1;
879 }
880
881 rsize = roundup(size, DEV_BSIZE);
882 if (bread(fsreadfd, buf, FFS_FSBTODB(sblock, blk), rsize) != 0)
883 return -1;
884 if (write(wrp->fd, buf, size) != size)
885 return -1;
886 wrp->written_size += size;
887 return 0;
888 }
889
890
891 #define CHARS_PER_LINES 70
892
893 static void
print_blks32(int32_t * buf,int size,uint64_t * blknum,struct wrinfo * wrp)894 print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp)
895 {
896 int chars;
897 char prbuf[CHARS_PER_LINES+1];
898 int blk;
899
900 chars = 0;
901 for (blk = 0; blk < size; blk++, (*blknum)++) {
902 if (buf[blk] == 0)
903 continue;
904 if (wrp && writefileblk(wrp, iswap32(buf[blk])) != 0) {
905 warn("unable to write block %d", iswap32(buf[blk]));
906 return;
907 }
908 snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk]));
909 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
910 printf("\n");
911 chars = 0;
912 }
913 if (chars == 0)
914 printf("%" PRIu64 ": ", *blknum);
915 printf("%s", prbuf);
916 chars += strlen(prbuf);
917 }
918 printf("\n");
919 }
920
921 static void
print_blks64(int64_t * buf,int size,uint64_t * blknum,struct wrinfo * wrp)922 print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp)
923 {
924 int chars;
925 char prbuf[CHARS_PER_LINES+1];
926 int blk;
927
928 chars = 0;
929 for (blk = 0; blk < size; blk++, (*blknum)++) {
930 if (buf[blk] == 0)
931 continue;
932 if (wrp && writefileblk(wrp, iswap64(buf[blk])) != 0) {
933 warn("unable to write block %lld",
934 (long long)iswap64(buf[blk]));
935 return;
936 }
937 snprintf(prbuf, CHARS_PER_LINES, "%lld ",
938 (long long)iswap64(buf[blk]));
939 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
940 printf("\n");
941 chars = 0;
942 }
943 if (chars == 0)
944 printf("%" PRIu64 ": ", *blknum);
945 printf("%s", prbuf);
946 chars += strlen(prbuf);
947 }
948 printf("\n");
949 }
950
951 #undef CHARS_PER_LINES
952
953 static void
print_indirblks32(uint32_t blk,int ind_level,uint64_t * blknum,struct wrinfo * wrp)954 print_indirblks32(uint32_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp)
955 {
956 #define MAXNINDIR (MAXBSIZE / sizeof(int32_t))
957 const int ptrperblk_shift = sblock->fs_bshift - 2;
958 const int ptrperblk = 1 << ptrperblk_shift;
959 int32_t idblk[MAXNINDIR];
960 int i;
961
962 if (blk == 0) {
963 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level);
964 return;
965 }
966
967 printf("Indirect block %lld (level %d):\n", (long long)blk,
968 ind_level+1);
969 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
970 (int)sblock->fs_bsize);
971 if (ind_level <= 0) {
972 print_blks32(idblk, ptrperblk, blknum, wrp);
973 } else {
974 ind_level--;
975 for (i = 0; i < ptrperblk; i++)
976 print_indirblks32(iswap32(idblk[i]), ind_level, blknum,
977 wrp);
978 }
979 #undef MAXNINDIR
980 }
981
982 static void
print_indirblks64(uint64_t blk,int ind_level,uint64_t * blknum,struct wrinfo * wrp)983 print_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp)
984 {
985 #define MAXNINDIR (MAXBSIZE / sizeof(int64_t))
986 const int ptrperblk_shift = sblock->fs_bshift - 3;
987 const int ptrperblk = 1 << ptrperblk_shift;
988 int64_t idblk[MAXNINDIR];
989 int i;
990
991 if (blk == 0) {
992 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level);
993 return;
994 }
995
996 printf("Indirect block %lld (level %d):\n", (long long)blk,
997 ind_level+1);
998 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
999 (int)sblock->fs_bsize);
1000 if (ind_level <= 0) {
1001 print_blks64(idblk, ptrperblk, blknum, wrp);
1002 } else {
1003 ind_level--;
1004 for (i = 0; i < ptrperblk; i++)
1005 print_indirblks64(iswap64(idblk[i]), ind_level, blknum,
1006 wrp);
1007 }
1008 #undef MAXNINDIR
1009 }
1010
1011 static int
dolookup(char * name)1012 dolookup(char *name)
1013 {
1014 struct inodesc idesc;
1015
1016 if (!checkactivedir())
1017 return 0;
1018 idesc.id_number = curinum;
1019 idesc.id_func = findino;
1020 idesc.id_name = name;
1021 idesc.id_type = DATA;
1022 idesc.id_fix = IGNORE;
1023 if (ckinode(curinode, &idesc) & FOUND) {
1024 curinum = idesc.id_parent;
1025 curinode = ginode(curinum);
1026 printactive();
1027 return 1;
1028 } else {
1029 warnx("name `%s' not found in current inode directory", name);
1030 return 0;
1031 }
1032 }
1033
CMDFUNC(focusname)1034 CMDFUNC(focusname)
1035 {
1036 char *p, *val;
1037
1038 if (!checkactive())
1039 return 1;
1040
1041 ocurrent = curinum;
1042
1043 if (argv[1][0] == '/') {
1044 curinum = UFS_ROOTINO;
1045 curinode = ginode(UFS_ROOTINO);
1046 } else {
1047 if (!checkactivedir())
1048 return 1;
1049 }
1050 for (p = argv[1]; p != NULL;) {
1051 while ((val = strsep(&p, "/")) != NULL && *val == '\0');
1052 if (val) {
1053 printf("component `%s': ", val);
1054 fflush(stdout);
1055 if (!dolookup(val)) {
1056 curinode = ginode(curinum);
1057 return (1);
1058 }
1059 }
1060 }
1061 return 0;
1062 }
1063
CMDFUNC(ln)1064 CMDFUNC(ln)
1065 {
1066 ino_t inum;
1067 int rval;
1068 char *cp;
1069
1070 GETINUM(1, inum);
1071
1072 if (!checkactivedir())
1073 return 1;
1074 rval = makeentry(curinum, inum, argv[2]);
1075 if (rval)
1076 printf("Ino %llu entered as `%s'\n", (unsigned long long)inum,
1077 argv[2]);
1078 else
1079 printf("could not enter name? weird.\n");
1080 curinode = ginode(curinum);
1081 return rval;
1082 }
1083
CMDFUNC(rm)1084 CMDFUNC(rm)
1085 {
1086 int rval;
1087
1088 if (!checkactivedir())
1089 return 1;
1090 rval = changeino(curinum, argv[1], 0);
1091 if (rval & ALTERED) {
1092 printf("Name `%s' removed\n", argv[1]);
1093 return 0;
1094 } else {
1095 printf("could not remove name? weird.\n");
1096 return 1;
1097 }
1098 }
1099
1100 static long slotcount, desired;
1101
1102 static int
chinumfunc(struct inodesc * idesc)1103 chinumfunc(struct inodesc *idesc)
1104 {
1105 struct direct *dirp = idesc->id_dirp;
1106
1107 if (slotcount++ == desired) {
1108 dirp->d_ino = iswap32(idesc->id_parent);
1109 return STOP | ALTERED | FOUND;
1110 }
1111 return KEEPON;
1112 }
1113
CMDFUNC(chinum)1114 CMDFUNC(chinum)
1115 {
1116 char *cp;
1117 ino_t inum;
1118 struct inodesc idesc;
1119
1120 slotcount = 0;
1121 if (!checkactivedir())
1122 return 1;
1123 GETINUM(2, inum);
1124
1125 desired = strtol(argv[1], &cp, 0);
1126 if (cp == argv[1] || *cp != '\0' || desired < 0) {
1127 printf("invalid slot number `%s'\n", argv[1]);
1128 return 1;
1129 }
1130 idesc.id_number = curinum;
1131 idesc.id_func = chinumfunc;
1132 idesc.id_fix = IGNORE;
1133 idesc.id_type = DATA;
1134 idesc.id_parent = inum; /* XXX convenient hiding place */
1135
1136 if (ckinode(curinode, &idesc) & FOUND)
1137 return 0;
1138 else {
1139 warnx("no %sth slot in current directory", argv[1]);
1140 return 1;
1141 }
1142 }
1143
1144 static int
chnamefunc(struct inodesc * idesc)1145 chnamefunc(struct inodesc *idesc)
1146 {
1147 struct direct *dirp = idesc->id_dirp;
1148 struct direct testdir;
1149
1150 if (slotcount++ == desired) {
1151 /* will name fit? */
1152 testdir.d_namlen = strlen(idesc->id_name);
1153 if (UFS_DIRSIZ(UFS_NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) {
1154 dirp->d_namlen = testdir.d_namlen;
1155 strlcpy(dirp->d_name, idesc->id_name,
1156 sizeof(dirp->d_name));
1157 return STOP | ALTERED | FOUND;
1158 } else
1159 return STOP | FOUND; /* won't fit, so give up */
1160 }
1161 return KEEPON;
1162 }
1163
CMDFUNC(chname)1164 CMDFUNC(chname)
1165 {
1166 int rval;
1167 char *cp;
1168 struct inodesc idesc;
1169
1170 slotcount = 0;
1171 if (!checkactivedir())
1172 return 1;
1173
1174 desired = strtoul(argv[1], &cp, 0);
1175 if (cp == argv[1] || *cp != '\0') {
1176 printf("invalid slot number `%s'\n", argv[1]);
1177 return 1;
1178 }
1179 idesc.id_number = curinum;
1180 idesc.id_func = chnamefunc;
1181 idesc.id_fix = IGNORE;
1182 idesc.id_type = DATA;
1183 idesc.id_name = argv[2];
1184
1185 rval = ckinode(curinode, &idesc);
1186 if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED))
1187 return 0;
1188 else
1189 if (rval & FOUND) {
1190 warnx("new name `%s' does not fit in slot %s",
1191 argv[2], argv[1]);
1192 return 1;
1193 } else {
1194 warnx("no %sth slot in current directory", argv[1]);
1195 return 1;
1196 }
1197 }
1198
1199 static int
chreclenfunc(struct inodesc * idesc)1200 chreclenfunc(struct inodesc *idesc)
1201 {
1202 struct direct *dirp = idesc->id_dirp;
1203
1204 if (slotcount++ == desired) {
1205 dirp->d_reclen = iswap16(idesc->id_parent);
1206 return STOP | ALTERED | FOUND;
1207 }
1208 return KEEPON;
1209 }
1210
CMDFUNC(chreclen)1211 CMDFUNC(chreclen)
1212 {
1213 char *cp;
1214 uint32_t reclen;
1215 struct inodesc idesc;
1216
1217 slotcount = 0;
1218 if (!checkactivedir())
1219 return 1;
1220
1221 desired = strtoul(argv[1], &cp, 0);
1222 if (cp == argv[1] || *cp != '\0') {
1223 printf("invalid slot number `%s'\n", argv[1]);
1224 return 1;
1225 }
1226 reclen = strtoul(argv[2], &cp, 0);
1227 if (reclen >= UINT16_MAX) {
1228 printf("invalid reclen `%s'\n", argv[2]);
1229 return 1;
1230 }
1231
1232 idesc.id_number = curinum;
1233 idesc.id_func = chreclenfunc;
1234 idesc.id_fix = IGNORE;
1235 idesc.id_type = DATA;
1236 idesc.id_parent = reclen; /* XXX convenient hiding place */
1237
1238 if (ckinode(curinode, &idesc) & FOUND)
1239 return 0;
1240 else {
1241 warnx("no %sth slot in current directory", argv[1]);
1242 return 1;
1243 }
1244 }
1245
1246 static struct typemap {
1247 const char *typename;
1248 int typebits;
1249 } typenamemap[] = {
1250 { "file", IFREG },
1251 { "dir", IFDIR },
1252 { "socket", IFSOCK },
1253 { "fifo", IFIFO },
1254 {"link", IFLNK},
1255 {"chr", IFCHR},
1256 {"blk", IFBLK},
1257 };
1258
CMDFUNC(newtype)1259 CMDFUNC(newtype)
1260 {
1261 int type;
1262 uint16_t mode;
1263 struct typemap *tp;
1264
1265 if (!checkactive())
1266 return 1;
1267 mode = iswap16(DIP(curinode, mode));
1268 type = mode & IFMT;
1269 for (tp = typenamemap;
1270 tp < &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)];
1271 tp++) {
1272 if (!strcmp(argv[1], tp->typename)) {
1273 printf("setting type to %s\n", tp->typename);
1274 type = tp->typebits;
1275 break;
1276 }
1277 }
1278 if (tp == &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]) {
1279 warnx("type `%s' not known", argv[1]);
1280 warnx("try one of `file', `dir', `socket', `fifo'");
1281 return 1;
1282 }
1283 DIP_SET(curinode, mode, iswap16((mode & ~IFMT) | type));
1284 inodirty();
1285 printactive();
1286 return 0;
1287 }
1288
CMDFUNC(chmode)1289 CMDFUNC(chmode)
1290 {
1291 long modebits;
1292 char *cp;
1293 uint16_t mode;
1294
1295 if (!checkactive())
1296 return 1;
1297
1298 modebits = strtol(argv[1], &cp, 8);
1299 if (cp == argv[1] || *cp != '\0') {
1300 warnx("bad modebits `%s'", argv[1]);
1301 return 1;
1302 }
1303 mode = iswap16(DIP(curinode, mode));
1304 DIP_SET(curinode, mode, iswap16((mode & ~07777) | modebits));
1305 inodirty();
1306 printactive();
1307 return 0;
1308 }
1309
CMDFUNC(chlen)1310 CMDFUNC(chlen)
1311 {
1312 off_t len;
1313 char *cp;
1314
1315 if (!checkactive())
1316 return 1;
1317
1318 len = strtoull(argv[1], &cp, 0);
1319 if (cp == argv[1] || *cp != '\0' || len < 0) {
1320 warnx("bad length '%s'", argv[1]);
1321 return 1;
1322 }
1323 DIP_SET(curinode, size, iswap64(len));
1324 inodirty();
1325 printactive();
1326 return 0;
1327 }
1328
CMDFUNC(chaflags)1329 CMDFUNC(chaflags)
1330 {
1331 u_long flags;
1332 char *cp;
1333
1334 if (!checkactive())
1335 return 1;
1336
1337 flags = strtoul(argv[1], &cp, 0);
1338 if (cp == argv[1] || *cp != '\0') {
1339 warnx("bad flags `%s'", argv[1]);
1340 return 1;
1341 }
1342 if (flags > UINT_MAX) {
1343 warnx("flags set beyond 32-bit range of field (0x%lx)",
1344 flags);
1345 return (1);
1346 }
1347 DIP_SET(curinode, flags, iswap32(flags));
1348 inodirty();
1349 printactive();
1350 return 0;
1351 }
1352
CMDFUNC(chgen)1353 CMDFUNC(chgen)
1354 {
1355 long gen;
1356 char *cp;
1357
1358 if (!checkactive())
1359 return 1;
1360
1361 gen = strtol(argv[1], &cp, 0);
1362 if (cp == argv[1] || *cp != '\0') {
1363 warnx("bad gen `%s'", argv[1]);
1364 return 1;
1365 }
1366 if (gen > INT_MAX || gen < INT_MIN) {
1367 warnx("gen set beyond 32-bit range of field (0x%lx)", gen);
1368 return (1);
1369 }
1370 DIP_SET(curinode, gen, iswap32(gen));
1371 inodirty();
1372 printactive();
1373 return 0;
1374 }
1375
CMDFUNC(chextsize)1376 CMDFUNC(chextsize)
1377 {
1378 uint32_t extsize;
1379 char *cp;
1380
1381 if (!is_ufs2)
1382 return 1;
1383 if (!checkactive())
1384 return 1;
1385
1386 extsize = strtol(argv[1], &cp, 0);
1387 if (cp == argv[1] || *cp != '\0') {
1388 warnx("bad extsize `%s'", argv[1]);
1389 return 1;
1390 }
1391
1392 curinode->dp2.di_extsize = extsize;
1393 inodirty();
1394 printactive();
1395 return 0;
1396 }
1397
CMDFUNC(chblocks)1398 CMDFUNC(chblocks)
1399 {
1400 uint64_t blocks;
1401 char *cp;
1402
1403 if (!checkactive())
1404 return 1;
1405
1406 blocks = strtoll(argv[1], &cp, 0);
1407 if (cp == argv[1] || *cp != '\0') {
1408 warnx("bad blocks `%s'", argv[1]);
1409 return 1;
1410 }
1411
1412 DIP_SET(curinode, blocks, blocks);
1413 inodirty();
1414 printactive();
1415 return 0;
1416 }
1417
CMDFUNC(chdb)1418 CMDFUNC(chdb)
1419 {
1420 unsigned int idx;
1421 daddr_t bno;
1422 char *cp;
1423
1424 if (!checkactive())
1425 return 1;
1426
1427 idx = strtoull(argv[1], &cp, 0);
1428 if (cp == argv[1] || *cp != '\0') {
1429 warnx("bad pointer idx `%s'", argv[1]);
1430 return 1;
1431 }
1432 bno = strtoll(argv[2], &cp, 0);
1433 if (cp == argv[2] || *cp != '\0') {
1434 warnx("bad block number `%s'", argv[2]);
1435 return 1;
1436 }
1437 if (idx >= UFS_NDADDR) {
1438 warnx("pointer index %d is out of range", idx);
1439 return 1;
1440 }
1441
1442 DIP_SET(curinode, db[idx], bno);
1443 inodirty();
1444 printactive();
1445 return 0;
1446 }
1447
CMDFUNC(chib)1448 CMDFUNC(chib)
1449 {
1450 unsigned int idx;
1451 daddr_t bno;
1452 char *cp;
1453
1454 if (!checkactive())
1455 return 1;
1456
1457 idx = strtoull(argv[1], &cp, 0);
1458 if (cp == argv[1] || *cp != '\0') {
1459 warnx("bad pointer idx `%s'", argv[1]);
1460 return 1;
1461 }
1462 bno = strtoll(argv[2], &cp, 0);
1463 if (cp == argv[2] || *cp != '\0') {
1464 warnx("bad block number `%s'", argv[2]);
1465 return 1;
1466 }
1467 if (idx >= UFS_NIADDR) {
1468 warnx("pointer index %d is out of range", idx);
1469 return 1;
1470 }
1471
1472 DIP_SET(curinode, ib[idx], bno);
1473 inodirty();
1474 printactive();
1475 return 0;
1476 }
1477
CMDFUNC(chextb)1478 CMDFUNC(chextb)
1479 {
1480 unsigned int idx;
1481 daddr_t bno;
1482 char *cp;
1483
1484 if (!checkactive())
1485 return 1;
1486
1487 idx = strtoull(argv[1], &cp, 0);
1488 if (cp == argv[1] || *cp != '\0') {
1489 warnx("bad pointer idx `%s'", argv[1]);
1490 return 1;
1491 }
1492 bno = strtoll(argv[2], &cp, 0);
1493 if (cp == argv[2] || *cp != '\0') {
1494 warnx("bad block number `%s'", argv[2]);
1495 return 1;
1496 }
1497 if (idx >= UFS_NXADDR) {
1498 warnx("pointer index %d is out of range", idx);
1499 return 1;
1500 }
1501
1502 curinode->dp2.di_extb[idx] = bno;
1503 inodirty();
1504 printactive();
1505 return 0;
1506 }
1507
CMDFUNC(chfreelink)1508 CMDFUNC(chfreelink)
1509 {
1510 #if 0
1511 ino_t freelink;
1512 char *cp;
1513
1514 if (!checkactive())
1515 return 1;
1516
1517 freelink = strtoll(argv[1], &cp, 0);
1518 if (cp == argv[1] || *cp != '\0') {
1519 warnx("bad freelink `%s'", argv[1]);
1520 return 1;
1521 }
1522
1523 DIP_SET(curinode, freelink, freelink);
1524 inodirty();
1525 printactive();
1526 #endif
1527 return 0;
1528 }
1529
CMDFUNC(linkcount)1530 CMDFUNC(linkcount)
1531 {
1532 int lcnt;
1533 char *cp;
1534
1535 if (!checkactive())
1536 return 1;
1537
1538 lcnt = strtol(argv[1], &cp, 0);
1539 if (cp == argv[1] || *cp != '\0') {
1540 warnx("bad link count `%s'", argv[1]);
1541 return 1;
1542 }
1543 if (lcnt > USHRT_MAX || lcnt < 0) {
1544 warnx("max link count is %d", USHRT_MAX);
1545 return 1;
1546 }
1547 DIP_SET(curinode, nlink, iswap16(lcnt));
1548 inodirty();
1549 printactive();
1550 return 0;
1551 }
1552
CMDFUNC(chowner)1553 CMDFUNC(chowner)
1554 {
1555 unsigned long uid;
1556 char *cp;
1557 struct passwd *pwd;
1558
1559 if (!checkactive())
1560 return 1;
1561
1562 uid = strtoul(argv[1], &cp, 0);
1563 if (cp == argv[1] || *cp != '\0') {
1564 /* try looking up name */
1565 if ((pwd = getpwnam(argv[1])) != 0) {
1566 uid = pwd->pw_uid;
1567 } else {
1568 warnx("bad uid `%s'", argv[1]);
1569 return 1;
1570 }
1571 }
1572 if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT)
1573 curinode->dp1.di_ouid = iswap32(uid);
1574 else
1575 DIP_SET(curinode, uid, iswap32(uid));
1576 inodirty();
1577 printactive();
1578 return 0;
1579 }
1580
CMDFUNC(chgroup)1581 CMDFUNC(chgroup)
1582 {
1583 unsigned long gid;
1584 char *cp;
1585 struct group *grp;
1586
1587 if (!checkactive())
1588 return 1;
1589
1590 gid = strtoul(argv[1], &cp, 0);
1591 if (cp == argv[1] || *cp != '\0') {
1592 if ((grp = getgrnam(argv[1])) != 0) {
1593 gid = grp->gr_gid;
1594 } else {
1595 warnx("bad gid `%s'", argv[1]);
1596 return 1;
1597 }
1598 }
1599 if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT)
1600 curinode->dp1.di_ogid = iswap32(gid);
1601 else
1602 DIP_SET(curinode, gid, iswap32(gid));
1603 inodirty();
1604 printactive();
1605 return 0;
1606 }
1607
1608 static int
dotime(char * name,int64_t * rsec,int32_t * rnsec)1609 dotime(char *name, int64_t *rsec, int32_t *rnsec)
1610 {
1611 char *p, *val;
1612 struct tm t;
1613 int64_t sec;
1614 int32_t nsec;
1615 p = strchr(name, '.');
1616 if (p) {
1617 *p = '\0';
1618 nsec = strtoul(++p, &val, 0);
1619 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) {
1620 warnx("invalid nanoseconds");
1621 goto badformat;
1622 }
1623 } else
1624 nsec = 0;
1625 if (strlen(name) != 14) {
1626 badformat:
1627 warnx("date format: YYYYMMDDHHMMSS[.nsec]");
1628 return 1;
1629 }
1630 for (p = name; *p; p++)
1631 if (*p < '0' || *p > '9')
1632 goto badformat;
1633
1634 p = name;
1635 #define VAL() ((*p++) - '0')
1636 t.tm_year = VAL();
1637 t.tm_year = VAL() + t.tm_year * 10;
1638 t.tm_year = VAL() + t.tm_year * 10;
1639 t.tm_year = VAL() + t.tm_year * 10 - 1900;
1640 t.tm_mon = VAL();
1641 t.tm_mon = VAL() + t.tm_mon * 10 - 1;
1642 t.tm_mday = VAL();
1643 t.tm_mday = VAL() + t.tm_mday * 10;
1644 t.tm_hour = VAL();
1645 t.tm_hour = VAL() + t.tm_hour * 10;
1646 t.tm_min = VAL();
1647 t.tm_min = VAL() + t.tm_min * 10;
1648 t.tm_sec = VAL();
1649 t.tm_sec = VAL() + t.tm_sec * 10;
1650 t.tm_isdst = -1;
1651
1652 sec = mktime(&t);
1653 if (sec == -1) {
1654 warnx("date/time out of range");
1655 return 1;
1656 }
1657 *rsec = iswap64(sec);
1658 *rnsec = iswap32(nsec);
1659 return 0;
1660 }
1661
CMDFUNC(chmtime)1662 CMDFUNC(chmtime)
1663 {
1664 int64_t rsec;
1665 int32_t nsec;
1666
1667 if (!checkactive())
1668 return 1;
1669 if (dotime(argv[1], &rsec, &nsec))
1670 return 1;
1671 DIP_SET(curinode, mtime, rsec);
1672 DIP_SET(curinode, mtimensec, nsec);
1673 inodirty();
1674 printactive();
1675 return 0;
1676 }
1677
CMDFUNC(chatime)1678 CMDFUNC(chatime)
1679 {
1680 int64_t rsec;
1681 int32_t nsec;
1682
1683 if (!checkactive())
1684 return 1;
1685 if (dotime(argv[1], &rsec, &nsec))
1686 return 1;
1687 DIP_SET(curinode, atime, rsec);
1688 DIP_SET(curinode, atimensec, nsec);
1689 inodirty();
1690 printactive();
1691 return 0;
1692 }
1693
CMDFUNC(chctime)1694 CMDFUNC(chctime)
1695 {
1696 int64_t rsec;
1697 int32_t nsec;
1698
1699 if (!checkactive())
1700 return 1;
1701 if (dotime(argv[1], &rsec, &nsec))
1702 return 1;
1703 DIP_SET(curinode, ctime, rsec);
1704 DIP_SET(curinode, ctimensec, nsec);
1705 inodirty();
1706 printactive();
1707 return 0;
1708 }
1709
CMDFUNC(chbirthtime)1710 CMDFUNC(chbirthtime)
1711 {
1712 int64_t rsec;
1713 int32_t nsec;
1714
1715 if (!is_ufs2) {
1716 warnx("birthtime can only be set in ufs2");
1717 return 1;
1718 }
1719 if (!checkactive())
1720 return 1;
1721
1722 if (dotime(argv[1], &rsec, &nsec))
1723 return 1;
1724 curinode->dp2.di_birthtime = rsec;
1725 curinode->dp2.di_birthnsec = nsec;
1726 inodirty();
1727 printactive();
1728 return 0;
1729 }
1730
CMDFUNC(iptrs)1731 CMDFUNC(iptrs)
1732 {
1733 int i;
1734
1735 if (!checkactive())
1736 return 1;
1737 for (i = 0; i < UFS_NDADDR; i++)
1738 printf("di_db %d %ju\n", i, DIP(curinode, db[i]));
1739 for (i = 0; i < UFS_NIADDR; i++)
1740 printf("di_ib %d %ju\n", i, DIP(curinode, ib[i]));
1741 if (is_ufs2)
1742 for (i = 0; i < UFS_NXADDR; i++)
1743 printf("di_extb %d %ju\n", i, curinode->dp2.di_extb[i]);
1744 return 0;
1745 }
1746
CMDFUNC(saveea)1747 CMDFUNC(saveea)
1748 {
1749 struct wrinfo wrinfo;
1750 uint64_t blkno = 0;
1751
1752 if (!is_ufs2) {
1753 warnx("dumping extattrs is only supported for ufs2");
1754 return 1;
1755 }
1756 if (!checkactive())
1757 return 1;
1758
1759 wrinfo.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644);
1760 if (wrinfo.fd == -1) {
1761 warn("unable to create file %s", argv[1]);
1762 return 0;
1763 }
1764
1765 wrinfo.size = iswap32(curinode->dp2.di_extsize);
1766 wrinfo.written_size = 0;
1767 print_blks64(curinode->dp2.di_extb, UFS_NXADDR, &blkno, &wrinfo);
1768 return 0;
1769 }
1770