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_LINK:
245 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
246 break;
247 case FUSE_LISTXATTR:
248 printf(" size=%" PRIu32, in.body.listxattr.size);
249 break;
250 case FUSE_LOOKUP:
251 printf(" %s", in.body.lookup);
252 break;
253 case FUSE_LSEEK:
254 switch (in.body.lseek.whence) {
255 case SEEK_HOLE:
256 printf(" SEEK_HOLE offset=%jd",
257 in.body.lseek.offset);
258 break;
259 case SEEK_DATA:
260 printf(" SEEK_DATA offset=%jd",
261 in.body.lseek.offset);
262 break;
263 default:
264 printf(" whence=%u offset=%jd",
265 in.body.lseek.whence, in.body.lseek.offset);
266 break;
267 }
268 break;
269 case FUSE_MKDIR:
270 name = (const char*)in.body.bytes +
271 sizeof(fuse_mkdir_in);
272 printf(" name=%s mode=%#o umask=%#o", name,
273 in.body.mkdir.mode, in.body.mkdir.umask);
274 break;
275 case FUSE_MKNOD:
276 if (m_kernel_minor_version >= 12)
277 name = (const char*)in.body.bytes +
278 sizeof(fuse_mknod_in);
279 else
280 name = (const char*)in.body.bytes +
281 FUSE_COMPAT_MKNOD_IN_SIZE;
282 printf(" mode=%#o rdev=%x umask=%#o name=%s",
283 in.body.mknod.mode, in.body.mknod.rdev,
284 in.body.mknod.umask, name);
285 break;
286 case FUSE_OPEN:
287 printf(" flags=%#x", in.body.open.flags);
288 break;
289 case FUSE_OPENDIR:
290 printf(" flags=%#x", in.body.opendir.flags);
291 break;
292 case FUSE_READ:
293 printf(" offset=%" PRIu64 " size=%u",
294 in.body.read.offset,
295 in.body.read.size);
296 if (verbosity > 1)
297 printf(" flags=%#x", in.body.read.flags);
298 break;
299 case FUSE_READDIR:
300 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
301 in.body.readdir.fh, in.body.readdir.offset,
302 in.body.readdir.size);
303 break;
304 case FUSE_RELEASE:
305 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
306 in.body.release.fh,
307 in.body.release.flags,
308 in.body.release.lock_owner);
309 break;
310 case FUSE_RENAME:
311 {
312 const char *src = (const char*)in.body.bytes +
313 sizeof(fuse_rename_in);
314 const char *dst = src + strlen(src) + 1;
315 printf(" src=%s newdir=%" PRIu64 " dst=%s",
316 src, in.body.rename.newdir, dst);
317 }
318 break;
319 case FUSE_SETATTR:
320 if (verbosity <= 1) {
321 printf(" valid=%#x", in.body.setattr.valid);
322 break;
323 }
324 if (in.body.setattr.valid & FATTR_MODE)
325 printf(" mode=%#o", in.body.setattr.mode);
326 if (in.body.setattr.valid & FATTR_UID)
327 printf(" uid=%u", in.body.setattr.uid);
328 if (in.body.setattr.valid & FATTR_GID)
329 printf(" gid=%u", in.body.setattr.gid);
330 if (in.body.setattr.valid & FATTR_SIZE)
331 printf(" size=%" PRIu64, in.body.setattr.size);
332 if (in.body.setattr.valid & FATTR_ATIME)
333 printf(" atime=%" PRIu64 ".%u",
334 in.body.setattr.atime,
335 in.body.setattr.atimensec);
336 if (in.body.setattr.valid & FATTR_MTIME)
337 printf(" mtime=%" PRIu64 ".%u",
338 in.body.setattr.mtime,
339 in.body.setattr.mtimensec);
340 if (in.body.setattr.valid & FATTR_FH)
341 printf(" fh=%" PRIu64 "", in.body.setattr.fh);
342 break;
343 case FUSE_SETLK:
344 printf(" fh=%#" PRIx64 " owner=%" PRIu64
345 " type=%u pid=%u",
346 in.body.setlk.fh, in.body.setlk.owner,
347 in.body.setlk.lk.type,
348 in.body.setlk.lk.pid);
349 if (verbosity >= 2) {
350 printf(" range=[%" PRIi64 ":%" PRIi64 "]",
351 in.body.setlk.lk.start,
352 in.body.setlk.lk.end);
353 }
354 break;
355 case FUSE_SETXATTR:
356 /*
357 * In theory neither the xattr name and value need be
358 * ASCII, but in this test suite they always are.
359 */
360 name = (const char*)in.body.bytes +
361 sizeof(fuse_setxattr_in);
362 value = name + strlen(name) + 1;
363 printf(" %s=%s", name, value);
364 break;
365 case FUSE_WRITE:
366 printf(" fh=%#" PRIx64 " offset=%" PRIu64
367 " size=%u write_flags=%u",
368 in.body.write.fh,
369 in.body.write.offset, in.body.write.size,
370 in.body.write.write_flags);
371 if (verbosity > 1)
372 printf(" flags=%#x", in.body.write.flags);
373 break;
374 default:
375 break;
376 }
377 printf("\n");
378 }
379
380 /*
381 * Debug a FUSE response.
382 *
383 * This is mostly useful for asynchronous notifications, which don't correspond
384 * to any request
385 */
debug_response(const mockfs_buf_out & out)386 void MockFS::debug_response(const mockfs_buf_out &out) {
387 const char *name;
388
389 if (verbosity == 0)
390 return;
391
392 switch (out.header.error) {
393 case FUSE_NOTIFY_INVAL_ENTRY:
394 name = (const char*)out.body.bytes +
395 sizeof(fuse_notify_inval_entry_out);
396 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
397 out.body.inval_entry.parent, name);
398 break;
399 case FUSE_NOTIFY_INVAL_INODE:
400 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
401 " len=%" PRIi64 "\n",
402 out.body.inval_inode.ino,
403 out.body.inval_inode.off,
404 out.body.inval_inode.len);
405 break;
406 case FUSE_NOTIFY_STORE:
407 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
408 " size=%" PRIu32 "\n",
409 out.body.store.nodeid,
410 out.body.store.offset,
411 out.body.store.size);
412 break;
413 default:
414 break;
415 }
416 }
417
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)418 MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
419 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, bool no_auto_init)
424 : m_daemon_id(NULL),
425 m_kernel_minor_version(kernel_minor_version),
426 m_kq(pm == KQ ? kqueue() : -1),
427 m_maxread(max_read),
428 m_maxreadahead(max_readahead),
429 m_pid(getpid()),
430 m_uniques(new std::unordered_set<uint64_t>),
431 m_pm(pm),
432 m_time_gran(time_gran),
433 m_child_pid(-1),
434 m_maxwrite(MIN(max_write, max_max_write)),
435 m_nready(-1),
436 m_quit(false)
437 {
438 struct sigaction sa;
439 struct iovec *iov = NULL;
440 int iovlen = 0;
441 char fdstr[15];
442 const bool trueval = true;
443
444 /*
445 * Kyua sets pwd to a testcase-unique tempdir; no need to use
446 * mkdtemp
447 */
448 /*
449 * googletest doesn't allow ASSERT_ in constructors, so we must throw
450 * instead.
451 */
452 if (mkdir("mountpoint" , 0755) && errno != EEXIST)
453 throw(std::system_error(errno, std::system_category(),
454 "Couldn't make mountpoint directory"));
455
456 switch (m_pm) {
457 case BLOCKING:
458 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
459 break;
460 default:
461 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
462 break;
463 }
464 if (m_fuse_fd < 0)
465 throw(std::system_error(errno, std::system_category(),
466 "Couldn't open /dev/fuse"));
467
468 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
469 build_iovec(&iov, &iovlen, "fspath",
470 __DECONST(void *, "mountpoint"), -1);
471 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
472 sprintf(fdstr, "%d", m_fuse_fd);
473 build_iovec(&iov, &iovlen, "fd", fdstr, -1);
474 if (m_maxread > 0) {
475 char val[10];
476
477 snprintf(val, sizeof(val), "%d", m_maxread);
478 build_iovec(&iov, &iovlen, "max_read=", &val, -1);
479 }
480 if (allow_other) {
481 build_iovec(&iov, &iovlen, "allow_other",
482 __DECONST(void*, &trueval), sizeof(bool));
483 }
484 if (default_permissions) {
485 build_iovec(&iov, &iovlen, "default_permissions",
486 __DECONST(void*, &trueval), sizeof(bool));
487 }
488 if (push_symlinks_in) {
489 build_iovec(&iov, &iovlen, "push_symlinks_in",
490 __DECONST(void*, &trueval), sizeof(bool));
491 }
492 if (ro) {
493 build_iovec(&iov, &iovlen, "ro",
494 __DECONST(void*, &trueval), sizeof(bool));
495 }
496 if (async) {
497 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
498 sizeof(bool));
499 }
500 if (noatime) {
501 build_iovec(&iov, &iovlen, "noatime",
502 __DECONST(void*, &trueval), sizeof(bool));
503 }
504 if (noclusterr) {
505 build_iovec(&iov, &iovlen, "noclusterr",
506 __DECONST(void*, &trueval), sizeof(bool));
507 }
508 if (nointr) {
509 build_iovec(&iov, &iovlen, "nointr",
510 __DECONST(void*, &trueval), sizeof(bool));
511 } else {
512 build_iovec(&iov, &iovlen, "intr",
513 __DECONST(void*, &trueval), sizeof(bool));
514 }
515 if (*fsname) {
516 build_iovec(&iov, &iovlen, "fsname=",
517 __DECONST(void*, fsname), -1);
518 }
519 if (*subtype) {
520 build_iovec(&iov, &iovlen, "subtype=",
521 __DECONST(void*, subtype), -1);
522 }
523 if (nmount(iov, iovlen, 0))
524 throw(std::system_error(errno, std::system_category(),
525 "Couldn't mount filesystem"));
526 free_iovec(&iov, &iovlen);
527
528 // Setup default handler
529 ON_CALL(*this, process(_, _))
530 .WillByDefault(Invoke(this, &MockFS::process_default));
531
532 if (!no_auto_init)
533 init(flags);
534
535 bzero(&sa, sizeof(sa));
536 sa.sa_handler = sigint_handler;
537 sa.sa_flags = 0; /* Don't set SA_RESTART! */
538 if (0 != sigaction(SIGUSR1, &sa, NULL))
539 throw(std::system_error(errno, std::system_category(),
540 "Couldn't handle SIGUSR1"));
541 if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
542 throw(std::system_error(errno, std::system_category(),
543 "Couldn't Couldn't start fuse thread"));
544 }
545
~MockFS()546 MockFS::~MockFS() {
547 kill_daemon();
548 join_daemon();
549 ::unmount("mountpoint", MNT_FORCE);
550 rmdir("mountpoint");
551 if (m_kq >= 0)
552 close(m_kq);
553 }
554
audit_request(const mockfs_buf_in & in,ssize_t buflen)555 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
556 uint32_t inlen = in.header.len;
557 size_t fih = sizeof(in.header);
558 switch (in.header.opcode) {
559 case FUSE_LOOKUP:
560 case FUSE_RMDIR:
561 case FUSE_SYMLINK:
562 case FUSE_UNLINK:
563 EXPECT_GT(inlen, fih) << "Missing request filename";
564 // No redundant information for checking buflen
565 break;
566 case FUSE_FORGET:
567 EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
568 EXPECT_EQ((size_t)buflen, inlen);
569 break;
570 case FUSE_GETATTR:
571 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
572 EXPECT_EQ((size_t)buflen, inlen);
573 break;
574 case FUSE_SETATTR:
575 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
576 EXPECT_EQ((size_t)buflen, inlen);
577 break;
578 case FUSE_READLINK:
579 EXPECT_EQ(inlen, fih) << "Unexpected request body";
580 EXPECT_EQ((size_t)buflen, inlen);
581 break;
582 case FUSE_MKNOD:
583 {
584 size_t s;
585 if (m_kernel_minor_version >= 12)
586 s = sizeof(in.body.mknod);
587 else
588 s = FUSE_COMPAT_MKNOD_IN_SIZE;
589 EXPECT_GE(inlen, fih + s) << "Missing request body";
590 EXPECT_GT(inlen, fih + s) << "Missing request filename";
591 // No redundant information for checking buflen
592 break;
593 }
594 case FUSE_MKDIR:
595 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
596 "Missing request body";
597 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
598 "Missing request filename";
599 // No redundant information for checking buflen
600 break;
601 case FUSE_RENAME:
602 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
603 "Missing request body";
604 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
605 "Missing request filename";
606 // No redundant information for checking buflen
607 break;
608 case FUSE_LINK:
609 EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
610 "Missing request body";
611 EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
612 "Missing request filename";
613 // No redundant information for checking buflen
614 break;
615 case FUSE_OPEN:
616 EXPECT_EQ(inlen, fih + sizeof(in.body.open));
617 EXPECT_EQ((size_t)buflen, inlen);
618 break;
619 case FUSE_READ:
620 EXPECT_EQ(inlen, fih + sizeof(in.body.read));
621 EXPECT_EQ((size_t)buflen, inlen);
622 break;
623 case FUSE_WRITE:
624 {
625 size_t s;
626
627 if (m_kernel_minor_version >= 9)
628 s = sizeof(in.body.write);
629 else
630 s = FUSE_COMPAT_WRITE_IN_SIZE;
631 // I suppose a 0-byte write should be allowed
632 EXPECT_GE(inlen, fih + s) << "Missing request body";
633 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
634 break;
635 }
636 case FUSE_DESTROY:
637 case FUSE_STATFS:
638 EXPECT_EQ(inlen, fih);
639 EXPECT_EQ((size_t)buflen, inlen);
640 break;
641 case FUSE_RELEASE:
642 EXPECT_EQ(inlen, fih + sizeof(in.body.release));
643 EXPECT_EQ((size_t)buflen, inlen);
644 break;
645 case FUSE_FSYNC:
646 case FUSE_FSYNCDIR:
647 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
648 EXPECT_EQ((size_t)buflen, inlen);
649 break;
650 case FUSE_SETXATTR:
651 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
652 "Missing request body";
653 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
654 "Missing request attribute name";
655 // No redundant information for checking buflen
656 break;
657 case FUSE_GETXATTR:
658 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
659 "Missing request body";
660 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
661 "Missing request attribute name";
662 // No redundant information for checking buflen
663 break;
664 case FUSE_LISTXATTR:
665 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
666 EXPECT_EQ((size_t)buflen, inlen);
667 break;
668 case FUSE_REMOVEXATTR:
669 EXPECT_GT(inlen, fih) << "Missing request attribute name";
670 // No redundant information for checking buflen
671 break;
672 case FUSE_FLUSH:
673 EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
674 EXPECT_EQ((size_t)buflen, inlen);
675 break;
676 case FUSE_INIT:
677 EXPECT_EQ(inlen, fih + sizeof(in.body.init));
678 EXPECT_EQ((size_t)buflen, inlen);
679 break;
680 case FUSE_OPENDIR:
681 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
682 EXPECT_EQ((size_t)buflen, inlen);
683 break;
684 case FUSE_READDIR:
685 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
686 EXPECT_EQ((size_t)buflen, inlen);
687 break;
688 case FUSE_RELEASEDIR:
689 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
690 EXPECT_EQ((size_t)buflen, inlen);
691 break;
692 case FUSE_GETLK:
693 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
694 EXPECT_EQ((size_t)buflen, inlen);
695 break;
696 case FUSE_SETLK:
697 case FUSE_SETLKW:
698 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
699 EXPECT_EQ((size_t)buflen, inlen);
700 break;
701 case FUSE_ACCESS:
702 EXPECT_EQ(inlen, fih + sizeof(in.body.access));
703 EXPECT_EQ((size_t)buflen, inlen);
704 break;
705 case FUSE_CREATE:
706 EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
707 "Missing request body";
708 EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
709 "Missing request filename";
710 // No redundant information for checking buflen
711 break;
712 case FUSE_INTERRUPT:
713 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
714 EXPECT_EQ((size_t)buflen, inlen);
715 break;
716 case FUSE_FALLOCATE:
717 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
718 EXPECT_EQ((size_t)buflen, inlen);
719 break;
720 case FUSE_BMAP:
721 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
722 EXPECT_EQ((size_t)buflen, inlen);
723 break;
724 case FUSE_LSEEK:
725 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
726 EXPECT_EQ((size_t)buflen, inlen);
727 break;
728 case FUSE_COPY_FILE_RANGE:
729 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
730 EXPECT_EQ(0ul, in.body.copy_file_range.flags);
731 EXPECT_EQ((size_t)buflen, inlen);
732 break;
733 case FUSE_NOTIFY_REPLY:
734 case FUSE_BATCH_FORGET:
735 case FUSE_IOCTL:
736 case FUSE_POLL:
737 case FUSE_READDIRPLUS:
738 FAIL() << "Unsupported opcode?";
739 default:
740 FAIL() << "Unknown opcode " << in.header.opcode;
741 }
742 /* Verify that the ticket's unique value is actually unique. */
743 if (m_uniques->find(in.header.unique) != m_uniques->end())
744 FAIL() << "Non-unique \"unique\" value";
745 m_uniques->insert(in.header.unique);
746 }
747
init(uint32_t flags)748 void MockFS::init(uint32_t flags) {
749 ssize_t buflen;
750
751 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
752 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
753
754 read_request(*in, buflen);
755 if (verbosity > 0)
756 debug_request(*in, buflen);
757 audit_request(*in, buflen);
758 ASSERT_EQ(FUSE_INIT, in->header.opcode);
759
760 out->header.unique = in->header.unique;
761 out->header.error = 0;
762 out->body.init.major = FUSE_KERNEL_VERSION;
763 out->body.init.minor = m_kernel_minor_version;;
764 out->body.init.flags = in->body.init.flags & flags;
765 out->body.init.max_write = m_maxwrite;
766 out->body.init.max_readahead = m_maxreadahead;
767
768 if (m_kernel_minor_version < 23) {
769 SET_OUT_HEADER_LEN(*out, init_7_22);
770 } else {
771 out->body.init.time_gran = m_time_gran;
772 SET_OUT_HEADER_LEN(*out, init);
773 }
774
775 write(m_fuse_fd, out.get(), out->header.len);
776 }
777
kill_daemon()778 void MockFS::kill_daemon() {
779 m_quit = true;
780 if (m_daemon_id != NULL)
781 pthread_kill(m_daemon_id, SIGUSR1);
782 // Closing the /dev/fuse file descriptor first allows unmount to
783 // succeed even if the daemon doesn't correctly respond to commands
784 // during the unmount sequence.
785 close(m_fuse_fd);
786 m_fuse_fd = -1;
787 }
788
join_daemon()789 void MockFS::join_daemon() {
790 if (m_daemon_id != NULL) {
791 pthread_join(m_daemon_id, NULL);
792 m_daemon_id = NULL;
793 }
794 }
795
loop()796 void MockFS::loop() {
797 std::vector<std::unique_ptr<mockfs_buf_out>> out;
798
799 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
800 ASSERT_TRUE(in != NULL);
801 while (!m_quit) {
802 ssize_t buflen;
803
804 bzero(in.get(), sizeof(*in));
805 read_request(*in, buflen);
806 if (m_quit)
807 break;
808 if (verbosity > 0)
809 debug_request(*in, buflen);
810 audit_request(*in, buflen);
811 if (pid_ok((pid_t)in->header.pid)) {
812 process(*in, out);
813 } else {
814 /*
815 * Reject any requests from unknown processes. Because
816 * we actually do mount a filesystem, plenty of
817 * unrelated system daemons may try to access it.
818 */
819 if (verbosity > 1)
820 printf("\tREJECTED (wrong pid %d)\n",
821 in->header.pid);
822 process_default(*in, out);
823 }
824 for (auto &it: out)
825 write_response(*it);
826 out.clear();
827 }
828 }
829
notify_inval_entry(ino_t parent,const char * name,size_t namelen)830 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
831 {
832 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
833
834 out->header.unique = 0; /* 0 means asynchronous notification */
835 out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
836 out->body.inval_entry.parent = parent;
837 out->body.inval_entry.namelen = namelen;
838 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
839 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
840 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
841 namelen;
842 debug_response(*out);
843 write_response(*out);
844 return 0;
845 }
846
notify_inval_inode(ino_t ino,off_t off,ssize_t len)847 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
848 {
849 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
850
851 out->header.unique = 0; /* 0 means asynchronous notification */
852 out->header.error = FUSE_NOTIFY_INVAL_INODE;
853 out->body.inval_inode.ino = ino;
854 out->body.inval_inode.off = off;
855 out->body.inval_inode.len = len;
856 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
857 debug_response(*out);
858 write_response(*out);
859 return 0;
860 }
861
notify_store(ino_t ino,off_t off,const void * data,ssize_t size)862 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
863 {
864 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
865
866 out->header.unique = 0; /* 0 means asynchronous notification */
867 out->header.error = FUSE_NOTIFY_STORE;
868 out->body.store.nodeid = ino;
869 out->body.store.offset = off;
870 out->body.store.size = size;
871 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
872 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
873 debug_response(*out);
874 write_response(*out);
875 return 0;
876 }
877
pid_ok(pid_t pid)878 bool MockFS::pid_ok(pid_t pid) {
879 if (pid == m_pid) {
880 return (true);
881 } else if (pid == m_child_pid) {
882 return (true);
883 } else {
884 struct kinfo_proc *ki;
885 bool ok = false;
886
887 ki = kinfo_getproc(pid);
888 if (ki == NULL)
889 return (false);
890 /*
891 * Allow access by the aio daemon processes so that our tests
892 * can use aio functions
893 */
894 if (0 == strncmp("aiod", ki->ki_comm, 4))
895 ok = true;
896 free(ki);
897 return (ok);
898 }
899 }
900
process_default(const mockfs_buf_in & in,std::vector<std::unique_ptr<mockfs_buf_out>> & out)901 void MockFS::process_default(const mockfs_buf_in& in,
902 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
903 {
904 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
905 out0->header.unique = in.header.unique;
906 out0->header.error = -EOPNOTSUPP;
907 out0->header.len = sizeof(out0->header);
908 out.push_back(std::move(out0));
909 }
910
read_request(mockfs_buf_in & in,ssize_t & res)911 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
912 int nready = 0;
913 fd_set readfds;
914 pollfd fds[1];
915 struct kevent changes[1];
916 struct kevent events[1];
917 struct timespec timeout_ts;
918 struct timeval timeout_tv;
919 const int timeout_ms = 999;
920 int timeout_int, nfds;
921 int fuse_fd;
922
923 switch (m_pm) {
924 case BLOCKING:
925 break;
926 case KQ:
927 timeout_ts.tv_sec = 0;
928 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
929 while (nready == 0) {
930 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
931 EV_ADD | EV_ONESHOT, 0, 0, 0);
932 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
933 &timeout_ts);
934 if (m_quit)
935 return;
936 }
937 ASSERT_LE(0, nready) << strerror(errno);
938 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
939 if (events[0].flags & EV_ERROR)
940 FAIL() << strerror(events[0].data);
941 else if (events[0].flags & EV_EOF)
942 FAIL() << strerror(events[0].fflags);
943 m_nready = events[0].data;
944 break;
945 case POLL:
946 timeout_int = timeout_ms;
947 fds[0].fd = m_fuse_fd;
948 fds[0].events = POLLIN;
949 while (nready == 0) {
950 nready = poll(fds, 1, timeout_int);
951 if (m_quit)
952 return;
953 }
954 ASSERT_LE(0, nready) << strerror(errno);
955 ASSERT_TRUE(fds[0].revents & POLLIN);
956 break;
957 case SELECT:
958 fuse_fd = m_fuse_fd;
959 if (fuse_fd < 0)
960 break;
961 timeout_tv.tv_sec = 0;
962 timeout_tv.tv_usec = timeout_ms * 1'000;
963 nfds = fuse_fd + 1;
964 while (nready == 0) {
965 FD_ZERO(&readfds);
966 FD_SET(fuse_fd, &readfds);
967 nready = select(nfds, &readfds, NULL, NULL,
968 &timeout_tv);
969 if (m_quit)
970 return;
971 }
972 ASSERT_LE(0, nready) << strerror(errno);
973 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
974 break;
975 default:
976 FAIL() << "not yet implemented";
977 }
978 res = read(m_fuse_fd, &in, sizeof(in));
979
980 if (res < 0 && !m_quit) {
981 m_quit = true;
982 FAIL() << "read: " << strerror(errno);
983 }
984 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
985 /*
986 * Inconsistently, fuse_in_header.len is the size of the entire
987 * request,including header, even though fuse_out_header.len excludes
988 * the size of the header.
989 */
990 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
991 }
992
write_response(const mockfs_buf_out & out)993 void MockFS::write_response(const mockfs_buf_out &out) {
994 fd_set writefds;
995 pollfd fds[1];
996 struct kevent changes[1];
997 struct kevent events[1];
998 int nready, nfds;
999 ssize_t r;
1000
1001 switch (m_pm) {
1002 case BLOCKING:
1003 break;
1004 case KQ:
1005 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1006 EV_ADD | EV_ONESHOT, 0, 0, 0);
1007 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1008 NULL);
1009 ASSERT_LE(0, nready) << strerror(errno);
1010 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1011 if (events[0].flags & EV_ERROR)
1012 FAIL() << strerror(events[0].data);
1013 else if (events[0].flags & EV_EOF)
1014 FAIL() << strerror(events[0].fflags);
1015 m_nready = events[0].data;
1016 break;
1017 case POLL:
1018 fds[0].fd = m_fuse_fd;
1019 fds[0].events = POLLOUT;
1020 nready = poll(fds, 1, INFTIM);
1021 ASSERT_LE(0, nready) << strerror(errno);
1022 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1023 ASSERT_TRUE(fds[0].revents & POLLOUT);
1024 break;
1025 case SELECT:
1026 FD_ZERO(&writefds);
1027 FD_SET(m_fuse_fd, &writefds);
1028 nfds = m_fuse_fd + 1;
1029 nready = select(nfds, NULL, &writefds, NULL, NULL);
1030 ASSERT_LE(0, nready) << strerror(errno);
1031 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1032 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1033 break;
1034 default:
1035 FAIL() << "not yet implemented";
1036 }
1037 r = write(m_fuse_fd, &out, out.header.len);
1038 if (out.expected_errno) {
1039 ASSERT_EQ(-1, r);
1040 ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1041 } else {
1042 if (r <= 0 && errno == EINVAL) {
1043 printf("Failed to write response. unique=%" PRIu64
1044 ":\n", out.header.unique);
1045 }
1046 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1047 }
1048 }
1049
service(void * pthr_data)1050 void* MockFS::service(void *pthr_data) {
1051 MockFS *mock_fs = (MockFS*)pthr_data;
1052
1053 mock_fs->loop();
1054
1055 return (NULL);
1056 }
1057
unmount()1058 void MockFS::unmount() {
1059 ::unmount("mountpoint", 0);
1060 }
1061