]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.hh
fusefs: implement VOP_ALLOCATE
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / mockfs.hh
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  * $FreeBSD$
31  */
32
33 extern "C" {
34 #include <sys/types.h>
35
36 #include <pthread.h>
37
38 #include "fuse_kernel.h"
39 }
40
41 #include <gmock/gmock.h>
42
43 #define TIME_T_MAX (std::numeric_limits<time_t>::max())
44
45 /* 
46  * A pseudo-fuse errno used indicate that a fuse operation should have no
47  * response, at least not immediately
48  */
49 #define FUSE_NORESPONSE 9999
50
51 #define SET_OUT_HEADER_LEN(out, variant) { \
52         (out).header.len = (sizeof((out).header) + \
53                             sizeof((out).body.variant)); \
54 }
55
56 /*
57  * Create an expectation on FUSE_LOOKUP and return it so the caller can set
58  * actions.
59  *
60  * This must be a macro instead of a method because EXPECT_CALL returns a type
61  * with a deleted constructor.
62  */
63 #define EXPECT_LOOKUP(parent, path)                                     \
64         EXPECT_CALL(*m_mock, process(                                   \
65                 ResultOf([=](auto in) {                                 \
66                         return (in.header.opcode == FUSE_LOOKUP &&      \
67                                 in.header.nodeid == (parent) && \
68                                 strcmp(in.body.lookup, (path)) == 0);   \
69                 }, Eq(true)),                                           \
70                 _)                                                      \
71         )
72
73 extern int verbosity;
74
75 /*
76  * The maximum that a test case can set max_write, limited by the buffer
77  * supplied when reading from /dev/fuse.  This limitation is imposed by
78  * fusefs-libs, but not by the FUSE protocol.
79  */
80 const uint32_t max_max_write = 0x20000;
81
82
83 /* This struct isn't defined by fuse_kernel.h or libfuse, but it should be */
84 struct fuse_create_out {
85         struct fuse_entry_out   entry;
86         struct fuse_open_out    open;
87 };
88
89 /* Protocol 7.8 version of struct fuse_attr */
90 struct fuse_attr_7_8
91 {
92         uint64_t        ino;
93         uint64_t        size;
94         uint64_t        blocks;
95         uint64_t        atime;
96         uint64_t        mtime;
97         uint64_t        ctime;
98         uint32_t        atimensec;
99         uint32_t        mtimensec;
100         uint32_t        ctimensec;
101         uint32_t        mode;
102         uint32_t        nlink;
103         uint32_t        uid;
104         uint32_t        gid;
105         uint32_t        rdev;
106 };
107
108 /* Protocol 7.8 version of struct fuse_attr_out */
109 struct fuse_attr_out_7_8
110 {
111         uint64_t        attr_valid;
112         uint32_t        attr_valid_nsec;
113         uint32_t        dummy;
114         struct fuse_attr_7_8 attr;
115 };
116
117 /* Protocol 7.8 version of struct fuse_entry_out */
118 struct fuse_entry_out_7_8 {
119         uint64_t        nodeid;         /* Inode ID */
120         uint64_t        generation;     /* Inode generation: nodeid:gen must
121                                    be unique for the fs's lifetime */
122         uint64_t        entry_valid;    /* Cache timeout for the name */
123         uint64_t        attr_valid;     /* Cache timeout for the attributes */
124         uint32_t        entry_valid_nsec;
125         uint32_t        attr_valid_nsec;
126         struct fuse_attr_7_8 attr;
127 };
128
129 /* Output struct for FUSE_CREATE for protocol 7.8 servers */
130 struct fuse_create_out_7_8 {
131         struct fuse_entry_out_7_8       entry;
132         struct fuse_open_out    open;
133 };
134
135 /* Output struct for FUSE_INIT for protocol 7.22 and earlier servers */
136 struct fuse_init_out_7_22 {
137         uint32_t        major;
138         uint32_t        minor;
139         uint32_t        max_readahead;
140         uint32_t        flags;
141         uint16_t        max_background;
142         uint16_t        congestion_threshold;
143         uint32_t        max_write;
144 };
145
146 union fuse_payloads_in {
147         fuse_access_in  access;
148         fuse_bmap_in    bmap;
149         /*
150          * In fusefs-libs 3.4.2 and below the buffer size is fixed at 0x21000
151          * minus the header sizes.  fusefs-libs 3.4.3 (and FUSE Protocol 7.29)
152          * add a FUSE_MAX_PAGES option that allows it to be greater.
153          *
154          * See fuse_kern_chan.c in fusefs-libs 2.9.9 and below, or
155          * FUSE_DEFAULT_MAX_PAGES_PER_REQ in fusefs-libs 3.4.3 and above.
156          */
157         uint8_t         bytes[
158             max_max_write + 0x1000 - sizeof(struct fuse_in_header)
159         ];
160         fuse_copy_file_range_in copy_file_range;
161         fuse_create_in  create;
162         fuse_fallocate_in fallocate;
163         fuse_flush_in   flush;
164         fuse_fsync_in   fsync;
165         fuse_fsync_in   fsyncdir;
166         fuse_forget_in  forget;
167         fuse_getattr_in getattr;
168         fuse_interrupt_in interrupt;
169         fuse_lk_in      getlk;
170         fuse_getxattr_in getxattr;
171         fuse_init_in    init;
172         fuse_link_in    link;
173         fuse_listxattr_in listxattr;
174         char            lookup[0];
175         fuse_lseek_in   lseek;
176         fuse_mkdir_in   mkdir;
177         fuse_mknod_in   mknod;
178         fuse_open_in    open;
179         fuse_open_in    opendir;
180         fuse_read_in    read;
181         fuse_read_in    readdir;
182         fuse_release_in release;
183         fuse_release_in releasedir;
184         fuse_rename_in  rename;
185         char            rmdir[0];
186         fuse_setattr_in setattr;
187         fuse_setxattr_in setxattr;
188         fuse_lk_in      setlk;
189         fuse_lk_in      setlkw;
190         char            unlink[0];
191         fuse_write_in   write;
192 };
193
194 struct mockfs_buf_in {
195         fuse_in_header          header;
196         union fuse_payloads_in  body;
197 };
198
199 union fuse_payloads_out {
200         fuse_attr_out           attr;
201         fuse_attr_out_7_8       attr_7_8;
202         fuse_bmap_out           bmap;
203         fuse_create_out         create;
204         fuse_create_out_7_8     create_7_8;
205         /*
206          * The protocol places no limits on the size of bytes.  Choose
207          * a size big enough for anything we'll test.
208          */
209         uint8_t                 bytes[0x20000];
210         fuse_entry_out          entry;
211         fuse_entry_out_7_8      entry_7_8;
212         fuse_lk_out             getlk;
213         fuse_getxattr_out       getxattr;
214         fuse_init_out           init;
215         fuse_init_out_7_22      init_7_22;
216         fuse_lseek_out          lseek;
217         /* The inval_entry structure should be followed by the entry's name */
218         fuse_notify_inval_entry_out     inval_entry;
219         fuse_notify_inval_inode_out     inval_inode;
220         /* The store structure should be followed by the data to store */
221         fuse_notify_store_out           store;
222         fuse_listxattr_out      listxattr;
223         fuse_open_out           open;
224         fuse_statfs_out         statfs;
225         /*
226          * The protocol places no limits on the length of the string.  This is
227          * merely convenient for testing.
228          */
229         char                    str[80];
230         fuse_write_out          write;
231 };
232
233 struct mockfs_buf_out {
234         fuse_out_header         header;
235         union fuse_payloads_out body;
236
237         /* Default constructor: zero everything */
238         mockfs_buf_out() {
239                 memset(this, 0, sizeof(*this));
240         }
241 };
242
243 /* A function that can be invoked in place of MockFS::process */
244 typedef std::function<void (const mockfs_buf_in& in,
245                             std::vector<std::unique_ptr<mockfs_buf_out>> &out)>
246 ProcessMockerT;
247
248 /*
249  * Helper function used for setting an error expectation for any fuse operation.
250  * The operation will return the supplied error
251  */
252 ProcessMockerT ReturnErrno(int error);
253
254 /* Helper function used for returning negative cache entries for LOOKUP */
255 ProcessMockerT ReturnNegativeCache(const struct timespec *entry_valid);
256
257 /* Helper function used for returning a single immediate response */
258 ProcessMockerT ReturnImmediate(
259         std::function<void(const mockfs_buf_in& in,
260                            struct mockfs_buf_out &out)> f);
261
262 /* How the daemon should check /dev/fuse for readiness */
263 enum poll_method {
264         BLOCKING,
265         SELECT,
266         POLL,
267         KQ
268 };
269
270 /*
271  * Fake FUSE filesystem
272  *
273  * "Mounts" a filesystem to a temporary directory and services requests
274  * according to the programmed expectations.
275  *
276  * Operates directly on the fusefs(4) kernel API, not the libfuse(3) user api.
277  */
278 class MockFS {
279         /*
280          * thread id of the fuse daemon thread
281          *
282          * It must run in a separate thread so it doesn't deadlock with the
283          * client test code.
284          */
285         pthread_t m_daemon_id;
286
287         /* file descriptor of /dev/fuse control device */
288         volatile int m_fuse_fd;
289         
290         /* The minor version of the kernel API that this mock daemon targets */
291         uint32_t m_kernel_minor_version;
292
293         int m_kq;
294
295         /* The max_readahead file system option */
296         uint32_t m_maxreadahead;
297
298         /* pid of the test process */
299         pid_t m_pid;
300
301         /* The unique value of the header of the last received operation */
302         uint64_t m_last_unique;
303
304         /* Method the daemon should use for I/O to and from /dev/fuse */
305         enum poll_method m_pm;
306
307         /* Timestamp granularity in nanoseconds */
308         unsigned m_time_gran;
309
310         void audit_request(const mockfs_buf_in &in, ssize_t buflen);
311         void debug_request(const mockfs_buf_in&, ssize_t buflen);
312         void debug_response(const mockfs_buf_out&);
313
314         /* Initialize a session after mounting */
315         void init(uint32_t flags);
316
317         /* Is pid from a process that might be involved in the test? */
318         bool pid_ok(pid_t pid);
319
320         /* Default request handler */
321         void process_default(const mockfs_buf_in&,
322                 std::vector<std::unique_ptr<mockfs_buf_out>>&);
323
324         /* Entry point for the daemon thread */
325         static void* service(void*);
326
327         /*
328          * Read, but do not process, a single request from the kernel
329          *
330          * @param in    Return storage for the FUSE request
331          * @param res   Return value of read(2).  If positive, the amount of
332          *              data read from the fuse device.
333          */
334         void read_request(mockfs_buf_in& in, ssize_t& res);
335
336         /* Write a single response back to the kernel */
337         void write_response(const mockfs_buf_out &out);
338
339         public:
340         /* pid of child process, for two-process test cases */
341         pid_t m_child_pid;
342
343         /* Maximum size of a FUSE_WRITE write */
344         uint32_t m_maxwrite;
345
346         /* 
347          * Number of events that were available from /dev/fuse after the last
348          * kevent call.  Only valid when m_pm = KQ.
349          */
350         int m_nready;
351
352         /* Tell the daemon to shut down ASAP */
353         bool m_quit;
354
355         /* Create a new mockfs and mount it to a tempdir */
356         MockFS(int max_readahead, bool allow_other,
357                 bool default_permissions, bool push_symlinks_in, bool ro,
358                 enum poll_method pm, uint32_t flags,
359                 uint32_t kernel_minor_version, uint32_t max_write, bool async,
360                 bool no_clusterr, unsigned time_gran, bool nointr,
361                 bool noatime);
362
363         virtual ~MockFS();
364
365         /* Kill the filesystem daemon without unmounting the filesystem */
366         void kill_daemon();
367
368         /* Process FUSE requests endlessly */
369         void loop();
370
371         /*
372          * Send an asynchronous notification to invalidate a directory entry.
373          * Similar to libfuse's fuse_lowlevel_notify_inval_entry
374          *
375          * This method will block until the client has responded, so it should
376          * generally be run in a separate thread from request processing.
377          *
378          * @param       parent  Parent directory's inode number
379          * @param       name    name of dirent to invalidate
380          * @param       namelen size of name, including the NUL
381          */
382         int notify_inval_entry(ino_t parent, const char *name, size_t namelen);
383
384         /*
385          * Send an asynchronous notification to invalidate an inode's cached
386          * data and/or attributes.  Similar to libfuse's
387          * fuse_lowlevel_notify_inval_inode.
388          *
389          * This method will block until the client has responded, so it should
390          * generally be run in a separate thread from request processing.
391          *
392          * @param       ino     File's inode number
393          * @param       off     offset at which to begin invalidation.  A
394          *                      negative offset means to invalidate attributes
395          *                      only.
396          * @param       len     Size of region of data to invalidate.  0 means
397          *                      to invalidate all cached data.
398          */
399         int notify_inval_inode(ino_t ino, off_t off, ssize_t len);
400
401         /*
402          * Send an asynchronous notification to store data directly into an
403          * inode's cache.  Similar to libfuse's fuse_lowlevel_notify_store.
404          *
405          * This method will block until the client has responded, so it should
406          * generally be run in a separate thread from request processing.
407          *
408          * @param       ino     File's inode number
409          * @param       off     Offset at which to store data
410          * @param       data    Pointer to the data to cache
411          * @param       len     Size of data
412          */
413         int notify_store(ino_t ino, off_t off, const void* data, ssize_t size);
414
415         /* 
416          * Request handler
417          *
418          * This method is expected to provide the responses to each FUSE
419          * operation.  For an immediate response, push one buffer into out.
420          * For a delayed response, push nothing.  For an immediate response
421          * plus a delayed response to an earlier operation, push two bufs.
422          * Test cases must define each response using Googlemock expectations
423          */
424         MOCK_METHOD2(process, void(const mockfs_buf_in&,
425                                 std::vector<std::unique_ptr<mockfs_buf_out>>&));
426
427         /* Gracefully unmount */
428         void unmount();
429 };