mirror of
https://github.com/versity/scoutfs.git
synced 2026-06-08 12:42:35 +00:00
scoutfs: add setattr_more ioctl
Add an ioctl that can be used by userspace to restore a file to its offline state. To do that it needs to set inode fields that are otherwise not exposed and create an offline extent. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -313,4 +313,19 @@ static inline const struct scoutfs_item_count SIC_FALLOCATE_ONE(void)
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* ioc_setattr_more can dirty the inode and add a single offline extent.
|
||||
*/
|
||||
static inline const struct scoutfs_item_count SIC_SETATTR_MORE(void)
|
||||
{
|
||||
struct scoutfs_item_count cnt = {0,};
|
||||
|
||||
__count_dirty_inode(&cnt);
|
||||
|
||||
cnt.items++;
|
||||
cnt.vals += sizeof(struct scoutfs_file_extent);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1239,6 +1239,49 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* A special case of initialzing a single large offline extent. This
|
||||
* chooses not to deal with any existing extents. It can only be used
|
||||
* on regular files with no data extents. It's used to restore a file
|
||||
* with an offline extent which can then trigger staging.
|
||||
*
|
||||
* The caller has taken care of locking and holding a transaction.
|
||||
*
|
||||
* This could be an fallocate mode.
|
||||
*/
|
||||
int scoutfs_data_init_offline_extent(struct inode *inode, u64 size,
|
||||
struct scoutfs_lock *lock)
|
||||
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct scoutfs_extent ext;
|
||||
u64 ino = scoutfs_ino(inode);
|
||||
u64 len;
|
||||
int ret;
|
||||
|
||||
if (!S_ISREG(inode->i_mode)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
scoutfs_extent_init(&ext, SCOUTFS_FILE_EXTENT_TYPE, ino, 0, 1, 0, 0);
|
||||
ret = scoutfs_extent_next(sb, data_extent_io, &ext, lock);
|
||||
if (ret != -ENOENT) {
|
||||
if (ret == 0)
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = (size + SCOUTFS_BLOCK_SIZE - 1) >> SCOUTFS_BLOCK_SHIFT;
|
||||
scoutfs_extent_init(&ext, SCOUTFS_FILE_EXTENT_TYPE, ino,
|
||||
0, len, 0, SEF_OFFLINE);
|
||||
ret = scoutfs_extent_add(sb, data_extent_io, &ext, lock);
|
||||
if (ret == 0)
|
||||
scoutfs_inode_add_onoff(inode, 0, len);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return all the file's extents whose blocks overlap with the caller's
|
||||
|
||||
@@ -45,6 +45,8 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
|
||||
int scoutfs_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len);
|
||||
long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len);
|
||||
int scoutfs_data_init_offline_extent(struct inode *inode, u64 size,
|
||||
struct scoutfs_lock *lock);
|
||||
|
||||
int scoutfs_data_wait_check(struct inode *inode, loff_t pos, loff_t len,
|
||||
u8 sef, u8 op, struct scoutfs_data_wait *ow,
|
||||
|
||||
@@ -540,6 +540,17 @@ void scoutfs_inode_inc_data_version(struct inode *inode)
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void scoutfs_inode_set_data_version(struct inode *inode, u64 data_version)
|
||||
{
|
||||
struct scoutfs_inode_info *si = SCOUTFS_I(inode);
|
||||
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&si->seqcount);
|
||||
si->data_version = data_version;
|
||||
write_seqcount_end(&si->seqcount);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void scoutfs_inode_add_onoff(struct inode *inode, s64 on, s64 off)
|
||||
{
|
||||
struct scoutfs_inode_info *si;
|
||||
|
||||
@@ -103,6 +103,7 @@ struct inode *scoutfs_new_inode(struct super_block *sb, struct inode *dir,
|
||||
void scoutfs_inode_set_meta_seq(struct inode *inode);
|
||||
void scoutfs_inode_set_data_seq(struct inode *inode);
|
||||
void scoutfs_inode_inc_data_version(struct inode *inode);
|
||||
void scoutfs_inode_set_data_version(struct inode *inode, u64 data_version);
|
||||
void scoutfs_inode_add_onoff(struct inode *inode, s64 on, s64 off);
|
||||
u64 scoutfs_inode_meta_seq(struct inode *inode);
|
||||
u64 scoutfs_inode_data_seq(struct inode *inode);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "client.h"
|
||||
#include "lock.h"
|
||||
#include "manifest.h"
|
||||
#include "trans.h"
|
||||
#include "scoutfs_trace.h"
|
||||
|
||||
/*
|
||||
@@ -591,6 +592,98 @@ static long scoutfs_ioc_data_waiting(struct file *file, unsigned long arg)
|
||||
return ret ?: total;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is used when restoring files, it lets the caller set all the
|
||||
* inode attributes which are otherwise unreachable. Changing the file
|
||||
* size can only be done for regular files with a data_version of 0.
|
||||
*/
|
||||
static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file->f_inode;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct scoutfs_ioctl_setattr_more __user *usm = (void __user *)arg;
|
||||
struct scoutfs_ioctl_setattr_more sm;
|
||||
struct scoutfs_lock *lock = NULL;
|
||||
LIST_HEAD(ind_locks);
|
||||
bool set_data_seq;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(file->f_mode & FMODE_WRITE)) {
|
||||
ret = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(&sm, usm, sizeof(sm))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((sm.i_size > 0 && sm.data_version == 0) ||
|
||||
((sm.flags & SCOUTFS_IOC_SETATTR_MORE_OFFLINE) && !sm.i_size) ||
|
||||
(sm.flags & SCOUTFS_IOC_SETATTR_MORE_UNKNOWN)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE,
|
||||
SCOUTFS_LKF_REFRESH_INODE, inode, &lock);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* can only change size/dv on untouched regular files */
|
||||
if ((sm.i_size != 0 || sm.data_version != 0) &&
|
||||
((!S_ISREG(inode->i_mode) ||
|
||||
scoutfs_inode_data_version(inode) != 0))) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* setting only so we don't see 0 data seq with nonzero data_version */
|
||||
set_data_seq = sm.data_version != 0 ? true : false;
|
||||
ret = scoutfs_inode_index_lock_hold(inode, &ind_locks, set_data_seq,
|
||||
SIC_SETATTR_MORE());
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
if (sm.flags & SCOUTFS_IOC_SETATTR_MORE_OFFLINE) {
|
||||
ret = scoutfs_data_init_offline_extent(inode, sm.i_size, lock);
|
||||
if (ret)
|
||||
goto release;
|
||||
}
|
||||
|
||||
if (sm.data_version)
|
||||
scoutfs_inode_set_data_version(inode, sm.data_version);
|
||||
if (sm.i_size)
|
||||
i_size_write(inode, sm.i_size);
|
||||
inode->i_ctime.tv_sec = le64_to_cpu(sm.ctime.sec);
|
||||
inode->i_ctime.tv_nsec = le32_to_cpu(sm.ctime.nsec);
|
||||
|
||||
scoutfs_update_inode_item(inode, lock, &ind_locks);
|
||||
ret = 0;
|
||||
|
||||
release:
|
||||
scoutfs_release_trans(sb);
|
||||
unlock:
|
||||
scoutfs_inode_index_unlock(sb, &ind_locks);
|
||||
scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
mnt_drop_write_file(file);
|
||||
out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
@@ -608,6 +701,8 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return scoutfs_ioc_item_cache_keys(file, arg);
|
||||
case SCOUTFS_IOC_DATA_WAITING:
|
||||
return scoutfs_ioc_data_waiting(file, arg);
|
||||
case SCOUTFS_IOC_SETATTR_MORE:
|
||||
return scoutfs_ioc_setattr_more(file, arg);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
|
||||
@@ -253,4 +253,22 @@ struct scoutfs_ioctl_data_waiting {
|
||||
#define SCOUTFS_IOC_DATA_WAITING _IOW(SCOUTFS_IOCTL_MAGIC, 9, \
|
||||
struct scoutfs_ioctl_data_waiting)
|
||||
|
||||
/*
|
||||
* If i_size is set then data_version must be non-zero. If the offline
|
||||
* flag is set then i_size must be set and a offline extent will be
|
||||
* created from offset 0 to i_size.
|
||||
*/
|
||||
struct scoutfs_ioctl_setattr_more {
|
||||
__u64 data_version;
|
||||
__u64 i_size;
|
||||
__u64 flags;
|
||||
struct scoutfs_timespec ctime;
|
||||
} __packed;
|
||||
|
||||
#define SCOUTFS_IOC_SETATTR_MORE_OFFLINE (1 << 0)
|
||||
#define SCOUTFS_IOC_SETATTR_MORE_UNKNOWN (U8_MAX << 1)
|
||||
|
||||
#define SCOUTFS_IOC_SETATTR_MORE _IOW(SCOUTFS_IOCTL_MAGIC, 10, \
|
||||
struct scoutfs_ioctl_setattr_more)
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user