summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2019-10-23 08:26:37 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2019-10-23 08:26:37 -0400
commitb24e7598db62386a95a3c8b9c75630c5d56fe077 (patch)
treec9e079d1a9a3e9fe320fbe0298aa3cd6c975510d
parent80da5a809d193c60d090cbdf4fe316781bc07965 (diff)
fuse: flush dirty data/metadata before non-truncate setattr
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>
-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 b77954a27538..54d638f9ba1c 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1522,6 +1522,19 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
1522 is_truncate = true; 1522 is_truncate = true;
1523 } 1523 }
1524 1524
1525 /* Flush dirty data/metadata before non-truncate SETATTR */
1526 if (is_wb && S_ISREG(inode->i_mode) &&
1527 attr->ia_valid &
1528 (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_MTIME_SET |
1529 ATTR_TIMES_SET)) {
1530 err = write_inode_now(inode, true);
1531 if (err)
1532 return err;
1533
1534 fuse_set_nowrite(inode);
1535 fuse_release_nowrite(inode);
1536 }
1537
1525 if (is_truncate) { 1538 if (is_truncate) {
1526 fuse_set_nowrite(inode); 1539 fuse_set_nowrite(inode);
1527 set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); 1540 set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);