The branch main has been updated by asomers:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=7124d2bc3a3bd58f6d3803a0579f2e0fa853b56d

commit 7124d2bc3a3bd58f6d3803a0579f2e0fa853b56d
Author:     Alan Somers <asom...@freebsd.org>
AuthorDate: 2021-09-25 03:53:28 +0000
Commit:     Alan Somers <asom...@freebsd.org>
CommitDate: 2021-09-27 03:57:29 +0000

    fusefs: implement FUSE_NO_OPEN_SUPPORT and FUSE_NO_OPENDIR_SUPPORT
    
    For file systems that allow it, fusefs will skip FUSE_OPEN,
    FUSE_RELEASE, FUSE_OPENDIR, and FUSE_RELEASEDIR operations, a minor
    optimization.
    
    MFC after:      2 weeks
    Reviewed by:    pfg
    Differential Revision: https://reviews.freebsd.org/D32141
---
 sys/fs/fuse/fuse_file.c        | 65 +++++++++++++++++++++++++++++------------
 sys/fs/fuse/fuse_file.h        |  3 +-
 sys/fs/fuse/fuse_internal.c    |  8 +++--
 sys/fs/fuse/fuse_ipc.h         |  2 ++
 sys/fs/fuse/fuse_kernel.h      |  4 ++-
 tests/sys/fs/fusefs/open.cc    | 66 ++++++++++++++++++++++++++++++++++++++++++
 tests/sys/fs/fusefs/opendir.cc | 48 ++++++++++++++++++++++++++++++
 7 files changed, 174 insertions(+), 22 deletions(-)

