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
expect_lookup(const char * relpath,uint64_t ino,uint64_t size=0)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 {
SetUp()57 virtual void SetUp() {
58 m_init_flags = FUSE_POSIX_LOCKS;
59 Fallback::SetUp();
60 }
61 };
62
63 class Fcntl: public Locks {
64 public:
expect_setlk(uint64_t ino,pid_t pid,uint64_t start,uint64_t end,uint32_t type,int err)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 }
expect_setlkw(uint64_t ino,pid_t pid,uint64_t start,uint64_t end,uint32_t type,int err)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:
expect_setlk(uint64_t ino,uint32_t type,int err)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 */
TEST_F(FlockFallback,local)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 */
TEST_F(Flock,local)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 */
TEST_F(Flock,DISABLED_set)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 */
TEST_F(Flock,DISABLED_eagain)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 */
TEST_F(GetlkFallback,local)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 */
TEST_F(Getlk,no_locks)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 */
TEST_F(Getlk,lock_exists)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 */
TEST_F(Getlk,seek_cur)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 */
TEST_F(Getlk,seek_end)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 */
TEST_F(SetlkFallback,local)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 */
TEST_F(Setlk,clear)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 */
TEST_F(Setlk,set)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 */
TEST_F(Setlk,set_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 */
TEST_F(Setlk,set_seek_cur)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 */
TEST_F(Setlk,set_seek_end)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 */
TEST_F(Setlk,eagain)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 */
TEST_F(SetlkwFallback,local)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 */
TEST_F(Setlkw,set)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