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