2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
38 /* This flag value should probably be defined in fuse_kernel.h */
39 #define OFFSET_MAX 0x7fffffffffffffffLL
41 using namespace testing;
43 /* For testing filesystems without posix locking support */
44 class Fallback: public FuseTest {
47 void expect_lookup(const char *relpath, uint64_t ino)
49 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
54 /* For testing filesystems with posix locking support */
55 class Locks: public Fallback {
56 virtual void SetUp() {
57 m_init_flags = FUSE_POSIX_LOCKS;
62 class GetlkFallback: public Fallback {};
63 class Getlk: public Locks {};
64 class SetlkFallback: public Fallback {};
65 class Setlk: public Locks {};
66 class SetlkwFallback: public Fallback {};
67 class Setlkw: public Locks {};
70 * If the fuse filesystem does not support posix file locks, then the kernel
71 * should fall back to local locks.
73 TEST_F(GetlkFallback, local)
75 const char FULLPATH[] = "mountpoint/some_file.txt";
76 const char RELPATH[] = "some_file.txt";
81 expect_lookup(RELPATH, ino);
82 expect_open(ino, 0, 1);
83 expect_getattr(ino, 0);
85 fd = open(FULLPATH, O_RDWR);
86 ASSERT_LE(0, fd) << strerror(errno);
91 fl.l_whence = SEEK_SET;
93 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
94 /* Deliberately leak fd. close(2) will be tested in release.cc */
98 * If the filesystem has no locks that fit the description, the filesystem
99 * should return F_UNLCK
101 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
102 TEST_F(Getlk, DISABLED_no_locks)
104 const char FULLPATH[] = "mountpoint/some_file.txt";
105 const char RELPATH[] = "some_file.txt";
111 expect_lookup(RELPATH, ino);
112 expect_open(ino, 0, 1);
113 expect_getattr(ino, 0);
114 EXPECT_CALL(*m_mock, process(
115 ResultOf([=](auto in) {
116 return (in->header.opcode == FUSE_GETLK &&
117 in->header.nodeid == ino &&
118 in->body.getlk.fh == FH &&
119 in->body.getlk.owner == (uint32_t)pid &&
120 in->body.getlk.lk.start == 10 &&
121 in->body.getlk.lk.end == 1009 &&
122 in->body.getlk.lk.type == F_RDLCK &&
123 in->body.getlk.lk.pid == 10);
126 ).WillOnce(Invoke([=](auto in, auto out) {
127 out->header.unique = in->header.unique;
128 SET_OUT_HEADER_LEN(out, getlk);
129 out->body.getlk.lk = in->body.getlk.lk;
130 out->body.getlk.lk.type = F_UNLCK;
133 fd = open(FULLPATH, O_RDWR);
134 ASSERT_LE(0, fd) << strerror(errno);
139 fl.l_whence = SEEK_SET;
141 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
142 ASSERT_EQ(F_UNLCK, fl.l_type);
143 /* Deliberately leak fd. close(2) will be tested in release.cc */
146 /* A different pid does have a lock */
147 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
148 TEST_F(Getlk, DISABLED_lock_exists)
150 const char FULLPATH[] = "mountpoint/some_file.txt";
151 const char RELPATH[] = "some_file.txt";
158 expect_lookup(RELPATH, ino);
159 expect_open(ino, 0, 1);
160 expect_getattr(ino, 0);
161 EXPECT_CALL(*m_mock, process(
162 ResultOf([=](auto in) {
163 return (in->header.opcode == FUSE_GETLK &&
164 in->header.nodeid == ino &&
165 in->body.getlk.fh == FH &&
166 in->body.getlk.owner == (uint32_t)pid &&
167 in->body.getlk.lk.start == 10 &&
168 in->body.getlk.lk.end == 1009 &&
169 in->body.getlk.lk.type == F_RDLCK &&
170 in->body.getlk.lk.pid == 10);
173 ).WillOnce(Invoke([=](auto in, auto out) {
174 out->header.unique = in->header.unique;
175 SET_OUT_HEADER_LEN(out, getlk);
176 out->body.getlk.lk.start = 100;
177 out->body.getlk.lk.end = 199;
178 out->body.getlk.lk.type = F_WRLCK;
179 out->body.getlk.lk.pid = (uint32_t)pid2;;
182 fd = open(FULLPATH, O_RDWR);
183 ASSERT_LE(0, fd) << strerror(errno);
188 fl.l_whence = SEEK_SET;
190 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
191 EXPECT_EQ(100, fl.l_start);
192 EXPECT_EQ(100, fl.l_len);
193 EXPECT_EQ(pid2, fl.l_pid);
194 EXPECT_EQ(F_WRLCK, fl.l_type);
195 EXPECT_EQ(SEEK_SET, fl.l_whence);
196 EXPECT_EQ(0, fl.l_sysid);
197 /* Deliberately leak fd. close(2) will be tested in release.cc */
201 * If the fuse filesystem does not support posix file locks, then the kernel
202 * should fall back to local locks.
204 TEST_F(SetlkFallback, local)
206 const char FULLPATH[] = "mountpoint/some_file.txt";
207 const char RELPATH[] = "some_file.txt";
212 expect_lookup(RELPATH, ino);
213 expect_open(ino, 0, 1);
214 expect_getattr(ino, 0);
216 fd = open(FULLPATH, O_RDWR);
217 ASSERT_LE(0, fd) << strerror(errno);
222 fl.l_whence = SEEK_SET;
224 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
225 /* Deliberately leak fd. close(2) will be tested in release.cc */
228 /* Set a new lock with FUSE_SETLK */
229 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
230 TEST_F(Setlk, DISABLED_set)
232 const char FULLPATH[] = "mountpoint/some_file.txt";
233 const char RELPATH[] = "some_file.txt";
239 expect_lookup(RELPATH, ino);
240 expect_open(ino, 0, 1);
241 expect_getattr(ino, 0);
242 EXPECT_CALL(*m_mock, process(
243 ResultOf([=](auto in) {
244 return (in->header.opcode == FUSE_SETLK &&
245 in->header.nodeid == ino &&
246 in->body.getlk.fh == FH &&
247 in->body.getlk.owner == (uint32_t)pid &&
248 in->body.getlk.lk.start == 10 &&
249 in->body.getlk.lk.end == 1009 &&
250 in->body.getlk.lk.type == F_RDLCK &&
251 in->body.getlk.lk.pid == 10);
254 ).WillOnce(Invoke([=](auto in, auto out) {
255 out->header.unique = in->header.unique;
256 SET_OUT_HEADER_LEN(out, getlk);
257 out->body.getlk.lk = in->body.getlk.lk;
258 out->body.getlk.lk.type = F_UNLCK;
261 fd = open(FULLPATH, O_RDWR);
262 ASSERT_LE(0, fd) << strerror(errno);
267 fl.l_whence = SEEK_SET;
269 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
270 /* Deliberately leak fd. close(2) will be tested in release.cc */
273 /* l_len = 0 is a flag value that means to lock until EOF */
274 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
275 TEST_F(Setlk, DISABLED_set_eof)
277 const char FULLPATH[] = "mountpoint/some_file.txt";
278 const char RELPATH[] = "some_file.txt";
284 expect_lookup(RELPATH, ino);
285 expect_open(ino, 0, 1);
286 expect_getattr(ino, 0);
287 EXPECT_CALL(*m_mock, process(
288 ResultOf([=](auto in) {
289 return (in->header.opcode == FUSE_SETLK &&
290 in->header.nodeid == ino &&
291 in->body.getlk.fh == FH &&
292 in->body.getlk.owner == (uint32_t)pid &&
293 in->body.getlk.lk.start == 10 &&
294 in->body.getlk.lk.end == OFFSET_MAX &&
295 in->body.getlk.lk.type == F_RDLCK &&
296 in->body.getlk.lk.pid == 10);
299 ).WillOnce(Invoke([=](auto in, auto out) {
300 out->header.unique = in->header.unique;
301 SET_OUT_HEADER_LEN(out, getlk);
302 out->body.getlk.lk = in->body.getlk.lk;
303 out->body.getlk.lk.type = F_UNLCK;
306 fd = open(FULLPATH, O_RDWR);
307 ASSERT_LE(0, fd) << strerror(errno);
312 fl.l_whence = SEEK_SET;
314 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
315 /* Deliberately leak fd. close(2) will be tested in release.cc */
318 /* Fail to set a new lock with FUSE_SETLK due to a conflict */
319 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
320 TEST_F(Setlk, DISABLED_eagain)
322 const char FULLPATH[] = "mountpoint/some_file.txt";
323 const char RELPATH[] = "some_file.txt";
329 expect_lookup(RELPATH, ino);
330 expect_open(ino, 0, 1);
331 expect_getattr(ino, 0);
332 EXPECT_CALL(*m_mock, process(
333 ResultOf([=](auto in) {
334 return (in->header.opcode == FUSE_SETLK &&
335 in->header.nodeid == ino &&
336 in->body.getlk.fh == FH &&
337 in->body.getlk.owner == (uint32_t)pid &&
338 in->body.getlk.lk.start == 10 &&
339 in->body.getlk.lk.end == 1009 &&
340 in->body.getlk.lk.type == F_RDLCK &&
341 in->body.getlk.lk.pid == 10);
344 ).WillOnce(Invoke(ReturnErrno(EAGAIN)));
346 fd = open(FULLPATH, O_RDWR);
347 ASSERT_LE(0, fd) << strerror(errno);
352 fl.l_whence = SEEK_SET;
354 ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl));
355 ASSERT_EQ(EAGAIN, errno);
356 /* Deliberately leak fd. close(2) will be tested in release.cc */
360 * If the fuse filesystem does not support posix file locks, then the kernel
361 * should fall back to local locks.
363 TEST_F(SetlkwFallback, local)
365 const char FULLPATH[] = "mountpoint/some_file.txt";
366 const char RELPATH[] = "some_file.txt";
371 expect_lookup(RELPATH, ino);
372 expect_open(ino, 0, 1);
373 expect_getattr(ino, 0);
375 fd = open(FULLPATH, O_RDWR);
376 ASSERT_LE(0, fd) << strerror(errno);
381 fl.l_whence = SEEK_SET;
383 ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
384 /* Deliberately leak fd. close(2) will be tested in release.cc */
388 * Set a new lock with FUSE_SETLK. If the lock is not available, then the
389 * command should block. But to the kernel, that's the same as just being
390 * slow, so we don't need a separate test method
392 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
393 TEST_F(Setlkw, DISABLED_set)
395 const char FULLPATH[] = "mountpoint/some_file.txt";
396 const char RELPATH[] = "some_file.txt";
402 expect_lookup(RELPATH, ino);
403 expect_open(ino, 0, 1);
404 expect_getattr(ino, 0);
405 EXPECT_CALL(*m_mock, process(
406 ResultOf([=](auto in) {
407 return (in->header.opcode == FUSE_SETLK &&
408 in->header.nodeid == ino &&
409 in->body.getlk.fh == FH &&
410 in->body.getlk.owner == (uint32_t)pid &&
411 in->body.getlk.lk.start == 10 &&
412 in->body.getlk.lk.end == 1009 &&
413 in->body.getlk.lk.type == F_RDLCK &&
414 in->body.getlk.lk.pid == 10);
417 ).WillOnce(Invoke([=](auto in, auto out) {
418 out->header.unique = in->header.unique;
419 SET_OUT_HEADER_LEN(out, getlk);
420 out->body.getlk.lk = in->body.getlk.lk;
421 out->body.getlk.lk.type = F_UNLCK;
424 fd = open(FULLPATH, O_RDWR);
425 ASSERT_LE(0, fd) << strerror(errno);
430 fl.l_whence = SEEK_SET;
432 ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
433 /* Deliberately leak fd. close(2) will be tested in release.cc */