fusefs: set FUSE_WRITE_CACHE when writing from cache

This bit tells the server that we're not sure which uid, gid, and/or pid
originated the write.  I don't know of a single file system that cares, but
it's part of the protocol.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-05-27 21:36:28 +00:00
parent 7637cc62ab
commit bda39894c5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=348313
6 changed files with 86 additions and 52 deletions

View File

@ -119,7 +119,7 @@ fuse_read_biobackend(struct vnode *vp, struct uio *uio, int ioflag,
static int
fuse_write_directbackend(struct vnode *vp, struct uio *uio,
struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
int ioflag);
int ioflag, bool pages);
static int
fuse_write_biobackend(struct vnode *vp, struct uio *uio,
struct ucred *cred, struct fuse_filehandle *fufh, int ioflag, pid_t pid);
@ -245,7 +245,7 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag, bool pages,
if (!pages )
v_inval_buf_range(vp, start, end, iosize);
err = fuse_write_directbackend(vp, uio, cred, fufh,
filesize, ioflag);
filesize, ioflag, pages);
} else {
SDT_PROBE2(fusefs, , io, trace, 1,
"buffered write of vnode");
@ -405,7 +405,7 @@ fuse_read_directbackend(struct vnode *vp, struct uio *uio,
static int
fuse_write_directbackend(struct vnode *vp, struct uio *uio,
struct ucred *cred, struct fuse_filehandle *fufh, off_t filesize,
int ioflag)
int ioflag, bool pages)
{
struct fuse_vnode_data *fvdat = VTOFUD(vp);
struct fuse_data *data;
@ -418,9 +418,29 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
int diff;
int err = 0;
bool direct_io = fufh->fuse_open_flags & FOPEN_DIRECT_IO;
uint32_t write_flags;
data = fuse_get_mpdata(vp->v_mount);
/*
* Don't set FUSE_WRITE_LOCKOWNER in write_flags. It can't be set
* accurately when using POSIX AIO, libfuse doesn't use it, and I'm not
* aware of any file systems that do. It was an attempt to add
* Linux-style mandatory locking to the FUSE protocol, but mandatory
* locking is deprecated even on Linux. See Linux commit
* f33321141b273d60cbb3a8f56a5489baad82ba5e .
*/
/*
* Set FUSE_WRITE_CACHE whenever we don't know the uid, gid, and/or pid
* that originated a write. For example when writing from the
* writeback cache. I don't know of a single file system that cares,
* but the protocol says we're supposed to do this.
*/
write_flags = !pages && (
(ioflag & IO_DIRECT) ||
!fsess_opt_datacache(vnode_mount(vp)) ||
fuse_data_cache_mode != FUSE_CACHE_WB) ? 0 : FUSE_WRITE_CACHE;
if (uio->uio_resid == 0)
return (0);
@ -439,17 +459,8 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
fwi->fh = fufh->fh_id;
fwi->offset = uio->uio_offset;
fwi->size = chunksize;
fwi->write_flags = write_flags;
if (fuse_libabi_geq(data, 7, 9)) {
/*
* Don't set FUSE_WRITE_LOCKOWNER. It can't be set
* accurately when using POSIX AIO, libfuse doesn't use
* it, and I'm not aware of any file systems that do.
* It was an attempt to add Linux-style mandatory
* locking to the FUSE protocol, but mandatory locking
* is deprecated even on Linux. See Linux commit
* f33321141b273d60cbb3a8f56a5489baad82ba5e .
*/
fwi->write_flags = 0;
fwi->flags = 0; /* TODO */
fwi_data = (char *)fdi.indata + sizeof(*fwi);
} else {
@ -525,6 +536,7 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
fwi->fh = fufh->fh_id;
fwi->offset = as_written_offset;
fwi->size = diff;
fwi->write_flags = write_flags;
goto retry;
}
}
@ -884,7 +896,7 @@ fuse_io_strategy(struct vnode *vp, struct buf *bp)
uiop->uio_rw = UIO_WRITE;
error = fuse_write_directbackend(vp, uiop, cred, fufh,
filesize, 0);
filesize, 0, false);
if (error == EINTR || error == ETIMEDOUT
|| (!error && (bp->b_flags & B_NEEDCOMMIT))) {

View File

@ -1229,7 +1229,7 @@ TEST_F(Write, clear_suid)
expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
expect_open(ino, 0, 1);
expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf);
expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
expect_chmod(ino, newmode, sizeof(wbuf));
fd = open(FULLPATH, O_WRONLY);
@ -1255,7 +1255,7 @@ TEST_F(Write, clear_sgid)
expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
expect_open(ino, 0, 1);
expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf);
expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
expect_chmod(ino, newmode, sizeof(wbuf));
fd = open(FULLPATH, O_WRONLY);
@ -1285,7 +1285,7 @@ TEST_F(Write, recursion_panic_while_clearing_suid)
expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
expect_open(ino, 0, 1);
expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf);
expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
/* XXX Return a smaller file size than what we just wrote! */
expect_chmod(ino, newmode, 0);

