summaryrefslogtreecommitdiff
path: root/fs/fuse
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2019-10-23 15:26:37 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-11-06 14:43:23 +0300
commit525c270c1fac4ba6659e12ccd3348606e53fe9ea (patch)
treec5436ddc43275b9286fc4b1dc5d5c25fd12fc4a5 /fs/fuse
parentce1dee3c1a88a534f561310f16e2eed3e686f28a (diff)
downloadlinux-525c270c1fac4ba6659e12ccd3348606e53fe9ea.tar.xz
fuse: flush dirty data/metadata before non-truncate setattr
commit b24e7598db62386a95a3c8b9c75630c5d56fe077 upstream. If writeback cache is enabled, then writes might get reordered with chmod/chown/utimes. The problem with this is that performing the write in the fuse daemon might itself change some of these attributes. In such case the following sequence of operations will result in file ending up with the wrong mode, for example: int fd = open ("suid", O_WRONLY|O_CREAT|O_EXCL); write (fd, "1", 1); fchown (fd, 0, 0); fchmod (fd, 04755); close (fd); This patch fixes this by flushing pending writes before performing chown/chmod/utimes. Reported-by: Giuseppe Scrivano <gscrivan@redhat.com> Tested-by: Giuseppe Scrivano <gscrivan@redhat.com> Fixes: 4d99ff8f12eb ("fuse: Turn writeback cache on") Cc: <stable@vger.kernel.org> # v3.15+ Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/dir.c13
1 files changed, 13 insertions, 0 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index d933ecb7a08c..b79bba77652a 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1650,6 +1650,19 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
if (attr->ia_valid & ATTR_SIZE)
is_truncate = true;
+ /* Flush dirty data/metadata before non-truncate SETATTR */
+ if (is_wb && S_ISREG(inode->i_mode) &&
+ attr->ia_valid &
+ (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_MTIME_SET |
+ ATTR_TIMES_SET)) {
+ err = write_inode_now(inode, true);
+ if (err)
+ return err;
+
+ fuse_set_nowrite(inode);
+ fuse_release_nowrite(inode);
+ }
+
if (is_truncate) {
fuse_set_nowrite(inode);
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);