mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-09 13:23:14 +00:00
scoutfs-utils: support single dirent format
Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -266,29 +266,13 @@ struct scoutfs_inode_key {
|
||||
__u8 type;
|
||||
} __packed;
|
||||
|
||||
/* value is struct scoutfs_dirent without the name */
|
||||
/* value is struct scoutfs_dirent with the name */
|
||||
struct scoutfs_dirent_key {
|
||||
__u8 zone;
|
||||
__be64 ino;
|
||||
__u8 type;
|
||||
__u8 name[0];
|
||||
} __packed;
|
||||
|
||||
/* value is struct scoutfs_dirent with the name */
|
||||
struct scoutfs_readdir_key {
|
||||
__u8 zone;
|
||||
__be64 ino;
|
||||
__u8 type;
|
||||
__be64 pos;
|
||||
} __packed;
|
||||
|
||||
/* value is empty */
|
||||
struct scoutfs_link_backref_key {
|
||||
__u8 zone;
|
||||
__be64 ino;
|
||||
__u8 type;
|
||||
__be64 dir_ino;
|
||||
__u8 name[0];
|
||||
__be64 major;
|
||||
__be64 minor;
|
||||
} __packed;
|
||||
|
||||
/* key is bytes of encoded block mapping */
|
||||
@@ -494,13 +478,17 @@ struct scoutfs_inode {
|
||||
#define SCOUTFS_SYMLINK_MAX_SIZE 4096
|
||||
|
||||
/*
|
||||
* Dirents are stored in items with an offset of the hash of their name.
|
||||
* Colliding names are packed into the value.
|
||||
* Dirents are stored in multiple places to isolate contention when
|
||||
* performing different operations: hashed by name for creation and
|
||||
* lookup, at incrementing positions for readdir and resolving inodes to
|
||||
* paths. Each entry has all the metadata needed to reference all the
|
||||
* items (so an entry cached by lookup can be used to unlink all the
|
||||
* items).
|
||||
*/
|
||||
struct scoutfs_dirent {
|
||||
__le64 ino;
|
||||
__le64 counter;
|
||||
__le64 readdir_pos;
|
||||
__le64 hash;
|
||||
__le64 pos;
|
||||
__u8 type;
|
||||
__u8 name[0];
|
||||
} __packed;
|
||||
@@ -526,9 +514,8 @@ enum {
|
||||
SCOUTFS_DT_WHT,
|
||||
};
|
||||
|
||||
/* ino_path can search for backref items with a null term */
|
||||
#define SCOUTFS_MAX_KEY_SIZE \
|
||||
offsetof(struct scoutfs_link_backref_key, name[SCOUTFS_NAME_LEN + 1])
|
||||
sizeof(struct scoutfs_dirent_key)
|
||||
|
||||
#define SCOUTFS_MAX_VAL_SIZE SCOUTFS_BLOCK_MAPPING_MAX_BYTES
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
static int ino_path_cmd(int argc, char **argv)
|
||||
{
|
||||
struct scoutfs_ioctl_ino_path args;
|
||||
struct scoutfs_ioctl_ino_path_result *res;
|
||||
unsigned int result_bytes;
|
||||
char *endptr = NULL;
|
||||
char *path = NULL;
|
||||
char *curs = NULL;
|
||||
u64 ino;
|
||||
int ret;
|
||||
int fd;
|
||||
@@ -38,6 +38,7 @@ static int ino_path_cmd(int argc, char **argv)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
fd = open(argv[2], O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
@@ -46,31 +47,39 @@ static int ino_path_cmd(int argc, char **argv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
path = malloc(PATH_MAX);
|
||||
if (!path) {
|
||||
fprintf(stderr, "couldn't allocate %d byte buffer\n", PATH_MAX);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
curs = calloc(1, SCOUTFS_IOC_INO_PATH_CURSOR_BYTES);
|
||||
if (!curs) {
|
||||
fprintf(stderr, "couldn't allocate %ld byte cursor\n",
|
||||
SCOUTFS_IOC_INO_PATH_CURSOR_BYTES);
|
||||
result_bytes = offsetof(struct scoutfs_ioctl_ino_path_result,
|
||||
path[PATH_MAX]);
|
||||
res = malloc(result_bytes);
|
||||
if (!res) {
|
||||
fprintf(stderr, "couldn't allocate %u byte buffer\n",
|
||||
result_bytes);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
args.ino = ino;
|
||||
args.cursor_ptr = (intptr_t)curs;
|
||||
args.path_ptr = (intptr_t)path;
|
||||
args.cursor_bytes = SCOUTFS_IOC_INO_PATH_CURSOR_BYTES;
|
||||
args.path_bytes = PATH_MAX;
|
||||
do {
|
||||
args.dir_ino = 0;
|
||||
args.dir_pos = 0;
|
||||
args.result_ptr = (intptr_t)res;
|
||||
args.result_bytes = result_bytes;
|
||||
for (;;) {
|
||||
ret = ioctl(fd, SCOUTFS_IOC_INO_PATH, &args);
|
||||
if (ret > 0)
|
||||
printf("%s\n", path);
|
||||
} while (ret > 0);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
if (ret == -ENOENT)
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%.*s\n", res->path_bytes, res->path);
|
||||
|
||||
args.dir_ino = res->dir_ino;
|
||||
args.dir_pos = res->dir_pos;
|
||||
if (++args.dir_pos == 0) {
|
||||
if (++args.dir_ino == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
@@ -78,8 +87,7 @@ static int ino_path_cmd(int argc, char **argv)
|
||||
strerror(errno), errno);
|
||||
}
|
||||
out:
|
||||
free(path);
|
||||
free(curs);
|
||||
free(res);
|
||||
close(fd);
|
||||
return ret;
|
||||
};
|
||||
|
||||
@@ -64,24 +64,36 @@ enum {
|
||||
struct scoutfs_ioctl_walk_inodes)
|
||||
|
||||
/*
|
||||
* Fill the path buffer with the next path to the target inode. An
|
||||
* iteration cursor is stored in the cursor buffer which advances
|
||||
* through the paths to the inode at each call.
|
||||
* Fill the result buffer with the next absolute path to the target
|
||||
* inode searching from a given position in a parent directory.
|
||||
*
|
||||
* @ino: The target ino that we're finding paths to. Constant across
|
||||
* all the calls that make up an iteration over all the inode's paths.
|
||||
*
|
||||
* @cursor_ptr: A pointer to the buffer that will hold the iteration
|
||||
* cursor. It must be initialized to 0 before iterating. Each call
|
||||
* modifies it to skip past the result of that call.
|
||||
* @dir_ino: The inode number of the directory containing the entry to
|
||||
* our inode to search from. If this parent directory contains no more
|
||||
* entries to our inode then we'll search through other parent directory
|
||||
* inodes in inode order.
|
||||
*
|
||||
* @cusur_bytes: The length of the cursor buffer. Must be
|
||||
* SCOUTFS_IOC_INO_PATH_CURSOR_BYTES.
|
||||
* @dir_pos: The position in the dir_ino parent directory of the entry
|
||||
* to our inode to search from. If there is no entry at this position
|
||||
* then we'll search through other entry positions in increasing order.
|
||||
* If we exhaust the parent directory then we'll search through
|
||||
* additional parent directories in inode order.
|
||||
*
|
||||
* @path_ptr: The buffer to store each found path.
|
||||
* @result_ptr: A pointer to the buffer where the result struct and
|
||||
* absolute path will be stored.
|
||||
*
|
||||
* @path_bytes: The size of the buffer that will the found path
|
||||
* including null termination. (PATH_MAX is a solid choice.)
|
||||
* @result_bytes: The size of the buffer that will contain the result
|
||||
* struct and the null terminated absolute path name.
|
||||
*
|
||||
* To start iterating set the desired target inode, dir_ino to 0,
|
||||
* dir_pos to 0, and set result_ptr and _bytes to a sufficiently large
|
||||
* buffeer (sizeof(result) + PATH_MAX is a solid choice).
|
||||
*
|
||||
* After each returned result set the next search dir_ino and dir_pos to
|
||||
* the returned dir_ino and dir_pos. Then increment the search dir_pos,
|
||||
* and if it wrapped to 0, increment dir_ino.
|
||||
*
|
||||
* This only walks back through full hard links. None of the returned
|
||||
* paths will reflect symlinks to components in the path.
|
||||
@@ -90,28 +102,39 @@ enum {
|
||||
* returned paths to the inode. It requires CAP_DAC_READ_SEARCH which
|
||||
* bypasses permissions checking.
|
||||
*
|
||||
* ENAMETOOLONG is returned when the next path found from the cursor
|
||||
* doesn't fit in the path buffer.
|
||||
*
|
||||
* This call is not serialized with any modification (create, rename,
|
||||
* unlink) of the path components. It will return all the paths that
|
||||
* were stable both before and after the call. It may or may not return
|
||||
* paths which are created or unlinked during the call.
|
||||
*
|
||||
* The number of bytes in the path, including the null terminator, are
|
||||
* returned when a path is found. 0 is returned when there are no more
|
||||
* paths to the link to the inode from the cursor.
|
||||
* On success 0 is returned and result struct is filled with the next
|
||||
* absolute path. The path_bytes length of the path includes a null
|
||||
* terminating byte. dir_ino and dir_pos refer to the position of the
|
||||
* final component in its parent directory and can be advanced to search
|
||||
* for the next terminal entry whose path is then built by walking up
|
||||
* parent directories.
|
||||
*
|
||||
* ENOENT is returned when no paths are found.
|
||||
*
|
||||
* ENAMETOOLONG is returned when the result struct and path found
|
||||
* doesn't fit in the result buffer.
|
||||
*
|
||||
* Many other errnos indicate hard failure to find the next path.
|
||||
*/
|
||||
struct scoutfs_ioctl_ino_path {
|
||||
__u64 ino;
|
||||
__u64 cursor_ptr;
|
||||
__u64 path_ptr;
|
||||
__u16 cursor_bytes;
|
||||
__u16 path_bytes;
|
||||
__u64 dir_ino;
|
||||
__u64 dir_pos;
|
||||
__u64 result_ptr;
|
||||
__u16 result_bytes;
|
||||
} __packed;
|
||||
|
||||
#define SCOUTFS_IOC_INO_PATH_CURSOR_BYTES \
|
||||
(sizeof(__u64) + SCOUTFS_NAME_LEN + 1)
|
||||
struct scoutfs_ioctl_ino_path_result {
|
||||
__u64 dir_ino;
|
||||
__u64 dir_pos;
|
||||
__u16 path_bytes;
|
||||
__u8 path[0];
|
||||
} __packed;
|
||||
|
||||
/* Get a single path from the root to the given inode number */
|
||||
#define SCOUTFS_IOC_INO_PATH _IOW(SCOUTFS_IOCTL_MAGIC, 2, \
|
||||
|
||||
@@ -248,35 +248,17 @@ static int pr_xattr(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
static int pr_dirent(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_dirent_key *dkey = key->data;
|
||||
int len = (int)key->key_len - sizeof(struct scoutfs_dirent_key);
|
||||
char *which = dkey->type == SCOUTFS_DIRENT_TYPE ? "dnt" :
|
||||
dkey->type == SCOUTFS_READDIR_TYPE ? "rdr" :
|
||||
dkey->type == SCOUTFS_LINK_BACKREF_TYPE ? "lbr" :
|
||||
"unk";
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_dirent_key), key->key_len,
|
||||
"fs.%llu.dnt.%.*s",
|
||||
be64_to_cpu(dkey->ino), len, dkey->name);
|
||||
}
|
||||
|
||||
static int pr_readdir(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_readdir_key *rkey = key->data;
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_readdir_key), 0,
|
||||
"fs.%llu.rdr.%llu",
|
||||
be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos));
|
||||
}
|
||||
|
||||
static int pr_link_backref(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_link_backref_key *lkey = key->data;
|
||||
int len = (int)key->key_len - sizeof(*lkey);
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_link_backref_key),
|
||||
key->key_len,
|
||||
"fs.%llu.lbr.%llu.%.*s",
|
||||
be64_to_cpu(lkey->ino), be64_to_cpu(lkey->dir_ino),
|
||||
len, lkey->name);
|
||||
"fs.%llu.%s.%llu.%llu",
|
||||
be64_to_cpu(dkey->ino), which,
|
||||
be64_to_cpu(dkey->major),
|
||||
be64_to_cpu(dkey->minor));
|
||||
}
|
||||
|
||||
static int pr_symlink(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
@@ -311,8 +293,8 @@ const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = {
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = pr_inode,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = pr_xattr,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = pr_dirent,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_readdir,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_link_backref,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_dirent,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_dirent,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = pr_symlink,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_BLOCK_MAPPING_TYPE] = pr_block_mapping,
|
||||
};
|
||||
|
||||
@@ -141,40 +141,16 @@ static void print_dirent(void *key, int key_len, void *val, int val_len)
|
||||
{
|
||||
struct scoutfs_dirent_key *dkey = key;
|
||||
struct scoutfs_dirent *dent = val;
|
||||
unsigned int name_len = key_len - sizeof(*dkey);
|
||||
u8 *name = global_printable_name(dkey->name, name_len);
|
||||
|
||||
printf(" dirent: dir ino %llu type %u rdpos %llu targ ino %llu\n"
|
||||
" name %s\n",
|
||||
be64_to_cpu(dkey->ino), dent->type,
|
||||
le64_to_cpu(dent->readdir_pos), le64_to_cpu(dent->ino),
|
||||
name);
|
||||
}
|
||||
|
||||
static void print_readdir(void *key, int key_len, void *val, int val_len)
|
||||
{
|
||||
struct scoutfs_readdir_key *rkey = key;
|
||||
struct scoutfs_dirent *dent = val;
|
||||
unsigned int name_len = val_len - sizeof(*dent);
|
||||
u8 *name = global_printable_name(dent->name, name_len);
|
||||
|
||||
printf(" readdir: dir ino %llu pos %llu type %u targ ino %llu\n"
|
||||
printf(" dirent: dir %llu hash %016llx pos %llu type %u ino %llu\n"
|
||||
" name %s\n",
|
||||
be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos),
|
||||
dent->type, le64_to_cpu(dent->ino),
|
||||
be64_to_cpu(dkey->ino), le64_to_cpu(dent->hash),
|
||||
le64_to_cpu(dent->pos), dent->type, le64_to_cpu(dent->ino),
|
||||
name);
|
||||
}
|
||||
|
||||
static void print_link_backref(void *key, int key_len, void *val, int val_len)
|
||||
{
|
||||
struct scoutfs_link_backref_key *lbkey = key;
|
||||
unsigned int name_len = key_len - sizeof(*lbkey);
|
||||
u8 *name = global_printable_name(lbkey->name, name_len);
|
||||
|
||||
printf(" lbref: ino: %llu dir_ino %llu name %s\n",
|
||||
be64_to_cpu(lbkey->ino), be64_to_cpu(lbkey->dir_ino), name);
|
||||
}
|
||||
|
||||
static void print_symlink(void *key, int key_len, void *val, int val_len)
|
||||
{
|
||||
struct scoutfs_symlink_key *skey = key;
|
||||
@@ -250,9 +226,9 @@ static print_func_t find_printer(u8 zone, u8 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_readdir;
|
||||
case SCOUTFS_READDIR_TYPE: return print_dirent;
|
||||
case SCOUTFS_SYMLINK_TYPE: return print_symlink;
|
||||
case SCOUTFS_LINK_BACKREF_TYPE: return print_link_backref;
|
||||
case SCOUTFS_LINK_BACKREF_TYPE: return print_dirent;
|
||||
case SCOUTFS_BLOCK_MAPPING_TYPE:
|
||||
return print_block_mapping;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user