mirror of
https://github.com/versity/scoutfs.git
synced 2026-04-30 18:05:43 +00:00
Compare commits
3 Commits
clk/srch-b
...
auke/clk_s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e2c1e83be | ||
|
|
8c4e9bfa3e | ||
|
|
91638191de |
@@ -402,25 +402,45 @@ before destroying an old empty data device.
|
||||
.PD
|
||||
|
||||
.TP
|
||||
.BI "print {-S|--skip-likely-huge} META-DEVICE"
|
||||
.BI "print {-a|--allocs} {-i|--items ITEMS} {-r|--roots ROOTS} {-S|--skip-likely-huge} {-V|--xattr-values} META-DEVICE"
|
||||
.sp
|
||||
Prints out all of the metadata in the file system. This makes no effort
|
||||
Prints out some or all of the metadata in the file system. This makes no effort
|
||||
to ensure that the structures are consistent as they're traversed and
|
||||
can present structures that seem corrupt as they change as they're
|
||||
output.
|
||||
.sp
|
||||
Structures that are related to the number of mounts and are maintained at a
|
||||
relatively reasonable size are always printed. These include per-mount log
|
||||
trees, srch files, allocators, and the metadata allocators used by server
|
||||
commits. Other btrees and their items can be selected as desired.
|
||||
.RS 1.0i
|
||||
.PD 0
|
||||
.TP
|
||||
.sp
|
||||
.TP
|
||||
.B "-a, --allocs"
|
||||
Print the metadata and data allocators. Enabled by default.
|
||||
.TP
|
||||
.B "-r, --roots ROOTS"
|
||||
This option can be used to select which btrees are traversed. It is a comma-separated list containing one or more of the following btree roots: logs, srch, fs. Default is all roots.
|
||||
.TP
|
||||
.B "-i, --items ITEMS"
|
||||
This option can be used to choose which btree items are printed from the
|
||||
selected btree roots. It is a comma-separated list containing one or
|
||||
more of the following items: inode, xattr, dirent, symlink, backref, extent,
|
||||
totl, indx, inoindex, orphan, quota.
|
||||
Default is all items.
|
||||
.TP
|
||||
.B "-V, --xattr-values"
|
||||
Print xattr values alongside the xattr item. Non-printable bytes are
|
||||
rendered as '.'. A trailing '...' indicates the value continues in
|
||||
additional item parts that aren't shown.
|
||||
.TP
|
||||
.B "-S, --skip-likely-huge"
|
||||
Skip printing structures that are likely to be very large. The
|
||||
structures that are skipped tend to be global and whose size tends to be
|
||||
related to the size of the volume. Examples of skipped structures include
|
||||
the global fs items, srch files, and metadata and data
|
||||
allocators. Similar structures that are not skipped are related to the
|
||||
number of mounts and are maintained at a relatively reasonable size.
|
||||
These include per-mount log trees, srch files, allocators, and the
|
||||
metadata allocators used by server commits.
|
||||
allocators.
|
||||
.sp
|
||||
Skipping the larger structures limits the print output to a relatively
|
||||
constant size rather than being a large multiple of the used metadata
|
||||
|
||||
@@ -29,6 +29,54 @@
|
||||
#include "leaf_item_hash.h"
|
||||
#include "dev.h"
|
||||
|
||||
struct print_args {
|
||||
char *meta_device;
|
||||
bool skip_likely_huge;
|
||||
bool roots_requested;
|
||||
bool items_requested;
|
||||
bool allocs_requested;
|
||||
bool walk_allocs;
|
||||
bool walk_logs_root;
|
||||
bool walk_fs_root;
|
||||
bool walk_srch_root;
|
||||
bool print_inodes;
|
||||
bool print_xattrs;
|
||||
bool print_dirents;
|
||||
bool print_symlinks;
|
||||
bool print_backrefs;
|
||||
bool print_extents;
|
||||
bool print_totl;
|
||||
bool print_indx;
|
||||
bool print_inode_index;
|
||||
bool print_orphan;
|
||||
bool print_quota;
|
||||
bool print_xattr_values;
|
||||
};
|
||||
|
||||
static struct print_args print_args = {
|
||||
.meta_device = NULL,
|
||||
.skip_likely_huge = false,
|
||||
.roots_requested = false,
|
||||
.items_requested = false,
|
||||
.allocs_requested = false,
|
||||
.walk_allocs = true,
|
||||
.walk_logs_root = true,
|
||||
.walk_fs_root = true,
|
||||
.walk_srch_root = true,
|
||||
.print_inodes = true,
|
||||
.print_xattrs = true,
|
||||
.print_dirents = true,
|
||||
.print_symlinks = true,
|
||||
.print_backrefs = true,
|
||||
.print_extents = true,
|
||||
.print_totl = true,
|
||||
.print_indx = true,
|
||||
.print_inode_index = true,
|
||||
.print_orphan = true,
|
||||
.print_quota = true,
|
||||
.print_xattr_values = false
|
||||
};
|
||||
|
||||
static void print_block_header(struct scoutfs_block_header *hdr, int size)
|
||||
{
|
||||
u32 crc = crc_block(hdr, size);
|
||||
@@ -135,15 +183,42 @@ static u8 *global_printable_name(u8 *name, int name_len)
|
||||
static void print_xattr(struct scoutfs_key *key, void *val, int val_len)
|
||||
{
|
||||
struct scoutfs_xattr *xat = val;
|
||||
unsigned int full_val_len;
|
||||
int avail;
|
||||
int show;
|
||||
int i;
|
||||
|
||||
printf(" xattr: ino %llu name_hash %08x id %llu part %u\n",
|
||||
le64_to_cpu(key->skx_ino), (u32)le64_to_cpu(key->skx_name_hash),
|
||||
le64_to_cpu(key->skx_id), key->skx_part);
|
||||
|
||||
if (key->skx_part == 0)
|
||||
printf(" name_len %u val_len %u name %s\n",
|
||||
xat->name_len, le16_to_cpu(xat->val_len),
|
||||
global_printable_name(xat->name, xat->name_len));
|
||||
if (key->skx_part != 0)
|
||||
return;
|
||||
|
||||
full_val_len = le16_to_cpu(xat->val_len);
|
||||
printf(" name_len %u val_len %u name %s",
|
||||
xat->name_len, full_val_len,
|
||||
global_printable_name(xat->name, xat->name_len));
|
||||
|
||||
if (!print_args.print_xattr_values) {
|
||||
putchar('\n');
|
||||
return;
|
||||
}
|
||||
|
||||
avail = val_len - (int)sizeof(*xat) - xat->name_len;
|
||||
if (avail < 0)
|
||||
avail = 0;
|
||||
show = avail < (int)full_val_len ? avail : (int)full_val_len;
|
||||
|
||||
printf(" value ");
|
||||
for (i = 0; i < show; i++) {
|
||||
u8 c = xat->name[xat->name_len + i];
|
||||
|
||||
putchar(isprint(c) ? c : '.');
|
||||
}
|
||||
if (show < (int)full_val_len)
|
||||
printf("...");
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static void print_dirent(struct scoutfs_key *key, void *val, int val_len)
|
||||
@@ -195,36 +270,72 @@ static void print_inode_index(struct scoutfs_key *key, void *val, int val_len)
|
||||
|
||||
typedef void (*print_func_t)(struct scoutfs_key *key, void *val, int val_len);
|
||||
|
||||
static print_func_t find_printer(u8 zone, u8 type)
|
||||
static print_func_t find_printer(u8 zone, u8 type, bool *suppress)
|
||||
{
|
||||
if (zone == SCOUTFS_INODE_INDEX_ZONE &&
|
||||
type >= SCOUTFS_INODE_INDEX_META_SEQ_TYPE &&
|
||||
type <= SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE)
|
||||
type <= SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE) {
|
||||
if (!print_args.print_inode_index)
|
||||
*suppress = true;
|
||||
return print_inode_index;
|
||||
|
||||
if (zone == SCOUTFS_ORPHAN_ZONE) {
|
||||
if (type == SCOUTFS_ORPHAN_TYPE)
|
||||
return print_orphan;
|
||||
}
|
||||
|
||||
if (zone == SCOUTFS_QUOTA_ZONE)
|
||||
if (zone == SCOUTFS_ORPHAN_ZONE) {
|
||||
if (type == SCOUTFS_ORPHAN_TYPE) {
|
||||
if (!print_args.print_orphan)
|
||||
*suppress = true;
|
||||
return print_orphan;
|
||||
}
|
||||
}
|
||||
|
||||
if (zone == SCOUTFS_QUOTA_ZONE) {
|
||||
if (!print_args.print_quota)
|
||||
*suppress = true;
|
||||
return print_quota;
|
||||
}
|
||||
|
||||
if (zone == SCOUTFS_XATTR_TOTL_ZONE)
|
||||
if (zone == SCOUTFS_XATTR_TOTL_ZONE) {
|
||||
if (!print_args.print_totl)
|
||||
*suppress = true;
|
||||
return print_xattr_totl;
|
||||
}
|
||||
|
||||
if (zone == SCOUTFS_XATTR_INDX_ZONE)
|
||||
if (zone == SCOUTFS_XATTR_INDX_ZONE) {
|
||||
if (!print_args.print_indx)
|
||||
*suppress = true;
|
||||
return print_xattr_indx;
|
||||
}
|
||||
|
||||
if (zone == SCOUTFS_FS_ZONE) {
|
||||
switch(type) {
|
||||
case SCOUTFS_INODE_TYPE: return print_inode;
|
||||
case SCOUTFS_XATTR_TYPE: return print_xattr;
|
||||
case SCOUTFS_DIRENT_TYPE: return print_dirent;
|
||||
case SCOUTFS_READDIR_TYPE: return print_dirent;
|
||||
case SCOUTFS_SYMLINK_TYPE: return print_symlink;
|
||||
case SCOUTFS_LINK_BACKREF_TYPE: return print_dirent;
|
||||
case SCOUTFS_DATA_EXTENT_TYPE: return print_data_extent;
|
||||
case SCOUTFS_INODE_TYPE:
|
||||
if (!print_args.print_inodes)
|
||||
*suppress = true;
|
||||
return print_inode;
|
||||
case SCOUTFS_XATTR_TYPE:
|
||||
if (!print_args.print_xattrs)
|
||||
*suppress = true;
|
||||
return print_xattr;
|
||||
case SCOUTFS_DIRENT_TYPE:
|
||||
if (!print_args.print_dirents)
|
||||
*suppress = true;
|
||||
return print_dirent;
|
||||
case SCOUTFS_READDIR_TYPE:
|
||||
if (!print_args.print_dirents)
|
||||
*suppress = true;
|
||||
return print_dirent;
|
||||
case SCOUTFS_SYMLINK_TYPE:
|
||||
if (!print_args.print_symlinks)
|
||||
*suppress = true;
|
||||
return print_symlink;
|
||||
case SCOUTFS_LINK_BACKREF_TYPE:
|
||||
if (!print_args.print_backrefs)
|
||||
*suppress = true;
|
||||
return print_dirent;
|
||||
case SCOUTFS_DATA_EXTENT_TYPE:
|
||||
if (!print_args.print_extents)
|
||||
*suppress = true;
|
||||
return print_data_extent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,12 +355,16 @@ static int print_fs_item(struct scoutfs_key *key, u64 seq, u8 flags, void *val,
|
||||
|
||||
/* only items in leaf blocks have values */
|
||||
if (val != NULL && !(flags & SCOUTFS_ITEM_FLAG_DELETION)) {
|
||||
printer = find_printer(key->sk_zone, key->sk_type);
|
||||
if (printer)
|
||||
printer(key, val, val_len);
|
||||
else
|
||||
bool suppress = false;
|
||||
|
||||
printer = find_printer(key->sk_zone, key->sk_type, &suppress);
|
||||
if (printer) {
|
||||
if (!suppress)
|
||||
printer(key, val, val_len);
|
||||
} else {
|
||||
printf(" (unknown zone %u type %u)\n",
|
||||
key->sk_zone, key->sk_type);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1037,12 +1152,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno)
|
||||
}
|
||||
}
|
||||
|
||||
struct print_args {
|
||||
char *meta_device;
|
||||
bool skip_likely_huge;
|
||||
};
|
||||
|
||||
static int print_volume(int fd, struct print_args *args)
|
||||
static int print_volume(int fd)
|
||||
{
|
||||
struct scoutfs_super_block *super = NULL;
|
||||
struct print_recursion_args pa;
|
||||
@@ -1092,7 +1202,7 @@ static int print_volume(int fd, struct print_args *args)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
if (!args->skip_likely_huge) {
|
||||
if (print_args.walk_allocs) {
|
||||
for (i = 0; i < array_size(super->meta_alloc); i++) {
|
||||
snprintf(str, sizeof(str), "meta_alloc[%u]", i);
|
||||
err = print_btree(fd, super, str, &super->meta_alloc[i].root,
|
||||
@@ -1119,18 +1229,21 @@ static int print_volume(int fd, struct print_args *args)
|
||||
|
||||
pa.super = super;
|
||||
pa.fd = fd;
|
||||
if (!args->skip_likely_huge) {
|
||||
if (print_args.walk_srch_root) {
|
||||
err = print_btree_leaf_items(fd, super, &super->srch_root.ref,
|
||||
print_srch_root_files, &pa);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
}
|
||||
err = print_btree_leaf_items(fd, super, &super->logs_root.ref,
|
||||
print_log_trees_roots, &pa);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
|
||||
if (!args->skip_likely_huge) {
|
||||
if (print_args.walk_logs_root) {
|
||||
err = print_btree_leaf_items(fd, super, &super->logs_root.ref,
|
||||
print_log_trees_roots, &pa);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
if (print_args.walk_fs_root) {
|
||||
err = print_btree(fd, super, "fs_root", &super->fs_root,
|
||||
print_fs_item, NULL);
|
||||
if (err && !ret)
|
||||
@@ -1143,16 +1256,16 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_print(struct print_args *args)
|
||||
static int do_print(void)
|
||||
{
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
fd = open(args->meta_device, O_RDONLY);
|
||||
fd = open(print_args.meta_device, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
||||
args->meta_device, strerror(errno), errno);
|
||||
print_args.meta_device, strerror(errno), errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1160,30 +1273,203 @@ static int do_print(struct print_args *args)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = print_volume(fd, args);
|
||||
ret = print_volume(fd);
|
||||
out:
|
||||
close(fd);
|
||||
return ret;
|
||||
};
|
||||
|
||||
enum {
|
||||
LOGS_OPT = 0,
|
||||
FS_OPT,
|
||||
SRCH_OPT
|
||||
};
|
||||
|
||||
static char *const root_tokens[] = {
|
||||
[LOGS_OPT] = "logs",
|
||||
[FS_OPT] = "fs",
|
||||
[SRCH_OPT] = "srch",
|
||||
NULL
|
||||
};
|
||||
|
||||
enum {
|
||||
INODE_OPT = 0,
|
||||
XATTR_OPT,
|
||||
DIRENT_OPT,
|
||||
SYMLINK_OPT,
|
||||
BACKREF_OPT,
|
||||
EXTENT_OPT,
|
||||
TOTL_OPT,
|
||||
INDX_OPT,
|
||||
INOINDEX_OPT,
|
||||
ORPHAN_OPT,
|
||||
QUOTA_OPT
|
||||
};
|
||||
|
||||
static char *const item_tokens[] = {
|
||||
[INODE_OPT] = "inode",
|
||||
[XATTR_OPT] = "xattr",
|
||||
[DIRENT_OPT] = "dirent",
|
||||
[SYMLINK_OPT] = "symlink",
|
||||
[BACKREF_OPT] = "backref",
|
||||
[EXTENT_OPT] = "extent",
|
||||
[TOTL_OPT] = "totl",
|
||||
[INDX_OPT] = "indx",
|
||||
[INOINDEX_OPT] = "inoindex",
|
||||
[ORPHAN_OPT] = "orphan",
|
||||
[QUOTA_OPT] = "quota",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void clear_items(void)
|
||||
{
|
||||
print_args.print_inodes = false;
|
||||
print_args.print_xattrs = false;
|
||||
print_args.print_dirents = false;
|
||||
print_args.print_symlinks = false;
|
||||
print_args.print_backrefs = false;
|
||||
print_args.print_extents = false;
|
||||
print_args.print_totl = false;
|
||||
print_args.print_indx = false;
|
||||
print_args.print_inode_index = false;
|
||||
print_args.print_orphan = false;
|
||||
print_args.print_quota = false;
|
||||
}
|
||||
|
||||
static void clear_roots(void)
|
||||
{
|
||||
print_args.walk_logs_root = false;
|
||||
print_args.walk_fs_root = false;
|
||||
print_args.walk_srch_root = false;
|
||||
}
|
||||
|
||||
static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct print_args *args = state->input;
|
||||
char *subopts;
|
||||
char *value;
|
||||
bool parse_err = false;
|
||||
|
||||
switch (key) {
|
||||
case 'S':
|
||||
args->skip_likely_huge = true;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
args->allocs_requested = true;
|
||||
args->walk_allocs = true;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
args->print_xattr_values = true;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
/* Specific items being requested- clear them all to start */
|
||||
if (!args->items_requested) {
|
||||
clear_items();
|
||||
if (!args->allocs_requested)
|
||||
args->walk_allocs = false;
|
||||
args->items_requested = true;
|
||||
}
|
||||
|
||||
subopts = arg;
|
||||
while (*subopts != '\0' && !parse_err) {
|
||||
switch (getsubopt(&subopts, item_tokens, &value)) {
|
||||
case INODE_OPT:
|
||||
args->print_inodes = true;
|
||||
break;
|
||||
case XATTR_OPT:
|
||||
args->print_xattrs = true;
|
||||
break;
|
||||
case DIRENT_OPT:
|
||||
args->print_dirents = true;
|
||||
break;
|
||||
case SYMLINK_OPT:
|
||||
args->print_symlinks = true;
|
||||
break;
|
||||
case BACKREF_OPT:
|
||||
args->print_backrefs = true;
|
||||
break;
|
||||
case EXTENT_OPT:
|
||||
args->print_extents = true;
|
||||
break;
|
||||
case TOTL_OPT:
|
||||
args->print_totl = true;
|
||||
break;
|
||||
case INDX_OPT:
|
||||
args->print_indx = true;
|
||||
break;
|
||||
case INOINDEX_OPT:
|
||||
args->print_inode_index = true;
|
||||
break;
|
||||
case ORPHAN_OPT:
|
||||
args->print_orphan = true;
|
||||
break;
|
||||
case QUOTA_OPT:
|
||||
args->print_quota = true;
|
||||
break;
|
||||
default:
|
||||
argp_usage(state);
|
||||
parse_err = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
/* Specific roots being requested- clear them all to start */
|
||||
if (!args->roots_requested) {
|
||||
clear_roots();
|
||||
if (!args->allocs_requested)
|
||||
args->walk_allocs = false;
|
||||
args->roots_requested = true;
|
||||
}
|
||||
|
||||
subopts = arg;
|
||||
while (*subopts != '\0' && !parse_err) {
|
||||
switch (getsubopt(&subopts, root_tokens, &value)) {
|
||||
case LOGS_OPT:
|
||||
args->walk_logs_root = true;
|
||||
break;
|
||||
case FS_OPT:
|
||||
args->walk_fs_root = true;
|
||||
break;
|
||||
case SRCH_OPT:
|
||||
args->walk_srch_root = true;
|
||||
break;
|
||||
default:
|
||||
argp_usage(state);
|
||||
parse_err = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ARGP_KEY_ARG:
|
||||
if (!args->meta_device)
|
||||
args->meta_device = strdup_or_error(state, arg);
|
||||
else
|
||||
argp_error(state, "more than one argument given");
|
||||
break;
|
||||
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->meta_device)
|
||||
argp_error(state, "no metadata device argument given");
|
||||
|
||||
/*
|
||||
* For backwards compatibility, translate -S. Should we warn if
|
||||
* this conflicts with other explicit options?
|
||||
*/
|
||||
if (args->skip_likely_huge) {
|
||||
if (!args->allocs_requested)
|
||||
args->walk_allocs = false;
|
||||
args->walk_fs_root = false;
|
||||
args->walk_srch_root = false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1192,7 +1478,11 @@ static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||
}
|
||||
|
||||
static struct argp_option options[] = {
|
||||
{ "skip-likely-huge", 'S', NULL, 0, "Skip large structures to minimize output size"},
|
||||
{ "allocs", 'a', NULL, 0, "Print metadata and data alloc lists" },
|
||||
{ "items", 'i', "ITEMS", 0, "Item(s) to print (inode, xattr, dirent, symlink, backref, extent, totl, indx, inoindex, orphan, quota)" },
|
||||
{ "roots", 'r', "ROOTS", 0, "Tree root(s) to walk (logs, srch, fs)" },
|
||||
{ "skip-likely-huge", 'S', NULL, 0, "Skip allocs, srch root and fs root to minimize output size" },
|
||||
{ "xattr-values", 'V', NULL, 0, "Print xattr values (non-printable bytes rendered as '.')" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -1205,17 +1495,15 @@ static struct argp argp = {
|
||||
|
||||
static int print_cmd(int argc, char **argv)
|
||||
{
|
||||
struct print_args print_args = {NULL};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&argp, argc, argv, 0, NULL, &print_args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_print(&print_args);
|
||||
return do_print();
|
||||
}
|
||||
|
||||
|
||||
static void __attribute__((constructor)) print_ctor(void)
|
||||
{
|
||||
cmd_register_argp("print", &argp, GROUP_DEBUG, print_cmd);
|
||||
|
||||
Reference in New Issue
Block a user