]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fuse/locks.cc
fuse(4): combine common code in the tests
[FreeBSD/FreeBSD.git] / tests / sys / fs / fuse / locks.cc
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 <fcntl.h>
33 }
34
35 #include "mockfs.hh"
36 #include "utils.hh"
37
38 /* This flag value should probably be defined in fuse_kernel.h */
39 #define OFFSET_MAX 0x7fffffffffffffffLL
40
41 using namespace testing;
42
43 /* For testing filesystems without posix locking support */
44 class Fallback: public FuseTest {
45 public:
46
47 void expect_lookup(const char *relpath, uint64_t ino)
48 {
49         FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
50 }
51
52 };
53
54 /* For testing filesystems with posix locking support */
55 class Locks: public Fallback {
56         virtual void SetUp() {
57                 m_init_flags = FUSE_POSIX_LOCKS;
58                 Fallback::SetUp();
59         }
60 };
61
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 {};
68
69 /*
70  * If the fuse filesystem does not support posix file locks, then the kernel
71  * should fall back to local locks.
72  */
73 TEST_F(GetlkFallback, local)
74 {
75         const char FULLPATH[] = "mountpoint/some_file.txt";
76         const char RELPATH[] = "some_file.txt";
77         uint64_t ino = 42;
78         struct flock fl;
79         int fd;
80
81         expect_lookup(RELPATH, ino);
82         expect_open(ino, 0, 1);
83         expect_getattr(ino, 0);
84
85         fd = open(FULLPATH, O_RDWR);
86         ASSERT_LE(0, fd) << strerror(errno);
87         fl.l_start = 10;
88         fl.l_len = 1000;
89         fl.l_pid = getpid();
90         fl.l_type = F_RDLCK;
91         fl.l_whence = SEEK_SET;
92         fl.l_sysid = 0;
93         ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
94         /* Deliberately leak fd.  close(2) will be tested in release.cc */
95 }
96
97 /* 
98  * If the filesystem has no locks that fit the description, the filesystem
99  * should return F_UNLCK
100  */
101 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
102 TEST_F(Getlk, DISABLED_no_locks)
103 {
104         const char FULLPATH[] = "mountpoint/some_file.txt";
105         const char RELPATH[] = "some_file.txt";
106         uint64_t ino = 42;
107         struct flock fl;
108         int fd;
109         pid_t pid = 1234;
110
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);
124                 }, Eq(true)),
125                 _)
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;
131         }));
132
133         fd = open(FULLPATH, O_RDWR);
134         ASSERT_LE(0, fd) << strerror(errno);
135         fl.l_start = 10;
136         fl.l_len = 1000;
137         fl.l_pid = pid;
138         fl.l_type = F_RDLCK;
139         fl.l_whence = SEEK_SET;
140         fl.l_sysid = 0;
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 */
144 }
145
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)
149 {
150         const char FULLPATH[] = "mountpoint/some_file.txt";
151         const char RELPATH[] = "some_file.txt";
152         uint64_t ino = 42;
153         struct flock fl;
154         int fd;
155         pid_t pid = 1234;
156         pid_t pid2 = 1234;
157
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);
171                 }, Eq(true)),
172                 _)
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;;
180         }));
181
182         fd = open(FULLPATH, O_RDWR);
183         ASSERT_LE(0, fd) << strerror(errno);
184         fl.l_start = 10;
185         fl.l_len = 1000;
186         fl.l_pid = pid;
187         fl.l_type = F_RDLCK;
188         fl.l_whence = SEEK_SET;
189         fl.l_sysid = 0;
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 */
198 }
199
200 /*
201  * If the fuse filesystem does not support posix file locks, then the kernel
202  * should fall back to local locks.
203  */
204 TEST_F(SetlkFallback, local)
205 {
206         const char FULLPATH[] = "mountpoint/some_file.txt";
207         const char RELPATH[] = "some_file.txt";
208         uint64_t ino = 42;
209         struct flock fl;
210         int fd;
211
212         expect_lookup(RELPATH, ino);
213         expect_open(ino, 0, 1);
214         expect_getattr(ino, 0);
215
216         fd = open(FULLPATH, O_RDWR);
217         ASSERT_LE(0, fd) << strerror(errno);
218         fl.l_start = 10;
219         fl.l_len = 1000;
220         fl.l_pid = getpid();
221         fl.l_type = F_RDLCK;
222         fl.l_whence = SEEK_SET;
223         fl.l_sysid = 0;
224         ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
225         /* Deliberately leak fd.  close(2) will be tested in release.cc */
226 }
227
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)
231 {
232         const char FULLPATH[] = "mountpoint/some_file.txt";
233         const char RELPATH[] = "some_file.txt";
234         uint64_t ino = 42;
235         struct flock fl;
236         int fd;
237         pid_t pid = 1234;
238
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);
252                 }, Eq(true)),
253                 _)
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;
259         }));
260
261         fd = open(FULLPATH, O_RDWR);
262         ASSERT_LE(0, fd) << strerror(errno);
263         fl.l_start = 10;
264         fl.l_len = 1000;
265         fl.l_pid = pid;
266         fl.l_type = F_RDLCK;
267         fl.l_whence = SEEK_SET;
268         fl.l_sysid = 0;
269         ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
270         /* Deliberately leak fd.  close(2) will be tested in release.cc */
271 }
272
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)
276 {
277         const char FULLPATH[] = "mountpoint/some_file.txt";
278         const char RELPATH[] = "some_file.txt";
279         uint64_t ino = 42;
280         struct flock fl;
281         int fd;
282         pid_t pid = 1234;
283
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);
297                 }, Eq(true)),
298                 _)
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;
304         }));
305
306         fd = open(FULLPATH, O_RDWR);
307         ASSERT_LE(0, fd) << strerror(errno);
308         fl.l_start = 10;
309         fl.l_len = 0;
310         fl.l_pid = pid;
311         fl.l_type = F_RDLCK;
312         fl.l_whence = SEEK_SET;
313         fl.l_sysid = 0;
314         ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
315         /* Deliberately leak fd.  close(2) will be tested in release.cc */
316 }
317
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)
321 {
322         const char FULLPATH[] = "mountpoint/some_file.txt";
323         const char RELPATH[] = "some_file.txt";
324         uint64_t ino = 42;
325         struct flock fl;
326         int fd;
327         pid_t pid = 1234;
328
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);
342                 }, Eq(true)),
343                 _)
344         ).WillOnce(Invoke(ReturnErrno(EAGAIN)));
345
346         fd = open(FULLPATH, O_RDWR);
347         ASSERT_LE(0, fd) << strerror(errno);
348         fl.l_start = 10;
349         fl.l_len = 1000;
350         fl.l_pid = pid;
351         fl.l_type = F_RDLCK;
352         fl.l_whence = SEEK_SET;
353         fl.l_sysid = 0;
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 */
357 }
358
359 /*
360  * If the fuse filesystem does not support posix file locks, then the kernel
361  * should fall back to local locks.
362  */
363 TEST_F(SetlkwFallback, local)
364 {
365         const char FULLPATH[] = "mountpoint/some_file.txt";
366         const char RELPATH[] = "some_file.txt";
367         uint64_t ino = 42;
368         struct flock fl;
369         int fd;
370
371         expect_lookup(RELPATH, ino);
372         expect_open(ino, 0, 1);
373         expect_getattr(ino, 0);
374
375         fd = open(FULLPATH, O_RDWR);
376         ASSERT_LE(0, fd) << strerror(errno);
377         fl.l_start = 10;
378         fl.l_len = 1000;
379         fl.l_pid = getpid();
380         fl.l_type = F_RDLCK;
381         fl.l_whence = SEEK_SET;
382         fl.l_sysid = 0;
383         ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
384         /* Deliberately leak fd.  close(2) will be tested in release.cc */
385 }
386
387 /*
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
391  */
392 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
393 TEST_F(Setlkw, DISABLED_set)
394 {
395         const char FULLPATH[] = "mountpoint/some_file.txt";
396         const char RELPATH[] = "some_file.txt";
397         uint64_t ino = 42;
398         struct flock fl;
399         int fd;
400         pid_t pid = 1234;
401
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);
415                 }, Eq(true)),
416                 _)
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;
422         }));
423
424         fd = open(FULLPATH, O_RDWR);
425         ASSERT_LE(0, fd) << strerror(errno);
426         fl.l_start = 10;
427         fl.l_len = 1000;
428         fl.l_pid = pid;
429         fl.l_type = F_RDLCK;
430         fl.l_whence = SEEK_SET;
431         fl.l_sysid = 0;
432         ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
433         /* Deliberately leak fd.  close(2) will be tested in release.cc */
434 }