fusefs: set the flags fields of fuse_write_in and fuse_read_in

These fields are supposed to contain the file descriptor flags as supplied
to open(2) or set by fcntl(2).  The feature is kindof useless on FreeBSD
since we don't supply all of these flags to fuse (because of the weak
relationship between struct file and struct vnode).  But we should at least
set the access mode flags (O_RDONLY, etc).

This is the last fusefs change needed to get full protocol 7.9 support.
There are still a few options we don't support for good reason (mandatory
file locking is dumb, flock support is broken in the protocol until 7.17,
etc), but there's nothing else to do at this protocol level.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Alan Somers 2019-05-28 01:09:19 +00:00
parent 8aa24ed381
commit d4fd0c8148
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=348319
8 changed files with 64 additions and 46 deletions

View File

@ -114,44 +114,6 @@ fflags_2_fufh_type(int fflags)
panic("FUSE: What kind of a flag is this (%x)?", fflags);
}
/*
* Get the flags to use for FUSE_CREATE, FUSE_OPEN and FUSE_RELEASE
*
* These are supposed to be the same as the flags argument to open(2).
* However, since we can't reliably associate a fuse_filehandle with a specific
* file descriptor it would would be dangerous to include anything more than
* the access mode flags. For example, suppose we open a file twice, once with
* O_APPEND and once without. Then the user pwrite(2)s to offset using the
* second file descriptor. If fusefs uses the first file handle, then the
* server may append the write to the end of the file rather than at offset 0.
* To prevent problems like this, we only ever send the portion of flags
* related to access mode.
*
* It's essential to send that portion, because FUSE uses it for server-side
* authorization.
*
* TODO: consider sending O_APPEND after upgrading to protocol 7.9, which
* includes flags in fuse_write_in.
*/
static inline int
fufh_type_2_fflags(fufh_type_t type)
{
int oflags = -1;
switch (type) {
case FUFH_RDONLY:
case FUFH_WRONLY:
case FUFH_RDWR:
case FUFH_EXEC:
oflags = type;
break;
default:
break;
}
return oflags;
}
int
fuse_filehandle_open(struct vnode *vp, int a_mode,
struct fuse_filehandle **fufhp, struct thread *td, struct ucred *cred)

View File

@ -148,6 +148,41 @@ struct fuse_filehandle {
#define FUFH_IS_VALID(f) ((f)->fufh_type != FUFH_INVALID)
/*
* Get the flags to use for FUSE_CREATE, FUSE_OPEN and FUSE_RELEASE
*
* These are supposed to be the same as the flags argument to open(2).
* However, since we can't reliably associate a fuse_filehandle with a specific
* file descriptor it would would be dangerous to include anything more than
* the access mode flags. For example, suppose we open a file twice, once with
* O_APPEND and once without. Then the user pwrite(2)s to offset using the
* second file descriptor. If fusefs uses the first file handle, then the
* server may append the write to the end of the file rather than at offset 0.
* To prevent problems like this, we only ever send the portion of flags
* related to access mode.
*
* It's essential to send that portion, because FUSE uses it for server-side
* authorization.
*/
static inline int
fufh_type_2_fflags(fufh_type_t type)
{
int oflags = -1;
switch (type) {
case FUFH_RDONLY:
case FUFH_WRONLY:
case FUFH_RDWR:
case FUFH_EXEC:
oflags = type;
break;
default:
break;
}
return oflags;
}
bool fuse_filehandle_validrw(struct vnode *vp, int mode,
struct ucred *cred, pid_t pid);
int fuse_filehandle_get(struct vnode *vp, int fflag,

View File

@ -779,6 +779,10 @@ fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
data->dataflags |= FSESS_POSIX_LOCKS;
if (fiio->flags & FUSE_EXPORT_SUPPORT)
data->dataflags |= FSESS_EXPORT_SUPPORT;
/*
* Don't bother to check FUSE_BIG_WRITES, because it's
* redundant with max_write
*/
} else {
err = EINVAL;
}
@ -816,7 +820,13 @@ fuse_internal_send_init(struct fuse_data *data, struct thread *td)
* the size of a buffer cache block.
*/
fiii->max_readahead = maxbcachebuf;
fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT ;
/*
* Unsupported features:
* FUSE_FILE_OPS: No known FUSE server or client supports it
* FUSE_ATOMIC_O_TRUNC: our VFS cannot support it
*/
fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT
| FUSE_BIG_WRITES;
fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
fuse_insert_message(fdi.tick, false);

View File

@ -380,7 +380,7 @@ fuse_read_directbackend(struct vnode *vp, struct uio *uio,
if (fuse_libabi_geq(data, 7, 9)) {
/* See comment regarding FUSE_WRITE_LOCKOWNER */
fri->read_flags = 0;
fri->flags = 0; /* TODO */
fri->flags = fufh_type_2_fflags(fufh->fufh_type);
}
SDT_PROBE1(fusefs, , io, read_directbackend_start, fri);
@ -461,7 +461,7 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
fwi->size = chunksize;
fwi->write_flags = write_flags;
if (fuse_libabi_geq(data, 7, 9)) {
fwi->flags = 0; /* TODO */
fwi->flags = fufh_type_2_fflags(fufh->fufh_type);
fwi_data = (char *)fdi.indata + sizeof(*fwi);
} else {
fwi_data = (char *)fdi.indata +

View File

@ -218,6 +218,8 @@ void debug_fuseop(const mockfs_buf_in &in)
printf(" offset=%" PRIu64 " size=%u",
in.body.read.offset,
in.body.read.size);
if (verbosity > 1)
printf(" flags=%#x", in.body.read.flags);
break;
case FUSE_READDIR:
printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
@ -278,10 +280,12 @@ void debug_fuseop(const mockfs_buf_in &in)
break;
case FUSE_WRITE:
printf(" fh=%#" PRIx64 " offset=%" PRIu64
" size=%u flags=%u",
" size=%u write_flags=%u",
in.body.write.fh,
in.body.write.offset, in.body.write.size,
in.body.write.write_flags);
if (verbosity > 1)
printf(" flags=%#x", in.body.write.flags);
break;
default:
break;

View File

@ -36,6 +36,7 @@ extern "C" {
#include <sys/wait.h>
#include <dirent.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <semaphore.h>
@ -273,7 +274,7 @@ void FuseTest::expect_opendir(uint64_t ino)
}
void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, const void *contents)
uint64_t osize, const void *contents, int flags)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
@ -281,7 +282,11 @@ void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
in.header.nodeid == ino &&
in.body.read.fh == FH &&
in.body.read.offset == offset &&
in.body.read.size == isize);
in.body.read.size == isize &&
flags == -1 ?
(in.body.read.flags == O_RDONLY ||
in.body.read.flags == O_RDWR)
: in.body.read.flags == (uint32_t)flags);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
@ -400,6 +405,8 @@ void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
pid_ok &&
(wf & flags_set) == flags_set &&
(wf & flags_unset) == 0 &&
(in.body.write.flags == O_WRONLY ||
in.body.write.flags == O_RDWR) &&
0 == bcmp(buf, contents, isize));
}, Eq(true)),
_)

View File

@ -139,7 +139,7 @@ class FuseTest : public ::testing::Test {
* nothing currently validates the size of the fuse_read_in struct.
*/
void expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, const void *contents);
uint64_t osize, const void *contents, int flags = -1);
/*
* Create an expectation that FUSE_READIR will be called any number of

View File

@ -636,7 +636,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_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
expect_write(ino, offset, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_WRONLY);