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 m_expect_unmount(false)
438 {
439 struct sigaction sa;
440 struct iovec *iov = NULL;
441 int iovlen = 0;
442 char fdstr[15];
443 const bool trueval = true;
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 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
470 build_iovec(&iov, &iovlen, "fspath",
471 __DECONST(void *, "mountpoint"), -1);
472 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
473 sprintf(fdstr, "%d", m_fuse_fd);
474 build_iovec(&iov, &iovlen, "fd", fdstr, -1);
475 if (m_maxread > 0) {
476 char val[12];
477
478 snprintf(val, sizeof(val), "%d", m_maxread);
479 build_iovec(&iov, &iovlen, "max_read=", &val, -1);
480 }
481 if (allow_other) {
482 build_iovec(&iov, &iovlen, "allow_other",
483 __DECONST(void*, &trueval), sizeof(bool));
484 }
485 if (default_permissions) {
486 build_iovec(&iov, &iovlen, "default_permissions",
487 __DECONST(void*, &trueval), sizeof(bool));
488 }
489 if (push_symlinks_in) {
490 build_iovec(&iov, &iovlen, "push_symlinks_in",
491 __DECONST(void*, &trueval), sizeof(bool));
492 }
493 if (ro) {
494 build_iovec(&iov, &iovlen, "ro",
495 __DECONST(void*, &trueval), sizeof(bool));
496 }
497 if (async) {
498 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
499 sizeof(bool));
500 }
501 if (noatime) {
502 build_iovec(&iov, &iovlen, "noatime",
503 __DECONST(void*, &trueval), sizeof(bool));
504 }
505 if (noclusterr) {
506 build_iovec(&iov, &iovlen, "noclusterr",
507 __DECONST(void*, &trueval), sizeof(bool));
508 }
509 if (nointr) {
510 build_iovec(&iov, &iovlen, "nointr",
511 __DECONST(void*, &trueval), sizeof(bool));
512 } else {
513 build_iovec(&iov, &iovlen, "intr",
514 __DECONST(void*, &trueval), sizeof(bool));
515 }
516 if (*fsname) {
517 build_iovec(&iov, &iovlen, "fsname=",
518 __DECONST(void*, fsname), -1);
519 }
520 if (*subtype) {
521 build_iovec(&iov, &iovlen, "subtype=",
522 __DECONST(void*, subtype), -1);
523 }
524 if (nmount(iov, iovlen, 0))
525 throw(std::system_error(errno, std::system_category(),
526 "Couldn't mount filesystem"));
527 free_iovec(&iov, &iovlen);
528
529 // Setup default handler
530 ON_CALL(*this, process(_, _))
531 .WillByDefault(Invoke(this, &MockFS::process_default));
532
533 if (!no_auto_init)
534 init(flags);
535
536 bzero(&sa, sizeof(sa));
537 sa.sa_handler = sigint_handler;
538 sa.sa_flags = 0; /* Don't set SA_RESTART! */
539 if (0 != sigaction(SIGUSR1, &sa, NULL))
540 throw(std::system_error(errno, std::system_category(),
541 "Couldn't handle SIGUSR1"));
542 if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
543 throw(std::system_error(errno, std::system_category(),
544 "Couldn't Couldn't start fuse thread"));
545 }
546
~MockFS()547 MockFS::~MockFS() {
548 kill_daemon();
549 join_daemon();
550 ::unmount("mountpoint", MNT_FORCE);
551 rmdir("mountpoint");
552 if (m_kq >= 0)
553 close(m_kq);
554 }
555
audit_request(const mockfs_buf_in & in,ssize_t buflen)556 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
557 uint32_t inlen = in.header.len;
558 size_t fih = sizeof(in.header);
559 switch (in.header.opcode) {
560 case FUSE_LOOKUP:
561 case FUSE_RMDIR:
562 case FUSE_SYMLINK:
563 case FUSE_UNLINK:
564 EXPECT_GT(inlen, fih) << "Missing request filename";
565 // No redundant information for checking buflen
566 break;
567 case FUSE_FORGET:
568 EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
569 EXPECT_EQ((size_t)buflen, inlen);
570 break;
571 case FUSE_GETATTR:
572 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
573 EXPECT_EQ((size_t)buflen, inlen);
574 break;
575 case FUSE_SETATTR:
576 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
577 EXPECT_EQ((size_t)buflen, inlen);
578 break;
579 case FUSE_READLINK:
580 EXPECT_EQ(inlen, fih) << "Unexpected request body";
581 EXPECT_EQ((size_t)buflen, inlen);
582 break;
583 case FUSE_MKNOD:
584 {
585 size_t s;
586 if (m_kernel_minor_version >= 12)
587 s = sizeof(in.body.mknod);
588 else
589 s = FUSE_COMPAT_MKNOD_IN_SIZE;
590 EXPECT_GE(inlen, fih + s) << "Missing request body";
591 EXPECT_GT(inlen, fih + s) << "Missing request filename";
592 // No redundant information for checking buflen
593 break;
594 }
595 case FUSE_MKDIR:
596 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
597 "Missing request body";
598 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
599 "Missing request filename";
600 // No redundant information for checking buflen
601 break;
602 case FUSE_RENAME:
603 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
604 "Missing request body";
605 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
606 "Missing request filename";
607 // No redundant information for checking buflen
608 break;
609 case FUSE_LINK:
610 EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
611 "Missing request body";
612 EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
613 "Missing request filename";
614 // No redundant information for checking buflen
615 break;
616 case FUSE_OPEN:
617 EXPECT_EQ(inlen, fih + sizeof(in.body.open));
618 EXPECT_EQ((size_t)buflen, inlen);
619 break;
620 case FUSE_READ:
621 EXPECT_EQ(inlen, fih + sizeof(in.body.read));
622 EXPECT_EQ((size_t)buflen, inlen);
623 break;
624 case FUSE_WRITE:
625 {
626 size_t s;
627
628 if (m_kernel_minor_version >= 9)
629 s = sizeof(in.body.write);
630 else
631 s = FUSE_COMPAT_WRITE_IN_SIZE;
632 // I suppose a 0-byte write should be allowed
633 EXPECT_GE(inlen, fih + s) << "Missing request body";
634 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
635 break;
636 }
637 case FUSE_DESTROY:
638 case FUSE_STATFS:
639 EXPECT_EQ(inlen, fih);
640 EXPECT_EQ((size_t)buflen, inlen);
641 break;
642 case FUSE_RELEASE:
643 EXPECT_EQ(inlen, fih + sizeof(in.body.release));
644 EXPECT_EQ((size_t)buflen, inlen);
645 break;
646 case FUSE_FSYNC:
647 case FUSE_FSYNCDIR:
648 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
649 EXPECT_EQ((size_t)buflen, inlen);
650 break;
651 case FUSE_SETXATTR:
652 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
653 "Missing request body";
654 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
655 "Missing request attribute name";
656 // No redundant information for checking buflen
657 break;
658 case FUSE_GETXATTR:
659 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
660 "Missing request body";
661 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
662 "Missing request attribute name";
663 // No redundant information for checking buflen
664 break;
665 case FUSE_LISTXATTR:
666 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
667 EXPECT_EQ((size_t)buflen, inlen);
668 break;
669 case FUSE_REMOVEXATTR:
670 EXPECT_GT(inlen, fih) << "Missing request attribute name";
671 // No redundant information for checking buflen
672 break;
673 case FUSE_FLUSH:
674 EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
675 EXPECT_EQ((size_t)buflen, inlen);
676 break;
677 case FUSE_INIT:
678 EXPECT_EQ(inlen, fih + sizeof(in.body.init));
679 EXPECT_EQ((size_t)buflen, inlen);
680 break;
681 case FUSE_OPENDIR:
682 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
683 EXPECT_EQ((size_t)buflen, inlen);
684 break;
685 case FUSE_READDIR:
686 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
687 EXPECT_EQ((size_t)buflen, inlen);
688 break;
689 case FUSE_RELEASEDIR:
690 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
691 EXPECT_EQ((size_t)buflen, inlen);
692 break;
693 case FUSE_GETLK:
694 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
695 EXPECT_EQ((size_t)buflen, inlen);
696 break;
697 case FUSE_SETLK:
698 case FUSE_SETLKW:
699 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
700 EXPECT_EQ((size_t)buflen, inlen);
701 break;
702 case FUSE_ACCESS:
703 EXPECT_EQ(inlen, fih + sizeof(in.body.access));
704 EXPECT_EQ((size_t)buflen, inlen);
705 break;
706 case FUSE_CREATE:
707 EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
708 "Missing request body";
709 EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
710 "Missing request filename";
711 // No redundant information for checking buflen
712 break;
713 case FUSE_INTERRUPT:
714 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
715 EXPECT_EQ((size_t)buflen, inlen);
716 break;
717 case FUSE_FALLOCATE:
718 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
719 EXPECT_EQ((size_t)buflen, inlen);
720 break;
721 case FUSE_BMAP:
722 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
723 EXPECT_EQ((size_t)buflen, inlen);
724 break;
725 case FUSE_LSEEK:
726 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
727 EXPECT_EQ((size_t)buflen, inlen);
728 break;
729 case FUSE_COPY_FILE_RANGE:
730 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
731 EXPECT_EQ(0ul, in.body.copy_file_range.flags);
732 EXPECT_EQ((size_t)buflen, inlen);
733 break;
734 case FUSE_NOTIFY_REPLY:
735 case FUSE_BATCH_FORGET:
736 case FUSE_IOCTL:
737 case FUSE_POLL:
738 case FUSE_READDIRPLUS:
739 FAIL() << "Unsupported opcode?";
740 default:
741 FAIL() << "Unknown opcode " << in.header.opcode;
742 }
743 /* Verify that the ticket's unique value is actually unique. */
744 if (m_uniques->find(in.header.unique) != m_uniques->end())
745 FAIL() << "Non-unique \"unique\" value";
746 m_uniques->insert(in.header.unique);
747 }
748
init(uint32_t flags)749 void MockFS::init(uint32_t flags) {
750 ssize_t buflen;
751
752 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
753 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
754
755 read_request(*in, buflen);
756 if (verbosity > 0)
757 debug_request(*in, buflen);
758 audit_request(*in, buflen);
759 ASSERT_EQ(FUSE_INIT, in->header.opcode);
760
761 out->header.unique = in->header.unique;
762 out->header.error = 0;
763 out->body.init.major = FUSE_KERNEL_VERSION;
764 out->body.init.minor = m_kernel_minor_version;;
765 out->body.init.flags = in->body.init.flags & flags;
766 out->body.init.max_write = m_maxwrite;
767 out->body.init.max_readahead = m_maxreadahead;
768
769 if (m_kernel_minor_version < 23) {
770 SET_OUT_HEADER_LEN(*out, init_7_22);
771 } else {
772 out->body.init.time_gran = m_time_gran;
773 SET_OUT_HEADER_LEN(*out, init);
774 }
775
776 write(m_fuse_fd, out.get(), out->header.len);
777 }
778
kill_daemon()779 void MockFS::kill_daemon() {
780 m_quit = true;
781 if (m_daemon_id != NULL)
782 pthread_kill(m_daemon_id, SIGUSR1);
783 // Closing the /dev/fuse file descriptor first allows unmount to
784 // succeed even if the daemon doesn't correctly respond to commands
785 // during the unmount sequence.
786 close(m_fuse_fd);
787 m_fuse_fd = -1;
788 }
789
join_daemon()790 void MockFS::join_daemon() {
791 if (m_daemon_id != NULL) {
792 pthread_join(m_daemon_id, NULL);
793 m_daemon_id = NULL;
794 }
795 }
796
loop()797 void MockFS::loop() {
798 std::vector<std::unique_ptr<mockfs_buf_out>> out;
799
800 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
801 ASSERT_TRUE(in != NULL);
802 while (!m_quit) {
803 ssize_t buflen;
804
805 bzero(in.get(), sizeof(*in));
806 read_request(*in, buflen);
807 if (m_quit)
808 break;
809 if (verbosity > 0)
810 debug_request(*in, buflen);
811 audit_request(*in, buflen);
812 if (pid_ok((pid_t)in->header.pid)) {
813 process(*in, out);
814 } else {
815 /*
816 * Reject any requests from unknown processes. Because
817 * we actually do mount a filesystem, plenty of
818 * unrelated system daemons may try to access it.
819 */
820 if (verbosity > 1)
821 printf("\tREJECTED (wrong pid %d)\n",
822 in->header.pid);
823 process_default(*in, out);
824 }
825 for (auto &it: out)
826 write_response(*it);
827 out.clear();
828 }
829 }
830
notify_inval_entry(ino_t parent,const char * name,size_t namelen,int expected_errno)831 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen,
832 int expected_errno)
833 {
834 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
835
836 out->expected_errno = expected_errno;
837 out->header.unique = 0; /* 0 means asynchronous notification */
838 out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
839 out->body.inval_entry.parent = parent;
840 out->body.inval_entry.namelen = namelen;
841 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
842 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
843 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
844 namelen;
845 debug_response(*out);
846 write_response(*out);
847 return 0;
848 }
849
notify_inval_inode(ino_t ino,off_t off,ssize_t len)850 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
851 {
852 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
853
854 out->header.unique = 0; /* 0 means asynchronous notification */
855 out->header.error = FUSE_NOTIFY_INVAL_INODE;
856 out->body.inval_inode.ino = ino;
857 out->body.inval_inode.off = off;
858 out->body.inval_inode.len = len;
859 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
860 debug_response(*out);
861 write_response(*out);
862 return 0;
863 }
864
notify_store(ino_t ino,off_t off,const void * data,ssize_t size)865 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
866 {
867 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
868
869 out->header.unique = 0; /* 0 means asynchronous notification */
870 out->header.error = FUSE_NOTIFY_STORE;
871 out->body.store.nodeid = ino;
872 out->body.store.offset = off;
873 out->body.store.size = size;
874 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
875 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
876 debug_response(*out);
877 write_response(*out);
878 return 0;
879 }
880
pid_ok(pid_t pid)881 bool MockFS::pid_ok(pid_t pid) {
882 if (pid == m_pid) {
883 return (true);
884 } else if (pid == m_child_pid) {
885 return (true);
886 } else {
887 struct kinfo_proc *ki;
888 bool ok = false;
889
890 ki = kinfo_getproc(pid);
891 if (ki == NULL)
892 return (false);
893 /*
894 * Allow access by the aio daemon processes so that our tests
895 * can use aio functions
896 */
897 if (0 == strncmp("aiod", ki->ki_comm, 4))
898 ok = true;
899 free(ki);
900 return (ok);
901 }
902 }
903
process_default(const mockfs_buf_in & in,std::vector<std::unique_ptr<mockfs_buf_out>> & out)904 void MockFS::process_default(const mockfs_buf_in& in,
905 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
906 {
907 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
908 out0->header.unique = in.header.unique;
909 out0->header.error = -EOPNOTSUPP;
910 out0->header.len = sizeof(out0->header);
911 out.push_back(std::move(out0));
912 }
913
read_request(mockfs_buf_in & in,ssize_t & res)914 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
915 int nready = 0;
916 fd_set readfds;
917 pollfd fds[1];
918 struct kevent changes[1];
919 struct kevent events[1];
920 struct timespec timeout_ts;
921 struct timeval timeout_tv;
922 const int timeout_ms = 999;
923 int timeout_int, nfds;
924 int fuse_fd;
925
926 switch (m_pm) {
927 case BLOCKING:
928 break;
929 case KQ:
930 timeout_ts.tv_sec = 0;
931 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
932 while (nready == 0) {
933 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
934 EV_ADD | EV_ONESHOT, 0, 0, 0);
935 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
936 &timeout_ts);
937 if (m_quit)
938 return;
939 }
940 ASSERT_LE(0, nready) << strerror(errno);
941 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
942 if (events[0].flags & EV_ERROR)
943 FAIL() << strerror(events[0].data);
944 else if (events[0].flags & EV_EOF)
945 FAIL() << strerror(events[0].fflags);
946 m_nready = events[0].data;
947 break;
948 case POLL:
949 timeout_int = timeout_ms;
950 fds[0].fd = m_fuse_fd;
951 fds[0].events = POLLIN;
952 while (nready == 0) {
953 nready = poll(fds, 1, timeout_int);
954 if (m_quit)
955 return;
956 }
957 ASSERT_LE(0, nready) << strerror(errno);
958 ASSERT_TRUE(fds[0].revents & POLLIN);
959 break;
960 case SELECT:
961 fuse_fd = m_fuse_fd;
962 if (fuse_fd < 0)
963 break;
964 timeout_tv.tv_sec = 0;
965 timeout_tv.tv_usec = timeout_ms * 1'000;
966 nfds = fuse_fd + 1;
967 while (nready == 0) {
968 FD_ZERO(&readfds);
969 FD_SET(fuse_fd, &readfds);
970 nready = select(nfds, &readfds, NULL, NULL,
971 &timeout_tv);
972 if (m_quit)
973 return;
974 }
975 ASSERT_LE(0, nready) << strerror(errno);
976 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
977 break;
978 default:
979 FAIL() << "not yet implemented";
980 }
981 res = read(m_fuse_fd, &in, sizeof(in));
982
983 if (res < 0 && errno != EBADF && !m_quit && !m_expect_unmount) {
984 m_quit = true;
985 FAIL() << "read: " << strerror(errno);
986 }
987 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
988 /*
989 * Inconsistently, fuse_in_header.len is the size of the entire
990 * request,including header, even though fuse_out_header.len excludes
991 * the size of the header.
992 */
993 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
994 }
995
write_response(const mockfs_buf_out & out)996 void MockFS::write_response(const mockfs_buf_out &out) {
997 fd_set writefds;
998 pollfd fds[1];
999 struct kevent changes[1];
1000 struct kevent events[1];
1001 int nready, nfds;
1002 ssize_t r;
1003
1004 switch (m_pm) {
1005 case BLOCKING:
1006 break;
1007 case KQ:
1008 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1009 EV_ADD | EV_ONESHOT, 0, 0, 0);
1010 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1011 NULL);
1012 ASSERT_LE(0, nready) << strerror(errno);
1013 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1014 if (events[0].flags & EV_ERROR)
1015 FAIL() << strerror(events[0].data);
1016 else if (events[0].flags & EV_EOF)
1017 FAIL() << strerror(events[0].fflags);
1018 m_nready = events[0].data;
1019 break;
1020 case POLL:
1021 fds[0].fd = m_fuse_fd;
1022 fds[0].events = POLLOUT;
1023 nready = poll(fds, 1, INFTIM);
1024 ASSERT_LE(0, nready) << strerror(errno);
1025 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1026 ASSERT_TRUE(fds[0].revents & POLLOUT);
1027 break;
1028 case SELECT:
1029 FD_ZERO(&writefds);
1030 FD_SET(m_fuse_fd, &writefds);
1031 nfds = m_fuse_fd + 1;
1032 nready = select(nfds, NULL, &writefds, NULL, NULL);
1033 ASSERT_LE(0, nready) << strerror(errno);
1034 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1035 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1036 break;
1037 default:
1038 FAIL() << "not yet implemented";
1039 }
1040 r = write(m_fuse_fd, &out, out.header.len);
1041 if (out.expected_errno) {
1042 ASSERT_EQ(-1, r);
1043 ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1044 } else {
1045 if (r <= 0 && errno == EINVAL) {
1046 printf("Failed to write response. unique=%" PRIu64
1047 ":\n", out.header.unique);
1048 }
1049 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1050 }
1051 }
1052
service(void * pthr_data)1053 void* MockFS::service(void *pthr_data) {
1054 MockFS *mock_fs = (MockFS*)pthr_data;
1055
1056 mock_fs->loop();
1057
1058 return (NULL);
1059 }
1060
unmount()1061 void MockFS::unmount() {
1062 ::unmount("mountpoint", 0);
1063 }
1064