summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/inode.c27
1 files changed, 27 insertions, 0 deletions
diff --git a/fs/inode.c b/fs/inode.c
index cf07378e5731..4261c709e50e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2215,6 +2215,14 @@ int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
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
* FS_IOC_SETFLAGS. If the flush fails we'll clear the flag before
@@ -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);