@@ -2204,6 +2204,14 @@ int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags)
!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;
+
return 0;
}
EXPORT_SYMBOL(vfs_ioc_setflags_check);
@@ -2246,6 +2254,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);