From 7a96e03148d8dd382e56d993455eab9300949e52 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 21 Jan 2022 16:00:06 -0800 Subject: [PATCH] Add get_allocated_inos ioctl Add an ioctl that can give some indication of inodes that have inode items. We're exposing this for tests that verify the handling of open unlinked inodes. Signed-off-by: Zach Brown --- kmod/src/ioctl.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ kmod/src/ioctl.h | 39 +++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/kmod/src/ioctl.c b/kmod/src/ioctl.c index 014527ec..d30a5021 100644 --- a/kmod/src/ioctl.c +++ b/kmod/src/ioctl.c @@ -1320,6 +1320,84 @@ out: return ret ?: count; } +static long scoutfs_ioc_get_allocated_inos(struct file *file, unsigned long arg) +{ + struct super_block *sb = file_inode(file)->i_sb; + struct scoutfs_ioctl_get_allocated_inos __user *ugai = (void __user *)arg; + struct scoutfs_ioctl_get_allocated_inos gai; + struct scoutfs_lock *lock = NULL; + struct scoutfs_key key; + struct scoutfs_key end; + u64 __user *uinos; + u64 bytes; + u64 ino; + int nr; + int ret; + + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + goto out; + } + + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto out; + } + + if (copy_from_user(&gai, ugai, sizeof(gai))) { + ret = -EFAULT; + goto out; + } + + if ((gai.inos_ptr & (sizeof(__u64) - 1)) || (gai.inos_bytes < sizeof(__u64))) { + ret = -EINVAL; + goto out; + } + + scoutfs_inode_init_key(&key, gai.start_ino); + scoutfs_inode_init_key(&end, gai.start_ino | SCOUTFS_LOCK_INODE_GROUP_MASK); + uinos = (void __user *)gai.inos_ptr; + bytes = gai.inos_bytes; + nr = 0; + + ret = scoutfs_lock_ino(sb, SCOUTFS_LOCK_READ, 0, gai.start_ino, &lock); + if (ret < 0) + goto out; + + while (bytes >= sizeof(*uinos)) { + + ret = scoutfs_item_next(sb, &key, &end, NULL, 0, lock); + if (ret < 0) { + if (ret == -ENOENT) + ret = 0; + break; + } + + if (key.sk_zone != SCOUTFS_FS_ZONE) { + ret = 0; + break; + } + + /* all fs items are owned by allocated inodes, and _first is always ino */ + ino = le64_to_cpu(key._sk_first); + if (put_user(ino, uinos)) { + ret = -EFAULT; + break; + } + + uinos++; + bytes -= sizeof(*uinos); + if (++nr == INT_MAX) + break; + + scoutfs_inode_init_key(&key, ino + 1); + } + + scoutfs_unlock(sb, lock, SCOUTFS_LOCK_READ); +out: + return ret ?: nr; +} + long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -1353,6 +1431,8 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return scoutfs_ioc_resize_devices(file, arg); case SCOUTFS_IOC_READ_XATTR_TOTALS: return scoutfs_ioc_read_xattr_totals(file, arg); + case SCOUTFS_IOC_GET_ALLOCATED_INOS: + return scoutfs_ioc_get_allocated_inos(file, arg); } return -ENOTTY; diff --git a/kmod/src/ioctl.h b/kmod/src/ioctl.h index 24cd9a46..459ee7fb 100644 --- a/kmod/src/ioctl.h +++ b/kmod/src/ioctl.h @@ -520,4 +520,43 @@ struct scoutfs_ioctl_xattr_total { #define SCOUTFS_IOC_READ_XATTR_TOTALS \ _IOW(SCOUTFS_IOCTL_MAGIC, 15, struct scoutfs_ioctl_read_xattr_totals) +/* + * This fills the caller's inos array with inode numbers that are in use + * after the start ino, within an internal inode group. + * + * This only makes a promise about the state of the inode numbers within + * the first and last numbers returned by one call. At one time, all of + * those inodes were still allocated. They could have changed before + * the call returned. And any numbers outside of the first and last + * (or single) are undefined. + * + * This doesn't iterate over all allocated inodes, it only probes a + * single group that the start inode is within. This interface was + * first introduced to support tests that needed to find out about a + * specific inode, while having some other similarly niche uses. It is + * unsuitable for a consistent iteration over all the inode numbers in + * use. + * + * This test of inode items doesn't serialize with the inode lifetime + * mechanism. It only tells you the numbers of inodes that were once + * active in the system and haven't yet been fully deleted. The inode + * numbers returned could have been in the process of being deleted and + * were already unreachable even before the call started. + * + * @start_ino: the first inode number that could be returned + * @inos_ptr: pointer to an aligned array of 64bit inode numbers + * @inos_bytes: the number of bytes available in the inos_ptr array + * + * Returns errors or the count of inode numbers returned, quite possibly + * including 0. + */ +struct scoutfs_ioctl_get_allocated_inos { + __u64 start_ino; + __u64 inos_ptr; + __u64 inos_bytes; +}; + +#define SCOUTFS_IOC_GET_ALLOCATED_INOS \ + _IOW(SCOUTFS_IOCTL_MAGIC, 16, struct scoutfs_ioctl_get_allocated_inos) + #endif