diff --git a/sys/fs/fuse/fuse_file.c b/sys/fs/fuse/fuse_file.c
index f9b0c781f49a..a7357d85a8b3 100644
--- a/sys/fs/fuse/fuse_file.c
+++ b/sys/fs/fuse/fuse_file.c
@@ -124,44 +124,68 @@ int
 fuse_filehandle_open(struct vnode *vp, int a_mode,
     struct fuse_filehandle **fufhp, struct thread *td, struct ucred *cred)
 {
+       struct mount *mp = vnode_mount(vp);
+       struct fuse_data *data = fuse_get_mpdata(mp);
        struct fuse_dispatcher fdi;
-       struct fuse_open_in *foi;
-       struct fuse_open_out *foo;
+       const struct fuse_open_out default_foo = {
+               .fh = 0,
+               .open_flags = FOPEN_KEEP_CACHE,
+               .padding = 0
+       };
+       struct fuse_open_in *foi = NULL;
+       const struct fuse_open_out *foo;
        fufh_type_t fufh_type;
-
+       int dataflags = data->dataflags;
        int err = 0;
        int oflags = 0;
        int op = FUSE_OPEN;
+       int relop = FUSE_RELEASE;
+       int fsess_no_op_support = FSESS_NO_OPEN_SUPPORT;
 
        fufh_type = fflags_2_fufh_type(a_mode);
        oflags = fufh_type_2_fflags(fufh_type);
 
        if (vnode_isdir(vp)) {
                op = FUSE_OPENDIR;
+               relop = FUSE_RELEASEDIR;
+               fsess_no_op_support = FSESS_NO_OPENDIR_SUPPORT;
                /* vn_open_vnode already rejects FWRITE on directories */
                MPASS(fufh_type == FUFH_RDONLY || fufh_type == FUFH_EXEC);
        }
        fdisp_init(&fdi, sizeof(*foi));
-       fdisp_make_vp(&fdi, op, vp, td, cred);
-
-       foi = fdi.indata;
-       foi->flags = oflags;
-
-       if ((err = fdisp_wait_answ(&fdi))) {
-               SDT_PROBE2(fusefs, , file, trace, 1,
-                       "OUCH ... daemon didn't give fh");
-               if (err == ENOENT) {
-                       fuse_internal_vnode_disappear(vp);
+       if (fsess_not_impl(mp, op) && dataflags & fsess_no_op_support) {
+               /* The operation implicitly succeeds */
+               foo = &default_foo;
+       } else {
+               fdisp_make_vp(&fdi, op, vp, td, cred);
+
+               foi = fdi.indata;
+               foi->flags = oflags;
+
+               err = fdisp_wait_answ(&fdi);
+               if (err == ENOSYS && dataflags & fsess_no_op_support) {
+                       /* The operation implicitly succeeds */
+                       foo = &default_foo;
+                       fsess_set_notimpl(mp, op);
+                       fsess_set_notimpl(mp, relop);
+                       err = 0;
+               } else if (err) {
+                       SDT_PROBE2(fusefs, , file, trace, 1,
+                               "OUCH ... daemon didn't give fh");
+                       if (err == ENOENT)
+                               fuse_internal_vnode_disappear(vp);
+                       goto out;
+               } else {
+                       foo = fdi.answ;
                }
-               goto out;
        }
-       foo = fdi.answ;
 
        fuse_filehandle_init(vp, fufh_type, fufhp, td, cred, foo);
        fuse_vnode_open(vp, foo->open_flags, td);
 
 out:
-       fdisp_destroy(&fdi);
+       if (foi)
+               fdisp_destroy(&fdi);
        return err;
 }
 
@@ -169,6 +193,7 @@ int
 fuse_filehandle_close(struct vnode *vp, struct fuse_filehandle *fufh,
     struct thread *td, struct ucred *cred)
 {
+       struct mount *mp = vnode_mount(vp);
        struct fuse_dispatcher fdi;
        struct fuse_release_in *fri;
 
@@ -180,6 +205,10 @@ fuse_filehandle_close(struct vnode *vp, struct 
fuse_filehandle *fufh,
        }
        if (vnode_isdir(vp))
                op = FUSE_RELEASEDIR;
+
+       if (fsess_not_impl(mp, op))
+               goto out;
+
        fdisp_init(&fdi, sizeof(*fri));
        fdisp_make_vp(&fdi, op, vp, td, cred);
        fri = fdi.indata;
@@ -327,8 +356,8 @@ fuse_filehandle_getrw(struct vnode *vp, int fflag,
 
 void
 fuse_filehandle_init(struct vnode *vp, fufh_type_t fufh_type,
-    struct fuse_filehandle **fufhp, struct thread *td, struct ucred *cred,
-    struct fuse_open_out *foo)
+    struct fuse_filehandle **fufhp, struct thread *td, const struct ucred 
*cred,
+    const struct fuse_open_out *foo)
 {
        struct fuse_vnode_data *fvdat = VTOFUD(vp);
        struct fuse_filehandle *fufh;
diff --git a/sys/fs/fuse/fuse_file.h b/sys/fs/fuse/fuse_file.h
index 10c799d5d40d..25004beead51 100644
--- a/sys/fs/fuse/fuse_file.h
+++ b/sys/fs/fuse/fuse_file.h
@@ -211,7 +211,8 @@ int fuse_filehandle_getrw(struct vnode *vp, int fflag,
 
 void fuse_filehandle_init(struct vnode *vp, fufh_type_t fufh_type,
                          struct fuse_filehandle **fufhp, struct thread *td,
-                         struct ucred *cred, struct fuse_open_out *foo);
+                         const struct ucred *cred,
+                         const struct fuse_open_out *foo);
 int fuse_filehandle_open(struct vnode *vp, int mode,
                          struct fuse_filehandle **fufhp, struct thread *td,
                          struct ucred *cred);
diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c
index 731ac9c2ba24..c52f12d3a71a 100644
--- a/sys/fs/fuse/fuse_internal.c
+++ b/sys/fs/fuse/fuse_internal.c
@@ -1009,6 +1009,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;
+                       if (fiio->flags & FUSE_NO_OPEN_SUPPORT)
+                               data->dataflags |= FSESS_NO_OPEN_SUPPORT;
+                       if (fiio->flags & FUSE_NO_OPENDIR_SUPPORT)
+                               data->dataflags |= FSESS_NO_OPENDIR_SUPPORT;
                        /* 
                         * Don't bother to check FUSE_BIG_WRITES, because it's
                         * redundant with max_write
@@ -1098,7 +1102,6 @@ fuse_internal_send_init(struct fuse_data *data, struct 
thread *td)
         * FUSE_DO_READDIRPLUS: not yet implemented
         * FUSE_READDIRPLUS_AUTO: not yet implemented
         * FUSE_ASYNC_DIO: not yet implemented
-        * FUSE_NO_OPEN_SUPPORT: not yet implemented
         * FUSE_PARALLEL_DIROPS: not yet implemented
         * FUSE_HANDLE_KILLPRIV: not yet implemented
         * FUSE_POSIX_ACL: not yet implemented
@@ -1107,7 +1110,8 @@ fuse_internal_send_init(struct fuse_data *data, struct 
thread *td)
         * FUSE_MAX_PAGES: not yet implemented
         */
        fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT
-               | FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE;
+               | FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE
+               | FUSE_NO_OPEN_SUPPORT | FUSE_NO_OPENDIR_SUPPORT;
 
        fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
        fuse_insert_message(fdi.tick, false);
diff --git a/sys/fs/fuse/fuse_ipc.h b/sys/fs/fuse/fuse_ipc.h
index 7b27fd97e212..fe616b3639a7 100644
--- a/sys/fs/fuse/fuse_ipc.h
+++ b/sys/fs/fuse/fuse_ipc.h
@@ -229,6 +229,8 @@ struct fuse_data {
                                          /* (and being observed by the daemon) 
*/
 #define FSESS_PUSH_SYMLINKS_IN    0x0020 /* prefix absolute symlinks with mp */
 #define FSESS_DEFAULT_PERMISSIONS 0x0040 /* kernel does permission checking */
+#define FSESS_NO_OPEN_SUPPORT     0x0080 /* can elide FUSE_OPEN ops */
+#define FSESS_NO_OPENDIR_SUPPORT  0x0100 /* can elide FUSE_OPENDIR ops */
 #define FSESS_ASYNC_READ          0x1000 /* allow multiple reads of some file 
*/
 #define FSESS_POSIX_LOCKS         0x2000 /* daemon supports POSIX locks */
 #define FSESS_EXPORT_SUPPORT      0x10000 /* daemon supports NFS-style lookups 
*/
diff --git a/sys/fs/fuse/fuse_kernel.h b/sys/fs/fuse/fuse_kernel.h
index bd7323e9def2..51445637b9a8 100644
--- a/sys/fs/fuse/fuse_kernel.h
+++ b/sys/fs/fuse/fuse_kernel.h
@@ -198,7 +198,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 28
+#define FUSE_KERNEL_MINOR_VERSION 29
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -300,6 +300,7 @@ struct fuse_file_lock {
  * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED
  * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
  * FUSE_CACHE_SYMLINKS: cache READLINK responses
+ * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
  */
 #define FUSE_ASYNC_READ                (1 << 0)
 #define FUSE_POSIX_LOCKS       (1 << 1)
@@ -325,6 +326,7 @@ struct fuse_file_lock {
 #define FUSE_ABORT_ERROR       (1 << 21)
 #define FUSE_MAX_PAGES         (1 << 22)
 #define FUSE_CACHE_SYMLINKS    (1 << 23)
+#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
 
 #ifdef linux
 /**
diff --git a/tests/sys/fs/fusefs/open.cc b/tests/sys/fs/fusefs/open.cc
index ede7ea9f04f7..7ac177a65d14 100644
--- a/tests/sys/fs/fusefs/open.cc
+++ b/tests/sys/fs/fusefs/open.cc
@@ -73,6 +73,13 @@ void test_ok(int os_flags, int fuse_flags) {
 };
 
 
+class OpenNoOpenSupport: public FuseTest {
+       virtual void SetUp() {
+               m_init_flags = FUSE_NO_OPEN_SUPPORT;
+               FuseTest::SetUp();
+       }
+};
+
 /* 
  * fusefs(5) does not support I/O on device nodes (neither does UFS).  But it
  * shouldn't crash
@@ -274,3 +281,62 @@ TEST_F(Open, o_rdwr)
        test_ok(O_RDWR, O_RDWR);
 }
 
+/*
+ * Without FUSE_NO_OPEN_SUPPORT, returning ENOSYS is an error
+ */
+TEST_F(Open, enosys)
+{
+       const char FULLPATH[] = "mountpoint/some_file.txt";
+       const char RELPATH[] = "some_file.txt";
+       uint64_t ino = 42;
+       int fd;
+
+       FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+       EXPECT_CALL(*m_mock, process(
+               ResultOf([=](auto in) {
+                       return (in.header.opcode == FUSE_OPEN &&
+                               in.body.open.flags == (uint32_t)O_RDONLY &&
+                               in.header.nodeid == ino);
+               }, Eq(true)),
+               _)
+       ).Times(1)
+       .WillOnce(Invoke(ReturnErrno(ENOSYS)));
+
+       fd = open(FULLPATH, O_RDONLY);
+       ASSERT_EQ(-1, fd) << strerror(errno);
+       EXPECT_EQ(ENOSYS, errno);
+}
+
+/*
+ * If a fuse server sets FUSE_NO_OPEN_SUPPORT and returns ENOSYS to a
+ * FUSE_OPEN, then it and subsequent FUSE_OPEN and FUSE_RELEASE operations will
+ * also succeed automatically without being sent to the server.
+ */
+TEST_F(OpenNoOpenSupport, enosys)
+{
+       const char FULLPATH[] = "mountpoint/some_file.txt";
+       const char RELPATH[] = "some_file.txt";
+       uint64_t ino = 42;
+       int fd;
+
+       FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+       EXPECT_CALL(*m_mock, process(
+               ResultOf([=](auto in) {
+                       return (in.header.opcode == FUSE_OPEN &&
+                               in.body.open.flags == (uint32_t)O_RDONLY &&
+                               in.header.nodeid == ino);
+               }, Eq(true)),
+               _)
+       ).Times(1)
+       .WillOnce(Invoke(ReturnErrno(ENOSYS)));
+       expect_flush(ino, 1, ReturnErrno(ENOSYS));
+
+       fd = open(FULLPATH, O_RDONLY);
+       ASSERT_LE(0, fd) << strerror(errno);
+       close(fd);
+
+       fd = open(FULLPATH, O_RDONLY);
+       ASSERT_LE(0, fd) << strerror(errno);
+
+       leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/opendir.cc b/tests/sys/fs/fusefs/opendir.cc
index 7943be3124ca..365aa8dff9e2 100644
--- a/tests/sys/fs/fusefs/opendir.cc
+++ b/tests/sys/fs/fusefs/opendir.cc
@@ -73,6 +73,13 @@ void expect_opendir(uint64_t ino, uint32_t flags, 
ProcessMockerT r)
 
 };
 
+class OpendirNoOpendirSupport: public Opendir {
+       virtual void SetUp() {
+               m_init_flags = FUSE_NO_OPENDIR_SUPPORT;
+               FuseTest::SetUp();
+       }
+};
+
 
 /* 
  * The fuse daemon fails the request with enoent.  This usually indicates a
@@ -172,3 +179,44 @@ TEST_F(Opendir, opendir)
        errno = 0;
        EXPECT_NE(nullptr, opendir(FULLPATH)) << strerror(errno);
 }
+
+/*
+ * Without FUSE_NO_OPENDIR_SUPPORT, returning ENOSYS is an error
+ */
+TEST_F(Opendir, enosys)
+{
+       const char FULLPATH[] = "mountpoint/some_file.txt";
+       const char RELPATH[] = "some_file.txt";
+       uint64_t ino = 42;
+
+       expect_lookup(RELPATH, ino);
+       expect_opendir(ino, O_RDONLY, ReturnErrno(ENOSYS));
+
+       EXPECT_EQ(-1, open(FULLPATH, O_DIRECTORY));
+       EXPECT_EQ(ENOSYS, errno);
+}
+
+/*
+ * If a fuse server sets FUSE_NO_OPENDIR_SUPPORT and returns ENOSYS to a
+ * FUSE_OPENDIR, then it and subsequent FUSE_OPENDIR and FUSE_RELEASEDIR
+ * operations will also succeed automatically without being sent to the server.
+ */
+TEST_F(OpendirNoOpendirSupport, enosys)
+{
+       const char FULLPATH[] = "mountpoint/some_file.txt";
+       const char RELPATH[] = "some_file.txt";
+       uint64_t ino = 42;
+       int fd;
+
+       FuseTest::expect_lookup(RELPATH, ino, S_IFDIR | 0755, 0, 2);
+       expect_opendir(ino, O_RDONLY, ReturnErrno(ENOSYS));
+
+       fd = open(FULLPATH, O_DIRECTORY);
+       ASSERT_LE(0, fd) << strerror(errno);
+       close(fd);
+
+       fd = open(FULLPATH, O_DIRECTORY);
+       ASSERT_LE(0, fd) << strerror(errno);
+
+       leak(fd);
+}
_______________________________________________
dev-commits-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "dev-commits-src-all-unsubscr...@freebsd.org"

Reply via email to