xref: /freebsd/tests/sys/fs/fusefs/locks.cc (revision e0c4386e)
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 
31 extern "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 
42 using namespace testing;
43 
44 /* For testing filesystems without posix locking support */
45 class Fallback: public FuseTest {
46 public:
47 
48 void 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 */
56 class Locks: public Fallback {
57 	virtual void SetUp() {
58 		m_init_flags = FUSE_POSIX_LOCKS;
59 		Fallback::SetUp();
60 	}
61 };
62 
63 class Fcntl: public Locks {
64 public:
65 void 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 }
82 void 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 
101 class Flock: public Locks {
102 public:
103 void 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 
122 class FlockFallback: public Fallback {};
123 class GetlkFallback: public Fallback {};
124 class Getlk: public Fcntl {};
125 class SetlkFallback: public Fallback {};
126 class Setlk: public Fcntl {};
127 class SetlkwFallback: public Fallback {};
128 class 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  */
134 TEST_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  */
155 TEST_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 */
173 TEST_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 */
192 TEST_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  */
214 TEST_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  */
241 TEST_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 */
301 TEST_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  */
359 TEST_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  */
424 TEST_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  */
490 TEST_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 */
514 TEST_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 */
540 TEST_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 */
566 TEST_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 */
592 TEST_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 */
621 TEST_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 */
649 TEST_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  */
679 TEST_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  */
707 TEST_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