]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/locks.cc
fusefs: make the tests more cplusplusy
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / 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, 0, 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
84         fd = open(FULLPATH, O_RDWR);
85         ASSERT_LE(0, fd) << strerror(errno);
86         fl.l_start = 10;
87         fl.l_len = 1000;
88         fl.l_pid = getpid();
89         fl.l_type = F_RDLCK;
90         fl.l_whence = SEEK_SET;
91         fl.l_sysid = 0;
92         ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
93         /* Deliberately leak fd.  close(2) will be tested in release.cc */
94 }
95
96 /* 
97  * If the filesystem has no locks that fit the description, the filesystem
98  * should return F_UNLCK
99  */
100 TEST_F(Getlk, no_locks)
101 {
102         const char FULLPATH[] = "mountpoint/some_file.txt";
103         const char RELPATH[] = "some_file.txt";
104         uint64_t ino = 42;
105         struct flock fl;
106         int fd;
107         pid_t pid = 1234;
108
109         expect_lookup(RELPATH, ino);
110         expect_open(ino, 0, 1);
111         EXPECT_CALL(*m_mock, process(
112                 ResultOf([=](auto in) {
113                         return (in.header.opcode == FUSE_GETLK &&
114                                 in.header.nodeid == ino &&
115                                 in.body.getlk.fh == FH &&
116                                 in.body.getlk.owner == (uint32_t)pid &&
117                                 in.body.getlk.lk.start == 10 &&
118                                 in.body.getlk.lk.end == 1009 &&
119                                 in.body.getlk.lk.type == F_RDLCK &&
120                                 in.body.getlk.lk.pid == (uint64_t)pid);
121                 }, Eq(true)),
122                 _)
123         ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
124                 SET_OUT_HEADER_LEN(out, getlk);
125                 out.body.getlk.lk = in.body.getlk.lk;
126                 out.body.getlk.lk.type = F_UNLCK;
127         })));
128
129         fd = open(FULLPATH, O_RDWR);
130         ASSERT_LE(0, fd) << strerror(errno);
131         fl.l_start = 10;
132         fl.l_len = 1000;
133         fl.l_pid = pid;
134         fl.l_type = F_RDLCK;
135         fl.l_whence = SEEK_SET;
136         fl.l_sysid = 0;
137         ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
138         ASSERT_EQ(F_UNLCK, fl.l_type);
139         /* Deliberately leak fd.  close(2) will be tested in release.cc */
140 }
141
142 /* A different pid does have a lock */
143 TEST_F(Getlk, lock_exists)
144 {
145         const char FULLPATH[] = "mountpoint/some_file.txt";
146         const char RELPATH[] = "some_file.txt";
147         uint64_t ino = 42;
148         struct flock fl;
149         int fd;
150         pid_t pid = 1234;
151         pid_t pid2 = 1235;
152
153         expect_lookup(RELPATH, ino);
154         expect_open(ino, 0, 1);
155         EXPECT_CALL(*m_mock, process(
156                 ResultOf([=](auto in) {
157                         return (in.header.opcode == FUSE_GETLK &&
158                                 in.header.nodeid == ino &&
159                                 in.body.getlk.fh == FH &&
160                                 in.body.getlk.owner == (uint32_t)pid &&
161                                 in.body.getlk.lk.start == 10 &&
162                                 in.body.getlk.lk.end == 1009 &&
163                                 in.body.getlk.lk.type == F_RDLCK &&
164                                 in.body.getlk.lk.pid == (uint64_t)pid);
165                 }, Eq(true)),
166                 _)
167         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
168                 SET_OUT_HEADER_LEN(out, getlk);
169                 out.body.getlk.lk.start = 100;
170                 out.body.getlk.lk.end = 199;
171                 out.body.getlk.lk.type = F_WRLCK;
172                 out.body.getlk.lk.pid = (uint32_t)pid2;;
173         })));
174
175         fd = open(FULLPATH, O_RDWR);
176         ASSERT_LE(0, fd) << strerror(errno);
177         fl.l_start = 10;
178         fl.l_len = 1000;
179         fl.l_pid = pid;
180         fl.l_type = F_RDLCK;
181         fl.l_whence = SEEK_SET;
182         fl.l_sysid = 0;
183         ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
184         EXPECT_EQ(100, fl.l_start);
185         EXPECT_EQ(100, fl.l_len);
186         EXPECT_EQ(pid2, fl.l_pid);
187         EXPECT_EQ(F_WRLCK, fl.l_type);
188         EXPECT_EQ(SEEK_SET, fl.l_whence);
189         EXPECT_EQ(0, fl.l_sysid);
190         /* Deliberately leak fd.  close(2) will be tested in release.cc */
191 }
192
193 /*
194  * If the fuse filesystem does not support posix file locks, then the kernel
195  * should fall back to local locks.
196  */
197 TEST_F(SetlkFallback, local)
198 {
199         const char FULLPATH[] = "mountpoint/some_file.txt";
200         const char RELPATH[] = "some_file.txt";
201         uint64_t ino = 42;
202         struct flock fl;
203         int fd;
204
205         expect_lookup(RELPATH, ino);
206         expect_open(ino, 0, 1);
207
208         fd = open(FULLPATH, O_RDWR);
209         ASSERT_LE(0, fd) << strerror(errno);
210         fl.l_start = 10;
211         fl.l_len = 1000;
212         fl.l_pid = getpid();
213         fl.l_type = F_RDLCK;
214         fl.l_whence = SEEK_SET;
215         fl.l_sysid = 0;
216         ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
217         /* Deliberately leak fd.  close(2) will be tested in release.cc */
218 }
219
220 /* Set a new lock with FUSE_SETLK */
221 TEST_F(Setlk, set)
222 {
223         const char FULLPATH[] = "mountpoint/some_file.txt";
224         const char RELPATH[] = "some_file.txt";
225         uint64_t ino = 42;
226         struct flock fl;
227         int fd;
228         pid_t pid = 1234;
229
230         expect_lookup(RELPATH, ino);
231         expect_open(ino, 0, 1);
232         EXPECT_CALL(*m_mock, process(
233                 ResultOf([=](auto in) {
234                         return (in.header.opcode == FUSE_SETLK &&
235                                 in.header.nodeid == ino &&
236                                 in.body.setlk.fh == FH &&
237                                 in.body.setlk.owner == (uint32_t)pid &&
238                                 in.body.setlk.lk.start == 10 &&
239                                 in.body.setlk.lk.end == 1009 &&
240                                 in.body.setlk.lk.type == F_RDLCK &&
241                                 in.body.setlk.lk.pid == (uint64_t)pid);
242                 }, Eq(true)),
243                 _)
244         ).WillOnce(Invoke(ReturnErrno(0)));
245
246         fd = open(FULLPATH, O_RDWR);
247         ASSERT_LE(0, fd) << strerror(errno);
248         fl.l_start = 10;
249         fl.l_len = 1000;
250         fl.l_pid = pid;
251         fl.l_type = F_RDLCK;
252         fl.l_whence = SEEK_SET;
253         fl.l_sysid = 0;
254         ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
255         /* Deliberately leak fd.  close(2) will be tested in release.cc */
256 }
257
258 /* l_len = 0 is a flag value that means to lock until EOF */
259 TEST_F(Setlk, set_eof)
260 {
261         const char FULLPATH[] = "mountpoint/some_file.txt";
262         const char RELPATH[] = "some_file.txt";
263         uint64_t ino = 42;
264         struct flock fl;
265         int fd;
266         pid_t pid = 1234;
267
268         expect_lookup(RELPATH, ino);
269         expect_open(ino, 0, 1);
270         EXPECT_CALL(*m_mock, process(
271                 ResultOf([=](auto in) {
272                         return (in.header.opcode == FUSE_SETLK &&
273                                 in.header.nodeid == ino &&
274                                 in.body.setlk.fh == FH &&
275                                 in.body.setlk.owner == (uint32_t)pid &&
276                                 in.body.setlk.lk.start == 10 &&
277                                 in.body.setlk.lk.end == OFFSET_MAX &&
278                                 in.body.setlk.lk.type == F_RDLCK &&
279                                 in.body.setlk.lk.pid == (uint64_t)pid);
280                 }, Eq(true)),
281                 _)
282         ).WillOnce(Invoke(ReturnErrno(0)));
283
284         fd = open(FULLPATH, O_RDWR);
285         ASSERT_LE(0, fd) << strerror(errno);
286         fl.l_start = 10;
287         fl.l_len = 0;
288         fl.l_pid = pid;
289         fl.l_type = F_RDLCK;
290         fl.l_whence = SEEK_SET;
291         fl.l_sysid = 0;
292         ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
293         /* Deliberately leak fd.  close(2) will be tested in release.cc */
294 }
295
296 /* Fail to set a new lock with FUSE_SETLK due to a conflict */
297 TEST_F(Setlk, eagain)
298 {
299         const char FULLPATH[] = "mountpoint/some_file.txt";
300         const char RELPATH[] = "some_file.txt";
301         uint64_t ino = 42;
302         struct flock fl;
303         int fd;
304         pid_t pid = 1234;
305
306         expect_lookup(RELPATH, ino);
307         expect_open(ino, 0, 1);
308         EXPECT_CALL(*m_mock, process(
309                 ResultOf([=](auto in) {
310                         return (in.header.opcode == FUSE_SETLK &&
311                                 in.header.nodeid == ino &&
312                                 in.body.setlk.fh == FH &&
313                                 in.body.setlk.owner == (uint32_t)pid &&
314                                 in.body.setlk.lk.start == 10 &&
315                                 in.body.setlk.lk.end == 1009 &&
316                                 in.body.setlk.lk.type == F_RDLCK &&
317                                 in.body.setlk.lk.pid == (uint64_t)pid);
318                 }, Eq(true)),
319                 _)
320         ).WillOnce(Invoke(ReturnErrno(EAGAIN)));
321
322         fd = open(FULLPATH, O_RDWR);
323         ASSERT_LE(0, fd) << strerror(errno);
324         fl.l_start = 10;
325         fl.l_len = 1000;
326         fl.l_pid = pid;
327         fl.l_type = F_RDLCK;
328         fl.l_whence = SEEK_SET;
329         fl.l_sysid = 0;
330         ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl));
331         ASSERT_EQ(EAGAIN, errno);
332         /* Deliberately leak fd.  close(2) will be tested in release.cc */
333 }
334
335 /*
336  * If the fuse filesystem does not support posix file locks, then the kernel
337  * should fall back to local locks.
338  */
339 TEST_F(SetlkwFallback, local)
340 {
341         const char FULLPATH[] = "mountpoint/some_file.txt";
342         const char RELPATH[] = "some_file.txt";
343         uint64_t ino = 42;
344         struct flock fl;
345         int fd;
346
347         expect_lookup(RELPATH, ino);
348         expect_open(ino, 0, 1);
349
350         fd = open(FULLPATH, O_RDWR);
351         ASSERT_LE(0, fd) << strerror(errno);
352         fl.l_start = 10;
353         fl.l_len = 1000;
354         fl.l_pid = getpid();
355         fl.l_type = F_RDLCK;
356         fl.l_whence = SEEK_SET;
357         fl.l_sysid = 0;
358         ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
359         /* Deliberately leak fd.  close(2) will be tested in release.cc */
360 }
361
362 /*
363  * Set a new lock with FUSE_SETLK.  If the lock is not available, then the
364  * command should block.  But to the kernel, that's the same as just being
365  * slow, so we don't need a separate test method
366  */
367 TEST_F(Setlkw, set)
368 {
369         const char FULLPATH[] = "mountpoint/some_file.txt";
370         const char RELPATH[] = "some_file.txt";
371         uint64_t ino = 42;
372         struct flock fl;
373         int fd;
374         pid_t pid = 1234;
375
376         expect_lookup(RELPATH, ino);
377         expect_open(ino, 0, 1);
378         EXPECT_CALL(*m_mock, process(
379                 ResultOf([=](auto in) {
380                         return (in.header.opcode == FUSE_SETLK &&
381                                 in.header.nodeid == ino &&
382                                 in.body.setlkw.fh == FH &&
383                                 in.body.setlkw.owner == (uint32_t)pid &&
384                                 in.body.setlkw.lk.start == 10 &&
385                                 in.body.setlkw.lk.end == 1009 &&
386                                 in.body.setlkw.lk.type == F_RDLCK &&
387                                 in.body.setlkw.lk.pid == (uint64_t)pid);
388                 }, Eq(true)),
389                 _)
390         ).WillOnce(Invoke(ReturnErrno(0)));
391
392         fd = open(FULLPATH, O_RDWR);
393         ASSERT_LE(0, fd) << strerror(errno);
394         fl.l_start = 10;
395         fl.l_len = 1000;
396         fl.l_pid = pid;
397         fl.l_type = F_RDLCK;
398         fl.l_whence = SEEK_SET;
399         fl.l_sysid = 0;
400         ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
401         /* Deliberately leak fd.  close(2) will be tested in release.cc */
402 }