aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ctree.h9
-rw-r--r--fs/btrfs/ioctl.c142
-rw-r--r--include/uapi/linux/btrfs.h12
3 files changed, 163 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 1aafccda05d1..498452ebfd37 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -524,7 +524,12 @@ struct btrfs_super_block {
524#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9) 524#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9)
525 525
526#define BTRFS_FEATURE_COMPAT_SUPP 0ULL 526#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
527#define BTRFS_FEATURE_COMPAT_SAFE_SET 0ULL
528#define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL
527#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL 529#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
530#define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL
531#define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL
532
528#define BTRFS_FEATURE_INCOMPAT_SUPP \ 533#define BTRFS_FEATURE_INCOMPAT_SUPP \
529 (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ 534 (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
530 BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \ 535 BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
@@ -536,6 +541,10 @@ struct btrfs_super_block {
536 BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \ 541 BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \
537 BTRFS_FEATURE_INCOMPAT_NO_HOLES) 542 BTRFS_FEATURE_INCOMPAT_NO_HOLES)
538 543
544#define BTRFS_FEATURE_INCOMPAT_SAFE_SET \
545 (BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
546#define BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR 0ULL
547
539/* 548/*
540 * A leaf is full of items. offset and size tell us where to find 549 * A leaf is full of items. offset and size tell us where to find
541 * the item in the leaf (relative to the start of the data area) 550 * the item in the leaf (relative to the start of the data area)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 21da5762b0b1..d4e105b54091 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4480,6 +4480,142 @@ out_unlock:
4480 return ret; 4480 return ret;
4481} 4481}
4482 4482
4483#define INIT_FEATURE_FLAGS(suffix) \
4484 { .compat_flags = BTRFS_FEATURE_COMPAT_##suffix, \
4485 .compat_ro_flags = BTRFS_FEATURE_COMPAT_RO_##suffix, \
4486 .incompat_flags = BTRFS_FEATURE_INCOMPAT_##suffix }
4487
4488static int btrfs_ioctl_get_supported_features(struct file *file,
4489 void __user *arg)
4490{
4491 static struct btrfs_ioctl_feature_flags features[3] = {
4492 INIT_FEATURE_FLAGS(SUPP),
4493 INIT_FEATURE_FLAGS(SAFE_SET),
4494 INIT_FEATURE_FLAGS(SAFE_CLEAR)
4495 };
4496
4497 if (copy_to_user(arg, &features, sizeof(features)))
4498 return -EFAULT;
4499
4500 return 0;
4501}
4502
4503static int btrfs_ioctl_get_features(struct file *file, void __user *arg)
4504{
4505 struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
4506 struct btrfs_super_block *super_block = root->fs_info->super_copy;
4507 struct btrfs_ioctl_feature_flags features;
4508
4509 features.compat_flags = btrfs_super_compat_flags(super_block);
4510 features.compat_ro_flags = btrfs_super_compat_ro_flags(super_block);
4511 features.incompat_flags = btrfs_super_incompat_flags(super_block);
4512
4513 if (copy_to_user(arg, &features, sizeof(features)))
4514 return -EFAULT;
4515
4516 return 0;
4517}
4518
4519static int check_feature_bits(struct btrfs_root *root, const char *type,
4520 u64 change_mask, u64 flags, u64 supported_flags,
4521 u64 safe_set, u64 safe_clear)
4522{
4523 u64 disallowed, unsupported;
4524 u64 set_mask = flags & change_mask;
4525 u64 clear_mask = ~flags & change_mask;
4526
4527 unsupported = set_mask & ~supported_flags;
4528 if (unsupported) {
4529 btrfs_warn(root->fs_info,
4530 "this kernel does not support %s bits 0x%llx",
4531 type, unsupported);
4532 return -EOPNOTSUPP;
4533 }
4534
4535 disallowed = set_mask & ~safe_set;
4536 if (disallowed) {
4537 btrfs_warn(root->fs_info,
4538 "can't set %s bits 0x%llx while mounted",
4539 type, disallowed);
4540 return -EPERM;
4541 }
4542
4543 disallowed = clear_mask & ~safe_clear;
4544 if (disallowed) {
4545 btrfs_warn(root->fs_info,
4546 "can't clear %s bits 0x%llx while mounted",
4547 type, disallowed);
4548 return -EPERM;
4549 }
4550
4551 return 0;
4552}
4553
4554#define check_feature(root, change_mask, flags, mask_base) \
4555check_feature_bits(root, # mask_base, change_mask, flags, \
4556 BTRFS_FEATURE_ ## mask_base ## _SUPP, \
4557 BTRFS_FEATURE_ ## mask_base ## _SAFE_SET, \
4558 BTRFS_FEATURE_ ## mask_base ## _SAFE_CLEAR)
4559
4560static int btrfs_ioctl_set_features(struct file *file, void __user *arg)
4561{
4562 struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
4563 struct btrfs_super_block *super_block = root->fs_info->super_copy;
4564 struct btrfs_ioctl_feature_flags flags[2];
4565 struct btrfs_trans_handle *trans;
4566 u64 newflags;
4567 int ret;
4568
4569 if (!capable(CAP_SYS_ADMIN))
4570 return -EPERM;
4571
4572 if (copy_from_user(flags, arg, sizeof(flags)))
4573 return -EFAULT;
4574
4575 /* Nothing to do */
4576 if (!flags[0].compat_flags && !flags[0].compat_ro_flags &&
4577 !flags[0].incompat_flags)
4578 return 0;
4579
4580 ret = check_feature(root, flags[0].compat_flags,
4581 flags[1].compat_flags, COMPAT);
4582 if (ret)
4583 return ret;
4584
4585 ret = check_feature(root, flags[0].compat_ro_flags,
4586 flags[1].compat_ro_flags, COMPAT_RO);
4587 if (ret)
4588 return ret;
4589
4590 ret = check_feature(root, flags[0].incompat_flags,
4591 flags[1].incompat_flags, INCOMPAT);
4592 if (ret)
4593 return ret;
4594
4595 trans = btrfs_start_transaction(root, 1);
4596 if (IS_ERR(trans))
4597 return PTR_ERR(trans);
4598
4599 spin_lock(&root->fs_info->super_lock);
4600 newflags = btrfs_super_compat_flags(super_block);
4601 newflags |= flags[0].compat_flags & flags[1].compat_flags;
4602 newflags &= ~(flags[0].compat_flags & ~flags[1].compat_flags);
4603 btrfs_set_super_compat_flags(super_block, newflags);
4604
4605 newflags = btrfs_super_compat_ro_flags(super_block);
4606 newflags |= flags[0].compat_ro_flags & flags[1].compat_ro_flags;
4607 newflags &= ~(flags[0].compat_ro_flags & ~flags[1].compat_ro_flags);
4608 btrfs_set_super_compat_ro_flags(super_block, newflags);
4609
4610 newflags = btrfs_super_incompat_flags(super_block);
4611 newflags |= flags[0].incompat_flags & flags[1].incompat_flags;
4612 newflags &= ~(flags[0].incompat_flags & ~flags[1].incompat_flags);
4613 btrfs_set_super_incompat_flags(super_block, newflags);
4614 spin_unlock(&root->fs_info->super_lock);
4615
4616 return btrfs_end_transaction(trans, root);
4617}
4618
4483long btrfs_ioctl(struct file *file, unsigned int 4619long btrfs_ioctl(struct file *file, unsigned int
4484 cmd, unsigned long arg) 4620 cmd, unsigned long arg)
4485{ 4621{
@@ -4598,6 +4734,12 @@ long btrfs_ioctl(struct file *file, unsigned int
4598 return btrfs_ioctl_set_fslabel(file, argp); 4734 return btrfs_ioctl_set_fslabel(file, argp);
4599 case BTRFS_IOC_FILE_EXTENT_SAME: 4735 case BTRFS_IOC_FILE_EXTENT_SAME:
4600 return btrfs_ioctl_file_extent_same(file, argp); 4736 return btrfs_ioctl_file_extent_same(file, argp);
4737 case BTRFS_IOC_GET_SUPPORTED_FEATURES:
4738 return btrfs_ioctl_get_supported_features(file, argp);
4739 case BTRFS_IOC_GET_FEATURES:
4740 return btrfs_ioctl_get_features(file, argp);
4741 case BTRFS_IOC_SET_FEATURES:
4742 return btrfs_ioctl_set_features(file, argp);
4601 } 4743 }
4602 4744
4603 return -ENOTTY; 4745 return -ENOTTY;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 45e618921c61..b4d69092fbdb 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -184,6 +184,12 @@ struct btrfs_ioctl_fs_info_args {
184 __u64 reserved[124]; /* pad to 1k */ 184 __u64 reserved[124]; /* pad to 1k */
185}; 185};
186 186
187struct btrfs_ioctl_feature_flags {
188 __u64 compat_flags;
189 __u64 compat_ro_flags;
190 __u64 incompat_flags;
191};
192
187/* balance control ioctl modes */ 193/* balance control ioctl modes */
188#define BTRFS_BALANCE_CTL_PAUSE 1 194#define BTRFS_BALANCE_CTL_PAUSE 1
189#define BTRFS_BALANCE_CTL_CANCEL 2 195#define BTRFS_BALANCE_CTL_CANCEL 2
@@ -606,5 +612,11 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
606 struct btrfs_ioctl_dev_replace_args) 612 struct btrfs_ioctl_dev_replace_args)
607#define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \ 613#define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \
608 struct btrfs_ioctl_same_args) 614 struct btrfs_ioctl_same_args)
615#define BTRFS_IOC_GET_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
616 struct btrfs_ioctl_feature_flags)
617#define BTRFS_IOC_SET_FEATURES _IOW(BTRFS_IOCTL_MAGIC, 57, \
618 struct btrfs_ioctl_feature_flags[2])
619#define BTRFS_IOC_GET_SUPPORTED_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
620 struct btrfs_ioctl_feature_flags[3])
609 621
610#endif /* _UAPI_LINUX_BTRFS_H */ 622#endif /* _UAPI_LINUX_BTRFS_H */