Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e538403 authored by Darrick J. Wong's avatar Darrick J. Wong Committed by Theodore Ts'o
Browse files

ext4: don't allow any modifications to an immutable file



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: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org
parent a4977306
Loading
Loading
Loading
Loading
+45 −1
Original line number Diff line number Diff line
@@ -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 inode *inode,
		}
	}

	/*
	 * 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,6 +806,10 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
			return err;

		inode_lock(inode);
		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);
@@ -1139,6 +1180,9 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
			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;