diff --git a/utils/src/format.h b/utils/src/format.h index ff3e4872..0509e647 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -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 diff --git a/utils/src/ino_path.c b/utils/src/ino_path.c index f23f3c7c..91cbd184 100644 --- a/utils/src/ino_path.c +++ b/utils/src/ino_path.c @@ -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; }; diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 33c94b95..721f1cde 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -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, \ diff --git a/utils/src/key.c b/utils/src/key.c index 7aa14ad4..a32f4442 100644 --- a/utils/src/key.c +++ b/utils/src/key.c @@ -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, }; diff --git a/utils/src/print.c b/utils/src/print.c index f5764949..a53963fa 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -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; }