From 8c4e9bfa3ed4de7b01f5640e57472fa1b0e7a8ee Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 16 Apr 2026 11:01:47 -0700 Subject: [PATCH 1/2] Add print filters for remaining types. Chris already added print filters for most common types, but omitted totl, indx, orphan, quota, and inode_index items. This adds those as well, completing the set. Signed-off-by: Auke Kok --- utils/man/scoutfs.8 | 3 +- utils/src/print.c | 83 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/utils/man/scoutfs.8 b/utils/man/scoutfs.8 index 291b5b17e..9b1ba4e2e 100644 --- a/utils/man/scoutfs.8 +++ b/utils/man/scoutfs.8 @@ -426,7 +426,8 @@ This option can be used to select which btrees are traversed. It is a comma-sepa .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. +more of the following items: inode, xattr, dirent, symlink, backref, extent, +totl, indx, inoindex, orphan, quota. Default is all items. .TP .B "-S, --skip-likely-huge" diff --git a/utils/src/print.c b/utils/src/print.c index 085497574..d7de12db8 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -45,6 +45,11 @@ struct print_args { 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; }; static struct print_args print_args = { @@ -62,7 +67,12 @@ static struct print_args print_args = { .print_dirents = true, .print_symlinks = true, .print_backrefs = true, - .print_extents = true + .print_extents = true, + .print_totl = true, + .print_indx = true, + .print_inode_index = true, + .print_orphan = true, + .print_quota = true }; static void print_block_header(struct scoutfs_block_header *hdr, int size) @@ -235,22 +245,37 @@ 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) + if (type == SCOUTFS_ORPHAN_TYPE) { + if (!print_args.print_orphan) + *suppress = true; return print_orphan; + } } - if (zone == SCOUTFS_QUOTA_ZONE) + 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) { @@ -1244,16 +1269,26 @@ enum { DIRENT_OPT, SYMLINK_OPT, BACKREF_OPT, - EXTENT_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", + [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 }; @@ -1265,6 +1300,11 @@ static void clear_items(void) 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) @@ -1321,6 +1361,21 @@ static int parse_opt(int key, char *arg, struct argp_state *state) 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; @@ -1391,7 +1446,7 @@ static int parse_opt(int key, char *arg, struct argp_state *state) static struct argp_option options[] = { { "allocs", 'a', NULL, 0, "Print metadata and data alloc lists" }, - { "items", 'i', "ITEMS", 0, "Item(s) to print (inode, xattr, dirent, symlink, backref, extent)" }, + { "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" }, { NULL } From 4e2c1e83befdc4f92ef126a2ac85a85b1d6e2ea7 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 16 Apr 2026 11:26:29 -0700 Subject: [PATCH 2/2] Optionally print out xattr values We cannot validate that totl keys have the correct count/value without also extracting and printing the value for xattrs, which by default is omitted (a sane default). Add a default-disabled --xattr-values/-V flag to enable printing these out. Because xattr values can span multiple items, this only will print out the first one, and ellipsize it if it continues elsewhere. It is filtered through isprint() to avoid printing non-printable characters. Signed-off-by: Auke Kok --- utils/man/scoutfs.8 | 7 ++++++- utils/src/print.c | 44 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/utils/man/scoutfs.8 b/utils/man/scoutfs.8 index 9b1ba4e2e..2c37a72f7 100644 --- a/utils/man/scoutfs.8 +++ b/utils/man/scoutfs.8 @@ -402,7 +402,7 @@ before destroying an old empty data device. .PD .TP -.BI "print {-a|--allocs} {-i|--items ITEMS} {-r|--roots ROOTS} {-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 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 @@ -430,6 +430,11 @@ 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 diff --git a/utils/src/print.c b/utils/src/print.c index d7de12db8..3a2e311be 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -50,6 +50,7 @@ struct print_args { bool print_inode_index; bool print_orphan; bool print_quota; + bool print_xattr_values; }; static struct print_args print_args = { @@ -72,7 +73,8 @@ static struct print_args print_args = { .print_indx = true, .print_inode_index = true, .print_orphan = true, - .print_quota = true + .print_quota = true, + .print_xattr_values = false }; static void print_block_header(struct scoutfs_block_header *hdr, int size) @@ -181,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) @@ -1331,6 +1360,10 @@ static int parse_opt(int key, char *arg, struct argp_state *state) 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) { @@ -1449,6 +1482,7 @@ static struct argp_option options[] = { { "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 } };