| From 2e53840362771c73eb0a5ff71611507e64e8eecd Mon Sep 17 00:00:00 2001 |
| From: "Darrick J. Wong" <darrick.wong@oracle.com> |
| Date: Sun, 9 Jun 2019 21:41:41 -0400 |
| Subject: ext4: don't allow any modifications to an immutable file |
| |
| From: Darrick J. Wong <darrick.wong@oracle.com> |
| |
| commit 2e53840362771c73eb0a5ff71611507e64e8eecd upstream. |
| |
| Don't allow any modifications to a file that's marked immutable, which |
| means that we have to flush all the writable pages to make the readonly |
| and we have to check the setattr/setflags parameters more closely. |
| |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Cc: stable@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/ext4/ioctl.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- |
| 1 file changed, 45 insertions(+), 1 deletion(-) |
| |
| --- a/fs/ext4/ioctl.c |
| +++ b/fs/ext4/ioctl.c |
| @@ -210,6 +210,29 @@ static int uuid_is_zero(__u8 u[16]) |
| } |
| #endif |
| |
| +/* |
| + * If immutable is set and we are not clearing it, we're not allowed to change |
| + * anything else in the inode. Don't error out if we're only trying to set |
| + * immutable on an immutable file. |
| + */ |
| +static int ext4_ioctl_check_immutable(struct inode *inode, __u32 new_projid, |
| + unsigned int flags) |
| +{ |
| + struct ext4_inode_info *ei = EXT4_I(inode); |
| + unsigned int oldflags = ei->i_flags; |
| + |
| + if (!(oldflags & EXT4_IMMUTABLE_FL) || !(flags & EXT4_IMMUTABLE_FL)) |
| + return 0; |
| + |
| + if ((oldflags & ~EXT4_IMMUTABLE_FL) != (flags & ~EXT4_IMMUTABLE_FL)) |
| + return -EPERM; |
| + if (ext4_has_feature_project(inode->i_sb) && |
| + __kprojid_val(ei->i_projid) != new_projid) |
| + return -EPERM; |
| + |
| + return 0; |
| +} |
| + |
| static int ext4_ioctl_setflags(struct inode *inode, |
| unsigned int flags) |
| { |
| @@ -263,6 +286,20 @@ static int ext4_ioctl_setflags(struct in |
| goto flags_out; |
| } |
| |
| + /* |
| + * Wait for all pending directio and then flush all the dirty pages |
| + * for this file. The flush marks all the pages readonly, so any |
| + * subsequent attempt to write to the file (particularly mmap pages) |
| + * will come through the filesystem and fail. |
| + */ |
| + if (S_ISREG(inode->i_mode) && !IS_IMMUTABLE(inode) && |
| + (flags & EXT4_IMMUTABLE_FL)) { |
| + inode_dio_wait(inode); |
| + err = filemap_write_and_wait(inode->i_mapping); |
| + if (err) |
| + goto flags_out; |
| + } |
| + |
| handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); |
| if (IS_ERR(handle)) { |
| err = PTR_ERR(handle); |
| @@ -653,7 +690,11 @@ long ext4_ioctl(struct file *filp, unsig |
| return err; |
| |
| inode_lock(inode); |
| - err = ext4_ioctl_setflags(inode, flags); |
| + err = ext4_ioctl_check_immutable(inode, |
| + from_kprojid(&init_user_ns, ei->i_projid), |
| + flags); |
| + if (!err) |
| + err = ext4_ioctl_setflags(inode, flags); |
| inode_unlock(inode); |
| mnt_drop_write_file(filp); |
| return err; |
| @@ -1061,6 +1102,9 @@ resizefs_out: |
| goto out; |
| flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | |
| (flags & EXT4_FL_XFLAG_VISIBLE); |
| + err = ext4_ioctl_check_immutable(inode, fa.fsx_projid, flags); |
| + if (err) |
| + goto out; |
| err = ext4_ioctl_setflags(inode, flags); |
| if (err) |
| goto out; |