19821f1d3SAlan Somers /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
39821f1d3SAlan Somers *
49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers *
69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers * from the FreeBSD Foundation.
89821f1d3SAlan Somers *
99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers * are met:
129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers *
189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers * SUCH DAMAGE.
299821f1d3SAlan Somers */
309821f1d3SAlan Somers
319821f1d3SAlan Somers extern "C" {
328aa24ed3SAlan Somers #include <sys/file.h>
339821f1d3SAlan Somers #include <fcntl.h>
349821f1d3SAlan Somers }
359821f1d3SAlan Somers
369821f1d3SAlan Somers #include "mockfs.hh"
379821f1d3SAlan Somers #include "utils.hh"
389821f1d3SAlan Somers
399821f1d3SAlan Somers /* This flag value should probably be defined in fuse_kernel.h */
409821f1d3SAlan Somers #define OFFSET_MAX 0x7fffffffffffffffLL
419821f1d3SAlan Somers
429821f1d3SAlan Somers using namespace testing;
439821f1d3SAlan Somers
449821f1d3SAlan Somers /* For testing filesystems without posix locking support */
459821f1d3SAlan Somers class Fallback: public FuseTest {
469821f1d3SAlan Somers public:
479821f1d3SAlan Somers
expect_lookup(const char * relpath,uint64_t ino,uint64_t size=0)483c3b906bSAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size = 0)
499821f1d3SAlan Somers {
503c3b906bSAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
519821f1d3SAlan Somers }
529821f1d3SAlan Somers
539821f1d3SAlan Somers };
549821f1d3SAlan Somers
559821f1d3SAlan Somers /* For testing filesystems with posix locking support */
569821f1d3SAlan Somers class Locks: public Fallback {
SetUp()579821f1d3SAlan Somers virtual void SetUp() {
589821f1d3SAlan Somers m_init_flags = FUSE_POSIX_LOCKS;
599821f1d3SAlan Somers Fallback::SetUp();
609821f1d3SAlan Somers }
619821f1d3SAlan Somers };
629821f1d3SAlan Somers
638aa24ed3SAlan Somers class Fcntl: public Locks {
648aa24ed3SAlan Somers public:
expect_setlk(uint64_t ino,pid_t pid,uint64_t start,uint64_t end,uint32_t type,int err)658aa24ed3SAlan Somers void expect_setlk(uint64_t ino, pid_t pid, uint64_t start, uint64_t end,
668aa24ed3SAlan Somers uint32_t type, int err)
678aa24ed3SAlan Somers {
688aa24ed3SAlan Somers EXPECT_CALL(*m_mock, process(
698aa24ed3SAlan Somers ResultOf([=](auto in) {
708aa24ed3SAlan Somers return (in.header.opcode == FUSE_SETLK &&
718aa24ed3SAlan Somers in.header.nodeid == ino &&
728aa24ed3SAlan Somers in.body.setlk.fh == FH &&
73929acdb1SAlan Somers in.body.setlk.owner == (uint32_t)pid &&
74929acdb1SAlan Somers in.body.setlk.lk.start == start &&
75929acdb1SAlan Somers in.body.setlk.lk.end == end &&
76929acdb1SAlan Somers in.body.setlk.lk.type == type &&
77929acdb1SAlan Somers in.body.setlk.lk.pid == (uint64_t)pid);
78929acdb1SAlan Somers }, Eq(true)),
79929acdb1SAlan Somers _)
80929acdb1SAlan Somers ).WillOnce(Invoke(ReturnErrno(err)));
81929acdb1SAlan Somers }
expect_setlkw(uint64_t ino,pid_t pid,uint64_t start,uint64_t end,uint32_t type,int err)82929acdb1SAlan Somers void expect_setlkw(uint64_t ino, pid_t pid, uint64_t start, uint64_t end,
83929acdb1SAlan Somers uint32_t type, int err)
84929acdb1SAlan Somers {
85929acdb1SAlan Somers EXPECT_CALL(*m_mock, process(
86929acdb1SAlan Somers ResultOf([=](auto in) {
87929acdb1SAlan Somers return (in.header.opcode == FUSE_SETLKW &&
88929acdb1SAlan Somers in.header.nodeid == ino &&
89929acdb1SAlan Somers in.body.setlkw.fh == FH &&
908aa24ed3SAlan Somers in.body.setlkw.owner == (uint32_t)pid &&
918aa24ed3SAlan Somers in.body.setlkw.lk.start == start &&
928aa24ed3SAlan Somers in.body.setlkw.lk.end == end &&
938aa24ed3SAlan Somers in.body.setlkw.lk.type == type &&
948aa24ed3SAlan Somers in.body.setlkw.lk.pid == (uint64_t)pid);
958aa24ed3SAlan Somers }, Eq(true)),
968aa24ed3SAlan Somers _)
978aa24ed3SAlan Somers ).WillOnce(Invoke(ReturnErrno(err)));
988aa24ed3SAlan Somers }
998aa24ed3SAlan Somers };
1008aa24ed3SAlan Somers
1018aa24ed3SAlan Somers class Flock: public Locks {
1028aa24ed3SAlan Somers public:
expect_setlk(uint64_t ino,uint32_t type,int err)1038aa24ed3SAlan Somers void expect_setlk(uint64_t ino, uint32_t type, int err)
1048aa24ed3SAlan Somers {
1058aa24ed3SAlan Somers EXPECT_CALL(*m_mock, process(
1068aa24ed3SAlan Somers ResultOf([=](auto in) {
1078aa24ed3SAlan Somers return (in.header.opcode == FUSE_SETLK &&
1088aa24ed3SAlan Somers in.header.nodeid == ino &&
1098aa24ed3SAlan Somers in.body.setlk.fh == FH &&
1108aa24ed3SAlan Somers /*
1118aa24ed3SAlan Somers * The owner should be set to the address of
1128aa24ed3SAlan Somers * the vnode. That's hard to verify.
1138aa24ed3SAlan Somers */
1148aa24ed3SAlan Somers /* in.body.setlk.owner == ??? && */
1158aa24ed3SAlan Somers in.body.setlk.lk.type == type);
1168aa24ed3SAlan Somers }, Eq(true)),
1178aa24ed3SAlan Somers _)
1188aa24ed3SAlan Somers ).WillOnce(Invoke(ReturnErrno(err)));
1198aa24ed3SAlan Somers }
1208aa24ed3SAlan Somers };
1218aa24ed3SAlan Somers
1228aa24ed3SAlan Somers class FlockFallback: public Fallback {};
1239821f1d3SAlan Somers class GetlkFallback: public Fallback {};
1248aa24ed3SAlan Somers class Getlk: public Fcntl {};
1259821f1d3SAlan Somers class SetlkFallback: public Fallback {};
1268aa24ed3SAlan Somers class Setlk: public Fcntl {};
1279821f1d3SAlan Somers class SetlkwFallback: public Fallback {};
1288aa24ed3SAlan Somers class Setlkw: public Fcntl {};
1298aa24ed3SAlan Somers
1308aa24ed3SAlan Somers /*
1318aa24ed3SAlan Somers * If the fuse filesystem does not support flock locks, then the kernel should
1328aa24ed3SAlan Somers * fall back to local locks.
1338aa24ed3SAlan Somers */
TEST_F(FlockFallback,local)1348aa24ed3SAlan Somers TEST_F(FlockFallback, local)
1358aa24ed3SAlan Somers {
1368aa24ed3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1378aa24ed3SAlan Somers const char RELPATH[] = "some_file.txt";
1388aa24ed3SAlan Somers uint64_t ino = 42;
1398aa24ed3SAlan Somers int fd;
1408aa24ed3SAlan Somers
1418aa24ed3SAlan Somers expect_lookup(RELPATH, ino);
1428aa24ed3SAlan Somers expect_open(ino, 0, 1);
1438aa24ed3SAlan Somers
1448aa24ed3SAlan Somers fd = open(FULLPATH, O_RDWR);
1458aa24ed3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1468aa24ed3SAlan Somers ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
1477fc0921dSAlan Somers leak(fd);
1488aa24ed3SAlan Somers }
1498aa24ed3SAlan Somers
1508aa24ed3SAlan Somers /*
1518aa24ed3SAlan Somers * Even if the fuse file system supports POSIX locks, we must implement flock
1528aa24ed3SAlan Somers * locks locally until protocol 7.17. Protocol 7.9 added partial buggy support
1538aa24ed3SAlan Somers * but we won't implement that.
1548aa24ed3SAlan Somers */
TEST_F(Flock,local)1558aa24ed3SAlan Somers TEST_F(Flock, local)
1568aa24ed3SAlan Somers {
1578aa24ed3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1588aa24ed3SAlan Somers const char RELPATH[] = "some_file.txt";
1598aa24ed3SAlan Somers uint64_t ino = 42;
1608aa24ed3SAlan Somers int fd;
1618aa24ed3SAlan Somers
1628aa24ed3SAlan Somers expect_lookup(RELPATH, ino);
1638aa24ed3SAlan Somers expect_open(ino, 0, 1);
1648aa24ed3SAlan Somers
1658aa24ed3SAlan Somers fd = open(FULLPATH, O_RDWR);
1668aa24ed3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1678aa24ed3SAlan Somers ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
1687fc0921dSAlan Somers leak(fd);
1698aa24ed3SAlan Somers }
1708aa24ed3SAlan Somers
1718aa24ed3SAlan Somers /* Set a new flock lock with FUSE_SETLK */
1728aa24ed3SAlan Somers /* TODO: enable after upgrading to protocol 7.17 */
TEST_F(Flock,DISABLED_set)1738aa24ed3SAlan Somers TEST_F(Flock, DISABLED_set)
1748aa24ed3SAlan Somers {
1758aa24ed3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1768aa24ed3SAlan Somers const char RELPATH[] = "some_file.txt";
1778aa24ed3SAlan Somers uint64_t ino = 42;
1788aa24ed3SAlan Somers int fd;
1798aa24ed3SAlan Somers
1808aa24ed3SAlan Somers expect_lookup(RELPATH, ino);
1818aa24ed3SAlan Somers expect_open(ino, 0, 1);
1828aa24ed3SAlan Somers expect_setlk(ino, F_WRLCK, 0);
1838aa24ed3SAlan Somers
1848aa24ed3SAlan Somers fd = open(FULLPATH, O_RDWR);
1858aa24ed3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1868aa24ed3SAlan Somers ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
1877fc0921dSAlan Somers leak(fd);
1888aa24ed3SAlan Somers }
1898aa24ed3SAlan Somers
1908aa24ed3SAlan Somers /* Fail to set a flock lock in non-blocking mode */
1918aa24ed3SAlan Somers /* TODO: enable after upgrading to protocol 7.17 */
TEST_F(Flock,DISABLED_eagain)1928aa24ed3SAlan Somers TEST_F(Flock, DISABLED_eagain)
1938aa24ed3SAlan Somers {
1948aa24ed3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1958aa24ed3SAlan Somers const char RELPATH[] = "some_file.txt";
1968aa24ed3SAlan Somers uint64_t ino = 42;
1978aa24ed3SAlan Somers int fd;
1988aa24ed3SAlan Somers
1998aa24ed3SAlan Somers expect_lookup(RELPATH, ino);
2008aa24ed3SAlan Somers expect_open(ino, 0, 1);
2018aa24ed3SAlan Somers expect_setlk(ino, F_WRLCK, EAGAIN);
2028aa24ed3SAlan Somers
2038aa24ed3SAlan Somers fd = open(FULLPATH, O_RDWR);
2048aa24ed3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
2058aa24ed3SAlan Somers ASSERT_NE(0, flock(fd, LOCK_EX | LOCK_NB));
2068aa24ed3SAlan Somers ASSERT_EQ(EAGAIN, errno);
2077fc0921dSAlan Somers leak(fd);
2088aa24ed3SAlan Somers }
2099821f1d3SAlan Somers
2109821f1d3SAlan Somers /*
2119821f1d3SAlan Somers * If the fuse filesystem does not support posix file locks, then the kernel
2129821f1d3SAlan Somers * should fall back to local locks.
2139821f1d3SAlan Somers */
TEST_F(GetlkFallback,local)2149821f1d3SAlan Somers TEST_F(GetlkFallback, local)
2159821f1d3SAlan Somers {
2169821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
2179821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
2189821f1d3SAlan Somers uint64_t ino = 42;
2199821f1d3SAlan Somers struct flock fl;
2209821f1d3SAlan Somers int fd;
2219821f1d3SAlan Somers
2229821f1d3SAlan Somers expect_lookup(RELPATH, ino);
2239821f1d3SAlan Somers expect_open(ino, 0, 1);
2249821f1d3SAlan Somers
2259821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
2269821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
2279821f1d3SAlan Somers fl.l_start = 10;
2289821f1d3SAlan Somers fl.l_len = 1000;
22918b19f8cSAlan Somers fl.l_pid = 0;
2309821f1d3SAlan Somers fl.l_type = F_RDLCK;
2319821f1d3SAlan Somers fl.l_whence = SEEK_SET;
2329821f1d3SAlan Somers fl.l_sysid = 0;
2339821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
2347fc0921dSAlan Somers leak(fd);
2359821f1d3SAlan Somers }
2369821f1d3SAlan Somers
2379821f1d3SAlan Somers /*
2389821f1d3SAlan Somers * If the filesystem has no locks that fit the description, the filesystem
2399821f1d3SAlan Somers * should return F_UNLCK
2409821f1d3SAlan Somers */
TEST_F(Getlk,no_locks)241f067b609SAlan Somers TEST_F(Getlk, no_locks)
2429821f1d3SAlan Somers {
2439821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
2449821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
2459821f1d3SAlan Somers uint64_t ino = 42;
2469821f1d3SAlan Somers struct flock fl;
2479821f1d3SAlan Somers int fd;
24818b19f8cSAlan Somers pid_t pid = getpid();
2499821f1d3SAlan Somers
2509821f1d3SAlan Somers expect_lookup(RELPATH, ino);
2519821f1d3SAlan Somers expect_open(ino, 0, 1);
2529821f1d3SAlan Somers EXPECT_CALL(*m_mock, process(
2539821f1d3SAlan Somers ResultOf([=](auto in) {
25429edc611SAlan Somers return (in.header.opcode == FUSE_GETLK &&
25529edc611SAlan Somers in.header.nodeid == ino &&
25629edc611SAlan Somers in.body.getlk.fh == FH &&
25718b19f8cSAlan Somers /*
25818b19f8cSAlan Somers * Though it seems useless, libfuse expects the
25918b19f8cSAlan Somers * owner and pid fields to be set during
26018b19f8cSAlan Somers * FUSE_GETLK.
26118b19f8cSAlan Somers */
26229edc611SAlan Somers in.body.getlk.owner == (uint32_t)pid &&
26318b19f8cSAlan Somers in.body.getlk.lk.pid == (uint64_t)pid &&
26429edc611SAlan Somers in.body.getlk.lk.start == 10 &&
26529edc611SAlan Somers in.body.getlk.lk.end == 1009 &&
26618b19f8cSAlan Somers in.body.getlk.lk.type == F_RDLCK);
2679821f1d3SAlan Somers }, Eq(true)),
2689821f1d3SAlan Somers _)
26929edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
2709821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, getlk);
27129edc611SAlan Somers out.body.getlk.lk = in.body.getlk.lk;
27229edc611SAlan Somers out.body.getlk.lk.type = F_UNLCK;
2739821f1d3SAlan Somers })));
2749821f1d3SAlan Somers
2759821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
2769821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
2779821f1d3SAlan Somers fl.l_start = 10;
2789821f1d3SAlan Somers fl.l_len = 1000;
27946fcf947SAlan Somers fl.l_pid = 42;
2809821f1d3SAlan Somers fl.l_type = F_RDLCK;
2819821f1d3SAlan Somers fl.l_whence = SEEK_SET;
28246fcf947SAlan Somers fl.l_sysid = 42;
2839821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
28446fcf947SAlan Somers
28546fcf947SAlan Somers /*
28646fcf947SAlan Somers * If no lock is found that would prevent this lock from being created,
28746fcf947SAlan Somers * the structure is left unchanged by this system call except for the
28846fcf947SAlan Somers * lock type which is set to F_UNLCK.
28946fcf947SAlan Somers */
2909821f1d3SAlan Somers ASSERT_EQ(F_UNLCK, fl.l_type);
29146fcf947SAlan Somers ASSERT_EQ(fl.l_pid, 42);
29246fcf947SAlan Somers ASSERT_EQ(fl.l_start, 10);
29346fcf947SAlan Somers ASSERT_EQ(fl.l_len, 1000);
29446fcf947SAlan Somers ASSERT_EQ(fl.l_whence, SEEK_SET);
29546fcf947SAlan Somers ASSERT_EQ(fl.l_sysid, 42);
29646fcf947SAlan Somers
2977fc0921dSAlan Somers leak(fd);
2989821f1d3SAlan Somers }
2999821f1d3SAlan Somers
3009821f1d3SAlan Somers /* A different pid does have a lock */
TEST_F(Getlk,lock_exists)301f067b609SAlan Somers TEST_F(Getlk, lock_exists)
3029821f1d3SAlan Somers {
3039821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
3049821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
3059821f1d3SAlan Somers uint64_t ino = 42;
3069821f1d3SAlan Somers struct flock fl;
3079821f1d3SAlan Somers int fd;
30818b19f8cSAlan Somers pid_t pid = getpid();
3099821f1d3SAlan Somers pid_t pid2 = 1235;
3109821f1d3SAlan Somers
3119821f1d3SAlan Somers expect_lookup(RELPATH, ino);
3129821f1d3SAlan Somers expect_open(ino, 0, 1);
3139821f1d3SAlan Somers EXPECT_CALL(*m_mock, process(
3149821f1d3SAlan Somers ResultOf([=](auto in) {
31529edc611SAlan Somers return (in.header.opcode == FUSE_GETLK &&
31629edc611SAlan Somers in.header.nodeid == ino &&
31729edc611SAlan Somers in.body.getlk.fh == FH &&
31818b19f8cSAlan Somers /*
31918b19f8cSAlan Somers * Though it seems useless, libfuse expects the
32018b19f8cSAlan Somers * owner and pid fields to be set during
32118b19f8cSAlan Somers * FUSE_GETLK.
32218b19f8cSAlan Somers */
32329edc611SAlan Somers in.body.getlk.owner == (uint32_t)pid &&
32418b19f8cSAlan Somers in.body.getlk.lk.pid == (uint64_t)pid &&
32529edc611SAlan Somers in.body.getlk.lk.start == 10 &&
32629edc611SAlan Somers in.body.getlk.lk.end == 1009 &&
32718b19f8cSAlan Somers in.body.getlk.lk.type == F_RDLCK);
3289821f1d3SAlan Somers }, Eq(true)),
3299821f1d3SAlan Somers _)
33029edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
3319821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, getlk);
33229edc611SAlan Somers out.body.getlk.lk.start = 100;
33329edc611SAlan Somers out.body.getlk.lk.end = 199;
33429edc611SAlan Somers out.body.getlk.lk.type = F_WRLCK;
33529edc611SAlan Somers out.body.getlk.lk.pid = (uint32_t)pid2;;
3369821f1d3SAlan Somers })));
3379821f1d3SAlan Somers
3389821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
3399821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
3409821f1d3SAlan Somers fl.l_start = 10;
3419821f1d3SAlan Somers fl.l_len = 1000;
34218b19f8cSAlan Somers fl.l_pid = 0;
3439821f1d3SAlan Somers fl.l_type = F_RDLCK;
3449821f1d3SAlan Somers fl.l_whence = SEEK_SET;
3459821f1d3SAlan Somers fl.l_sysid = 0;
3469821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
3479821f1d3SAlan Somers EXPECT_EQ(100, fl.l_start);
3489821f1d3SAlan Somers EXPECT_EQ(100, fl.l_len);
3499821f1d3SAlan Somers EXPECT_EQ(pid2, fl.l_pid);
3509821f1d3SAlan Somers EXPECT_EQ(F_WRLCK, fl.l_type);
3519821f1d3SAlan Somers EXPECT_EQ(SEEK_SET, fl.l_whence);
3529821f1d3SAlan Somers EXPECT_EQ(0, fl.l_sysid);
3537fc0921dSAlan Somers leak(fd);
3549821f1d3SAlan Somers }
3559821f1d3SAlan Somers
3569821f1d3SAlan Somers /*
3573c3b906bSAlan Somers * F_GETLK with SEEK_CUR
3583c3b906bSAlan Somers */
TEST_F(Getlk,seek_cur)3593c3b906bSAlan Somers TEST_F(Getlk, seek_cur)
3603c3b906bSAlan Somers {
3613c3b906bSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
3623c3b906bSAlan Somers const char RELPATH[] = "some_file.txt";
3633c3b906bSAlan Somers uint64_t ino = 42;
3643c3b906bSAlan Somers struct flock fl;
3653c3b906bSAlan Somers int fd;
3663c3b906bSAlan Somers pid_t pid = getpid();
3673c3b906bSAlan Somers
3683c3b906bSAlan Somers expect_lookup(RELPATH, ino, 1024);
3693c3b906bSAlan Somers expect_open(ino, 0, 1);
3703c3b906bSAlan Somers EXPECT_CALL(*m_mock, process(
3713c3b906bSAlan Somers ResultOf([=](auto in) {
3723c3b906bSAlan Somers return (in.header.opcode == FUSE_GETLK &&
3733c3b906bSAlan Somers in.header.nodeid == ino &&
3743c3b906bSAlan Somers in.body.getlk.fh == FH &&
3753c3b906bSAlan Somers /*
3763c3b906bSAlan Somers * Though it seems useless, libfuse expects the
3773c3b906bSAlan Somers * owner and pid fields to be set during
3783c3b906bSAlan Somers * FUSE_GETLK.
3793c3b906bSAlan Somers */
3803c3b906bSAlan Somers in.body.getlk.owner == (uint32_t)pid &&
3813c3b906bSAlan Somers in.body.getlk.lk.pid == (uint64_t)pid &&
3823c3b906bSAlan Somers in.body.getlk.lk.start == 500 &&
3833c3b906bSAlan Somers in.body.getlk.lk.end == 509 &&
3843c3b906bSAlan Somers in.body.getlk.lk.type == F_RDLCK);
3853c3b906bSAlan Somers }, Eq(true)),
3863c3b906bSAlan Somers _)
3873c3b906bSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
3883c3b906bSAlan Somers SET_OUT_HEADER_LEN(out, getlk);
3893c3b906bSAlan Somers out.body.getlk.lk.start = 400;
3903c3b906bSAlan Somers out.body.getlk.lk.end = 499;
3913c3b906bSAlan Somers out.body.getlk.lk.type = F_WRLCK;
3923c3b906bSAlan Somers out.body.getlk.lk.pid = (uint32_t)pid + 1;
3933c3b906bSAlan Somers })));
3943c3b906bSAlan Somers
3953c3b906bSAlan Somers fd = open(FULLPATH, O_RDWR);
3963c3b906bSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
3973c3b906bSAlan Somers ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
3983c3b906bSAlan Somers
3993c3b906bSAlan Somers fl.l_start = 0;
4003c3b906bSAlan Somers fl.l_len = 10;
4013c3b906bSAlan Somers fl.l_pid = 42;
4023c3b906bSAlan Somers fl.l_type = F_RDLCK;
4033c3b906bSAlan Somers fl.l_whence = SEEK_CUR;
4043c3b906bSAlan Somers fl.l_sysid = 0;
4053c3b906bSAlan Somers ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
4063c3b906bSAlan Somers
4073c3b906bSAlan Somers /*
4083c3b906bSAlan Somers * After a successful F_GETLK request, the value of l_whence is
4093c3b906bSAlan Somers * SEEK_SET.
4103c3b906bSAlan Somers */
4113c3b906bSAlan Somers EXPECT_EQ(F_WRLCK, fl.l_type);
4123c3b906bSAlan Somers EXPECT_EQ(fl.l_pid, pid + 1);
4133c3b906bSAlan Somers EXPECT_EQ(fl.l_start, 400);
4143c3b906bSAlan Somers EXPECT_EQ(fl.l_len, 100);
4153c3b906bSAlan Somers EXPECT_EQ(fl.l_whence, SEEK_SET);
4163c3b906bSAlan Somers ASSERT_EQ(fl.l_sysid, 0);
4173c3b906bSAlan Somers
4183c3b906bSAlan Somers leak(fd);
4193c3b906bSAlan Somers }
4203c3b906bSAlan Somers
4213c3b906bSAlan Somers /*
422f6e53195SAlan Somers * F_GETLK with SEEK_END
423f6e53195SAlan Somers */
TEST_F(Getlk,seek_end)424f6e53195SAlan Somers TEST_F(Getlk, seek_end)
425f6e53195SAlan Somers {
426f6e53195SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
427f6e53195SAlan Somers const char RELPATH[] = "some_file.txt";
428f6e53195SAlan Somers uint64_t ino = 42;
429f6e53195SAlan Somers struct flock fl;
430f6e53195SAlan Somers int fd;
431f6e53195SAlan Somers pid_t pid = getpid();
432f6e53195SAlan Somers
433f6e53195SAlan Somers expect_lookup(RELPATH, ino, 1024);
434f6e53195SAlan Somers expect_open(ino, 0, 1);
435f6e53195SAlan Somers EXPECT_CALL(*m_mock, process(
436f6e53195SAlan Somers ResultOf([=](auto in) {
437f6e53195SAlan Somers return (in.header.opcode == FUSE_GETLK &&
438f6e53195SAlan Somers in.header.nodeid == ino &&
439f6e53195SAlan Somers in.body.getlk.fh == FH &&
440f6e53195SAlan Somers /*
441f6e53195SAlan Somers * Though it seems useless, libfuse expects the
442f6e53195SAlan Somers * owner and pid fields to be set during
443f6e53195SAlan Somers * FUSE_GETLK.
444f6e53195SAlan Somers */
445f6e53195SAlan Somers in.body.getlk.owner == (uint32_t)pid &&
446f6e53195SAlan Somers in.body.getlk.lk.pid == (uint64_t)pid &&
447f6e53195SAlan Somers in.body.getlk.lk.start == 512 &&
448f6e53195SAlan Somers in.body.getlk.lk.end == 1023 &&
449f6e53195SAlan Somers in.body.getlk.lk.type == F_RDLCK);
450f6e53195SAlan Somers }, Eq(true)),
451f6e53195SAlan Somers _)
452f6e53195SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
453f6e53195SAlan Somers SET_OUT_HEADER_LEN(out, getlk);
454f6e53195SAlan Somers out.body.getlk.lk.start = 400;
455f6e53195SAlan Somers out.body.getlk.lk.end = 499;
456f6e53195SAlan Somers out.body.getlk.lk.type = F_WRLCK;
457f6e53195SAlan Somers out.body.getlk.lk.pid = (uint32_t)pid + 1;
458f6e53195SAlan Somers })));
459f6e53195SAlan Somers
460f6e53195SAlan Somers fd = open(FULLPATH, O_RDWR);
461f6e53195SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
462f6e53195SAlan Somers ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
463f6e53195SAlan Somers
464f6e53195SAlan Somers fl.l_start = -512;
465f6e53195SAlan Somers fl.l_len = 512;
466f6e53195SAlan Somers fl.l_pid = 42;
467f6e53195SAlan Somers fl.l_type = F_RDLCK;
468f6e53195SAlan Somers fl.l_whence = SEEK_END;
469f6e53195SAlan Somers fl.l_sysid = 0;
470f6e53195SAlan Somers ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
471f6e53195SAlan Somers
472f6e53195SAlan Somers /*
473f6e53195SAlan Somers * After a successful F_GETLK request, the value of l_whence is
474f6e53195SAlan Somers * SEEK_SET.
475f6e53195SAlan Somers */
476f6e53195SAlan Somers EXPECT_EQ(F_WRLCK, fl.l_type);
477f6e53195SAlan Somers EXPECT_EQ(fl.l_pid, pid + 1);
478f6e53195SAlan Somers EXPECT_EQ(fl.l_start, 400);
479f6e53195SAlan Somers EXPECT_EQ(fl.l_len, 100);
480f6e53195SAlan Somers EXPECT_EQ(fl.l_whence, SEEK_SET);
481f6e53195SAlan Somers ASSERT_EQ(fl.l_sysid, 0);
482f6e53195SAlan Somers
483f6e53195SAlan Somers leak(fd);
484f6e53195SAlan Somers }
485f6e53195SAlan Somers
486f6e53195SAlan Somers /*
4879821f1d3SAlan Somers * If the fuse filesystem does not support posix file locks, then the kernel
4889821f1d3SAlan Somers * should fall back to local locks.
4899821f1d3SAlan Somers */
TEST_F(SetlkFallback,local)4909821f1d3SAlan Somers TEST_F(SetlkFallback, local)
4919821f1d3SAlan Somers {
4929821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
4939821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
4949821f1d3SAlan Somers uint64_t ino = 42;
4959821f1d3SAlan Somers struct flock fl;
4969821f1d3SAlan Somers int fd;
4979821f1d3SAlan Somers
4989821f1d3SAlan Somers expect_lookup(RELPATH, ino);
4999821f1d3SAlan Somers expect_open(ino, 0, 1);
5009821f1d3SAlan Somers
5019821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
5029821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
5039821f1d3SAlan Somers fl.l_start = 10;
5049821f1d3SAlan Somers fl.l_len = 1000;
5059821f1d3SAlan Somers fl.l_pid = getpid();
5069821f1d3SAlan Somers fl.l_type = F_RDLCK;
5079821f1d3SAlan Somers fl.l_whence = SEEK_SET;
5089821f1d3SAlan Somers fl.l_sysid = 0;
5099821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
5107fc0921dSAlan Somers leak(fd);
5119821f1d3SAlan Somers }
5129821f1d3SAlan Somers
513929acdb1SAlan Somers /* Clear a lock with FUSE_SETLK */
TEST_F(Setlk,clear)514929acdb1SAlan Somers TEST_F(Setlk, clear)
515929acdb1SAlan Somers {
516929acdb1SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
517929acdb1SAlan Somers const char RELPATH[] = "some_file.txt";
518929acdb1SAlan Somers uint64_t ino = 42;
519929acdb1SAlan Somers struct flock fl;
520929acdb1SAlan Somers int fd;
52118b19f8cSAlan Somers pid_t pid = getpid();
522929acdb1SAlan Somers
523929acdb1SAlan Somers expect_lookup(RELPATH, ino);
524929acdb1SAlan Somers expect_open(ino, 0, 1);
525929acdb1SAlan Somers expect_setlk(ino, pid, 10, 1009, F_UNLCK, 0);
526929acdb1SAlan Somers
527929acdb1SAlan Somers fd = open(FULLPATH, O_RDWR);
528929acdb1SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
529929acdb1SAlan Somers fl.l_start = 10;
530929acdb1SAlan Somers fl.l_len = 1000;
53118b19f8cSAlan Somers fl.l_pid = 0;
532929acdb1SAlan Somers fl.l_type = F_UNLCK;
533929acdb1SAlan Somers fl.l_whence = SEEK_SET;
534929acdb1SAlan Somers fl.l_sysid = 0;
535929acdb1SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
536929acdb1SAlan Somers leak(fd);
537929acdb1SAlan Somers }
538929acdb1SAlan Somers
5399821f1d3SAlan Somers /* Set a new lock with FUSE_SETLK */
TEST_F(Setlk,set)540f067b609SAlan Somers TEST_F(Setlk, set)
5419821f1d3SAlan Somers {
5429821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
5439821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
5449821f1d3SAlan Somers uint64_t ino = 42;
5459821f1d3SAlan Somers struct flock fl;
5469821f1d3SAlan Somers int fd;
54718b19f8cSAlan Somers pid_t pid = getpid();
5489821f1d3SAlan Somers
5499821f1d3SAlan Somers expect_lookup(RELPATH, ino);
5509821f1d3SAlan Somers expect_open(ino, 0, 1);
5518aa24ed3SAlan Somers expect_setlk(ino, pid, 10, 1009, F_RDLCK, 0);
5529821f1d3SAlan Somers
5539821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
5549821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
5559821f1d3SAlan Somers fl.l_start = 10;
5569821f1d3SAlan Somers fl.l_len = 1000;
55718b19f8cSAlan Somers fl.l_pid = 0;
5589821f1d3SAlan Somers fl.l_type = F_RDLCK;
5599821f1d3SAlan Somers fl.l_whence = SEEK_SET;
5609821f1d3SAlan Somers fl.l_sysid = 0;
5619821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
5627fc0921dSAlan Somers leak(fd);
5639821f1d3SAlan Somers }
5649821f1d3SAlan Somers
5659821f1d3SAlan Somers /* l_len = 0 is a flag value that means to lock until EOF */
TEST_F(Setlk,set_eof)566f067b609SAlan Somers TEST_F(Setlk, set_eof)
5679821f1d3SAlan Somers {
5689821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
5699821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
5709821f1d3SAlan Somers uint64_t ino = 42;
5719821f1d3SAlan Somers struct flock fl;
5729821f1d3SAlan Somers int fd;
57318b19f8cSAlan Somers pid_t pid = getpid();
5749821f1d3SAlan Somers
5759821f1d3SAlan Somers expect_lookup(RELPATH, ino);
5769821f1d3SAlan Somers expect_open(ino, 0, 1);
5778aa24ed3SAlan Somers expect_setlk(ino, pid, 10, OFFSET_MAX, F_RDLCK, 0);
5789821f1d3SAlan Somers
5799821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
5809821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
5819821f1d3SAlan Somers fl.l_start = 10;
5829821f1d3SAlan Somers fl.l_len = 0;
58318b19f8cSAlan Somers fl.l_pid = 0;
5849821f1d3SAlan Somers fl.l_type = F_RDLCK;
5859821f1d3SAlan Somers fl.l_whence = SEEK_SET;
5869821f1d3SAlan Somers fl.l_sysid = 0;
5879821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
5887fc0921dSAlan Somers leak(fd);
5899821f1d3SAlan Somers }
5909821f1d3SAlan Somers
591f6e53195SAlan Somers /* Set a new lock with FUSE_SETLK, using SEEK_CUR for l_whence */
TEST_F(Setlk,set_seek_cur)592f6e53195SAlan Somers TEST_F(Setlk, set_seek_cur)
593f6e53195SAlan Somers {
594f6e53195SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
595f6e53195SAlan Somers const char RELPATH[] = "some_file.txt";
596f6e53195SAlan Somers uint64_t ino = 42;
597f6e53195SAlan Somers struct flock fl;
598f6e53195SAlan Somers int fd;
599f6e53195SAlan Somers pid_t pid = getpid();
600f6e53195SAlan Somers
601f6e53195SAlan Somers expect_lookup(RELPATH, ino, 1024);
602f6e53195SAlan Somers expect_open(ino, 0, 1);
603f6e53195SAlan Somers expect_setlk(ino, pid, 500, 509, F_RDLCK, 0);
604f6e53195SAlan Somers
605f6e53195SAlan Somers fd = open(FULLPATH, O_RDWR);
606f6e53195SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
607f6e53195SAlan Somers ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
608f6e53195SAlan Somers
609f6e53195SAlan Somers fl.l_start = 0;
610f6e53195SAlan Somers fl.l_len = 10;
611f6e53195SAlan Somers fl.l_pid = 0;
612f6e53195SAlan Somers fl.l_type = F_RDLCK;
613f6e53195SAlan Somers fl.l_whence = SEEK_CUR;
614f6e53195SAlan Somers fl.l_sysid = 0;
615f6e53195SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
616f6e53195SAlan Somers
617f6e53195SAlan Somers leak(fd);
618f6e53195SAlan Somers }
619f6e53195SAlan Somers
620f6e53195SAlan Somers /* Set a new lock with FUSE_SETLK, using SEEK_END for l_whence */
TEST_F(Setlk,set_seek_end)621f6e53195SAlan Somers TEST_F(Setlk, set_seek_end)
622f6e53195SAlan Somers {
623f6e53195SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
624f6e53195SAlan Somers const char RELPATH[] = "some_file.txt";
625f6e53195SAlan Somers uint64_t ino = 42;
626f6e53195SAlan Somers struct flock fl;
627f6e53195SAlan Somers int fd;
628f6e53195SAlan Somers pid_t pid = getpid();
629f6e53195SAlan Somers
630f6e53195SAlan Somers expect_lookup(RELPATH, ino, 1024);
631f6e53195SAlan Somers expect_open(ino, 0, 1);
632f6e53195SAlan Somers expect_setlk(ino, pid, 1000, 1009, F_RDLCK, 0);
633f6e53195SAlan Somers
634f6e53195SAlan Somers fd = open(FULLPATH, O_RDWR);
635f6e53195SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
636f6e53195SAlan Somers
637f6e53195SAlan Somers fl.l_start = -24;
638f6e53195SAlan Somers fl.l_len = 10;
639f6e53195SAlan Somers fl.l_pid = 0;
640f6e53195SAlan Somers fl.l_type = F_RDLCK;
641f6e53195SAlan Somers fl.l_whence = SEEK_END;
642f6e53195SAlan Somers fl.l_sysid = 0;
643f6e53195SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
644f6e53195SAlan Somers
645f6e53195SAlan Somers leak(fd);
646f6e53195SAlan Somers }
647f6e53195SAlan Somers
6489821f1d3SAlan Somers /* Fail to set a new lock with FUSE_SETLK due to a conflict */
TEST_F(Setlk,eagain)649f067b609SAlan Somers TEST_F(Setlk, eagain)
6509821f1d3SAlan Somers {
6519821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
6529821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
6539821f1d3SAlan Somers uint64_t ino = 42;
6549821f1d3SAlan Somers struct flock fl;
6559821f1d3SAlan Somers int fd;
65618b19f8cSAlan Somers pid_t pid = getpid();
6579821f1d3SAlan Somers
6589821f1d3SAlan Somers expect_lookup(RELPATH, ino);
6599821f1d3SAlan Somers expect_open(ino, 0, 1);
6608aa24ed3SAlan Somers expect_setlk(ino, pid, 10, 1009, F_RDLCK, EAGAIN);
6619821f1d3SAlan Somers
6629821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
6639821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
6649821f1d3SAlan Somers fl.l_start = 10;
6659821f1d3SAlan Somers fl.l_len = 1000;
66618b19f8cSAlan Somers fl.l_pid = 0;
6679821f1d3SAlan Somers fl.l_type = F_RDLCK;
6689821f1d3SAlan Somers fl.l_whence = SEEK_SET;
6699821f1d3SAlan Somers fl.l_sysid = 0;
6709821f1d3SAlan Somers ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl));
6719821f1d3SAlan Somers ASSERT_EQ(EAGAIN, errno);
6727fc0921dSAlan Somers leak(fd);
6739821f1d3SAlan Somers }
6749821f1d3SAlan Somers
6759821f1d3SAlan Somers /*
6769821f1d3SAlan Somers * If the fuse filesystem does not support posix file locks, then the kernel
6779821f1d3SAlan Somers * should fall back to local locks.
6789821f1d3SAlan Somers */
TEST_F(SetlkwFallback,local)6799821f1d3SAlan Somers TEST_F(SetlkwFallback, local)
6809821f1d3SAlan Somers {
6819821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
6829821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
6839821f1d3SAlan Somers uint64_t ino = 42;
6849821f1d3SAlan Somers struct flock fl;
6859821f1d3SAlan Somers int fd;
6869821f1d3SAlan Somers
6879821f1d3SAlan Somers expect_lookup(RELPATH, ino);
6889821f1d3SAlan Somers expect_open(ino, 0, 1);
6899821f1d3SAlan Somers
6909821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
6919821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
6929821f1d3SAlan Somers fl.l_start = 10;
6939821f1d3SAlan Somers fl.l_len = 1000;
69418b19f8cSAlan Somers fl.l_pid = 0;
6959821f1d3SAlan Somers fl.l_type = F_RDLCK;
6969821f1d3SAlan Somers fl.l_whence = SEEK_SET;
6979821f1d3SAlan Somers fl.l_sysid = 0;
6989821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
6997fc0921dSAlan Somers leak(fd);
7009821f1d3SAlan Somers }
7019821f1d3SAlan Somers
7029821f1d3SAlan Somers /*
7039821f1d3SAlan Somers * Set a new lock with FUSE_SETLK. If the lock is not available, then the
7049821f1d3SAlan Somers * command should block. But to the kernel, that's the same as just being
7059821f1d3SAlan Somers * slow, so we don't need a separate test method
7069821f1d3SAlan Somers */
TEST_F(Setlkw,set)707f067b609SAlan Somers TEST_F(Setlkw, set)
7089821f1d3SAlan Somers {
7099821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
7109821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
7119821f1d3SAlan Somers uint64_t ino = 42;
7129821f1d3SAlan Somers struct flock fl;
7139821f1d3SAlan Somers int fd;
71418b19f8cSAlan Somers pid_t pid = getpid();
7159821f1d3SAlan Somers
7169821f1d3SAlan Somers expect_lookup(RELPATH, ino);
7179821f1d3SAlan Somers expect_open(ino, 0, 1);
718929acdb1SAlan Somers expect_setlkw(ino, pid, 10, 1009, F_RDLCK, 0);
7199821f1d3SAlan Somers
7209821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
7219821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
7229821f1d3SAlan Somers fl.l_start = 10;
7239821f1d3SAlan Somers fl.l_len = 1000;
72418b19f8cSAlan Somers fl.l_pid = 0;
7259821f1d3SAlan Somers fl.l_type = F_RDLCK;
7269821f1d3SAlan Somers fl.l_whence = SEEK_SET;
7279821f1d3SAlan Somers fl.l_sysid = 0;
7289821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
7297fc0921dSAlan Somers leak(fd);
7309821f1d3SAlan Somers }
731