scoutfs-utils: support single dirent format

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2018-01-03 14:02:42 -08:00
committed by Mark Swan
parent 787555158a
commit 0770cc8c57
5 changed files with 104 additions and 128 deletions

View File

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

View File

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

View File

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

View File

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

View File

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