summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/f2fs/file.c9
-rw-r--r--fs/inode.c24
-rw-r--r--include/linux/fs.h3
3 files changed, 35 insertions, 1 deletions
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 6a19adbf1604..08d71330d2ae 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1779,7 +1779,8 @@ static int f2fs_ioc_getflags(struct file *filp, unsigned long arg)
static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
- u32 fsflags;
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ u32 fsflags, old_fsflags;
u32 iflags;
int ret;
@@ -1803,8 +1804,14 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
inode_lock(inode);
+ old_fsflags = f2fs_iflags_to_fsflags(fi->i_flags);
+ ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out;
+
ret = f2fs_setflags_common(inode, iflags,
f2fs_fsflags_to_iflags(F2FS_SETTABLE_FS_FL));
+out:
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
diff --git a/fs/inode.c b/fs/inode.c
index 9fa27369e8a2..527789e43c94 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2029,3 +2029,27 @@ void inode_set_flags(struct inode *inode, unsigned int flags,
new_flags) != old_flags));
}
EXPORT_SYMBOL(inode_set_flags);
+
+/*
+ * Generic function to check FS_IOC_SETFLAGS values and reject any invalid
+ * configurations.
+ *
+ * Note: the caller should be holding i_mutex, or else be sure that they have
+ * exclusive access to the inode structure.
+ */
+int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
+ unsigned int flags)
+{
+ /*
+ * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+ * the relevant capability.
+ *
+ * This test looks nicer. Thanks to Pauline Middelink
+ */
+ if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
+ !capable(CAP_LINUX_IMMUTABLE))
+ return -EPERM;
+
+ return 0;
+}
+EXPORT_SYMBOL(vfs_ioc_setflags_prepare);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 314bf410e3ae..5bdae7cb4eb1 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3088,4 +3088,7 @@ static inline bool dir_relax(struct inode *inode)
extern bool path_noexec(const struct path *path);
+int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
+ unsigned int flags);
+
#endif /* _LINUX_FS_H */