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