From 1bc83e9e2d5d4b2dd4133ec3660db0fb62d83e50 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 29 Jan 2024 13:29:55 -0800 Subject: [PATCH] Add indx xattr tag support to utils Signed-off-by: Zach Brown --- utils/man/scoutfs.5 | 18 +++- utils/src/key.h | 9 ++ utils/src/print.c | 14 +++ utils/src/read_xattr_index.c | 186 +++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 utils/src/read_xattr_index.c diff --git a/utils/man/scoutfs.5 b/utils/man/scoutfs.5 index 4cc6489d..f57d8fe8 100644 --- a/utils/man/scoutfs.5 +++ b/utils/man/scoutfs.5 @@ -270,6 +270,21 @@ metadata that is bound to a specific volume and should not be transferred with the file by tools that read extended attributes, like .BR tar(1) . .TP +.B .indx. +Attributes with the .indx. tag dd the inode containing the attribute to +a filesystem-wide index. The name of the extended attribute must end +with strings representing two values separated by dots. The first value +is an unsigned 8bit value and the second is an unsigned 64bit value. +These attributes can only be modified with root privileges and the +attributes can not have a value. +.sp +The inodes in the index are stored in increasing sort order of the +values, with the first u8 value being most significant. Inodes can be +at many positions as tracked by many extended attributes, and their +position follows the creation, renaming, or deletion of the attributes. +The index can be read with the read-xattr-index command which uses the +underlying READ_XATTR_INDEX ioctl. +.TP .B .srch. Attributes with the .srch. tag are indexed so that they can be found by the @@ -413,7 +428,8 @@ Initial format version. .TP .B 2 Added retention mode by setting the retention attribute. Added the -project ID inode attribute. Added quota rules and enforcement. +project ID inode attribute. Added quota rules and enforcement. Added +the .indx. extended attribute tag. .RE .SH CORRUPTION DETECTION diff --git a/utils/src/key.h b/utils/src/key.h index 7e3ad20d..38b6672c 100644 --- a/utils/src/key.h +++ b/utils/src/key.h @@ -141,4 +141,13 @@ static inline void scoutfs_key_dec(struct scoutfs_key *key) key->sk_zone--; } +static inline void scoutfs_xattr_get_indx_key(struct scoutfs_key *key, u8 *major, u64 *minor, + u64 *ino, u64 *xid) +{ + *major = le64_to_cpu(key->_sk_first) >> 56; + *minor = (le64_to_cpu(key->_sk_first) << 8) | (le64_to_cpu(key->_sk_second) >> 56); + *ino = (le64_to_cpu(key->_sk_second) << 8) | (le64_to_cpu(key->_sk_third) >> 56); + *xid = (le64_to_cpu(key->_sk_third) << 8) | key->_sk_fourth; +} + #endif diff --git a/utils/src/print.c b/utils/src/print.c index 2975b05d..fb8d60d1 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -108,6 +108,17 @@ static void print_xattr_totl(struct scoutfs_key *key, void *val, int val_len) le64_to_cpu(tval->count)); } +static void print_xattr_indx(struct scoutfs_key *key, void *val, int val_len) +{ + u64 minor; + u64 ino; + u64 xid; + u8 major; + + scoutfs_xattr_get_indx_key(key, &major, &minor, &ino, &xid); + printf(" xattr indx: major %u minor %llu ino %llu xid %llu", major, minor, ino, xid); +} + static u8 *global_printable_name(u8 *name, int name_len) { static u8 name_buf[SCOUTFS_NAME_LEN + 1]; @@ -202,6 +213,9 @@ static print_func_t find_printer(u8 zone, u8 type) if (zone == SCOUTFS_XATTR_TOTL_ZONE) return print_xattr_totl; + if (zone == SCOUTFS_XATTR_INDX_ZONE) + return print_xattr_indx; + if (zone == SCOUTFS_FS_ZONE) { switch(type) { case SCOUTFS_INODE_TYPE: return print_inode; diff --git a/utils/src/read_xattr_index.c b/utils/src/read_xattr_index.c new file mode 100644 index 00000000..7f378558 --- /dev/null +++ b/utils/src/read_xattr_index.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "parse.h" +#include "util.h" +#include "format.h" +#include "ioctl.h" +#include "cmd.h" +#include "cmp.h" + +#define ENTF "%u.%llu.%llu" +#define ENTA(e) (e)->major, (e)->minor, (e)->ino + +struct xattr_args { + char *path; + char *first_entry; + char *last_entry; +}; + +static int compare_entries(struct scoutfs_ioctl_xattr_index_entry *a, + struct scoutfs_ioctl_xattr_index_entry *b) +{ + return scoutfs_cmp(a->major, b->major) ?: scoutfs_cmp(a->minor, b->minor) ?: + scoutfs_cmp(a->ino, b->ino); +} + +static int parse_entry(struct scoutfs_ioctl_xattr_index_entry *ent, char *str) +{ + int major; + int ret; + + ret = sscanf(str, "%i.%lli.%lli", &major, &ent->minor, &ent->ino); + if (ret != 3) { + fprintf(stderr, "bad index position entry argument '%s', it must be " + "in the form \"a.b.ino\" where each value can be prefixed by " + "'0' for octal or '0x' for hex\n", str); + return -EINVAL; + } + + if (major < 0 || major > UCHAR_MAX) { + fprintf(stderr, "initial major index position '%d' must be between 0 and 255, " + "inclusive.\n", major); + return -EINVAL; + } + + ent->major = major; + + return 0; +} + +#define NR_ENTRIES 1024 + +static int do_read_xattr_index(struct xattr_args *args) +{ + struct scoutfs_ioctl_read_xattr_index rxi; + struct scoutfs_ioctl_xattr_index_entry *ents; + struct scoutfs_ioctl_xattr_index_entry *ent; + int fd = -1; + int ret; + int i; + + ents = calloc(NR_ENTRIES, sizeof(struct scoutfs_ioctl_xattr_index_entry)); + if (!ents) { + fprintf(stderr, "xattr index entry allocation failed\n"); + ret = -ENOMEM; + goto out; + } + + fd = get_path(args->path, O_RDONLY); + if (fd < 0) + return fd; + + memset(&rxi, 0, sizeof(rxi)); + memset(&rxi.last, 0xff, sizeof(rxi.last)); + rxi.entries_ptr = (unsigned long)ents; + rxi.entries_nr = NR_ENTRIES; + + ret = 0; + if (args->first_entry) + ret = parse_entry(&rxi.first, args->first_entry); + if (args->last_entry) + ret = parse_entry(&rxi.last, args->last_entry); + if (ret < 0) + goto out; + + if (compare_entries(&rxi.first, &rxi.last) > 0) { + fprintf(stderr, "first index position "ENTF" must be less than last index position "ENTF"\n", + ENTA(&rxi.first), ENTA(&rxi.last)); + ret = -EINVAL; + goto out; + } + + for (;;) { + ret = ioctl(fd, SCOUTFS_IOC_READ_XATTR_INDEX, &rxi); + if (ret == 0) + break; + if (ret < 0) { + ret = -errno; + fprintf(stderr, "read_xattr_index ioctl failed: " + "%s (%d)\n", strerror(errno), errno); + goto out; + } + + for (i = 0; i < ret; i++) { + ent = &ents[i]; + printf("%u.%llu = %llu\n", + ent->major, ent->minor, ent->ino); + } + + rxi.first = *ent; + + if ((++rxi.first.ino == 0 && ++rxi.first.minor == 0 && ++rxi.first.major == 0) || + compare_entries(&rxi.first, &rxi.last) > 0) + break; + } + + ret = 0; +out: + if (fd >= 0) + close(fd); + free(ents); + + return ret; +}; + +static int parse_opt(int key, char *arg, struct argp_state *state) +{ + struct xattr_args *args = state->input; + + switch (key) { + case 'p': + args->path = strdup_or_error(state, arg); + break; + case ARGP_KEY_ARG: + if (!args->first_entry) + args->first_entry = strdup_or_error(state, arg); + else if (!args->last_entry) + args->last_entry = strdup_or_error(state, arg); + else + argp_error(state, "more than two entry arguments given"); + break; + default: + break; + } + + return 0; +} + +static struct argp_option options[] = { + { "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"}, + { NULL } +}; + +static struct argp argp = { + options, + parse_opt, + "FIRST-ENTRY LAST-ENTRY", + "Search and print inode numbers indexed by their .indx. xattrs" +}; + +static int read_xattr_index_cmd(int argc, char **argv) +{ + + struct xattr_args xattr_args = {NULL}; + int ret; + + ret = argp_parse(&argp, argc, argv, 0, NULL, &xattr_args); + if (ret) + return ret; + + return do_read_xattr_index(&xattr_args); +} + +static void __attribute__((constructor)) read_xattr_index_ctor(void) +{ + cmd_register_argp("read-xattr-index", &argp, GROUP_INFO, read_xattr_index_cmd); +}