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