@@ -2214,6 +2214,14 @@ int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;
+ /*
+ * We aren't allowed to change any other flags if the immutable flag is
+ * already set and is not being unset.
+ */
+ if ((oldflags & FS_IMMUTABLE_FL) && (flags & FS_IMMUTABLE_FL) &&
+ oldflags != flags)
+ return -EPERM;
+
/*
* Now that we're done checking the new flags, flush all pending IO and
* dirty mappings before setting S_IMMUTABLE on an inode via
@@ -2284,6 +2292,25 @@ int vfs_ioc_fssetxattr_check(struct inode *inode, const struct fsxattr *old_fa,
!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return -EINVAL;
+ /*
+ * We aren't allowed to change any fields if the immutable flag is
+ * already set and is not being unset.
+ */
+ if ((old_fa->fsx_xflags & FS_XFLAG_IMMUTABLE) &&
+ (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)) {
+ if (old_fa->fsx_xflags != fa->fsx_xflags)
+ return -EPERM;
+ if (old_fa->fsx_projid != fa->fsx_projid)
+ return -EPERM;
+ if ((fa->fsx_xflags & (FS_XFLAG_EXTSIZE |
+ FS_XFLAG_EXTSZINHERIT)) &&
+ old_fa->fsx_extsize != fa->fsx_extsize)
+ return -EPERM;
+ if ((old_fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) &&
+ old_fa->fsx_cowextsize != fa->fsx_cowextsize)
+ return -EPERM;
+ }
+
/* Extent size hints of zero turn off the flags. */
if (fa->fsx_extsize == 0)
fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);