From: Darrick J. Wong <darrick.w...@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.w...@oracle.com>
Signed-off-by: Theodore Ts'o <ty...@mit.edu>
Cc: sta...@kernel.org
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 fs/ext4/ioctl.c |   46 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -269,6 +269,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)
 {
@@ -340,6 +363,20 @@ static int ext4_ioctl_setflags(struct in
                }
        }
 
+       /*
+        * 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);
@@ -769,7 +806,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;
@@ -1139,6 +1180,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;


Reply via email to