mirror of
https://github.com/opnsense/src.git
synced 2026-06-09 08:43:19 -04:00
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.
Reviewed by: emaste
Differential Revision: https://reviews.freebsd.org/D37040
(cherry picked from commit f6e5319550)
This commit is contained in:
parent
7ea6564c24
commit
4a0c5d391f
3 changed files with 166 additions and 3 deletions
|
|
@ -476,7 +476,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;
|
||||
|
||||
|
|
@ -511,6 +513,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;
|
||||
|
|
@ -521,9 +550,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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue