| From b24e7598db62386a95a3c8b9c75630c5d56fe077 Mon Sep 17 00:00:00 2001 |
| From: Miklos Szeredi <mszeredi@redhat.com> |
| Date: Wed, 23 Oct 2019 14:26:37 +0200 |
| Subject: fuse: flush dirty data/metadata before non-truncate setattr |
| |
| From: Miklos Szeredi <mszeredi@redhat.com> |
| |
| 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> |
| |
| --- |
| fs/fuse/dir.c | 13 +++++++++++++ |
| 1 file changed, 13 insertions(+) |
| |
| --- a/fs/fuse/dir.c |
| +++ b/fs/fuse/dir.c |
| @@ -1650,6 +1650,19 @@ int fuse_do_setattr(struct dentry *dentr |
| 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); |