diff options
author | Hugo Mills <hugo@carfax.org.uk> | 2014-01-30 15:17:00 -0500 |
---|---|---|
committer | Josef Bacik <jbacik@fb.com> | 2014-03-10 15:15:40 -0400 |
commit | abccd00f8af27c585be48904515bad5658130e48 (patch) | |
tree | 123068ed0cc192d6a719a98ad94f422d4aef1f64 /fs/btrfs/ioctl.c | |
parent | d86477b303da51832002eec1cdec2938c42fccc3 (diff) |
btrfs: Fix 32/64-bit problem with BTRFS_SET_RECEIVED_SUBVOL ioctl
The structure for BTRFS_SET_RECEIVED_IOCTL packs differently on 32-bit
and 64-bit systems. This means that it is impossible to use btrfs
receive on a system with a 64-bit kernel and 32-bit userspace, because
the structure size (and hence the ioctl number) is different.
This patch adds a compatibility structure and ioctl to deal with the
above case.
Signed-off-by: Hugo Mills <hugo@carfax.org.uk>
Signed-off-by: Josef Bacik <jbacik@fb.com>
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 123 |
1 files changed, 111 insertions, 12 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a692aad8fa5a..d4c179502775 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -59,6 +59,32 @@ | |||
59 | #include "props.h" | 59 | #include "props.h" |
60 | #include "sysfs.h" | 60 | #include "sysfs.h" |
61 | 61 | ||
62 | #ifdef CONFIG_64BIT | ||
63 | /* If we have a 32-bit userspace and 64-bit kernel, then the UAPI | ||
64 | * structures are incorrect, as the timespec structure from userspace | ||
65 | * is 4 bytes too small. We define these alternatives here to teach | ||
66 | * the kernel about the 32-bit struct packing. | ||
67 | */ | ||
68 | struct btrfs_ioctl_timespec_32 { | ||
69 | __u64 sec; | ||
70 | __u32 nsec; | ||
71 | } __attribute__ ((__packed__)); | ||
72 | |||
73 | struct btrfs_ioctl_received_subvol_args_32 { | ||
74 | char uuid[BTRFS_UUID_SIZE]; /* in */ | ||
75 | __u64 stransid; /* in */ | ||
76 | __u64 rtransid; /* out */ | ||
77 | struct btrfs_ioctl_timespec_32 stime; /* in */ | ||
78 | struct btrfs_ioctl_timespec_32 rtime; /* out */ | ||
79 | __u64 flags; /* in */ | ||
80 | __u64 reserved[16]; /* in */ | ||
81 | } __attribute__ ((__packed__)); | ||
82 | |||
83 | #define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \ | ||
84 | struct btrfs_ioctl_received_subvol_args_32) | ||
85 | #endif | ||
86 | |||
87 | |||
62 | static int btrfs_clone(struct inode *src, struct inode *inode, | 88 | static int btrfs_clone(struct inode *src, struct inode *inode, |
63 | u64 off, u64 olen, u64 olen_aligned, u64 destoff); | 89 | u64 off, u64 olen, u64 olen_aligned, u64 destoff); |
64 | 90 | ||
@@ -4375,10 +4401,9 @@ static long btrfs_ioctl_quota_rescan_wait(struct file *file, void __user *arg) | |||
4375 | return btrfs_qgroup_wait_for_completion(root->fs_info); | 4401 | return btrfs_qgroup_wait_for_completion(root->fs_info); |
4376 | } | 4402 | } |
4377 | 4403 | ||
4378 | static long btrfs_ioctl_set_received_subvol(struct file *file, | 4404 | static long _btrfs_ioctl_set_received_subvol(struct file *file, |
4379 | void __user *arg) | 4405 | struct btrfs_ioctl_received_subvol_args *sa) |
4380 | { | 4406 | { |
4381 | struct btrfs_ioctl_received_subvol_args *sa = NULL; | ||
4382 | struct inode *inode = file_inode(file); | 4407 | struct inode *inode = file_inode(file); |
4383 | struct btrfs_root *root = BTRFS_I(inode)->root; | 4408 | struct btrfs_root *root = BTRFS_I(inode)->root; |
4384 | struct btrfs_root_item *root_item = &root->root_item; | 4409 | struct btrfs_root_item *root_item = &root->root_item; |
@@ -4406,13 +4431,6 @@ static long btrfs_ioctl_set_received_subvol(struct file *file, | |||
4406 | goto out; | 4431 | goto out; |
4407 | } | 4432 | } |
4408 | 4433 | ||
4409 | sa = memdup_user(arg, sizeof(*sa)); | ||
4410 | if (IS_ERR(sa)) { | ||
4411 | ret = PTR_ERR(sa); | ||
4412 | sa = NULL; | ||
4413 | goto out; | ||
4414 | } | ||
4415 | |||
4416 | /* | 4434 | /* |
4417 | * 1 - root item | 4435 | * 1 - root item |
4418 | * 2 - uuid items (received uuid + subvol uuid) | 4436 | * 2 - uuid items (received uuid + subvol uuid) |
@@ -4466,14 +4484,91 @@ static long btrfs_ioctl_set_received_subvol(struct file *file, | |||
4466 | goto out; | 4484 | goto out; |
4467 | } | 4485 | } |
4468 | 4486 | ||
4487 | out: | ||
4488 | up_write(&root->fs_info->subvol_sem); | ||
4489 | mnt_drop_write_file(file); | ||
4490 | return ret; | ||
4491 | } | ||
4492 | |||
4493 | #ifdef CONFIG_64BIT | ||
4494 | static long btrfs_ioctl_set_received_subvol_32(struct file *file, | ||
4495 | void __user *arg) | ||
4496 | { | ||
4497 | struct btrfs_ioctl_received_subvol_args_32 *args32 = NULL; | ||
4498 | struct btrfs_ioctl_received_subvol_args *args64 = NULL; | ||
4499 | int ret = 0; | ||
4500 | |||
4501 | args32 = memdup_user(arg, sizeof(*args32)); | ||
4502 | if (IS_ERR(args32)) { | ||
4503 | ret = PTR_ERR(args32); | ||
4504 | args32 = NULL; | ||
4505 | goto out; | ||
4506 | } | ||
4507 | |||
4508 | args64 = kmalloc(sizeof(*args64), GFP_NOFS); | ||
4509 | if (IS_ERR(args64)) { | ||
4510 | ret = PTR_ERR(args64); | ||
4511 | args64 = NULL; | ||
4512 | goto out; | ||
4513 | } | ||
4514 | |||
4515 | memcpy(args64->uuid, args32->uuid, BTRFS_UUID_SIZE); | ||
4516 | args64->stransid = args32->stransid; | ||
4517 | args64->rtransid = args32->rtransid; | ||
4518 | args64->stime.sec = args32->stime.sec; | ||
4519 | args64->stime.nsec = args32->stime.nsec; | ||
4520 | args64->rtime.sec = args32->rtime.sec; | ||
4521 | args64->rtime.nsec = args32->rtime.nsec; | ||
4522 | args64->flags = args32->flags; | ||
4523 | |||
4524 | ret = _btrfs_ioctl_set_received_subvol(file, args64); | ||
4525 | if (ret) | ||
4526 | goto out; | ||
4527 | |||
4528 | memcpy(args32->uuid, args64->uuid, BTRFS_UUID_SIZE); | ||
4529 | args32->stransid = args64->stransid; | ||
4530 | args32->rtransid = args64->rtransid; | ||
4531 | args32->stime.sec = args64->stime.sec; | ||
4532 | args32->stime.nsec = args64->stime.nsec; | ||
4533 | args32->rtime.sec = args64->rtime.sec; | ||
4534 | args32->rtime.nsec = args64->rtime.nsec; | ||
4535 | args32->flags = args64->flags; | ||
4536 | |||
4537 | ret = copy_to_user(arg, args32, sizeof(*args32)); | ||
4538 | if (ret) | ||
4539 | ret = -EFAULT; | ||
4540 | |||
4541 | out: | ||
4542 | kfree(args32); | ||
4543 | kfree(args64); | ||
4544 | return ret; | ||
4545 | } | ||
4546 | #endif | ||
4547 | |||
4548 | static long btrfs_ioctl_set_received_subvol(struct file *file, | ||
4549 | void __user *arg) | ||
4550 | { | ||
4551 | struct btrfs_ioctl_received_subvol_args *sa = NULL; | ||
4552 | int ret = 0; | ||
4553 | |||
4554 | sa = memdup_user(arg, sizeof(*sa)); | ||
4555 | if (IS_ERR(sa)) { | ||
4556 | ret = PTR_ERR(sa); | ||
4557 | sa = NULL; | ||
4558 | goto out; | ||
4559 | } | ||
4560 | |||
4561 | ret = _btrfs_ioctl_set_received_subvol(file, sa); | ||
4562 | |||
4563 | if (ret) | ||
4564 | goto out; | ||
4565 | |||
4469 | ret = copy_to_user(arg, sa, sizeof(*sa)); | 4566 | ret = copy_to_user(arg, sa, sizeof(*sa)); |
4470 | if (ret) | 4567 | if (ret) |
4471 | ret = -EFAULT; | 4568 | ret = -EFAULT; |
4472 | 4569 | ||
4473 | out: | 4570 | out: |
4474 | kfree(sa); | 4571 | kfree(sa); |
4475 | up_write(&root->fs_info->subvol_sem); | ||
4476 | mnt_drop_write_file(file); | ||
4477 | return ret; | 4572 | return ret; |
4478 | } | 4573 | } |
4479 | 4574 | ||
@@ -4792,6 +4887,10 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
4792 | return btrfs_ioctl_balance_progress(root, argp); | 4887 | return btrfs_ioctl_balance_progress(root, argp); |
4793 | case BTRFS_IOC_SET_RECEIVED_SUBVOL: | 4888 | case BTRFS_IOC_SET_RECEIVED_SUBVOL: |
4794 | return btrfs_ioctl_set_received_subvol(file, argp); | 4889 | return btrfs_ioctl_set_received_subvol(file, argp); |
4890 | #ifdef CONFIG_64BIT | ||
4891 | case BTRFS_IOC_SET_RECEIVED_SUBVOL_32: | ||
4892 | return btrfs_ioctl_set_received_subvol_32(file, argp); | ||
4893 | #endif | ||
4795 | case BTRFS_IOC_SEND: | 4894 | case BTRFS_IOC_SEND: |
4796 | return btrfs_ioctl_send(file, argp); | 4895 | return btrfs_ioctl_send(file, argp); |
4797 | case BTRFS_IOC_GET_DEV_STATS: | 4896 | case BTRFS_IOC_GET_DEV_STATS: |