| From 6d65261a09adaa374c05de807f73a144d783669e Mon Sep 17 00:00:00 2001 |
| From: Tyler Hicks <tyhicks@canonical.com> |
| Date: Tue, 24 Feb 2015 19:28:10 -0600 |
| Subject: eCryptfs: don't pass fs-specific ioctl commands through |
| |
| From: Tyler Hicks <tyhicks@canonical.com> |
| |
| commit 6d65261a09adaa374c05de807f73a144d783669e upstream. |
| |
| eCryptfs can't be aware of what to expect when after passing an |
| arbitrary ioctl command through to the lower filesystem. The ioctl |
| command may trigger an action in the lower filesystem that is |
| incompatible with eCryptfs. |
| |
| One specific example is when one attempts to use the Btrfs clone |
| ioctl command when the source file is in the Btrfs filesystem that |
| eCryptfs is mounted on top of and the destination fd is from a new file |
| created in the eCryptfs mount. The ioctl syscall incorrectly returns |
| success because the command is passed down to Btrfs which thinks that it |
| was able to do the clone operation. However, the result is an empty |
| eCryptfs file. |
| |
| This patch allows the trim, {g,s}etflags, and {g,s}etversion ioctl |
| commands through and then copies up the inode metadata from the lower |
| inode to the eCryptfs inode to catch any changes made to the lower |
| inode's metadata. Those five ioctl commands are mostly common across all |
| filesystems but the whitelist may need to be further pruned in the |
| future. |
| |
| https://bugzilla.kernel.org/show_bug.cgi?id=93691 |
| https://launchpad.net/bugs/1305335 |
| |
| Signed-off-by: Tyler Hicks <tyhicks@canonical.com> |
| Cc: Rocko <rockorequin@hotmail.com> |
| Cc: Colin Ian King <colin.king@canonical.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/ecryptfs/file.c | 34 ++++++++++++++++++++++++++++++---- |
| 1 file changed, 30 insertions(+), 4 deletions(-) |
| |
| --- a/fs/ecryptfs/file.c |
| +++ b/fs/ecryptfs/file.c |
| @@ -303,9 +303,22 @@ ecryptfs_unlocked_ioctl(struct file *fil |
| struct file *lower_file = ecryptfs_file_to_lower(file); |
| long rc = -ENOTTY; |
| |
| - if (lower_file->f_op->unlocked_ioctl) |
| + if (!lower_file->f_op->unlocked_ioctl) |
| + return rc; |
| + |
| + switch (cmd) { |
| + case FITRIM: |
| + case FS_IOC_GETFLAGS: |
| + case FS_IOC_SETFLAGS: |
| + case FS_IOC_GETVERSION: |
| + case FS_IOC_SETVERSION: |
| rc = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); |
| - return rc; |
| + fsstack_copy_attr_all(file_inode(file), file_inode(lower_file)); |
| + |
| + return rc; |
| + default: |
| + return rc; |
| + } |
| } |
| |
| #ifdef CONFIG_COMPAT |
| @@ -315,9 +328,22 @@ ecryptfs_compat_ioctl(struct file *file, |
| struct file *lower_file = ecryptfs_file_to_lower(file); |
| long rc = -ENOIOCTLCMD; |
| |
| - if (lower_file->f_op->compat_ioctl) |
| + if (!lower_file->f_op->compat_ioctl) |
| + return rc; |
| + |
| + switch (cmd) { |
| + case FITRIM: |
| + case FS_IOC32_GETFLAGS: |
| + case FS_IOC32_SETFLAGS: |
| + case FS_IOC32_GETVERSION: |
| + case FS_IOC32_SETVERSION: |
| rc = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); |
| - return rc; |
| + fsstack_copy_attr_all(file_inode(file), file_inode(lower_file)); |
| + |
| + return rc; |
| + default: |
| + return rc; |
| + } |
| } |
| #endif |
| |