From 43619a245d1f1473c6cf00746d90e4a65163fef7 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 11 Aug 2016 14:34:10 -0700 Subject: [PATCH] Add inode-paths via link backrefs Add the inode-paths command which uses the ioctl to display all the paths that lead to the given inode. We add support for printing the new link backref items and inode and dirent fields. Signed-off-by: Zach Brown --- utils/src/format.h | 21 ++++++++-- utils/src/ino_paths.c | 92 +++++++++++++++++++++++++++++++++++++++++++ utils/src/ioctl.h | 13 ++++++ utils/src/print.c | 19 +++++++-- 4 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 utils/src/ino_paths.c diff --git a/utils/src/format.h b/utils/src/format.h index fbd09ddd..a2137ca9 100644 --- a/utils/src/format.h +++ b/utils/src/format.h @@ -97,10 +97,11 @@ struct scoutfs_key { * isn't necessary. We could have an arbitrary sort order. So we don't * have to stress about cleverly allocating the types. */ -#define SCOUTFS_INODE_KEY 1 -#define SCOUTFS_XATTR_KEY 2 -#define SCOUTFS_DIRENT_KEY 3 -#define SCOUTFS_BMAP_KEY 4 +#define SCOUTFS_INODE_KEY 1 +#define SCOUTFS_XATTR_KEY 2 +#define SCOUTFS_DIRENT_KEY 3 +#define SCOUTFS_LINK_BACKREF_KEY 4 +#define SCOUTFS_BMAP_KEY 5 #define SCOUTFS_MAX_ITEM_LEN 512 @@ -173,6 +174,7 @@ struct scoutfs_timespec { struct scoutfs_inode { __le64 size; __le64 blocks; + __le64 link_counter; __le32 nlink; __le32 uid; __le32 gid; @@ -192,6 +194,7 @@ struct scoutfs_inode { */ struct scoutfs_dirent { __le64 ino; + __le64 counter; __u8 type; __u8 name[0]; } __packed; @@ -262,4 +265,14 @@ struct scoutfs_block_map { __le64 blkno[SCOUTFS_BLOCK_MAP_COUNT]; }; +/* + * link backrefs give us a way to find all the hard links that refer + * to a target inode. They're stored at an offset determined by an + * advancing counter in their inode. + */ +struct scoutfs_link_backref { + __le64 ino; + __le64 offset; +} __packed; + #endif diff --git a/utils/src/ino_paths.c b/utils/src/ino_paths.c new file mode 100644 index 00000000..17a087e5 --- /dev/null +++ b/utils/src/ino_paths.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "cmd.h" + +static int inode_paths_cmd(int argc, char **argv) +{ + struct scoutfs_ioctl_inode_paths args; + char *endptr; + void *ptr = NULL; + char *path; + u64 ino; + int len; + int ret; + int fd; + + if (argc != 2) { + fprintf(stderr, "must specify ino and path\n"); + return -EINVAL; + } + + ino = strtoull(argv[0], &endptr, 0); + if (*endptr != '\0' || + ((ino == LLONG_MIN || ino == LLONG_MAX) && errno == ERANGE)) { + fprintf(stderr, "error parsing inode number '%s'\n", + argv[0]); + return -EINVAL; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + argv[1], strerror(errno), errno); + return ret; + } + + len = 16 * PATH_MAX; + do { + free(ptr); + ptr = malloc(len); + if (!ptr) { + fprintf(stderr, "couldn't allocate %d byte buffer\n", + len); + ret = -EINVAL; + goto out; + } + + args.ino = ino; + args.buf_ptr = (intptr_t)ptr; + args.buf_len = len; + + ret = ioctl(fd, SCOUTFS_IOC_INODE_PATHS, &args); + if (ret < 0 && errno != EOVERFLOW) { + ret = -errno; + fprintf(stderr, "inodes_since ioctl failed: %s (%d)\n", + strerror(errno), errno); + goto out; + } + + len *= 2; + + } while (ret < 0 && errno == EOVERFLOW); + + path = ptr; + while (*path) { + printf("%s\n", path); + path += strlen(path) + 1; + } + +out: + free(ptr); + close(fd); + return ret; +}; + +static void __attribute__((constructor)) since_ctor(void) +{ + cmd_register("inode-paths", " ", + "print paths that refer to inode #", inode_paths_cmd); +} diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h index 009ef7d4..278d255f 100644 --- a/utils/src/ioctl.h +++ b/utils/src/ioctl.h @@ -24,4 +24,17 @@ struct scoutfs_ioctl_inodes_since { #define SCOUTFS_IOC_INODES_SINCE _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ struct scoutfs_ioctl_inodes_since) +struct scoutfs_ioctl_inode_paths { + __u64 ino; + __u64 buf_ptr; + __u32 buf_len; +} __packed; + +/* + * Fills the callers buffer with all the paths from the root to the + * target inode. + */ +#define SCOUTFS_IOC_INODE_PATHS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ + struct scoutfs_ioctl_inode_paths) + #endif diff --git a/utils/src/print.c b/utils/src/print.c index fca5837e..b6399ca2 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -58,12 +58,13 @@ static void print_block_header(struct scoutfs_block_header *hdr) static void print_inode(struct scoutfs_inode *inode) { - printf(" inode: size: %llu blocks: %llu nlink: %u\n" + printf(" inode: size: %llu blocks: %llu lctr: %llu nlink: %u\n" " uid: %u gid: %u mode: 0%o rdev: 0x%x\n" " salt: 0x%x\n" " atime: %llu.%08u ctime: %llu.%08u\n" " mtime: %llu.%08u\n", le64_to_cpu(inode->size), le64_to_cpu(inode->blocks), + le64_to_cpu(inode->link_counter), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev), le32_to_cpu(inode->salt), @@ -85,8 +86,16 @@ static void print_dirent(struct scoutfs_dirent *dent, unsigned int val_len) name[i] = isprint(dent->name[i]) ? dent->name[i] : '.'; name[i] = '\0'; - printf(" dirent: ino: %llu type: %u name: \"%.*s\"\n", - le64_to_cpu(dent->ino), dent->type, i, name); + printf(" dirent: ino: %llu ctr: %llu type: %u name: \"%.*s\"\n", + le64_to_cpu(dent->ino), le64_to_cpu(dent->counter), + dent->type, i, name); +} + +static void print_link_backref(struct scoutfs_link_backref *lref, + unsigned int val_len) +{ + printf(" lref: ino: %llu offset: %llu\n", + le64_to_cpu(lref->ino), le64_to_cpu(lref->offset)); } static void print_block_map(struct scoutfs_block_map *map) @@ -121,6 +130,10 @@ static void print_btree_val(struct scoutfs_btree_item *item, u8 level) case SCOUTFS_DIRENT_KEY: print_dirent((void *)item->val, le16_to_cpu(item->val_len)); break; + case SCOUTFS_LINK_BACKREF_KEY: + print_link_backref((void *)item->val, + le16_to_cpu(item->val_len)); + break; case SCOUTFS_BMAP_KEY: print_block_map((void *)item->val); break;