The branch main has been updated by asomers:

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

commit f6e5319550f60170840f1a07a9cbdd45b5014a21
Author:     Alan Somers <asom...@freebsd.org>
AuthorDate: 2022-10-11 23:00:07 +0000
Commit:     Alan Somers <asom...@freebsd.org>
CommitDate: 2022-10-19 01:11:49 +0000

    fusefs: fix VOP_ADVLOCK with SEEK_END
    
    When the user specifies SEEK_END, unlike SEEK_CUR, VOP_ADVLOCK must
    adjust lock offsets itself.
    
    Sort-of related to bug 266886.
    
    MFC after:      2 weeks
    Reviewed by:    emaste
    Differential Revision: https://reviews.freebsd.org/D37040
---
 sys/fs/fuse/fuse_vnops.c      |  33 +++++++++++-
 tests/sys/fs/fusefs/locks.cc  | 122 ++++++++++++++++++++++++++++++++++++++++++
 tests/sys/fs/fusefs/mockfs.cc |  14 ++++-
 3 files changed, 166 insertions(+), 3 deletions(-)

diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
index 5ad4f6bebd08..a088fe318d81 100644
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -478,7 +478,9 @@ fuse_vnop_advlock(struct vop_advlock_args *ap)
        struct fuse_dispatcher fdi;
        struct fuse_lk_in *fli;
        struct fuse_lk_out *flo;
+       struct vattr vattr;
        enum fuse_opcode op;
+       off_t size, start;
        int dataflags, err;
        int flags = ap->a_flags;
 
@@ -513,6 +515,33 @@ fuse_vnop_advlock(struct vop_advlock_args *ap)
 
        vn_lock(vp, LK_SHARED | LK_RETRY);
 
+       switch (fl->l_whence) {
+       case SEEK_SET:
+       case SEEK_CUR:
+               /*
+                * Caller is responsible for adding any necessary offset
+                * when SEEK_CUR is used.
+                */
+               start = fl->l_start;
+               break;
+
+       case SEEK_END:
+               err = fuse_internal_getattr(vp, &vattr, cred, td);
+               if (err)
+                       goto out;
+               size = vattr.va_size;
+               if (size > OFF_MAX ||
+                   (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) {
+                       err = EOVERFLOW;
+                       goto out;
+               }
+               start = size + fl->l_start;
+               break;
+
+       default:
+               return (EINVAL);
+       }
+
        err = fuse_filehandle_get_anyflags(vp, &fufh, cred, pid);
        if (err)
                goto out;
@@ -523,9 +552,9 @@ fuse_vnop_advlock(struct vop_advlock_args *ap)
        fli = fdi.indata;
        fli->fh = fufh->fh_id;
        fli->owner = td->td_proc->p_pid;
-       fli->lk.start = fl->l_start;
+       fli->lk.start = start;
        if (fl->l_len != 0)
-               fli->lk.end = fl->l_start + fl->l_len - 1;
+               fli->lk.end = start + fl->l_len - 1;
        else
                fli->lk.end = INT64_MAX;
        fli->lk.type = fl->l_type;
diff --git a/tests/sys/fs/fusefs/locks.cc b/tests/sys/fs/fusefs/locks.cc
index f637c65cff24..c7c652dd573b 100644
--- a/tests/sys/fs/fusefs/locks.cc
+++ b/tests/sys/fs/fusefs/locks.cc
@@ -420,6 +420,71 @@ TEST_F(Getlk, seek_cur)
        leak(fd);
 }
 
