1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 The FreeBSD Foundation
5 *
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31extern "C" {
32#include <sys/file.h>
33#include <fcntl.h>
34}
35
36#include "mockfs.hh"
37#include "utils.hh"
38
39/* This flag value should probably be defined in fuse_kernel.h */
40#define OFFSET_MAX 0x7fffffffffffffffLL
41
42using namespace testing;
43
44/* For testing filesystems without posix locking support */
45class Fallback: public FuseTest {
46public:
47
48void expect_lookup(const char *relpath, uint64_t ino, uint64_t size = 0)
49{
50	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
51}
52
53};
54
55/* For testing filesystems with posix locking support */
56class Locks: public Fallback {
57	virtual void SetUp() {
58		m_init_flags = FUSE_POSIX_LOCKS;
59		Fallback::SetUp();
60	}
61};
62
63class Fcntl: public Locks {
64public:
65void expect_setlk(uint64_t ino, pid_t pid, uint64_t start, uint64_t end,
66	uint32_t type, int err)
67{
68	EXPECT_CALL(*m_mock, process(
69		ResultOf([=](auto in) {
70			return (in.header.opcode == FUSE_SETLK &&
71				in.header.nodeid == ino &&
72				in.body.setlk.fh == FH &&
73				in.body.setlk.owner == (uint32_t)pid &&
74				in.body.setlk.lk.start == start &&
75				in.body.setlk.lk.end == end &&
76				in.body.setlk.lk.type == type &&
77				in.body.setlk.lk.pid == (uint64_t)pid);
78		}, Eq(true)),
79		_)
80	).WillOnce(Invoke(ReturnErrno(err)));
81}
82void expect_setlkw(uint64_t ino, pid_t pid, uint64_t start, uint64_t end,
83	uint32_t type, int err)
84{
85	EXPECT_CALL(*m_mock, process(
86		ResultOf([=](auto in) {
87			return (in.header.opcode == FUSE_SETLKW &&
88				in.header.nodeid == ino &&
89				in.body.setlkw.fh == FH &&
90				in.body.setlkw.owner == (uint32_t)pid &&
91				in.body.setlkw.lk.start == start &&
92				in.body.setlkw.lk.end == end &&
93				in.body.setlkw.lk.type == type &&
94				in.body.setlkw.lk.pid == (uint64_t)pid);
95		}, Eq(true)),
96		_)
97	).WillOnce(Invoke(ReturnErrno(err)));
98}
99};
100
101class Flock: public Locks {
102public:
103void expect_setlk(uint64_t ino, uint32_t type, int err)
104{
105	EXPECT_CALL(*m_mock, process(
106		ResultOf([=](auto in) {
107			return (in.header.opcode == FUSE_SETLK &&
108				in.header.nodeid == ino &&
109				in.body.setlk.fh == FH &&
110				/*
111				 * The owner should be set to the address of
112				 * the vnode.  That's hard to verify.
113				 */
114				/* in.body.setlk.owner == ??? && */
115				in.body.setlk.lk.type == type);
116		}, Eq(true)),
117		_)
118	).WillOnce(Invoke(ReturnErrno(err)));
119}
120};
121
122class FlockFallback: public Fallback {};
123class GetlkFallback: public Fallback {};
124class Getlk: public Fcntl {};
125class SetlkFallback: public Fallback {};
126class Setlk: public Fcntl {};
127class SetlkwFallback: public Fallback {};
128class Setlkw: public Fcntl {};
129
130/*
131 * If the fuse filesystem does not support flock locks, then the kernel should
132 * fall back to local locks.
133 */
134TEST_F(FlockFallback, local)
135{
136	const char FULLPATH[] = "mountpoint/some_file.txt";
137	const char RELPATH[] = "some_file.txt";
138	uint64_t ino = 42;
139	int fd;
140
141	expect_lookup(RELPATH, ino);
142	expect_open(ino, 0, 1);
143
144	fd = open(FULLPATH, O_RDWR);
145	ASSERT_LE(0, fd) << strerror(errno);
146	ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
147	leak(fd);
148}
149
150/*
151 * Even if the fuse file system supports POSIX locks, we must implement flock
152 * locks locally until protocol 7.17.  Protocol 7.9 added partial buggy support
153 * but we won't implement that.
154 */
155TEST_F(Flock, local)
156{
157	const char FULLPATH[] = "mountpoint/some_file.txt";
158	const char RELPATH[] = "some_file.txt";
159	uint64_t ino = 42;
160	int fd;
161
162	expect_lookup(RELPATH, ino);
163	expect_open(ino, 0, 1);
164
165	fd = open(FULLPATH, O_RDWR);
166	ASSERT_LE(0, fd) << strerror(errno);
167	ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
168	leak(fd);
169}
170
171/* Set a new flock lock with FUSE_SETLK */
172/* TODO: enable after upgrading to protocol 7.17 */
173TEST_F(Flock, DISABLED_set)
174{
175	const char FULLPATH[] = "mountpoint/some_file.txt";
176	const char RELPATH[] = "some_file.txt";
177	uint64_t ino = 42;
178	int fd;
179
180	expect_lookup(RELPATH, ino);
181	expect_open(ino, 0, 1);
182	expect_setlk(ino, F_WRLCK, 0);
183
184	fd = open(FULLPATH, O_RDWR);
185	ASSERT_LE(0, fd) << strerror(errno);
186	ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
187	leak(fd);
188}
189
190/* Fail to set a flock lock in non-blocking mode */
191/* TODO: enable after upgrading to protocol 7.17 */
192TEST_F(Flock, DISABLED_eagain)
193{
194	const char FULLPATH[] = "mountpoint/some_file.txt";
195	const char RELPATH[] = "some_file.txt";
196	uint64_t ino = 42;
197	int fd;
198
199	expect_lookup(RELPATH, ino);
200	expect_open(ino, 0, 1);
201	expect_setlk(ino, F_WRLCK, EAGAIN);
202
203	fd = open(FULLPATH, O_RDWR);
204	ASSERT_LE(0, fd) << strerror(errno);
205	ASSERT_NE(0, flock(fd, LOCK_EX | LOCK_NB));
206	ASSERT_EQ(EAGAIN, errno);
207	leak(fd);
208}
209
210/*
211 * If the fuse filesystem does not support posix file locks, then the kernel
212 * should fall back to local locks.
213 */
214TEST_F(GetlkFallback, local)
215{
216	const char FULLPATH[] = "mountpoint/some_file.txt";
217	const char RELPATH[] = "some_file.txt";
218	uint64_t ino = 42;
219	struct flock fl;
220	int fd;
221
222	expect_lookup(RELPATH, ino);
223	expect_open(ino, 0, 1);
224
225	fd = open(FULLPATH, O_RDWR);
226	ASSERT_LE(0, fd) << strerror(errno);
227	fl.l_start = 10;
228	fl.l_len = 1000;
229	fl.l_pid = 0;
230	fl.l_type = F_RDLCK;
231	fl.l_whence = SEEK_SET;
232	fl.l_sysid = 0;
233	ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
234	leak(fd);
235}
236
237/*
238 * If the filesystem has no locks that fit the description, the filesystem
239 * should return F_UNLCK
240 */
241TEST_F(Getlk, no_locks)
242{
243	const char FULLPATH[] = "mountpoint/some_file.txt";
244	const char RELPATH[] = "some_file.txt";
245	uint64_t ino = 42;
246	struct flock fl;
247	int fd;
248	pid_t pid = getpid();
249
250	expect_lookup(RELPATH, ino);
251	expect_open(ino, 0, 1);
252	EXPECT_CALL(*m_mock, process(
253		ResultOf([=](auto in) {
254			return (in.header.opcode == FUSE_GETLK &&
255				in.header.nodeid == ino &&
256				in.body.getlk.fh == FH &&
257				/*
258				 * Though it seems useless, libfuse expects the
259				 * owner and pid fields to be set during
260				 * FUSE_GETLK.
261				 */
262				in.body.getlk.owner == (uint32_t)pid &&
263				in.body.getlk.lk.pid == (uint64_t)pid &&
264				in.body.getlk.lk.start == 10 &&
265				in.body.getlk.lk.end == 1009 &&
266				in.body.getlk.lk.type == F_RDLCK);
267		}, Eq(true)),
268		_)
269	).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
270		SET_OUT_HEADER_LEN(out, getlk);
271		out.body.getlk.lk = in.body.getlk.lk;
272		out.body.getlk.lk.type = F_UNLCK;
273	})));
274
275	fd = open(FULLPATH, O_RDWR);
276	ASSERT_LE(0, fd) << strerror(errno);
277	fl.l_start = 10;
278	fl.l_len = 1000;
279	fl.l_pid = 42;
280	fl.l_type = F_RDLCK;
281	fl.l_whence = SEEK_SET;
282	fl.l_sysid = 42;
283	ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
284
285	/*
286	 * If no lock is found that would prevent this lock from being created,
287	 * the structure is left unchanged by this system call except for the
288	 * lock type which is set to F_UNLCK.
289	 */
290	ASSERT_EQ(F_UNLCK, fl.l_type);
291	ASSERT_EQ(fl.l_pid, 42);
292	ASSERT_EQ(fl.l_start, 10);
293	ASSERT_EQ(fl.l_len, 1000);
294	ASSERT_EQ(fl.l_whence, SEEK_SET);
295	ASSERT_EQ(fl.l_sysid, 42);
296
297	leak(fd);
298}
299
300/* A different pid does have a lock */
301TEST_F(Getlk, lock_exists)
302{
303	const char FULLPATH[] = "mountpoint/some_file.txt";
304	const char RELPATH[] = "some_file.txt";
305	uint64_t ino = 42;
306	struct flock fl;
307	int fd;
308	pid_t pid = getpid();
309	pid_t pid2 = 1235;
310
311	expect_lookup(RELPATH, ino);
312	expect_open(ino, 0, 1);
313	EXPECT_CALL(*m_mock, process(
314		ResultOf([=](auto in) {
315			return (in.header.opcode == FUSE_GETLK &&
316				in.header.nodeid == ino &&
317				in.body.getlk.fh == FH &&
318				/*
319				 * Though it seems useless, libfuse expects the
320				 * owner and pid fields to be set during
321				 * FUSE_GETLK.
322				 */
323				in.body.getlk.owner == (uint32_t)pid &&
324				in.body.getlk.lk.pid == (uint64_t)pid &&
325				in.body.getlk.lk.start == 10 &&
326				in.body.getlk.lk.end == 1009 &&
327				in.body.getlk.lk.type == F_RDLCK);
328		}, Eq(true)),
329		_)
330	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
331		SET_OUT_HEADER_LEN(out, getlk);
332		out.body.getlk.lk.start = 100;
333		out.body.getlk.lk.end = 199;
334		out.body.getlk.lk.type = F_WRLCK;
335		out.body.getlk.lk.pid = (uint32_t)pid2;;
336	})));
337
338	fd = open(FULLPATH, O_RDWR);
339	ASSERT_LE(0, fd) << strerror(errno);
340	fl.l_start = 10;
341	fl.l_len = 1000;
342	fl.l_pid = 0;
343	fl.l_type = F_RDLCK;
344	fl.l_whence = SEEK_SET;
345	fl.l_sysid = 0;
346	ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
347	EXPECT_EQ(100, fl.l_start);
348	EXPECT_EQ(100, fl.l_len);
349	EXPECT_EQ(pid2, fl.l_pid);
350	EXPECT_EQ(F_WRLCK, fl.l_type);
351	EXPECT_EQ(SEEK_SET, fl.l_whence);
352	EXPECT_EQ(0, fl.l_sysid);
353	leak(fd);
354}
355
356/*
357 * F_GETLK with SEEK_CUR
358 */
359TEST_F(Getlk, seek_cur)
360{
361	const char FULLPATH[] = "mountpoint/some_file.txt";
362	const char RELPATH[] = "some_file.txt";
363	uint64_t ino = 42;
364	struct flock fl;
365	int fd;
366	pid_t pid = getpid();
367
368	expect_lookup(RELPATH, ino, 1024);
369	expect_open(ino, 0, 1);
370	EXPECT_CALL(*m_mock, process(
371		ResultOf([=](auto in) {
372			return (in.header.opcode == FUSE_GETLK &&
373				in.header.nodeid == ino &&
374				in.body.getlk.fh == FH &&
375				/*
376				 * Though it seems useless, libfuse expects the
377				 * owner and pid fields to be set during
378				 * FUSE_GETLK.
379				 */
380				in.body.getlk.owner == (uint32_t)pid &&
381				in.body.getlk.lk.pid == (uint64_t)pid &&
382				in.body.getlk.lk.start == 500 &&
383				in.body.getlk.lk.end == 509 &&
384				in.body.getlk.lk.type == F_RDLCK);
385		}, Eq(true)),
386		_)
387	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
388		SET_OUT_HEADER_LEN(out, getlk);
389		out.body.getlk.lk.start = 400;
390		out.body.getlk.lk.end = 499;
391		out.body.getlk.lk.type = F_WRLCK;
392		out.body.getlk.lk.pid = (uint32_t)pid + 1;
393	})));
394
395	fd = open(FULLPATH, O_RDWR);
396	ASSERT_LE(0, fd) << strerror(errno);
397	ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
398
399	fl.l_start = 0;
400	fl.l_len = 10;
401	fl.l_pid = 42;
402	fl.l_type = F_RDLCK;
403	fl.l_whence = SEEK_CUR;
404	fl.l_sysid = 0;
405	ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
406
407	/*
408	 * After a successful F_GETLK request, the value of l_whence is
409	 * SEEK_SET.
410	 */
411	EXPECT_EQ(F_WRLCK, fl.l_type);
412	EXPECT_EQ(fl.l_pid, pid + 1);
413	EXPECT_EQ(fl.l_start, 400);
414	EXPECT_EQ(fl.l_len, 100);
415	EXPECT_EQ(fl.l_whence, SEEK_SET);
416	ASSERT_EQ(fl.l_sysid, 0);
417
418	leak(fd);
419}
420
421/*
422 * F_GETLK with SEEK_END
423 */
424TEST_F(Getlk, seek_end)
425{
426	const char FULLPATH[] = "mountpoint/some_file.txt";
427	const char RELPATH[] = "some_file.txt";
428	uint64_t ino = 42;
429	struct flock fl;
430	int fd;
431	pid_t pid = getpid();
432
433	expect_lookup(RELPATH, ino, 1024);
434	expect_open(ino, 0, 1);
435	EXPECT_CALL(*m_mock, process(
436		ResultOf([=](auto in) {
437			return (in.header.opcode == FUSE_GETLK &&
438				in.header.nodeid == ino &&
439				in.body.getlk.fh == FH &&
440				/*
441				 * Though it seems useless, libfuse expects the
442				 * owner and pid fields to be set during
443				 * FUSE_GETLK.
444				 */
445				in.body.getlk.owner == (uint32_t)pid &&
446				in.body.getlk.lk.pid == (uint64_t)pid &&
447				in.body.getlk.lk.start == 512 &&
448				in.body.getlk.lk.end == 1023 &&
449				in.body.getlk.lk.type == F_RDLCK);
450		}, Eq(true)),
451		_)
452	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
453		SET_OUT_HEADER_LEN(out, getlk);
454		out.body.getlk.lk.start = 400;
455		out.body.getlk.lk.end = 499;
456		out.body.getlk.lk.type = F_WRLCK;
457		out.body.getlk.lk.pid = (uint32_t)pid + 1;
458	})));
459
460	fd = open(FULLPATH, O_RDWR);
461	ASSERT_LE(0, fd) << strerror(errno);
462	ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
463
464	fl.l_start = -512;
465	fl.l_len = 512;
466	fl.l_pid = 42;
467	fl.l_type = F_RDLCK;
468	fl.l_whence = SEEK_END;
469	fl.l_sysid = 0;
470	ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
471
472	/*
473	 * After a successful F_GETLK request, the value of l_whence is
474	 * SEEK_SET.
475	 */
476	EXPECT_EQ(F_WRLCK, fl.l_type);
477	EXPECT_EQ(fl.l_pid, pid + 1);
478	EXPECT_EQ(fl.l_start, 400);
479	EXPECT_EQ(fl.l_len, 100);
480	EXPECT_EQ(fl.l_whence, SEEK_SET);
481	ASSERT_EQ(fl.l_sysid, 0);
482
483	leak(fd);
484}
485
486/*
487 * If the fuse filesystem does not support posix file locks, then the kernel
488 * should fall back to local locks.
489 */
490TEST_F(SetlkFallback, local)
491{
492	const char FULLPATH[] = "mountpoint/some_file.txt";
493	const char RELPATH[] = "some_file.txt";
494	uint64_t ino = 42;
495	struct flock fl;
496	int fd;
497
498	expect_lookup(RELPATH, ino);
499	expect_open(ino, 0, 1);
500
501	fd = open(FULLPATH, O_RDWR);
502	ASSERT_LE(0, fd) << strerror(errno);
503	fl.l_start = 10;
504	fl.l_len = 1000;
505	fl.l_pid = getpid();
506	fl.l_type = F_RDLCK;
507	fl.l_whence = SEEK_SET;
508	fl.l_sysid = 0;
509	ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
510	leak(fd);
511}
512
513/* Clear a lock with FUSE_SETLK */
514TEST_F(Setlk, clear)
515{
516	const char FULLPATH[] = "mountpoint/some_file.txt";
517	const char RELPATH[] = "some_file.txt";
518	uint64_t ino = 42;
519	struct flock fl;
520	int fd;
521	pid_t pid = getpid();
522
523	expect_lookup(RELPATH, ino);
524	expect_open(ino, 0, 1);
525	expect_setlk(ino, pid, 10, 1009, F_UNLCK, 0);
526
527	fd = open(FULLPATH, O_RDWR);
528	ASSERT_LE(0, fd) << strerror(errno);
529	fl.l_start = 10;
530	fl.l_len = 1000;
531	fl.l_pid = 0;
532	fl.l_type = F_UNLCK;
533	fl.l_whence = SEEK_SET;
534	fl.l_sysid = 0;
535	ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
536	leak(fd);
537}
538
539/* Set a new lock with FUSE_SETLK */
540TEST_F(Setlk, set)
541{
542	const char FULLPATH[] = "mountpoint/some_file.txt";
543	const char RELPATH[] = "some_file.txt";
544	uint64_t ino = 42;
545	struct flock fl;
546	int fd;
547	pid_t pid = getpid();
548
549	expect_lookup(RELPATH, ino);
550	expect_open(ino, 0, 1);
551	expect_setlk(ino, pid, 10, 1009, F_RDLCK, 0);
552
553	fd = open(FULLPATH, O_RDWR);
554	ASSERT_LE(0, fd) << strerror(errno);
555	fl.l_start = 10;
556	fl.l_len = 1000;
557	fl.l_pid = 0;
558	fl.l_type = F_RDLCK;
559	fl.l_whence = SEEK_SET;
560	fl.l_sysid = 0;
561	ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
562	leak(fd);
563}
564
565/* l_len = 0 is a flag value that means to lock until EOF */
566TEST_F(Setlk, set_eof)
567{
568	const char FULLPATH[] = "mountpoint/some_file.txt";
569	const char RELPATH[] = "some_file.txt";
570	uint64_t ino = 42;
571	struct flock fl;
572	int fd;
573	pid_t pid = getpid();
574
575	expect_lookup(RELPATH, ino);
576	expect_open(ino, 0, 1);
577	expect_setlk(ino, pid, 10, OFFSET_MAX, F_RDLCK, 0);
578
579	fd = open(FULLPATH, O_RDWR);
580	ASSERT_LE(0, fd) << strerror(errno);
581	fl.l_start = 10;
582	fl.l_len = 0;
583	fl.l_pid = 0;
584	fl.l_type = F_RDLCK;
585	fl.l_whence = SEEK_SET;
586	fl.l_sysid = 0;
587	ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
588	leak(fd);
589}
590
591/* Set a new lock with FUSE_SETLK, using SEEK_CUR for l_whence */
592TEST_F(Setlk, set_seek_cur)
593{
594	const char FULLPATH[] = "mountpoint/some_file.txt";
595	const char RELPATH[] = "some_file.txt";
596	uint64_t ino = 42;
597	struct flock fl;
598	int fd;
599	pid_t pid = getpid();
600
601	expect_lookup(RELPATH, ino, 1024);
602	expect_open(ino, 0, 1);
603	expect_setlk(ino, pid, 500, 509, F_RDLCK, 0);
604
605	fd = open(FULLPATH, O_RDWR);
606	ASSERT_LE(0, fd) << strerror(errno);
607	ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
608
609	fl.l_start = 0;
610	fl.l_len = 10;
611	fl.l_pid = 0;
612	fl.l_type = F_RDLCK;
613	fl.l_whence = SEEK_CUR;
614	fl.l_sysid = 0;
615	ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
616
617	leak(fd);
618}
619
620/* Set a new lock with FUSE_SETLK, using SEEK_END for l_whence */
621TEST_F(Setlk, set_seek_end)
622{
623	const char FULLPATH[] = "mountpoint/some_file.txt";
624	const char RELPATH[] = "some_file.txt";
625	uint64_t ino = 42;
626	struct flock fl;
627	int fd;
628	pid_t pid = getpid();
629
630	expect_lookup(RELPATH, ino, 1024);
631	expect_open(ino, 0, 1);
632	expect_setlk(ino, pid, 1000, 1009, F_RDLCK, 0);
633
634	fd = open(FULLPATH, O_RDWR);
635	ASSERT_LE(0, fd) << strerror(errno);
636
637	fl.l_start = -24;
638	fl.l_len = 10;
639	fl.l_pid = 0;
640	fl.l_type = F_RDLCK;
641	fl.l_whence = SEEK_END;
642	fl.l_sysid = 0;
643	ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
644
645	leak(fd);
646}
647
648/* Fail to set a new lock with FUSE_SETLK due to a conflict */
649TEST_F(Setlk, eagain)
650{
651	const char FULLPATH[] = "mountpoint/some_file.txt";
652	const char RELPATH[] = "some_file.txt";
653	uint64_t ino = 42;
654	struct flock fl;
655	int fd;
656	pid_t pid = getpid();
657
658	expect_lookup(RELPATH, ino);
659	expect_open(ino, 0, 1);
660	expect_setlk(ino, pid, 10, 1009, F_RDLCK, EAGAIN);
661
662	fd = open(FULLPATH, O_RDWR);
663	ASSERT_LE(0, fd) << strerror(errno);
664	fl.l_start = 10;
665	fl.l_len = 1000;
666	fl.l_pid = 0;
667	fl.l_type = F_RDLCK;
668	fl.l_whence = SEEK_SET;
669	fl.l_sysid = 0;
670	ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl));
671	ASSERT_EQ(EAGAIN, errno);
672	leak(fd);
673}
674
675/*
676 * If the fuse filesystem does not support posix file locks, then the kernel
677 * should fall back to local locks.
678 */
679TEST_F(SetlkwFallback, local)
680{
681	const char FULLPATH[] = "mountpoint/some_file.txt";
682	const char RELPATH[] = "some_file.txt";
683	uint64_t ino = 42;
684	struct flock fl;
685	int fd;
686
687	expect_lookup(RELPATH, ino);
688	expect_open(ino, 0, 1);
689
690	fd = open(FULLPATH, O_RDWR);
691	ASSERT_LE(0, fd) << strerror(errno);
692	fl.l_start = 10;
693	fl.l_len = 1000;
694	fl.l_pid = 0;
695	fl.l_type = F_RDLCK;
696	fl.l_whence = SEEK_SET;
697	fl.l_sysid = 0;
698	ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
699	leak(fd);
700}
701
702/*
703 * Set a new lock with FUSE_SETLK.  If the lock is not available, then the
704 * command should block.  But to the kernel, that's the same as just being
705 * slow, so we don't need a separate test method
706 */
707TEST_F(Setlkw, set)
708{
709	const char FULLPATH[] = "mountpoint/some_file.txt";
710	const char RELPATH[] = "some_file.txt";
711	uint64_t ino = 42;
712	struct flock fl;
713	int fd;
714	pid_t pid = getpid();
715
716	expect_lookup(RELPATH, ino);
717	expect_open(ino, 0, 1);
718	expect_setlkw(ino, pid, 10, 1009, F_RDLCK, 0);
719
720	fd = open(FULLPATH, O_RDWR);
721	ASSERT_LE(0, fd) << strerror(errno);
722	fl.l_start = 10;
723	fl.l_len = 1000;
724	fl.l_pid = 0;
725	fl.l_type = F_RDLCK;
726	fl.l_whence = SEEK_SET;
727	fl.l_sysid = 0;
728	ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
729	leak(fd);
730}
731