View File

@ -75,7 +75,7 @@ void expect_lookup(const char *relpath, uint64_t ino)
void expect_write(uint64_t ino, uint64_t size, const void *contents)
{
FuseTest::expect_write(ino, 0, size, size, 0, contents);
FuseTest::expect_write(ino, 0, size, size, 0, 0, contents);
}
};

View File

@ -377,15 +377,17 @@ void FuseTest::expect_unlink(uint64_t parent, const char *path, int error)
}
void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, uint32_t flags, const void *contents)
uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
const char *buf = (const char*)in.body.bytes +
sizeof(struct fuse_write_in);
bool pid_ok;
uint32_t wf = in.body.write.write_flags;
if (in.body.write.write_flags & FUSE_WRITE_CACHE)
if (wf & FUSE_WRITE_CACHE)
pid_ok = true;
else
pid_ok = (pid_t)in.header.pid == getpid();
@ -396,7 +398,8 @@ void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
in.body.write.offset == offset &&
in.body.write.size == isize &&
pid_ok &&
in.body.write.write_flags == flags &&
(wf & flags_set) == flags_set &&
(wf & flags_unset) == 0 &&
0 == bcmp(buf, contents, isize));
}, Eq(true)),
_)
@ -407,7 +410,7 @@ void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
}
void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, uint32_t flags, const void *contents)
uint64_t osize, const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
@ -420,7 +423,6 @@ void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
in.body.write.offset == offset &&
in.body.write.size == isize &&
pid_ok &&
in.body.write.write_flags == flags &&
0 == bcmp(buf, contents, isize));
}, Eq(true)),
_)

View File

