mirror of
https://github.com/versity/scoutfs.git
synced 2026-09-19 06:26:28 +00:00
scoutfs-utils: add support for srch
Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
#define SCOUTFS_BLOCK_MAGIC_BTREE 0xe597f96d
|
||||
#define SCOUTFS_BLOCK_MAGIC_BLOOM 0x31995604
|
||||
#define SCOUTFS_BLOCK_MAGIC_RADIX 0xebeb5e65
|
||||
#define SCOUTFS_BLOCK_MAGIC_SRCH_BLOCK 0x897e4a7d
|
||||
#define SCOUTFS_BLOCK_MAGIC_SRCH_PARENT 0xb23a2a05
|
||||
|
||||
/*
|
||||
* The super block, quorum block, and file data allocation granularity
|
||||
@@ -275,6 +277,93 @@ struct scoutfs_mounted_client_btree_val {
|
||||
|
||||
#define SCOUTFS_MOUNTED_CLIENT_VOTER (1 << 0)
|
||||
|
||||
/*
|
||||
* srch files are a contiguous run of blocks with compressed entries
|
||||
* described by a dense parent radix. The files can be stored in
|
||||
* log_tree items when the files contain unsorted entries written by
|
||||
* mounts during their transactions. Sorted files of increasing size
|
||||
* are kept in a btree off the super for searching and further
|
||||
* compacting.
|
||||
*/
|
||||
struct scoutfs_srch_entry {
|
||||
__le64 hash;
|
||||
__le64 ino;
|
||||
__le64 id;
|
||||
} __packed;
|
||||
|
||||
#define SCOUTFS_SRCH_ENTRY_MAX_BYTES (2 + (sizeof(__u64) * 3))
|
||||
|
||||
struct scoutfs_srch_ref {
|
||||
__le64 blkno;
|
||||
__le64 seq;
|
||||
} __packed;
|
||||
|
||||
struct scoutfs_srch_file {
|
||||
struct scoutfs_srch_entry first;
|
||||
struct scoutfs_srch_entry last;
|
||||
__le64 blocks;
|
||||
__le64 entries;
|
||||
struct scoutfs_srch_ref ref;
|
||||
__u8 height;
|
||||
} __packed;
|
||||
|
||||
struct scoutfs_srch_parent {
|
||||
struct scoutfs_block_header hdr;
|
||||
struct scoutfs_srch_ref refs[0];
|
||||
} __packed;
|
||||
|
||||
#define SCOUTFS_SRCH_PARENT_REFS \
|
||||
((SCOUTFS_BLOCK_LG_SIZE - \
|
||||
offsetof(struct scoutfs_srch_parent, refs)) / \
|
||||
sizeof(struct scoutfs_srch_ref))
|
||||
|
||||
struct scoutfs_srch_block {
|
||||
struct scoutfs_block_header hdr;
|
||||
struct scoutfs_srch_entry first;
|
||||
struct scoutfs_srch_entry last;
|
||||
struct scoutfs_srch_entry tail;
|
||||
__le32 entry_nr;
|
||||
__le32 entry_bytes;
|
||||
__u8 entries[0];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Decoding loads final small deltas with full __u64 loads. Rather than
|
||||
* check the size before each load we stop coding entries past the point
|
||||
* where a full size entry could overflow the block. A final entry can
|
||||
* start at this byte count and consume the rest of the block, though
|
||||
* its unlikely.
|
||||
*/
|
||||
#define SCOUTFS_SRCH_BLOCK_SAFE_BYTES \
|
||||
(SCOUTFS_BLOCK_LG_SIZE - sizeof(struct scoutfs_srch_block) - \
|
||||
SCOUTFS_SRCH_ENTRY_MAX_BYTES)
|
||||
|
||||
#define SCOUTFS_SRCH_LOG_BLOCK_LIMIT (1024 * 1024 / SCOUTFS_BLOCK_LG_SIZE)
|
||||
#define SCOUTFS_SRCH_COMPACT_ORDER 3
|
||||
#define SCOUTFS_SRCH_COMPACT_NR (1 << SCOUTFS_SRCH_COMPACT_ORDER)
|
||||
|
||||
struct scoutfs_srch_compact_input {
|
||||
struct scoutfs_radix_root meta_avail;
|
||||
struct scoutfs_radix_root meta_freed;
|
||||
__le64 id;
|
||||
__u8 nr;
|
||||
__u8 flags;
|
||||
struct scoutfs_srch_file sfl[SCOUTFS_SRCH_COMPACT_NR];
|
||||
} __packed;
|
||||
|
||||
struct scoutfs_srch_compact_result {
|
||||
struct scoutfs_radix_root meta_avail;
|
||||
struct scoutfs_radix_root meta_freed;
|
||||
__le64 id;
|
||||
__u8 flags;
|
||||
struct scoutfs_srch_file sfl;
|
||||
} __packed;
|
||||
|
||||
/* files are insorted logs */
|
||||
#define SCOUTFS_SRCH_COMPACT_FLAG_LOG (1 << 0)
|
||||
/* compaction failed, release inputs */
|
||||
#define SCOUTFS_SRCH_COMPACT_FLAG_ERROR (1 << 1)
|
||||
|
||||
/*
|
||||
* XXX I imagine we should rename these now that they've evolved to track
|
||||
* all the btrees that clients use during a transaction. It's not just
|
||||
@@ -287,6 +376,7 @@ struct scoutfs_log_trees {
|
||||
struct scoutfs_btree_ref bloom_ref;
|
||||
struct scoutfs_radix_root data_avail;
|
||||
struct scoutfs_radix_root data_freed;
|
||||
struct scoutfs_srch_file srch_file;
|
||||
__le64 rid;
|
||||
__le64 nr;
|
||||
} __packed;
|
||||
@@ -298,6 +388,7 @@ struct scoutfs_log_trees_val {
|
||||
struct scoutfs_btree_ref bloom_ref;
|
||||
struct scoutfs_radix_root data_avail;
|
||||
struct scoutfs_radix_root data_freed;
|
||||
struct scoutfs_srch_file srch_file;
|
||||
} __packed;
|
||||
|
||||
struct scoutfs_log_item_value {
|
||||
@@ -348,6 +439,7 @@ struct scoutfs_bloom_block {
|
||||
#define SCOUTFS_LOCK_CLIENTS_ZONE 7
|
||||
#define SCOUTFS_TRANS_SEQ_ZONE 8
|
||||
#define SCOUTFS_MOUNTED_CLIENT_ZONE 9
|
||||
#define SCOUTFS_SRCH_ZONE 10
|
||||
|
||||
/* inode index zone */
|
||||
#define SCOUTFS_INODE_INDEX_META_SEQ_TYPE 1
|
||||
@@ -372,6 +464,11 @@ struct scoutfs_bloom_block {
|
||||
/* lock zone, only ever found in lock ranges, never in persistent items */
|
||||
#define SCOUTFS_RENAME_TYPE 1
|
||||
|
||||
/* srch zone, only in server btrees */
|
||||
#define SCOUTFS_SRCH_LOG_TYPE 1
|
||||
#define SCOUTFS_SRCH_BLOCKS_TYPE 2
|
||||
#define SCOUTFS_SRCH_BUSY_TYPE 3
|
||||
|
||||
/*
|
||||
* The extents that map blocks in a fixed-size logical region of a file
|
||||
* are packed and stored in item values. The packed extents are
|
||||
@@ -496,6 +593,7 @@ struct scoutfs_super_block {
|
||||
struct scoutfs_btree_root lock_clients;
|
||||
struct scoutfs_btree_root trans_seqs;
|
||||
struct scoutfs_btree_root mounted_clients;
|
||||
struct scoutfs_btree_root srch_root;
|
||||
} __packed;
|
||||
|
||||
#define SCOUTFS_ROOT_INO 1
|
||||
@@ -688,6 +786,8 @@ enum {
|
||||
SCOUTFS_NET_CMD_STATFS,
|
||||
SCOUTFS_NET_CMD_LOCK,
|
||||
SCOUTFS_NET_CMD_LOCK_RECOVER,
|
||||
SCOUTFS_NET_CMD_SRCH_GET_COMPACT,
|
||||
SCOUTFS_NET_CMD_SRCH_COMMIT_COMPACT,
|
||||
SCOUTFS_NET_CMD_FAREWELL,
|
||||
SCOUTFS_NET_CMD_UNKNOWN,
|
||||
};
|
||||
@@ -734,6 +834,7 @@ struct scoutfs_net_statfs {
|
||||
struct scoutfs_net_roots {
|
||||
struct scoutfs_btree_root fs_root;
|
||||
struct scoutfs_btree_root logs_root;
|
||||
struct scoutfs_btree_root srch_root;
|
||||
} __packed;
|
||||
|
||||
struct scoutfs_net_lock {
|
||||
|
||||
+36
-13
@@ -296,34 +296,57 @@ struct scoutfs_ioctl_listxattr_hidden {
|
||||
|
||||
/*
|
||||
* Return the inode numbers of inodes which might contain the given
|
||||
* named xattr. The inode may not have a set xattr with that name, the
|
||||
* caller must check the returned inodes to see if they match.
|
||||
* xattr. The inode may not have a set xattr with that name, the caller
|
||||
* must check the returned inodes to see if they match.
|
||||
*
|
||||
* @next_ino: The next inode number that could be returned. Initialized
|
||||
* to 0 when first searching and set to one past the last inode number
|
||||
* returned to continue searching.
|
||||
* @name_ptr: The address of the name of the xattr to search for. It does
|
||||
* not need to be null terminated.
|
||||
* @inodes_ptr: The address of the array of uint64_t inode numbers in which
|
||||
* to store inode numbers that may contain the xattr. EFAULT may be returned
|
||||
* if this address is not naturally aligned.
|
||||
* @name_bytes: The number of non-null bytes found in the name at name_ptr.
|
||||
* @last_ino: The last inode number that could be returned. U64_MAX to
|
||||
* find all inodes.
|
||||
* @name_ptr: The address of the name of the xattr to search for. It is
|
||||
* not null terminated.
|
||||
* @inodes_ptr: The address of the array of uint64_t inode numbers in
|
||||
* which to store inode numbers that may contain the xattr. EFAULT may
|
||||
* be returned if this address is not naturally aligned.
|
||||
* @output_flags: Set as success is returned. If an error is returned
|
||||
* then this field is undefined and should not be read.
|
||||
* @nr_inodes: The number of elements in the array found at inodes_ptr.
|
||||
* @name_bytes: The number of non-null bytes found in the name at
|
||||
* name_ptr.
|
||||
*
|
||||
* This requires the CAP_SYS_ADMIN capability and will return -EPERM if
|
||||
* it's not granted.
|
||||
*
|
||||
* The number of inode numbers stored in the inodes_ptr array is
|
||||
* returned. If nr_inodes is 0 or last_ino is less than next_ino then 0
|
||||
* will be immediately returned.
|
||||
*
|
||||
* Partial progress can be returned if an error is hit or if nr_inodes
|
||||
* was larger than the internal limit on the number of inodes returned
|
||||
* in a search pass. The _END output flag is set if all the results
|
||||
* including last_ino were searched in this pass.
|
||||
*
|
||||
* It's valuable to provide a large inodes array so that all the results
|
||||
* can be found in one search pass and _END can be set. There are
|
||||
* significant constant costs for performing each search pass.
|
||||
*/
|
||||
struct scoutfs_ioctl_find_xattrs {
|
||||
struct scoutfs_ioctl_search_xattrs {
|
||||
__u64 next_ino;
|
||||
__u64 last_ino;
|
||||
__u64 name_ptr;
|
||||
__u64 inodes_ptr;
|
||||
__u64 output_flags;
|
||||
__u64 nr_inodes;
|
||||
__u16 name_bytes;
|
||||
__u16 nr_inodes;
|
||||
__u8 _pad[4];
|
||||
__u8 _pad[6];
|
||||
};
|
||||
|
||||
#define SCOUTFS_IOC_FIND_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 9, \
|
||||
struct scoutfs_ioctl_find_xattrs)
|
||||
/* set in output_flags if returned inodes reached last_ino */
|
||||
#define SCOUTFS_SEARCH_XATTRS_OFLAG_END (1ULL << 0)
|
||||
|
||||
#define SCOUTFS_IOC_SEARCH_XATTRS _IOR(SCOUTFS_IOCTL_MAGIC, 9, \
|
||||
struct scoutfs_ioctl_search_xattrs)
|
||||
|
||||
/*
|
||||
* Give the user information about the filesystem.
|
||||
|
||||
+163
-2
@@ -22,6 +22,7 @@
|
||||
#include "key.h"
|
||||
#include "radix.h"
|
||||
#include "avl.h"
|
||||
#include "srch.h"
|
||||
#include "leaf_item_hash.h"
|
||||
|
||||
static void *read_block(int fd, u64 blkno, int shift)
|
||||
@@ -324,6 +325,19 @@ static int print_logs_item(struct scoutfs_key *key, void *val,
|
||||
(root)->height, le64_to_cpu((root)->next_find_bit), \
|
||||
RADREF_A(&(root)->ref)
|
||||
|
||||
#define SRE_FMT "%016llx.%llu.%llu"
|
||||
#define SRE_A(sre) \
|
||||
le64_to_cpu((sre)->hash), le64_to_cpu((sre)->ino), \
|
||||
le64_to_cpu((sre)->id)
|
||||
|
||||
#define SRF_FMT \
|
||||
"f "SRE_FMT" l "SRE_FMT" blks %llu ents %llu hei %u blkno %llu seq %016llx"
|
||||
#define SRF_A(srf) \
|
||||
SRE_A(&(srf)->first), SRE_A(&(srf)->last), \
|
||||
le64_to_cpu((srf)->blocks), le64_to_cpu((srf)->entries), \
|
||||
(srf)->height, le64_to_cpu((srf)->ref.blkno), \
|
||||
le64_to_cpu((srf)->ref.seq)
|
||||
|
||||
/* same as fs item but with a small header in the value */
|
||||
static int print_log_trees_item(struct scoutfs_key *key, void *val,
|
||||
unsigned val_len, void *arg)
|
||||
@@ -340,7 +354,8 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val,
|
||||
" item_root: height %u blkno %llu seq %llu\n"
|
||||
" bloom_ref: blkno %llu seq %llu\n"
|
||||
" data_avail: "RADROOT_F"\n"
|
||||
" data_freed: "RADROOT_F"\n",
|
||||
" data_freed: "RADROOT_F"\n"
|
||||
" srch_file: "SRF_FMT"\n",
|
||||
RADROOT_A(<v->meta_avail),
|
||||
RADROOT_A(<v->meta_freed),
|
||||
ltv->item_root.height,
|
||||
@@ -349,7 +364,37 @@ static int print_log_trees_item(struct scoutfs_key *key, void *val,
|
||||
le64_to_cpu(ltv->bloom_ref.blkno),
|
||||
le64_to_cpu(ltv->bloom_ref.seq),
|
||||
RADROOT_A(<v->data_avail),
|
||||
RADROOT_A(<v->data_freed));
|
||||
RADROOT_A(<v->data_freed),
|
||||
SRF_A(<v->srch_file));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_srch_root_item(struct scoutfs_key *key, void *val,
|
||||
unsigned val_len, void *arg)
|
||||
{
|
||||
struct scoutfs_srch_file *sfl = val;
|
||||
struct scoutfs_srch_compact_input *scin = val;
|
||||
int i;
|
||||
|
||||
printf(" "SK_FMT"\n", SK_ARG(key));
|
||||
|
||||
/* only items in leaf blocks have values */
|
||||
if (val) {
|
||||
if (key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) {
|
||||
scin = val;
|
||||
printf(" compact: nr_in %u in_flags 0x%x\n",
|
||||
scin->nr, scin->flags);
|
||||
for (i = 0; i < scin->nr; i++) {
|
||||
sfl = &scin->sfl[i];
|
||||
printf(" [%u] "SRF_FMT"\n",
|
||||
i, SRF_A(sfl));
|
||||
}
|
||||
} else {
|
||||
sfl = val;
|
||||
printf(" "SRF_FMT"\n", SRF_A(sfl));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -595,6 +640,79 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int print_srch_block(int fd, struct scoutfs_srch_ref *ref, int level)
|
||||
{
|
||||
struct scoutfs_srch_parent *srp;
|
||||
struct scoutfs_srch_block *srb;
|
||||
struct scoutfs_srch_entry sre;
|
||||
struct scoutfs_srch_entry prev;
|
||||
u64 blkno;
|
||||
int pos;
|
||||
int ret;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
blkno = le64_to_cpu(ref->blkno);
|
||||
if (blkno == 0)
|
||||
return 0;
|
||||
|
||||
srp = read_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT);
|
||||
if (!srp) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
srb = (void *)srp;
|
||||
|
||||
printf("srch %sblock blkno %llu\n", level ? "parent " : "", blkno);
|
||||
print_block_header(&srp->hdr, SCOUTFS_BLOCK_LG_SIZE);
|
||||
|
||||
for (i = 0; level > 0 && i < SCOUTFS_SRCH_PARENT_REFS; i++) {
|
||||
if (le64_to_cpu(srp->refs[i].blkno) == 0)
|
||||
continue;
|
||||
printf(" [%u]: blkno %llu seq %llu\n",
|
||||
i, le64_to_cpu(srp->refs[i].blkno),
|
||||
le64_to_cpu(srp->refs[i].seq));
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; level > 0 && i < SCOUTFS_SRCH_PARENT_REFS; i++) {
|
||||
if (le64_to_cpu(srp->refs[i].blkno) == 0)
|
||||
continue;
|
||||
err = print_srch_block(fd, &srp->refs[i], level - 1);
|
||||
if (err < 0 && ret == 0)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
if (level > 0)
|
||||
goto out;
|
||||
|
||||
printf(" first "SRE_FMT" last "SRE_FMT" tail "SRE_FMT"\n"
|
||||
" entry_nr %u entry_bytes %u\n",
|
||||
SRE_A(&srb->first), SRE_A(&srb->last), SRE_A(&srb->tail),
|
||||
le32_to_cpu(srb->entry_nr), le32_to_cpu(srb->entry_bytes));
|
||||
|
||||
memset(&prev, 0, sizeof(prev));
|
||||
pos = 0;
|
||||
for (i = 0; level == 0 && i < le32_to_cpu(srb->entry_nr); i++) {
|
||||
if (pos > SCOUTFS_SRCH_BLOCK_SAFE_BYTES) {
|
||||
ret = EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = srch_decode_entry(srb->entries + pos, &sre, &prev);
|
||||
if (ret < 0)
|
||||
break;
|
||||
pos += ret;
|
||||
prev = sre;
|
||||
printf(" [%u]: (%u) "SRE_FMT"\n", i, ret, SRE_A(&sre));
|
||||
}
|
||||
|
||||
out:
|
||||
free(srp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct print_recursion_args {
|
||||
struct scoutfs_super_block *super;
|
||||
int fd;
|
||||
@@ -627,6 +745,10 @@ static int print_log_trees_roots(struct scoutfs_key *key, void *val,
|
||||
ltv->data_avail.height - 1);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
err = print_srch_block(pa->fd, <v->srch_file.ref,
|
||||
ltv->srch_file.height - 1);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
|
||||
err = print_btree(pa->fd, pa->super, "", <v->item_root,
|
||||
print_logs_item, NULL);
|
||||
@@ -636,6 +758,33 @@ static int print_log_trees_roots(struct scoutfs_key *key, void *val,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int print_srch_root_files(struct scoutfs_key *key, void *val,
|
||||
unsigned val_len, void *arg)
|
||||
{
|
||||
struct print_recursion_args *pa = arg;
|
||||
struct scoutfs_srch_compact_input *scin;
|
||||
struct scoutfs_srch_file *sfl;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (key->sk_type == SCOUTFS_SRCH_BUSY_TYPE) {
|
||||
scin = val;
|
||||
for (i = 0; i < scin->nr; i++) {
|
||||
sfl = &scin->sfl[i];
|
||||
ret = print_srch_block(pa->fd, &sfl->ref,
|
||||
sfl->height - 1);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
sfl = val;
|
||||
ret = print_srch_block(pa->fd, &sfl->ref, sfl->height - 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int print_btree_leaf_items(int fd, struct scoutfs_super_block *super,
|
||||
struct scoutfs_btree_ref *ref,
|
||||
print_item_func func, void *arg)
|
||||
@@ -785,6 +934,7 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno)
|
||||
" core_data_freed: "RADROOT_F"\n"
|
||||
" lock_clients root: height %u blkno %llu seq %llu\n"
|
||||
" mounted_clients root: height %u blkno %llu seq %llu\n"
|
||||
" srch_root root: height %u blkno %llu seq %llu\n"
|
||||
" trans_seqs root: height %u blkno %llu seq %llu\n"
|
||||
" fs_root btree root: height %u blkno %llu seq %llu\n",
|
||||
le64_to_cpu(super->next_ino),
|
||||
@@ -812,6 +962,9 @@ static void print_super_block(struct scoutfs_super_block *super, u64 blkno)
|
||||
super->mounted_clients.height,
|
||||
le64_to_cpu(super->mounted_clients.ref.blkno),
|
||||
le64_to_cpu(super->mounted_clients.ref.seq),
|
||||
super->srch_root.height,
|
||||
le64_to_cpu(super->srch_root.ref.blkno),
|
||||
le64_to_cpu(super->srch_root.ref.seq),
|
||||
super->trans_seqs.height,
|
||||
le64_to_cpu(super->trans_seqs.ref.blkno),
|
||||
le64_to_cpu(super->trans_seqs.ref.seq),
|
||||
@@ -869,6 +1022,10 @@ static int print_volume(int fd)
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
|
||||
err = print_btree(fd, super, "srch_root", &super->srch_root,
|
||||
print_srch_root_item, NULL);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
err = print_btree(fd, super, "logs_root", &super->logs_root,
|
||||
print_log_trees_item, NULL);
|
||||
if (err && !ret)
|
||||
@@ -876,6 +1033,10 @@ static int print_volume(int fd)
|
||||
|
||||
pa.super = super;
|
||||
pa.fd = fd;
|
||||
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)
|
||||
|
||||
@@ -21,18 +21,30 @@ static struct option long_ops[] = {
|
||||
{ NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
static int find_xattrs_cmd(int argc, char **argv)
|
||||
/*
|
||||
* There are significant constant costs to each search call, we
|
||||
* want to get the inodes in as few calls as possible.
|
||||
*/
|
||||
#define BATCH_SIZE 1000000
|
||||
|
||||
static int search_xattrs_cmd(int argc, char **argv)
|
||||
{
|
||||
struct scoutfs_ioctl_find_xattrs fx;
|
||||
struct scoutfs_ioctl_search_xattrs sx;
|
||||
char *path = NULL;
|
||||
char *name = NULL;
|
||||
u64 inos[32];
|
||||
u64 *inos = NULL;
|
||||
int fd = -1;
|
||||
int ret;
|
||||
int c;
|
||||
int i;
|
||||
|
||||
memset(&fx, 0, sizeof(fx));
|
||||
memset(&sx, 0, sizeof(sx));
|
||||
inos = malloc(BATCH_SIZE * sizeof(inos[0]));
|
||||
if (!inos) {
|
||||
fprintf(stderr, "inos mem alloc failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((c = getopt_long(argc, argv, "f:n:", long_ops, NULL)) != -1) {
|
||||
switch (c) {
|
||||
@@ -65,6 +77,12 @@ static int find_xattrs_cmd(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (name == NULL) {
|
||||
fprintf(stderr, "must specify -n xattr name to search for\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
@@ -73,20 +91,20 @@ static int find_xattrs_cmd(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fx.next_ino = 0;
|
||||
fx.name_ptr = (unsigned long)name;
|
||||
fx.inodes_ptr = (unsigned long)inos;
|
||||
fx.name_bytes = strlen(name);
|
||||
fx.nr_inodes = array_size(inos);
|
||||
sx.next_ino = 0;
|
||||
sx.last_ino = U64_MAX;
|
||||
sx.name_ptr = (unsigned long)name;
|
||||
sx.inodes_ptr = (unsigned long)inos;
|
||||
sx.name_bytes = strlen(name);
|
||||
sx.nr_inodes = BATCH_SIZE;
|
||||
|
||||
for (;;) {
|
||||
|
||||
ret = ioctl(fd, SCOUTFS_IOC_FIND_XATTRS, &fx);
|
||||
do {
|
||||
ret = ioctl(fd, SCOUTFS_IOC_SEARCH_XATTRS, &sx);
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "find_xattrs ioctl failed: "
|
||||
fprintf(stderr, "search_xattrs ioctl failed: "
|
||||
"%s (%d)\n", strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
@@ -94,10 +112,8 @@ static int find_xattrs_cmd(int argc, char **argv)
|
||||
for (i = 0; i < ret; i++)
|
||||
printf("%llu\n", inos[i]);
|
||||
|
||||
fx.next_ino = inos[ret - 1] + 1;
|
||||
if (fx.next_ino == 0)
|
||||
break;
|
||||
}
|
||||
sx.next_ino = inos[ret - 1] + 1;
|
||||
} while (!(sx.output_flags & SCOUTFS_SEARCH_XATTRS_OFLAG_END));
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
@@ -105,13 +121,14 @@ out:
|
||||
close(fd);
|
||||
free(path);
|
||||
free(name);
|
||||
free(inos);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static void __attribute__((constructor)) find_xattrs_ctor(void)
|
||||
static void __attribute__((constructor)) search_xattrs_ctor(void)
|
||||
{
|
||||
cmd_register("find-xattrs", "-n name -f <path>",
|
||||
cmd_register("search-xattrs", "-n name -f <path>",
|
||||
"print inode numbers of inodes which may have given xattr",
|
||||
find_xattrs_cmd);
|
||||
search_xattrs_cmd);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "sparse.h"
|
||||
#include "util.h"
|
||||
#include "format.h"
|
||||
#include "srch.h"
|
||||
|
||||
/* shifting by width is undefined :/ */
|
||||
#define BYTE_MASK(b) ((1ULL << (b << 3)) - 1)
|
||||
static u64 byte_masks[] = {
|
||||
0, BYTE_MASK(1), BYTE_MASK(2), BYTE_MASK(3),
|
||||
BYTE_MASK(4), BYTE_MASK(5), BYTE_MASK(6), BYTE_MASK(7), U64_MAX,
|
||||
};
|
||||
|
||||
static u64 decode_u64(void *buf, int bytes)
|
||||
{
|
||||
u64 val = get_unaligned_le64(buf) & byte_masks[bytes];
|
||||
|
||||
return (val >> 1) ^ (-(val & 1));
|
||||
}
|
||||
|
||||
int srch_decode_entry(void *buf, struct scoutfs_srch_entry *sre,
|
||||
struct scoutfs_srch_entry *prev)
|
||||
{
|
||||
u64 diffs[3];
|
||||
u16 lengths;
|
||||
int bytes;
|
||||
int tot;
|
||||
int i;
|
||||
|
||||
lengths = get_unaligned_le16(buf);
|
||||
tot = 2;
|
||||
|
||||
for (i = 0; i < array_size(diffs); i++) {
|
||||
bytes = min(8, lengths & 15);
|
||||
diffs[i] = decode_u64(buf + tot, bytes);
|
||||
tot += bytes;
|
||||
lengths >>= 4;
|
||||
}
|
||||
|
||||
sre->hash = cpu_to_le64(le64_to_cpu(prev->hash) + diffs[0]);
|
||||
sre->ino = cpu_to_le64(le64_to_cpu(prev->ino) + diffs[1]);
|
||||
sre->id = cpu_to_le64(le64_to_cpu(prev->id) + diffs[2]);
|
||||
|
||||
return tot;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef _SRCH_H_
|
||||
#define _SRCH_H_
|
||||
|
||||
int srch_decode_entry(void *buf, struct scoutfs_srch_entry *sre,
|
||||
struct scoutfs_srch_entry *prev);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user