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/param.h>
33
34 #include <sys/mount.h>
35 #include <sys/select.h>
36 #include <sys/stat.h>
37 #include <sys/uio.h>
38 #include <sys/user.h>
39
40 #include <fcntl.h>
41 #include <libutil.h>
42 #include <poll.h>
43 #include <pthread.h>
44 #include <signal.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47
48 #include "mntopts.h" // for build_iovec
49 }
50
51 #include <cinttypes>
52
53 #include <gtest/gtest.h>
54
55 #include "mockfs.hh"
56
57 using namespace testing;
58
59 int verbosity = 0;
60
opcode2opname(uint32_t opcode)61 const char* opcode2opname(uint32_t opcode)
62 {
63 const char* table[] = {
64 "Unknown (opcode 0)",
65 "LOOKUP",
66 "FORGET",
67 "GETATTR",
68 "SETATTR",
69 "READLINK",
70 "SYMLINK",
71 "Unknown (opcode 7)",
72 "MKNOD",
73 "MKDIR",
74 "UNLINK",
75 "RMDIR",
76 "RENAME",
77 "LINK",
78 "OPEN",
79 "READ",
80 "WRITE",
81 "STATFS",
82 "RELEASE",
83 "Unknown (opcode 19)",
84 "FSYNC",
85 "SETXATTR",
86 "GETXATTR",
87 "LISTXATTR",
88 "REMOVEXATTR",
89 "FLUSH",
90 "INIT",
91 "OPENDIR",
92 "READDIR",
93 "RELEASEDIR",
94 "FSYNCDIR",
95 "GETLK",
96 "SETLK",
97 "SETLKW",
98 "ACCESS",
99 "CREATE",
100 "INTERRUPT",
101 "BMAP",
102 "DESTROY",
103 "IOCTL",
104 "POLL",
105 "NOTIFY_REPLY",
106 "BATCH_FORGET",
107 "FALLOCATE",
108 "READDIRPLUS",
109 "RENAME2",
110 "LSEEK",
111 "COPY_FILE_RANGE",
112 };
113 if (opcode >= nitems(table))
114 return ("Unknown (opcode > max)");
115 else
116 return (table[opcode]);
117 }
118
119 ProcessMockerT
ReturnErrno(int error)120 ReturnErrno(int error)
121 {
122 return([=](auto in, auto &out) {
123 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
124 out0->header.unique = in.header.unique;
125 out0->header.error = -error;
126 out0->header.len = sizeof(out0->header);
127 out.push_back(std::move(out0));
128 });
129 }
130
131 /* Helper function used for returning negative cache entries for LOOKUP */
132 ProcessMockerT
ReturnNegativeCache(const struct timespec * entry_valid)133 ReturnNegativeCache(const struct timespec *entry_valid)
134 {
135 return([=](auto in, auto &out) {
136 /* nodeid means ENOENT and cache it */
137 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
138 out0->body.entry.nodeid = 0;
139 out0->header.unique = in.header.unique;
140 out0->header.error = 0;
141 out0->body.entry.entry_valid = entry_valid->tv_sec;
142 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
143 SET_OUT_HEADER_LEN(*out0, entry);
144 out.push_back(std::move(out0));
145 });
146 }
147
148 ProcessMockerT
ReturnImmediate(std::function<void (const mockfs_buf_in & in,struct mockfs_buf_out & out)> f)149 ReturnImmediate(std::function<void(const mockfs_buf_in& in,
150 struct mockfs_buf_out &out)> f)
151 {
152 return([=](auto& in, auto &out) {
153 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
154 out0->header.unique = in.header.unique;
155 f(in, *out0);
156 out.push_back(std::move(out0));
157 });
158 }
159
sigint_handler(int __unused sig)160 void sigint_handler(int __unused sig) {
161 // Don't do anything except interrupt the daemon's read(2) call
162 }
163
debug_request(const mockfs_buf_in & in,ssize_t buflen)164 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
165 {
166 printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
167 in.header.nodeid);
168 if (verbosity > 1) {
169 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
170 " buflen=%zd",
171 in.header.uid, in.header.gid, in.header.pid,
172 in.header.unique, in.header.len, buflen);
173 }
174 switch (in.header.opcode) {
175 const char *name, *value;
176
177 case FUSE_ACCESS:
178 printf(" mask=%#x", in.body.access.mask);
179 break;
180 case FUSE_BMAP:
181 printf(" block=%" PRIx64 " blocksize=%#x",
182 in.body.bmap.block, in.body.bmap.blocksize);
183 break;
184 case FUSE_COPY_FILE_RANGE:
185 printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
186 " off_out=%" PRIu64 " size=%" PRIu64,
187 in.body.copy_file_range.off_in,
188 in.body.copy_file_range.nodeid_out,
189 in.body.copy_file_range.off_out,
190 in.body.copy_file_range.len);
191 if (verbosity > 1)
192 printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
193 " flags=%" PRIx64,
194 in.body.copy_file_range.fh_in,
195 in.body.copy_file_range.fh_out,
196 in.body.copy_file_range.flags);
197 break;
198 case FUSE_CREATE:
199 if (m_kernel_minor_version >= 12)
200 name = (const char*)in.body.bytes +
201 sizeof(fuse_create_in);
202 else
203 name = (const char*)in.body.bytes +
204 sizeof(fuse_open_in);
205 printf(" flags=%#x name=%s",
206 in.body.open.flags, name);
207 break;
208 case FUSE_FALLOCATE:
209 printf(" fh=%#" PRIx64 " offset=%" PRIu64
210 " length=%" PRIx64 " mode=%#x",
211 in.body.fallocate.fh,
212 in.body.fallocate.offset,
213 in.body.fallocate.length,
214 in.body.fallocate.mode);
215 break;
216 case FUSE_FLUSH:
217 printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
218 in.body.flush.fh,
219 in.body.flush.lock_owner);
220 break;
221 case FUSE_FORGET:
222 printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
223 break;
224 case FUSE_FSYNC:
225 printf(" flags=%#x", in.body.fsync.fsync_flags);
226 break;
227 case FUSE_FSYNCDIR:
228 printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
229 break;
230 case FUSE_GETLK:
231 printf(" fh=%#" PRIx64
232 " type=%u pid=%u",
233 in.body.getlk.fh,
234 in.body.getlk.lk.type,
235 in.body.getlk.lk.pid);
236 if (verbosity >= 2) {
237 printf(" range=[%" PRIi64 ":%" PRIi64 "]",
238 in.body.getlk.lk.start,
239 in.body.getlk.lk.end);
240 }
241 break;
242 case FUSE_INTERRUPT:
243 printf(" unique=%" PRIu64, in.body.interrupt.unique);
244 break;
245 case FUSE_LINK:
246 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
247 break;
248 case FUSE_LISTXATTR:
249 printf(" size=%" PRIu32, in.body.listxattr.size);
250 break;
251 case FUSE_LOOKUP:
252 printf(" %s", in.body.lookup);
253 break;
254 case FUSE_LSEEK:
255 switch (in.body.lseek.whence) {
256 case SEEK_HOLE:
257 printf(" SEEK_HOLE offset=%jd",
258 in.body.lseek.offset);
259 break;
260 case SEEK_DATA:
261 printf(" SEEK_DATA offset=%jd",
262 in.body.lseek.offset);
263 break;
264 default:
265 printf(" whence=%u offset=%jd",
266 in.body.lseek.whence, in.body.lseek.offset);
267 break;
268 }
269 break;
270 case FUSE_MKDIR:
271 name = (const char*)in.body.bytes +
272 sizeof(fuse_mkdir_in);
273 printf(" name=%s mode=%#o umask=%#o", name,
274 in.body.mkdir.mode, in.body.mkdir.umask);
275 break;
276 case FUSE_MKNOD:
277 if (m_kernel_minor_version >= 12)
278 name = (const char*)in.body.bytes +
279 sizeof(fuse_mknod_in);
280 else
281 name = (const char*)in.body.bytes +
282 FUSE_COMPAT_MKNOD_IN_SIZE;
283 printf(" mode=%#o rdev=%x umask=%#o name=%s",
284 in.body.mknod.mode, in.body.mknod.rdev,
285 in.body.mknod.umask, name);
286 break;
287 case FUSE_OPEN:
288 printf(" flags=%#x", in.body.open.flags);
289 break;
290 case FUSE_OPENDIR:
291 printf(" flags=%#x", in.body.opendir.flags);
292 break;
293 case FUSE_READ:
294 printf(" offset=%" PRIu64 " size=%u",
295 in.body.read.offset,
296 in.body.read.size);
297 if (verbosity > 1)
298 printf(" flags=%#x", in.body.read.flags);
299 break;
300 case FUSE_READDIR:
301 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
302 in.body.readdir.fh, in.body.readdir.offset,
303 in.body.readdir.size);
304 break;
305 case FUSE_RELEASE:
306 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
307 in.body.release.fh,
308 in.body.release.flags,
309 in.body.release.lock_owner);
310 break;
311 case FUSE_RENAME:
312 {
313 const char *src = (const char*)in.body.bytes +
314 sizeof(fuse_rename_in);
315 const char *dst = src + strlen(src) + 1;
316 printf(" src=%s newdir=%" PRIu64 " dst=%s",
317 src, in.body.rename.newdir, dst);
318 }
319 break;
320 case FUSE_SETATTR:
321 if (verbosity <= 1) {
322 printf(" valid=%#x", in.body.setattr.valid);
323 break;
324 }
325 if (in.body.setattr.valid & FATTR_MODE)
326 printf(" mode=%#o", in.body.setattr.mode);
327 if (in.body.setattr.valid & FATTR_UID)
328 printf(" uid=%u", in.body.setattr.uid);
329 if (in.body.setattr.valid & FATTR_GID)
330 printf(" gid=%u", in.body.setattr.gid);
331 if (in.body.setattr.valid & FATTR_SIZE)
332 printf(" size=%" PRIu64, in.body.setattr.size);
333 if (in.body.setattr.valid & FATTR_ATIME)
334 printf(" atime=%" PRIu64 ".%u",
335 in.body.setattr.atime,
336 in.body.setattr.atimensec);
337 if (in.body.setattr.valid & FATTR_MTIME)
338 printf(" mtime=%" PRIu64 ".%u",
339 in.body.setattr.mtime,
340 in.body.setattr.mtimensec);
341 if (in.body.setattr.valid & FATTR_FH)
342 printf(" fh=%" PRIu64 "", in.body.setattr.fh);
343 break;
344 case FUSE_SETLK:
345 printf(" fh=%#" PRIx64 " owner=%" PRIu64
346 " type=%u pid=%u",
347 in.body.setlk.fh, in.body.setlk.owner,
348 in.body.setlk.lk.type,
349 in.body.setlk.lk.pid);
350 if (verbosity >= 2) {
351 printf(" range=[%" PRIi64 ":%" PRIi64 "]",
352 in.body.setlk.lk.start,
353 in.body.setlk.lk.end);
354 }
355 break;
356 case FUSE_SETXATTR:
357 /*
358 * In theory neither the xattr name and value need be
359 * ASCII, but in this test suite they always are.
360 */
361 name = (const char*)in.body.bytes +
362 sizeof(fuse_setxattr_in);
363 value = name + strlen(name) + 1;
364 printf(" %s=%s", name, value);
365 break;
366 case FUSE_WRITE:
367 printf(" fh=%#" PRIx64 " offset=%" PRIu64
368 " size=%u write_flags=%u",
369 in.body.write.fh,
370 in.body.write.offset, in.body.write.size,
371 in.body.write.write_flags);
372 if (verbosity > 1)
373 printf(" flags=%#x", in.body.write.flags);
374 break;
375 default:
376 break;
377 }
378 printf("\n");
379 }
380
381 /*
382 * Debug a FUSE response.
383 *
384 * This is mostly useful for asynchronous notifications, which don't correspond
385 * to any request
386 */
debug_response(const mockfs_buf_out & out)387 void MockFS::debug_response(const mockfs_buf_out &out) {
388 const char *name;
389
390 if (verbosity == 0)
391 return;
392
393 switch (out.header.error) {
394 case FUSE_NOTIFY_INVAL_ENTRY:
395 name = (const char*)out.body.bytes +
396 sizeof(fuse_notify_inval_entry_out);
397 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
398 out.body.inval_entry.parent, name);
399 break;
400 case FUSE_NOTIFY_INVAL_INODE:
401 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
402 " len=%" PRIi64 "\n",
403 out.body.inval_inode.ino,
404 out.body.inval_inode.off,
405 out.body.inval_inode.len);
406 break;
407 case FUSE_NOTIFY_STORE:
408 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
409 " size=%" PRIu32 "\n",
410 out.body.store.nodeid,
411 out.body.store.offset,
412 out.body.store.size);
413 break;
414 default:
415 break;
416 }
417 }
418
MockFS(int max_readahead,bool allow_other,bool default_permissions,bool push_symlinks_in,bool ro,enum poll_method pm,uint32_t flags,uint32_t kernel_minor_version,uint32_t max_write,bool async,bool noclusterr,unsigned time_gran,bool nointr,bool noatime,const char * fsname,const char * subtype)419 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
420 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
421 uint32_t kernel_minor_version, uint32_t max_write, bool async,
422 bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
423 const char *fsname, const char *subtype)
424 {
425 struct sigaction sa;
426 struct iovec *iov = NULL;
427 int iovlen = 0;
428 char fdstr[15];
429 const bool trueval = true;
430
431 m_daemon_id = NULL;
432 m_kernel_minor_version = kernel_minor_version;
433 m_maxreadahead = max_readahead;
434 m_maxwrite = MIN(max_write, max_max_write);
435 m_nready = -1;
436 m_pm = pm;
437 m_time_gran = time_gran;
438 m_quit = false;
439 m_last_unique = 0;
440 if (m_pm == KQ)
441 m_kq = kqueue();
442 else
443 m_kq = -1;
444
445 /*
446 * Kyua sets pwd to a testcase-unique tempdir; no need to use
447 * mkdtemp
448 */
449 /*
450 * googletest doesn't allow ASSERT_ in constructors, so we must throw
451 * instead.
452 */
453 if (mkdir("mountpoint" , 0755) && errno != EEXIST)
454 throw(std::system_error(errno, std::system_category(),
455 "Couldn't make mountpoint directory"));
456
457 switch (m_pm) {
458 case BLOCKING:
459 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
460 break;
461 default:
462 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
463 break;
464 }
465 if (m_fuse_fd < 0)
466 throw(std::system_error(errno, std::system_category(),
467 "Couldn't open /dev/fuse"));
468
469 m_pid = getpid();
470 m_child_pid = -1;
471
472 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
473 build_iovec(&iov, &iovlen, "fspath",
474 __DECONST(void *, "mountpoint"), -1);
475 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
476 sprintf(fdstr, "%d", m_fuse_fd);
477 build_iovec(&iov, &iovlen, "fd", fdstr, -1);
478 if (allow_other) {
479 build_iovec(&iov, &iovlen, "allow_other",
480 __DECONST(void*, &trueval), sizeof(bool));
481 }
482 if (default_permissions) {
483 build_iovec(&iov, &iovlen, "default_permissions",
484 __DECONST(void*, &trueval), sizeof(bool));
485 }
486 if (push_symlinks_in) {
487 build_iovec(&iov, &iovlen, "push_symlinks_in",
488 __DECONST(void*, &trueval), sizeof(bool));
489 }
490 if (ro) {
491 build_iovec(&iov, &iovlen, "ro",
492 __DECONST(void*, &trueval), sizeof(bool));
493 }
494 if (async) {
495 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
496 sizeof(bool));
497 }
498 if (noatime) {
499 build_iovec(&iov, &iovlen, "noatime",
500 __DECONST(void*, &trueval), sizeof(bool));
501 }
502 if (noclusterr) {
503 build_iovec(&iov, &iovlen, "noclusterr",
504 __DECONST(void*, &trueval), sizeof(bool));
505 }
506 if (nointr) {
507 build_iovec(&iov, &iovlen, "nointr",
508 __DECONST(void*, &trueval), sizeof(bool));
509 } else {
510 build_iovec(&iov, &iovlen, "intr",
511 __DECONST(void*, &trueval), sizeof(bool));
512 }
513 if (*fsname) {
514 build_iovec(&iov, &iovlen, "fsname=",
515 __DECONST(void*, fsname), -1);
516 }
517 if (*subtype) {
518 build_iovec(&iov, &iovlen, "subtype=",
519 __DECONST(void*, subtype), -1);
520 }
521 if (nmount(iov, iovlen, 0))
522 throw(std::system_error(errno, std::system_category(),
523 "Couldn't mount filesystem"));
524 free_iovec(&iov, &iovlen);
525
526 // Setup default handler
527 ON_CALL(*this, process(_, _))
528 .WillByDefault(Invoke(this, &MockFS::process_default));
529
530 init(flags);
531 bzero(&sa, sizeof(sa));
532 sa.sa_handler = sigint_handler;
533 sa.sa_flags = 0; /* Don't set SA_RESTART! */
534 if (0 != sigaction(SIGUSR1, &sa, NULL))
535 throw(std::system_error(errno, std::system_category(),
536 "Couldn't handle SIGUSR1"));
537 if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
538 throw(std::system_error(errno, std::system_category(),
539 "Couldn't Couldn't start fuse thread"));
540 }
541
~MockFS()542 MockFS::~MockFS() {
543 kill_daemon();
544 if (m_daemon_id != NULL) {
545 pthread_join(m_daemon_id, NULL);
546 m_daemon_id = NULL;
547 }
548 ::unmount("mountpoint", MNT_FORCE);
549 rmdir("mountpoint");
550 if (m_kq >= 0)
551 close(m_kq);
552 }
553
audit_request(const mockfs_buf_in & in,ssize_t buflen)554 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
555 uint32_t inlen = in.header.len;
556 size_t fih = sizeof(in.header);
557 switch (in.header.opcode) {
558 case FUSE_LOOKUP:
559 case FUSE_RMDIR:
560 case FUSE_SYMLINK:
561 case FUSE_UNLINK:
562 EXPECT_GT(inlen, fih) << "Missing request filename";
563 // No redundant information for checking buflen
564 break;
565 case FUSE_FORGET:
566 EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
567 EXPECT_EQ((size_t)buflen, inlen);
568 break;
569 case FUSE_GETATTR:
570 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
571 EXPECT_EQ((size_t)buflen, inlen);
572 break;
573 case FUSE_SETATTR:
574 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
575 EXPECT_EQ((size_t)buflen, inlen);
576 break;
577 case FUSE_READLINK:
578 EXPECT_EQ(inlen, fih) << "Unexpected request body";
579 EXPECT_EQ((size_t)buflen, inlen);
580 break;
581 case FUSE_MKNOD:
582 {
583 size_t s;
584 if (m_kernel_minor_version >= 12)
585 s = sizeof(in.body.mknod);
586 else
587 s = FUSE_COMPAT_MKNOD_IN_SIZE;
588 EXPECT_GE(inlen, fih + s) << "Missing request body";
589 EXPECT_GT(inlen, fih + s) << "Missing request filename";
590 // No redundant information for checking buflen
591 break;
592 }
593 case FUSE_MKDIR:
594 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
595 "Missing request body";
596 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
597 "Missing request filename";
598 // No redundant information for checking buflen
599 break;
600 case FUSE_RENAME:
601 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
602 "Missing request body";
603 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
604 "Missing request filename";
605 // No redundant information for checking buflen
606 break;
607 case FUSE_LINK:
608 EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
609 "Missing request body";
610 EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
611 "Missing request filename";
612 // No redundant information for checking buflen
613 break;
614 case FUSE_OPEN:
615 EXPECT_EQ(inlen, fih + sizeof(in.body.open));
616 EXPECT_EQ((size_t)buflen, inlen);
617 break;
618 case FUSE_READ:
619 EXPECT_EQ(inlen, fih + sizeof(in.body.read));
620 EXPECT_EQ((size_t)buflen, inlen);
621 break;
622 case FUSE_WRITE:
623 {
624 size_t s;
625
626 if (m_kernel_minor_version >= 9)
627 s = sizeof(in.body.write);
628 else
629 s = FUSE_COMPAT_WRITE_IN_SIZE;
630 // I suppose a 0-byte write should be allowed
631 EXPECT_GE(inlen, fih + s) << "Missing request body";
632 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
633 break;
634 }
635 case FUSE_DESTROY:
636 case FUSE_STATFS:
637 EXPECT_EQ(inlen, fih);
638 EXPECT_EQ((size_t)buflen, inlen);
639 break;
640 case FUSE_RELEASE:
641 EXPECT_EQ(inlen, fih + sizeof(in.body.release));
642 EXPECT_EQ((size_t)buflen, inlen);
643 break;
644 case FUSE_FSYNC:
645 case FUSE_FSYNCDIR:
646 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
647 EXPECT_EQ((size_t)buflen, inlen);
648 break;
649 case FUSE_SETXATTR:
650 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
651 "Missing request body";
652 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
653 "Missing request attribute name";
654 // No redundant information for checking buflen
655 break;
656 case FUSE_GETXATTR:
657 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
658 "Missing request body";
659 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
660 "Missing request attribute name";
661 // No redundant information for checking buflen
662 break;
663 case FUSE_LISTXATTR:
664 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
665 EXPECT_EQ((size_t)buflen, inlen);
666 break;
667 case FUSE_REMOVEXATTR:
668 EXPECT_GT(inlen, fih) << "Missing request attribute name";
669 // No redundant information for checking buflen
670 break;
671 case FUSE_FLUSH:
672 EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
673 EXPECT_EQ((size_t)buflen, inlen);
674 break;
675 case FUSE_INIT:
676 EXPECT_EQ(inlen, fih + sizeof(in.body.init));
677 EXPECT_EQ((size_t)buflen, inlen);
678 break;
679 case FUSE_OPENDIR:
680 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
681 EXPECT_EQ((size_t)buflen, inlen);
682 break;
683 case FUSE_READDIR:
684 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
685 EXPECT_EQ((size_t)buflen, inlen);
686 break;
687 case FUSE_RELEASEDIR:
688 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
689 EXPECT_EQ((size_t)buflen, inlen);
690 break;
691 case FUSE_GETLK:
692 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
693 EXPECT_EQ((size_t)buflen, inlen);
694 break;
695 case FUSE_SETLK:
696 case FUSE_SETLKW:
697 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
698 EXPECT_EQ((size_t)buflen, inlen);
699 break;
700 case FUSE_ACCESS:
701 EXPECT_EQ(inlen, fih + sizeof(in.body.access));
702 EXPECT_EQ((size_t)buflen, inlen);
703 break;
704 case FUSE_CREATE:
705 EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
706 "Missing request body";
707 EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
708 "Missing request filename";
709 // No redundant information for checking buflen
710 break;
711 case FUSE_INTERRUPT:
712 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
713 EXPECT_EQ((size_t)buflen, inlen);
714 break;
715 case FUSE_FALLOCATE:
716 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
717 EXPECT_EQ((size_t)buflen, inlen);
718 break;
719 case FUSE_BMAP:
720 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
721 EXPECT_EQ((size_t)buflen, inlen);
722 break;
723 case FUSE_LSEEK:
724 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
725 EXPECT_EQ((size_t)buflen, inlen);
726 break;
727 case FUSE_COPY_FILE_RANGE:
728 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
729 EXPECT_EQ(0ul, in.body.copy_file_range.flags);
730 EXPECT_EQ((size_t)buflen, inlen);
731 break;
732 case FUSE_NOTIFY_REPLY:
733 case FUSE_BATCH_FORGET:
734 case FUSE_IOCTL:
735 case FUSE_POLL:
736 case FUSE_READDIRPLUS:
737 FAIL() << "Unsupported opcode?";
738 default:
739 FAIL() << "Unknown opcode " << in.header.opcode;
740 }
741 /*
742 * Check that the ticket's unique value is sequential. Technically it
743 * doesn't need to be sequential, merely unique. But the current
744 * fusefs driver _does_ make it sequential, and that's easy to check
745 * for.
746 */
747 if (in.header.unique != ++m_last_unique)
748 FAIL() << "Non-sequential unique value";
749 }
750
init(uint32_t flags)751 void MockFS::init(uint32_t flags) {
752 ssize_t buflen;
753
754 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
755 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
756
757 read_request(*in, buflen);
758 if (verbosity > 0)
759 debug_request(*in, buflen);
760 audit_request(*in, buflen);
761 ASSERT_EQ(FUSE_INIT, in->header.opcode);
762
763 out->header.unique = in->header.unique;
764 out->header.error = 0;
765 out->body.init.major = FUSE_KERNEL_VERSION;
766 out->body.init.minor = m_kernel_minor_version;;
767 out->body.init.flags = in->body.init.flags & flags;
768 out->body.init.max_write = m_maxwrite;
769 out->body.init.max_readahead = m_maxreadahead;
770
771 if (m_kernel_minor_version < 23) {
772 SET_OUT_HEADER_LEN(*out, init_7_22);
773 } else {
774 out->body.init.time_gran = m_time_gran;
775 SET_OUT_HEADER_LEN(*out, init);
776 }
777
778 write(m_fuse_fd, out.get(), out->header.len);
779 }
780
kill_daemon()781 void MockFS::kill_daemon() {
782 m_quit = true;
783 if (m_daemon_id != NULL)
784 pthread_kill(m_daemon_id, SIGUSR1);
785 // Closing the /dev/fuse file descriptor first allows unmount to
786 // succeed even if the daemon doesn't correctly respond to commands
787 // during the unmount sequence.
788 close(m_fuse_fd);
789 m_fuse_fd = -1;
790 }
791
loop()792 void MockFS::loop() {
793 std::vector<std::unique_ptr<mockfs_buf_out>> out;
794
795 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
796 ASSERT_TRUE(in != NULL);
797 while (!m_quit) {
798 ssize_t buflen;
799
800 bzero(in.get(), sizeof(*in));
801 read_request(*in, buflen);
802 if (m_quit)
803 break;
804 if (verbosity > 0)
805 debug_request(*in, buflen);
806 audit_request(*in, buflen);
807 if (pid_ok((pid_t)in->header.pid)) {
808 process(*in, out);
809 } else {
810 /*
811 * Reject any requests from unknown processes. Because
812 * we actually do mount a filesystem, plenty of
813 * unrelated system daemons may try to access it.
814 */
815 if (verbosity > 1)
816 printf("\tREJECTED (wrong pid %d)\n",
817 in->header.pid);
818 process_default(*in, out);
819 }
820 for (auto &it: out)
821 write_response(*it);
822 out.clear();
823 }
824 }
825
notify_inval_entry(ino_t parent,const char * name,size_t namelen)826 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
827 {
828 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
829
830 out->header.unique = 0; /* 0 means asynchronous notification */
831 out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
832 out->body.inval_entry.parent = parent;
833 out->body.inval_entry.namelen = namelen;
834 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
835 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
836 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
837 namelen;
838 debug_response(*out);
839 write_response(*out);
840 return 0;
841 }
842
notify_inval_inode(ino_t ino,off_t off,ssize_t len)843 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
844 {
845 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
846
847 out->header.unique = 0; /* 0 means asynchronous notification */
848 out->header.error = FUSE_NOTIFY_INVAL_INODE;
849 out->body.inval_inode.ino = ino;
850 out->body.inval_inode.off = off;
851 out->body.inval_inode.len = len;
852 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
853 debug_response(*out);
854 write_response(*out);
855 return 0;
856 }
857
notify_store(ino_t ino,off_t off,const void * data,ssize_t size)858 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
859 {
860 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
861
862 out->header.unique = 0; /* 0 means asynchronous notification */
863 out->header.error = FUSE_NOTIFY_STORE;
864 out->body.store.nodeid = ino;
865 out->body.store.offset = off;
866 out->body.store.size = size;
867 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
868 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
869 debug_response(*out);
870 write_response(*out);
871 return 0;
872 }
873
pid_ok(pid_t pid)874 bool MockFS::pid_ok(pid_t pid) {
875 if (pid == m_pid) {
876 return (true);
877 } else if (pid == m_child_pid) {
878 return (true);
879 } else {
880 struct kinfo_proc *ki;
881 bool ok = false;
882
883 ki = kinfo_getproc(pid);
884 if (ki == NULL)
885 return (false);
886 /*
887 * Allow access by the aio daemon processes so that our tests
888 * can use aio functions
889 */
890 if (0 == strncmp("aiod", ki->ki_comm, 4))
891 ok = true;
892 free(ki);
893 return (ok);
894 }
895 }
896
process_default(const mockfs_buf_in & in,std::vector<std::unique_ptr<mockfs_buf_out>> & out)897 void MockFS::process_default(const mockfs_buf_in& in,
898 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
899 {
900 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
901 out0->header.unique = in.header.unique;
902 out0->header.error = -EOPNOTSUPP;
903 out0->header.len = sizeof(out0->header);
904 out.push_back(std::move(out0));
905 }
906
read_request(mockfs_buf_in & in,ssize_t & res)907 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
908 int nready = 0;
909 fd_set readfds;
910 pollfd fds[1];
911 struct kevent changes[1];
912 struct kevent events[1];
913 struct timespec timeout_ts;
914 struct timeval timeout_tv;
915 const int timeout_ms = 999;
916 int timeout_int, nfds;
917 int fuse_fd;
918
919 switch (m_pm) {
920 case BLOCKING:
921 break;
922 case KQ:
923 timeout_ts.tv_sec = 0;
924 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
925 while (nready == 0) {
926 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
927 EV_ADD | EV_ONESHOT, 0, 0, 0);
928 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
929 &timeout_ts);
930 if (m_quit)
931 return;
932 }
933 ASSERT_LE(0, nready) << strerror(errno);
934 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
935 if (events[0].flags & EV_ERROR)
936 FAIL() << strerror(events[0].data);
937 else if (events[0].flags & EV_EOF)
938 FAIL() << strerror(events[0].fflags);
939 m_nready = events[0].data;
940 break;
941 case POLL:
942 timeout_int = timeout_ms;
943 fds[0].fd = m_fuse_fd;
944 fds[0].events = POLLIN;
945 while (nready == 0) {
946 nready = poll(fds, 1, timeout_int);
947 if (m_quit)
948 return;
949 }
950 ASSERT_LE(0, nready) << strerror(errno);
951 ASSERT_TRUE(fds[0].revents & POLLIN);
952 break;
953 case SELECT:
954 fuse_fd = m_fuse_fd;
955 if (fuse_fd < 0)
956 break;
957 timeout_tv.tv_sec = 0;
958 timeout_tv.tv_usec = timeout_ms * 1'000;
959 nfds = fuse_fd + 1;
960 while (nready == 0) {
961 FD_ZERO(&readfds);
962 FD_SET(fuse_fd, &readfds);
963 nready = select(nfds, &readfds, NULL, NULL,
964 &timeout_tv);
965 if (m_quit)
966 return;
967 }
968 ASSERT_LE(0, nready) << strerror(errno);
969 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
970 break;
971 default:
972 FAIL() << "not yet implemented";
973 }
974 res = read(m_fuse_fd, &in, sizeof(in));
975
976 if (res < 0 && !m_quit) {
977 m_quit = true;
978 FAIL() << "read: " << strerror(errno);
979 }
980 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
981 /*
982 * Inconsistently, fuse_in_header.len is the size of the entire
983 * request,including header, even though fuse_out_header.len excludes
984 * the size of the header.
985 */
986 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
987 }
988
write_response(const mockfs_buf_out & out)989 void MockFS::write_response(const mockfs_buf_out &out) {
990 fd_set writefds;
991 pollfd fds[1];
992 struct kevent changes[1];
993 struct kevent events[1];
994 int nready, nfds;
995 ssize_t r;
996
997 switch (m_pm) {
998 case BLOCKING:
999 break;
1000 case KQ:
1001 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1002 EV_ADD | EV_ONESHOT, 0, 0, 0);
1003 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1004 NULL);
1005 ASSERT_LE(0, nready) << strerror(errno);
1006 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1007 if (events[0].flags & EV_ERROR)
1008 FAIL() << strerror(events[0].data);
1009 else if (events[0].flags & EV_EOF)
1010 FAIL() << strerror(events[0].fflags);
1011 m_nready = events[0].data;
1012 break;
1013 case POLL:
1014 fds[0].fd = m_fuse_fd;
1015 fds[0].events = POLLOUT;
1016 nready = poll(fds, 1, INFTIM);
1017 ASSERT_LE(0, nready) << strerror(errno);
1018 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1019 ASSERT_TRUE(fds[0].revents & POLLOUT);
1020 break;
1021 case SELECT:
1022 FD_ZERO(&writefds);
1023 FD_SET(m_fuse_fd, &writefds);
1024 nfds = m_fuse_fd + 1;
1025 nready = select(nfds, NULL, &writefds, NULL, NULL);
1026 ASSERT_LE(0, nready) << strerror(errno);
1027 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1028 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1029 break;
1030 default:
1031 FAIL() << "not yet implemented";
1032 }
1033 r = write(m_fuse_fd, &out, out.header.len);
1034 if (out.expected_errno) {
1035 ASSERT_EQ(-1, r);
1036 ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1037 } else {
1038 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1039 }
1040 }
1041
service(void * pthr_data)1042 void* MockFS::service(void *pthr_data) {
1043 MockFS *mock_fs = (MockFS*)pthr_data;
1044
1045 mock_fs->loop();
1046
1047 return (NULL);
1048 }
1049
unmount()1050 void MockFS::unmount() {
1051 ::unmount("mountpoint", 0);
1052 }
1053