mirror of
https://github.com/versity/scoutfs.git
synced 2026-06-08 12:42:35 +00:00
scoutfs: add listxattr_raw ioctl
Add an ioctl which can be used to iterate over the keys for all the xattrs on an inode. It is privileged, can see hidden inodes, and has an iteration cursor so that it can make its way through very large numbers of xattrs. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include "lock.h"
|
||||
#include "manifest.h"
|
||||
#include "trans.h"
|
||||
#include "xattr.h"
|
||||
#include "scoutfs_trace.h"
|
||||
|
||||
/*
|
||||
@@ -684,6 +685,68 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long scoutfs_ioc_listxattr_raw(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file->f_inode;
|
||||
struct scoutfs_ioctl_listxattr_raw __user *ulxr = (void __user *)arg;
|
||||
struct scoutfs_ioctl_listxattr_raw lxr;
|
||||
struct page *page = NULL;
|
||||
unsigned int bytes;
|
||||
int total = 0;
|
||||
int ret;
|
||||
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
ret = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(&lxr, ulxr, sizeof(lxr))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (lxr.buf_bytes) {
|
||||
bytes = min_t(int, lxr.buf_bytes, PAGE_SIZE);
|
||||
ret = scoutfs_list_xattrs(inode, page_address(page), bytes,
|
||||
&lxr.hash_pos, &lxr.id_pos,
|
||||
false, true);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
if (copy_to_user((void __user *)lxr.buf_ptr,
|
||||
page_address(page), ret)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
lxr.buf_ptr += ret;
|
||||
lxr.buf_bytes -= ret;
|
||||
total += ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
if (page)
|
||||
__free_page(page);
|
||||
|
||||
if (ret == 0 && (__put_user(lxr.hash_pos, &ulxr->hash_pos) ||
|
||||
__put_user(lxr.id_pos, &ulxr->id_pos)))
|
||||
ret = -EFAULT;
|
||||
|
||||
return ret ?: total;
|
||||
}
|
||||
|
||||
long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
@@ -703,6 +766,8 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return scoutfs_ioc_data_waiting(file, arg);
|
||||
case SCOUTFS_IOC_SETATTR_MORE:
|
||||
return scoutfs_ioc_setattr_more(file, arg);
|
||||
case SCOUTFS_IOC_LISTXATTR_RAW:
|
||||
return scoutfs_ioc_listxattr_raw(file, arg);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
|
||||
@@ -271,4 +271,14 @@ struct scoutfs_ioctl_setattr_more {
|
||||
#define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 10, \
|
||||
struct scoutfs_ioctl_setattr_more)
|
||||
|
||||
struct scoutfs_ioctl_listxattr_raw {
|
||||
__u64 id_pos;
|
||||
__u64 buf_ptr;
|
||||
__u32 buf_bytes;
|
||||
__u32 hash_pos;
|
||||
} __packed;
|
||||
|
||||
#define SCOUTFS_IOC_LISTXATTR_RAW _IOW(SCOUTFS_IOCTL_MAGIC, 11, \
|
||||
struct scoutfs_ioctl_listxattr_raw)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -533,9 +533,10 @@ int scoutfs_removexattr(struct dentry *dentry, const char *name)
|
||||
return scoutfs_xattr_set(dentry, name, NULL, 0, XATTR_REPLACE);
|
||||
}
|
||||
|
||||
ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
ssize_t scoutfs_list_xattrs(struct inode *inode, char *buffer,
|
||||
size_t size, __u32 *hash_pos, __u64 *id_pos,
|
||||
bool e_range, bool hidden)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct scoutfs_xattr *xat = NULL;
|
||||
@@ -543,11 +544,16 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
struct scoutfs_key key;
|
||||
struct prefix_tags tgs;
|
||||
unsigned int bytes;
|
||||
ssize_t total;
|
||||
u32 name_hash;
|
||||
u64 id;
|
||||
ssize_t total = 0;
|
||||
u32 name_hash = 0;
|
||||
u64 id = 0;
|
||||
int ret;
|
||||
|
||||
if (hash_pos)
|
||||
name_hash = *hash_pos;
|
||||
if (id_pos)
|
||||
id = *id_pos;
|
||||
|
||||
/* need a buffer large enough for all possible names */
|
||||
bytes = sizeof(struct scoutfs_xattr) + SCOUTFS_XATTR_MAX_NAME_LEN;
|
||||
xat = kmalloc(bytes, GFP_NOFS);
|
||||
@@ -562,10 +568,6 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
|
||||
down_read(&si->xattr_rwsem);
|
||||
|
||||
name_hash = 0;
|
||||
id = 0;
|
||||
total = 0;
|
||||
|
||||
for (;;) {
|
||||
ret = get_next_xattr(inode, &key, xat, bytes,
|
||||
NULL, 0, name_hash, id, lck);
|
||||
@@ -575,12 +577,14 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
break;
|
||||
}
|
||||
|
||||
if (parse_tags(xat->name, &tgs) != 0 || !tgs.hide) {
|
||||
total += xat->name_len + 1;
|
||||
if (hidden || parse_tags(xat->name, &tgs) != 0 || !tgs.hide) {
|
||||
|
||||
if (size) {
|
||||
if (total > size) {
|
||||
ret = -ERANGE;
|
||||
if ((total + xat->name_len + 1) > size) {
|
||||
if (e_range)
|
||||
ret = -ERANGE;
|
||||
else
|
||||
ret = total;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -588,6 +592,8 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
buffer += xat->name_len;
|
||||
*(buffer++) = '\0';
|
||||
}
|
||||
|
||||
total += xat->name_len + 1;
|
||||
}
|
||||
|
||||
name_hash = le64_to_cpu(key.skx_name_hash);
|
||||
@@ -599,9 +605,22 @@ ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
out:
|
||||
kfree(xat);
|
||||
|
||||
if (hash_pos)
|
||||
*hash_pos = name_hash;
|
||||
if (id_pos)
|
||||
*id_pos = id;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
return scoutfs_list_xattrs(inode, buffer, size,
|
||||
NULL, NULL, true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete all the xattr items associated with this inode. The inode is
|
||||
* dead so we don't need the xattr rwsem.
|
||||
|
||||
@@ -7,6 +7,9 @@ int scoutfs_setxattr(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags);
|
||||
int scoutfs_removexattr(struct dentry *dentry, const char *name);
|
||||
ssize_t scoutfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
|
||||
ssize_t scoutfs_list_xattrs(struct inode *inode, char *buffer,
|
||||
size_t size, __u32 *hash_pos, __u64 *id_pos,
|
||||
bool e_range, bool hidden);
|
||||
|
||||
int scoutfs_xattr_drop(struct super_block *sb, u64 ino,
|
||||
struct scoutfs_lock *lock);
|
||||
|
||||
Reference in New Issue
Block a user