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