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