@ -169,15 +169,18 @@ class FuseTest : public ::testing::Test {
/*
* Create an expectation that FUSE_WRITE will be called exactly once
* for the given inode, at offset offset, with write_flags flags,
* size isize and buffer contents. It will return osize
* for the given inode, at offset offset, with size isize and buffer
* contents. Any flags present in flags_set must be set, and any
* present in flags_unset must not be set. Other flags are don't care.
* It will return osize.
*/
void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, uint32_t flags, const void *contents);
uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
const void *contents);
/* Protocol 7.8 version of expect_write */
void expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, uint32_t flags, const void *contents);
uint64_t osize, const void *contents);
/*
* Helper that runs code in a child process.

View File

@ -65,6 +65,12 @@ void expect_release(uint64_t ino, ProcessMockerT r)
).WillRepeatedly(Invoke(r));
}
void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, const void *contents)
{
FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
}
};
class Write_7_8: public FuseTest {
@ -100,7 +106,7 @@ virtual void SetUp() {
/* Tests for the write-through cache mode */
class WriteThrough: public Write {
public:
virtual void SetUp() {
const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
int val = 0;
@ -117,11 +123,17 @@ virtual void SetUp() {
"(writethrough) for this test";
}
void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, const void *contents)
{
FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE,
contents);
}
};
/* Tests for the writeback cache mode */
class WriteBack: public Write {
public:
virtual void SetUp() {
const char *node = "vfs.fusefs.data_cache_mode";
int val = 0;
@ -138,6 +150,12 @@ virtual void SetUp() {
"(writeback) for this test";
}
void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, const void *contents)
{
FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
contents);
}
};
/* AIO writes need to set the header's pid field correctly */
@ -155,7 +173,7 @@ TEST_F(AioWrite, DISABLED_aio_write)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, offset, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -196,7 +214,7 @@ TEST_F(Write, append)
expect_lookup(RELPATH, ino, initial_offset);
expect_open(ino, 0, 1);
expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
fd = open(FULLPATH, O_RDWR | O_APPEND);
@ -218,7 +236,7 @@ TEST_F(Write, append_direct_io)
expect_lookup(RELPATH, ino, initial_offset);
expect_open(ino, FOPEN_DIRECT_IO, 1);
expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
fd = open(FULLPATH, O_WRONLY | O_APPEND);
EXPECT_LE(0, fd) << strerror(errno);
@ -242,7 +260,7 @@ TEST_F(Write, direct_io_evicts_cache)
expect_lookup(RELPATH, ino, bufsize);
expect_open(ino, 0, 1);
expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1);
expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
fd = open(FULLPATH, O_RDWR);
EXPECT_LE(0, fd) << strerror(errno);
@ -285,9 +303,8 @@ TEST_F(Write, indirect_io_short_write)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, bufsize, bufsize0, 0, CONTENTS);
expect_write(ino, bufsize0, bufsize1, bufsize1, 0,
contents1);
expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -312,7 +329,7 @@ TEST_F(Write, direct_io_short_write)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, FOPEN_DIRECT_IO, 1);
expect_write(ino, 0, bufsize, halfbufsize, 0, CONTENTS);
expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -342,7 +359,7 @@ TEST_F(Write, direct_io_short_write_iov)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, FOPEN_DIRECT_IO, 1);
expect_write(ino, 0, totalsize, size0, 0, EXPECTED0);
expect_write(ino, 0, totalsize, size0, EXPECTED0);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -388,9 +405,8 @@ TEST_F(Write, mmap)
/*
* Writes from the pager may or may not be associated with the correct
* pid, so they must set FUSE_WRITE_CACHE.
* TODO: expect FUSE_WRITE_CACHE after upgrading to protocol 7.9
*/
expect_write(ino, 0, len, len, 0, expected);
FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
expect_flush(ino, 1, ReturnErrno(0));
expect_release(ino, ReturnErrno(0));
@ -435,7 +451,7 @@ TEST_F(WriteThrough, evicts_read_cache)
expect_lookup(RELPATH, ino, bufsize);
expect_open(ino, 0, 1);
expect_read(ino, 0, bufsize, bufsize, contents0);
expect_write(ino, 0, wrsize, wrsize, 0, contents1);
expect_write(ino, 0, wrsize, wrsize, contents1);
fd = open(FULLPATH, O_RDWR);
EXPECT_LE(0, fd) << strerror(errno);
@ -468,7 +484,7 @@ TEST_F(WriteThrough, pwrite)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, offset, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -489,7 +505,7 @@ TEST_F(Write, write)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, 0, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -518,8 +534,8 @@ TEST_F(Write, write_large)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, halfbufsize, halfbufsize, 0, contents);
expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0,
expect_write(ino, 0, halfbufsize, halfbufsize, contents);
expect_write(ino, halfbufsize, halfbufsize, halfbufsize,
&contents[halfbufsize / sizeof(int)]);
fd = open(FULLPATH, O_WRONLY);
@ -561,7 +577,7 @@ TEST_F(Write_7_8, write)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write_7_8(ino, 0, bufsize, bufsize, 0, CONTENTS);
expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -582,7 +598,7 @@ TEST_F(WriteBack, close)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, 0, bufsize, bufsize, CONTENTS);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_SETATTR);
@ -621,7 +637,7 @@ TEST_F(WriteBack, rmw)
FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
expect_open(ino, 0, 1);
expect_read(ino, 0, fsize, fsize, INITIAL);
expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, offset, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -646,7 +662,7 @@ TEST_F(WriteBack, writeback)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, 0, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_RDWR);
EXPECT_LE(0, fd) << strerror(errno);
@ -678,7 +694,8 @@ TEST_F(WriteBack, o_direct)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
CONTENTS);
expect_read(ino, 0, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_RDWR | O_DIRECT);
@ -711,7 +728,7 @@ TEST_F(WriteThrough, DISABLED_writethrough)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, 0, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_RDWR);
EXPECT_LE(0, fd) << strerror(errno);
@ -738,7 +755,7 @@ TEST_F(WriteThrough, update_file_size)
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
expect_write(ino, 0, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_RDWR);
EXPECT_LE(0, fd) << strerror(errno);