Add indx xattr tag support to utils

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2024-01-29 13:29:55 -08:00
parent 38c6d66ffc
commit 1bc83e9e2d
4 changed files with 226 additions and 1 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -0,0 +1,186 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <argp.h>
#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);
}