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;