Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs
Pull btrfs updates from Chris Mason: "This is a pretty big pull, and most of these changes have been floating in btrfs-next for a long time. Filipe's properties work is a cool building block for inheriting attributes like compression down on a per inode basis. Jeff Mahoney kicked in code to export filesystem info into sysfs. Otherwise, lots of performance improvements, cleanups and bug fixes. Looks like there are still a few other small pending incrementals, but I wanted to get the bulk of this in first" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs: (149 commits) Btrfs: fix spin_unlock in check_ref_cleanup Btrfs: setup inode location during btrfs_init_inode_locked Btrfs: don't use ram_bytes for uncompressed inline items Btrfs: fix btrfs_search_slot_for_read backwards iteration Btrfs: do not export ulist functions Btrfs: rework ulist with list+rb_tree Btrfs: fix memory leaks on walking backrefs failure Btrfs: fix send file hole detection leading to data corruption Btrfs: add a reschedule point in btrfs_find_all_roots() Btrfs: make send's file extent item search more efficient Btrfs: fix to catch all errors when resolving indirect ref Btrfs: fix protection between walking backrefs and root deletion btrfs: fix warning while merging two adjacent extents Btrfs: fix infinite path build loops in incremental send btrfs: undo sysfs when open_ctree() fails Btrfs: fix snprintf usage by send's gen_unique_name btrfs: fix defrag 32-bit integer overflow btrfs: sysfs: list the NO_HOLES feature btrfs: sysfs: don't show reserved incompat feature btrfs: call permission checks earlier in ioctls and return EPERM ...
This commit is contained in:
348
fs/btrfs/ioctl.c
348
fs/btrfs/ioctl.c
@@ -56,6 +56,8 @@
|
||||
#include "rcu-string.h"
|
||||
#include "send.h"
|
||||
#include "dev-replace.h"
|
||||
#include "props.h"
|
||||
#include "sysfs.h"
|
||||
|
||||
static int btrfs_clone(struct inode *src, struct inode *inode,
|
||||
u64 off, u64 olen, u64 olen_aligned, u64 destoff);
|
||||
@@ -190,6 +192,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
unsigned int i_oldflags;
|
||||
umode_t mode;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EPERM;
|
||||
|
||||
if (btrfs_root_readonly(root))
|
||||
return -EROFS;
|
||||
|
||||
@@ -200,9 +205,6 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EACCES;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -280,9 +282,25 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
if (flags & FS_NOCOMP_FL) {
|
||||
ip->flags &= ~BTRFS_INODE_COMPRESS;
|
||||
ip->flags |= BTRFS_INODE_NOCOMPRESS;
|
||||
|
||||
ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0);
|
||||
if (ret && ret != -ENODATA)
|
||||
goto out_drop;
|
||||
} else if (flags & FS_COMPR_FL) {
|
||||
const char *comp;
|
||||
|
||||
ip->flags |= BTRFS_INODE_COMPRESS;
|
||||
ip->flags &= ~BTRFS_INODE_NOCOMPRESS;
|
||||
|
||||
if (root->fs_info->compress_type == BTRFS_COMPRESS_LZO)
|
||||
comp = "lzo";
|
||||
else
|
||||
comp = "zlib";
|
||||
ret = btrfs_set_prop(inode, "btrfs.compression",
|
||||
comp, strlen(comp), 0);
|
||||
if (ret)
|
||||
goto out_drop;
|
||||
|
||||
} else {
|
||||
ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
|
||||
}
|
||||
@@ -392,6 +410,7 @@ static noinline int create_subvol(struct inode *dir,
|
||||
struct btrfs_root *new_root;
|
||||
struct btrfs_block_rsv block_rsv;
|
||||
struct timespec cur_time = CURRENT_TIME;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
int err;
|
||||
u64 objectid;
|
||||
@@ -417,7 +436,9 @@ static noinline int create_subvol(struct inode *dir,
|
||||
trans = btrfs_start_transaction(root, 0);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
goto out;
|
||||
btrfs_subvolume_release_metadata(root, &block_rsv,
|
||||
qgroup_reserved);
|
||||
return ret;
|
||||
}
|
||||
trans->block_rsv = &block_rsv;
|
||||
trans->bytes_reserved = block_rsv.size;
|
||||
@@ -500,7 +521,7 @@ static noinline int create_subvol(struct inode *dir,
|
||||
|
||||
btrfs_record_root_in_trans(trans, new_root);
|
||||
|
||||
ret = btrfs_create_subvol_root(trans, new_root, new_dirid);
|
||||
ret = btrfs_create_subvol_root(trans, new_root, root, new_dirid);
|
||||
if (ret) {
|
||||
/* We potentially lose an unused inode item here */
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
@@ -542,6 +563,8 @@ static noinline int create_subvol(struct inode *dir,
|
||||
fail:
|
||||
trans->block_rsv = NULL;
|
||||
trans->bytes_reserved = 0;
|
||||
btrfs_subvolume_release_metadata(root, &block_rsv, qgroup_reserved);
|
||||
|
||||
if (async_transid) {
|
||||
*async_transid = trans->transid;
|
||||
err = btrfs_commit_transaction_async(trans, root, 1);
|
||||
@@ -553,10 +576,12 @@ fail:
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
|
||||
if (!ret)
|
||||
d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry));
|
||||
out:
|
||||
btrfs_subvolume_release_metadata(root, &block_rsv, qgroup_reserved);
|
||||
if (!ret) {
|
||||
inode = btrfs_lookup_dentry(dir, dentry);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -642,7 +667,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
|
||||
ret = PTR_ERR(inode);
|
||||
goto fail;
|
||||
}
|
||||
BUG_ON(!inode);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
ret = 0;
|
||||
fail:
|
||||
@@ -1011,7 +1036,7 @@ out:
|
||||
static int cluster_pages_for_defrag(struct inode *inode,
|
||||
struct page **pages,
|
||||
unsigned long start_index,
|
||||
int num_pages)
|
||||
unsigned long num_pages)
|
||||
{
|
||||
unsigned long file_end;
|
||||
u64 isize = i_size_read(inode);
|
||||
@@ -1169,8 +1194,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
|
||||
int defrag_count = 0;
|
||||
int compress_type = BTRFS_COMPRESS_ZLIB;
|
||||
int extent_thresh = range->extent_thresh;
|
||||
int max_cluster = (256 * 1024) >> PAGE_CACHE_SHIFT;
|
||||
int cluster = max_cluster;
|
||||
unsigned long max_cluster = (256 * 1024) >> PAGE_CACHE_SHIFT;
|
||||
unsigned long cluster = max_cluster;
|
||||
u64 new_align = ~((u64)128 * 1024 - 1);
|
||||
struct page **pages = NULL;
|
||||
|
||||
@@ -1254,7 +1279,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
|
||||
break;
|
||||
|
||||
if (btrfs_defrag_cancelled(root->fs_info)) {
|
||||
printk(KERN_DEBUG "btrfs: defrag_file cancelled\n");
|
||||
printk(KERN_DEBUG "BTRFS: defrag_file cancelled\n");
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
@@ -1416,20 +1441,20 @@ static noinline int btrfs_ioctl_resize(struct file *file,
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
printk(KERN_INFO "btrfs: resizing devid %llu\n", devid);
|
||||
btrfs_info(root->fs_info, "resizing devid %llu", devid);
|
||||
}
|
||||
|
||||
device = btrfs_find_device(root->fs_info, devid, NULL, NULL);
|
||||
if (!device) {
|
||||
printk(KERN_INFO "btrfs: resizer unable to find device %llu\n",
|
||||
btrfs_info(root->fs_info, "resizer unable to find device %llu",
|
||||
devid);
|
||||
ret = -ENODEV;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!device->writeable) {
|
||||
printk(KERN_INFO "btrfs: resizer unable to apply on "
|
||||
"readonly device %llu\n",
|
||||
btrfs_info(root->fs_info,
|
||||
"resizer unable to apply on readonly device %llu",
|
||||
devid);
|
||||
ret = -EPERM;
|
||||
goto out_free;
|
||||
@@ -1466,6 +1491,10 @@ static noinline int btrfs_ioctl_resize(struct file *file,
|
||||
}
|
||||
new_size = old_size - new_size;
|
||||
} else if (mod > 0) {
|
||||
if (new_size > ULLONG_MAX - old_size) {
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
new_size = old_size + new_size;
|
||||
}
|
||||
|
||||
@@ -1481,7 +1510,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
|
||||
do_div(new_size, root->sectorsize);
|
||||
new_size *= root->sectorsize;
|
||||
|
||||
printk_in_rcu(KERN_INFO "btrfs: new size for %s is %llu\n",
|
||||
printk_in_rcu(KERN_INFO "BTRFS: new size for %s is %llu\n",
|
||||
rcu_str_deref(device->name), new_size);
|
||||
|
||||
if (new_size > old_size) {
|
||||
@@ -1542,9 +1571,15 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
|
||||
|
||||
src_inode = file_inode(src.file);
|
||||
if (src_inode->i_sb != file_inode(file)->i_sb) {
|
||||
printk(KERN_INFO "btrfs: Snapshot src from "
|
||||
"another FS\n");
|
||||
btrfs_info(BTRFS_I(src_inode)->root->fs_info,
|
||||
"Snapshot src from another FS");
|
||||
ret = -EINVAL;
|
||||
} else if (!inode_owner_or_capable(src_inode)) {
|
||||
/*
|
||||
* Subvolume creation is not restricted, but snapshots
|
||||
* are limited to own subvolumes only
|
||||
*/
|
||||
ret = -EPERM;
|
||||
} else {
|
||||
ret = btrfs_mksubvol(&file->f_path, name, namelen,
|
||||
BTRFS_I(src_inode)->root,
|
||||
@@ -1662,6 +1697,9 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
|
||||
u64 flags;
|
||||
int ret = 0;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EPERM;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -1686,11 +1724,6 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
if (!inode_owner_or_capable(inode)) {
|
||||
ret = -EACCES;
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
down_write(&root->fs_info->subvol_sem);
|
||||
|
||||
/* nothing to do */
|
||||
@@ -1698,12 +1731,28 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
|
||||
goto out_drop_sem;
|
||||
|
||||
root_flags = btrfs_root_flags(&root->root_item);
|
||||
if (flags & BTRFS_SUBVOL_RDONLY)
|
||||
if (flags & BTRFS_SUBVOL_RDONLY) {
|
||||
btrfs_set_root_flags(&root->root_item,
|
||||
root_flags | BTRFS_ROOT_SUBVOL_RDONLY);
|
||||
else
|
||||
btrfs_set_root_flags(&root->root_item,
|
||||
} else {
|
||||
/*
|
||||
* Block RO -> RW transition if this subvolume is involved in
|
||||
* send
|
||||
*/
|
||||
spin_lock(&root->root_item_lock);
|
||||
if (root->send_in_progress == 0) {
|
||||
btrfs_set_root_flags(&root->root_item,
|
||||
root_flags & ~BTRFS_ROOT_SUBVOL_RDONLY);
|
||||
spin_unlock(&root->root_item_lock);
|
||||
} else {
|
||||
spin_unlock(&root->root_item_lock);
|
||||
btrfs_warn(root->fs_info,
|
||||
"Attempt to set subvolume %llu read-write during send",
|
||||
root->root_key.objectid);
|
||||
ret = -EPERM;
|
||||
goto out_drop_sem;
|
||||
}
|
||||
}
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
if (IS_ERR(trans)) {
|
||||
@@ -1910,7 +1959,7 @@ static noinline int search_ioctl(struct inode *inode,
|
||||
key.offset = (u64)-1;
|
||||
root = btrfs_read_fs_root_no_name(info, &key);
|
||||
if (IS_ERR(root)) {
|
||||
printk(KERN_ERR "could not find root %llu\n",
|
||||
printk(KERN_ERR "BTRFS: could not find root %llu\n",
|
||||
sk->tree_id);
|
||||
btrfs_free_path(path);
|
||||
return -ENOENT;
|
||||
@@ -2000,7 +2049,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
|
||||
key.offset = (u64)-1;
|
||||
root = btrfs_read_fs_root_no_name(info, &key);
|
||||
if (IS_ERR(root)) {
|
||||
printk(KERN_ERR "could not find root %llu\n", tree_id);
|
||||
printk(KERN_ERR "BTRFS: could not find root %llu\n", tree_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
@@ -2838,12 +2887,14 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
|
||||
* note the key will change type as we walk through the
|
||||
* tree.
|
||||
*/
|
||||
path->leave_spinning = 1;
|
||||
ret = btrfs_search_slot(NULL, BTRFS_I(src)->root, &key, path,
|
||||
0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
nritems = btrfs_header_nritems(path->nodes[0]);
|
||||
process_slot:
|
||||
if (path->slots[0] >= nritems) {
|
||||
ret = btrfs_next_leaf(BTRFS_I(src)->root, path);
|
||||
if (ret < 0)
|
||||
@@ -2870,11 +2921,6 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
|
||||
u8 comp;
|
||||
u64 endoff;
|
||||
|
||||
size = btrfs_item_size_nr(leaf, slot);
|
||||
read_extent_buffer(leaf, buf,
|
||||
btrfs_item_ptr_offset(leaf, slot),
|
||||
size);
|
||||
|
||||
extent = btrfs_item_ptr(leaf, slot,
|
||||
struct btrfs_file_extent_item);
|
||||
comp = btrfs_file_extent_compression(leaf, extent);
|
||||
@@ -2893,11 +2939,20 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
|
||||
datal = btrfs_file_extent_ram_bytes(leaf,
|
||||
extent);
|
||||
}
|
||||
btrfs_release_path(path);
|
||||
|
||||
if (key.offset + datal <= off ||
|
||||
key.offset >= off + len - 1)
|
||||
goto next;
|
||||
key.offset >= off + len - 1) {
|
||||
path->slots[0]++;
|
||||
goto process_slot;
|
||||
}
|
||||
|
||||
size = btrfs_item_size_nr(leaf, slot);
|
||||
read_extent_buffer(leaf, buf,
|
||||
btrfs_item_ptr_offset(leaf, slot),
|
||||
size);
|
||||
|
||||
btrfs_release_path(path);
|
||||
path->leave_spinning = 0;
|
||||
|
||||
memcpy(&new_key, &key, sizeof(new_key));
|
||||
new_key.objectid = btrfs_ino(inode);
|
||||
@@ -3068,7 +3123,6 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
|
||||
}
|
||||
ret = btrfs_end_transaction(trans, root);
|
||||
}
|
||||
next:
|
||||
btrfs_release_path(path);
|
||||
key.offset++;
|
||||
}
|
||||
@@ -3196,9 +3250,17 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||
|
||||
unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
|
||||
out_unlock:
|
||||
mutex_unlock(&src->i_mutex);
|
||||
if (!same_inode)
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
if (!same_inode) {
|
||||
if (inode < src) {
|
||||
mutex_unlock(&src->i_mutex);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
} else {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
mutex_unlock(&src->i_mutex);
|
||||
}
|
||||
} else {
|
||||
mutex_unlock(&src->i_mutex);
|
||||
}
|
||||
out_fput:
|
||||
fdput(src_file);
|
||||
out_drop_write:
|
||||
@@ -3321,8 +3383,8 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
|
||||
if (IS_ERR_OR_NULL(di)) {
|
||||
btrfs_free_path(path);
|
||||
btrfs_end_transaction(trans, root);
|
||||
printk(KERN_ERR "Umm, you don't have the default dir item, "
|
||||
"this isn't going to work\n");
|
||||
btrfs_err(new_root->fs_info, "Umm, you don't have the default dir"
|
||||
"item, this isn't going to work");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
@@ -3475,6 +3537,20 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long btrfs_ioctl_global_rsv(struct btrfs_root *root, void __user *arg)
|
||||
{
|
||||
struct btrfs_block_rsv *block_rsv = &root->fs_info->global_block_rsv;
|
||||
u64 reserved;
|
||||
|
||||
spin_lock(&block_rsv->lock);
|
||||
reserved = block_rsv->reserved;
|
||||
spin_unlock(&block_rsv->lock);
|
||||
|
||||
if (arg && copy_to_user(arg, &reserved, sizeof(reserved)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* there are many ways the trans_start and trans_end ioctls can lead
|
||||
* to deadlocks. They should only be used by applications that
|
||||
@@ -4303,6 +4379,9 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,
|
||||
int ret = 0;
|
||||
int received_uuid_changed;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EPERM;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -4319,11 +4398,6 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!inode_owner_or_capable(inode)) {
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sa = memdup_user(arg, sizeof(*sa));
|
||||
if (IS_ERR(sa)) {
|
||||
ret = PTR_ERR(sa);
|
||||
@@ -4409,8 +4483,8 @@ static int btrfs_ioctl_get_fslabel(struct file *file, void __user *arg)
|
||||
len = strnlen(label, BTRFS_LABEL_SIZE);
|
||||
|
||||
if (len == BTRFS_LABEL_SIZE) {
|
||||
pr_warn("btrfs: label is too long, return the first %zu bytes\n",
|
||||
--len);
|
||||
btrfs_warn(root->fs_info,
|
||||
"label is too long, return the first %zu bytes", --len);
|
||||
}
|
||||
|
||||
ret = copy_to_user(arg, label, len);
|
||||
@@ -4433,7 +4507,7 @@ static int btrfs_ioctl_set_fslabel(struct file *file, void __user *arg)
|
||||
return -EFAULT;
|
||||
|
||||
if (strnlen(label, BTRFS_LABEL_SIZE) == BTRFS_LABEL_SIZE) {
|
||||
pr_err("btrfs: unable to set label with more than %d bytes\n",
|
||||
btrfs_err(root->fs_info, "unable to set label with more than %d bytes",
|
||||
BTRFS_LABEL_SIZE - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -4458,6 +4532,166 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define INIT_FEATURE_FLAGS(suffix) \
|
||||
{ .compat_flags = BTRFS_FEATURE_COMPAT_##suffix, \
|
||||
.compat_ro_flags = BTRFS_FEATURE_COMPAT_RO_##suffix, \
|
||||
.incompat_flags = BTRFS_FEATURE_INCOMPAT_##suffix }
|
||||
|
||||
static int btrfs_ioctl_get_supported_features(struct file *file,
|
||||
void __user *arg)
|
||||
{
|
||||
static struct btrfs_ioctl_feature_flags features[3] = {
|
||||
INIT_FEATURE_FLAGS(SUPP),
|
||||
INIT_FEATURE_FLAGS(SAFE_SET),
|
||||
INIT_FEATURE_FLAGS(SAFE_CLEAR)
|
||||
};
|
||||
|
||||
if (copy_to_user(arg, &features, sizeof(features)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_ioctl_get_features(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
|
||||
struct btrfs_super_block *super_block = root->fs_info->super_copy;
|
||||
struct btrfs_ioctl_feature_flags features;
|
||||
|
||||
features.compat_flags = btrfs_super_compat_flags(super_block);
|
||||
features.compat_ro_flags = btrfs_super_compat_ro_flags(super_block);
|
||||
features.incompat_flags = btrfs_super_incompat_flags(super_block);
|
||||
|
||||
if (copy_to_user(arg, &features, sizeof(features)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_feature_bits(struct btrfs_root *root,
|
||||
enum btrfs_feature_set set,
|
||||
u64 change_mask, u64 flags, u64 supported_flags,
|
||||
u64 safe_set, u64 safe_clear)
|
||||
{
|
||||
const char *type = btrfs_feature_set_names[set];
|
||||
char *names;
|
||||
u64 disallowed, unsupported;
|
||||
u64 set_mask = flags & change_mask;
|
||||
u64 clear_mask = ~flags & change_mask;
|
||||
|
||||
unsupported = set_mask & ~supported_flags;
|
||||
if (unsupported) {
|
||||
names = btrfs_printable_features(set, unsupported);
|
||||
if (names) {
|
||||
btrfs_warn(root->fs_info,
|
||||
"this kernel does not support the %s feature bit%s",
|
||||
names, strchr(names, ',') ? "s" : "");
|
||||
kfree(names);
|
||||
} else
|
||||
btrfs_warn(root->fs_info,
|
||||
"this kernel does not support %s bits 0x%llx",
|
||||
type, unsupported);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
disallowed = set_mask & ~safe_set;
|
||||
if (disallowed) {
|
||||
names = btrfs_printable_features(set, disallowed);
|
||||
if (names) {
|
||||
btrfs_warn(root->fs_info,
|
||||
"can't set the %s feature bit%s while mounted",
|
||||
names, strchr(names, ',') ? "s" : "");
|
||||
kfree(names);
|
||||
} else
|
||||
btrfs_warn(root->fs_info,
|
||||
"can't set %s bits 0x%llx while mounted",
|
||||
type, disallowed);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
disallowed = clear_mask & ~safe_clear;
|
||||
if (disallowed) {
|
||||
names = btrfs_printable_features(set, disallowed);
|
||||
if (names) {
|
||||
btrfs_warn(root->fs_info,
|
||||
"can't clear the %s feature bit%s while mounted",
|
||||
names, strchr(names, ',') ? "s" : "");
|
||||
kfree(names);
|
||||
} else
|
||||
btrfs_warn(root->fs_info,
|
||||
"can't clear %s bits 0x%llx while mounted",
|
||||
type, disallowed);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define check_feature(root, change_mask, flags, mask_base) \
|
||||
check_feature_bits(root, FEAT_##mask_base, change_mask, flags, \
|
||||
BTRFS_FEATURE_ ## mask_base ## _SUPP, \
|
||||
BTRFS_FEATURE_ ## mask_base ## _SAFE_SET, \
|
||||
BTRFS_FEATURE_ ## mask_base ## _SAFE_CLEAR)
|
||||
|
||||
static int btrfs_ioctl_set_features(struct file *file, void __user *arg)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
|
||||
struct btrfs_super_block *super_block = root->fs_info->super_copy;
|
||||
struct btrfs_ioctl_feature_flags flags[2];
|
||||
struct btrfs_trans_handle *trans;
|
||||
u64 newflags;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(flags, arg, sizeof(flags)))
|
||||
return -EFAULT;
|
||||
|
||||
/* Nothing to do */
|
||||
if (!flags[0].compat_flags && !flags[0].compat_ro_flags &&
|
||||
!flags[0].incompat_flags)
|
||||
return 0;
|
||||
|
||||
ret = check_feature(root, flags[0].compat_flags,
|
||||
flags[1].compat_flags, COMPAT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = check_feature(root, flags[0].compat_ro_flags,
|
||||
flags[1].compat_ro_flags, COMPAT_RO);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = check_feature(root, flags[0].incompat_flags,
|
||||
flags[1].incompat_flags, INCOMPAT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
if (IS_ERR(trans))
|
||||
return PTR_ERR(trans);
|
||||
|
||||
spin_lock(&root->fs_info->super_lock);
|
||||
newflags = btrfs_super_compat_flags(super_block);
|
||||
newflags |= flags[0].compat_flags & flags[1].compat_flags;
|
||||
newflags &= ~(flags[0].compat_flags & ~flags[1].compat_flags);
|
||||
btrfs_set_super_compat_flags(super_block, newflags);
|
||||
|
||||
newflags = btrfs_super_compat_ro_flags(super_block);
|
||||
newflags |= flags[0].compat_ro_flags & flags[1].compat_ro_flags;
|
||||
newflags &= ~(flags[0].compat_ro_flags & ~flags[1].compat_ro_flags);
|
||||
btrfs_set_super_compat_ro_flags(super_block, newflags);
|
||||
|
||||
newflags = btrfs_super_incompat_flags(super_block);
|
||||
newflags |= flags[0].incompat_flags & flags[1].incompat_flags;
|
||||
newflags &= ~(flags[0].incompat_flags & ~flags[1].incompat_flags);
|
||||
btrfs_set_super_incompat_flags(super_block, newflags);
|
||||
spin_unlock(&root->fs_info->super_lock);
|
||||
|
||||
return btrfs_end_transaction(trans, root);
|
||||
}
|
||||
|
||||
long btrfs_ioctl(struct file *file, unsigned int
|
||||
cmd, unsigned long arg)
|
||||
{
|
||||
@@ -4523,6 +4757,8 @@ long btrfs_ioctl(struct file *file, unsigned int
|
||||
return btrfs_ioctl_logical_to_ino(root, argp);
|
||||
case BTRFS_IOC_SPACE_INFO:
|
||||
return btrfs_ioctl_space_info(root, argp);
|
||||
case BTRFS_IOC_GLOBAL_RSV:
|
||||
return btrfs_ioctl_global_rsv(root, argp);
|
||||
case BTRFS_IOC_SYNC: {
|
||||
int ret;
|
||||
|
||||
@@ -4576,6 +4812,12 @@ long btrfs_ioctl(struct file *file, unsigned int
|
||||
return btrfs_ioctl_set_fslabel(file, argp);
|
||||
case BTRFS_IOC_FILE_EXTENT_SAME:
|
||||
return btrfs_ioctl_file_extent_same(file, argp);
|
||||
case BTRFS_IOC_GET_SUPPORTED_FEATURES:
|
||||
return btrfs_ioctl_get_supported_features(file, argp);
|
||||
case BTRFS_IOC_GET_FEATURES:
|
||||
return btrfs_ioctl_get_features(file, argp);
|
||||
case BTRFS_IOC_SET_FEATURES:
|
||||
return btrfs_ioctl_set_features(file, argp);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
|
Reference in New Issue
Block a user