+/*
+ * F_GETLK with SEEK_END
+ */
+TEST_F(Getlk, seek_end)
+{
+       const char FULLPATH[] = "mountpoint/some_file.txt";
+       const char RELPATH[] = "some_file.txt";
+       uint64_t ino = 42;
+       struct flock fl;
+       int fd;
+       pid_t pid = getpid();
+
+       expect_lookup(RELPATH, ino, 1024);
+       expect_open(ino, 0, 1);
+       EXPECT_CALL(*m_mock, process(
+               ResultOf([=](auto in) {
+                       return (in.header.opcode == FUSE_GETLK &&
+                               in.header.nodeid == ino &&
+                               in.body.getlk.fh == FH &&
+                               /*
+                                * Though it seems useless, libfuse expects the
+                                * owner and pid fields to be set during
+                                * FUSE_GETLK.
+                                */
+                               in.body.getlk.owner == (uint32_t)pid &&
+                               in.body.getlk.lk.pid == (uint64_t)pid &&
+                               in.body.getlk.lk.start == 512 &&
+                               in.body.getlk.lk.end == 1023 &&
+                               in.body.getlk.lk.type == F_RDLCK);
+               }, Eq(true)),
+               _)
+       ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+               SET_OUT_HEADER_LEN(out, getlk);
+               out.body.getlk.lk.start = 400;
+               out.body.getlk.lk.end = 499;
+               out.body.getlk.lk.type = F_WRLCK;
+               out.body.getlk.lk.pid = (uint32_t)pid + 1;
+       })));
+
+       fd = open(FULLPATH, O_RDWR);
+       ASSERT_LE(0, fd) << strerror(errno);
+       ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
+
+       fl.l_start = -512;
+       fl.l_len = 512;
+       fl.l_pid = 42;
+       fl.l_type = F_RDLCK;
+       fl.l_whence = SEEK_END;
+       fl.l_sysid = 0;
+       ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
+
+       /*
+        * After a successful F_GETLK request, the value of l_whence is
+        * SEEK_SET.
+        */
+       EXPECT_EQ(F_WRLCK, fl.l_type);
+       EXPECT_EQ(fl.l_pid, pid + 1);
+       EXPECT_EQ(fl.l_start, 400);
+       EXPECT_EQ(fl.l_len, 100);
+       EXPECT_EQ(fl.l_whence, SEEK_SET);
+       ASSERT_EQ(fl.l_sysid, 0);
+
+       leak(fd);
+}
+
 /*
  * If the fuse filesystem does not support posix file locks, then the kernel
  * should fall back to local locks.
@@ -525,6 +590,63 @@ TEST_F(Setlk, set_eof)
        leak(fd);
 }
 
+/* Set a new lock with FUSE_SETLK, using SEEK_CUR for l_whence */
+TEST_F(Setlk, set_seek_cur)
+{
+       const char FULLPATH[] = "mountpoint/some_file.txt";
+       const char RELPATH[] = "some_file.txt";
+       uint64_t ino = 42;
+       struct flock fl;
+       int fd;
+       pid_t pid = getpid();
+
+       expect_lookup(RELPATH, ino, 1024);
+       expect_open(ino, 0, 1);
+       expect_setlk(ino, pid, 500, 509, F_RDLCK, 0);
+
+       fd = open(FULLPATH, O_RDWR);
+       ASSERT_LE(0, fd) << strerror(errno);
+       ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
+
+       fl.l_start = 0;
+       fl.l_len = 10;
+       fl.l_pid = 0;
+       fl.l_type = F_RDLCK;
+       fl.l_whence = SEEK_CUR;
+       fl.l_sysid = 0;
+       ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+
+       leak(fd);
+}
+
+/* Set a new lock with FUSE_SETLK, using SEEK_END for l_whence */
+TEST_F(Setlk, set_seek_end)
+{
+       const char FULLPATH[] = "mountpoint/some_file.txt";
+       const char RELPATH[] = "some_file.txt";
+       uint64_t ino = 42;
+       struct flock fl;
+       int fd;
+       pid_t pid = getpid();
+
+       expect_lookup(RELPATH, ino, 1024);
+       expect_open(ino, 0, 1);
+       expect_setlk(ino, pid, 1000, 1009, F_RDLCK, 0);
+
+       fd = open(FULLPATH, O_RDWR);
+       ASSERT_LE(0, fd) << strerror(errno);
+
+       fl.l_start = -24;
+       fl.l_len = 10;
+       fl.l_pid = 0;
+       fl.l_type = F_RDLCK;
+       fl.l_whence = SEEK_END;
+       fl.l_sysid = 0;
+       ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+
+       leak(fd);
+}
+
 /* Fail to set a new lock with FUSE_SETLK due to a conflict */
 TEST_F(Setlk, eagain)
 {
diff --git a/tests/sys/fs/fusefs/mockfs.cc b/tests/sys/fs/fusefs/mockfs.cc
index 9a197d8a80f0..e012df8c3488 100644
--- a/tests/sys/fs/fusefs/mockfs.cc
+++ b/tests/sys/fs/fusefs/mockfs.cc
@@ -229,6 +229,18 @@ void MockFS::debug_request(const mockfs_buf_in &in, 
ssize_t buflen)
                case FUSE_FSYNCDIR:
                        printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
                        break;
+               case FUSE_GETLK:
+                       printf(" fh=%#" PRIx64
+                               " type=%u pid=%u",
+                               in.body.getlk.fh,
+                               in.body.getlk.lk.type,
+                               in.body.getlk.lk.pid);
+                       if (verbosity >= 2) {
+                               printf(" range=[%" PRIi64 ":%" PRIi64 "]",
+                                       in.body.getlk.lk.start,
+                                       in.body.getlk.lk.end);
+                       }
+                       break;
                case FUSE_INTERRUPT:
                        printf(" unique=%" PRIu64, in.body.interrupt.unique);
                        break;
@@ -338,7 +350,7 @@ void MockFS::debug_request(const mockfs_buf_in &in, ssize_t 
buflen)
                                in.body.setlk.lk.type,
                                in.body.setlk.lk.pid);
                        if (verbosity >= 2) {
-                               printf(" range=[%" PRIu64 "-%" PRIu64 "]",
+                               printf(" range=[%" PRIi64 ":%" PRIi64 "]",
                                        in.body.setlk.lk.start,
                                        in.body.setlk.lk.end);
                        }

Reply via email to