19821f1d3SAlan Somers /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
39821f1d3SAlan Somers *
49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers *
69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers * from the FreeBSD Foundation.
89821f1d3SAlan Somers *
99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers * are met:
129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers *
189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers * SUCH DAMAGE.
299821f1d3SAlan Somers */
309821f1d3SAlan Somers
319821f1d3SAlan Somers extern "C" {
329821f1d3SAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers
349821f1d3SAlan Somers #include <sys/mount.h>
353429092cSAlan Somers #include <sys/select.h>
369821f1d3SAlan Somers #include <sys/stat.h>
379821f1d3SAlan Somers #include <sys/uio.h>
389821f1d3SAlan Somers #include <sys/user.h>
399821f1d3SAlan Somers
409821f1d3SAlan Somers #include <fcntl.h>
419821f1d3SAlan Somers #include <libutil.h>
423429092cSAlan Somers #include <poll.h>
439821f1d3SAlan Somers #include <pthread.h>
449821f1d3SAlan Somers #include <signal.h>
459821f1d3SAlan Somers #include <stdlib.h>
469821f1d3SAlan Somers #include <unistd.h>
479821f1d3SAlan Somers
489821f1d3SAlan Somers #include "mntopts.h" // for build_iovec
499821f1d3SAlan Somers }
509821f1d3SAlan Somers
51cc04566cSAlan Somers #include <cinttypes>
52cc04566cSAlan Somers
539821f1d3SAlan Somers #include <gtest/gtest.h>
549821f1d3SAlan Somers
559821f1d3SAlan Somers #include "mockfs.hh"
569821f1d3SAlan Somers
579821f1d3SAlan Somers using namespace testing;
589821f1d3SAlan Somers
599821f1d3SAlan Somers int verbosity = 0;
609821f1d3SAlan Somers
opcode2opname(uint32_t opcode)619821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode)
629821f1d3SAlan Somers {
6337df9d3bSAlan Somers const char* table[] = {
649821f1d3SAlan Somers "Unknown (opcode 0)",
659821f1d3SAlan Somers "LOOKUP",
669821f1d3SAlan Somers "FORGET",
679821f1d3SAlan Somers "GETATTR",
689821f1d3SAlan Somers "SETATTR",
699821f1d3SAlan Somers "READLINK",
709821f1d3SAlan Somers "SYMLINK",
719821f1d3SAlan Somers "Unknown (opcode 7)",
729821f1d3SAlan Somers "MKNOD",
739821f1d3SAlan Somers "MKDIR",
749821f1d3SAlan Somers "UNLINK",
759821f1d3SAlan Somers "RMDIR",
769821f1d3SAlan Somers "RENAME",
779821f1d3SAlan Somers "LINK",
789821f1d3SAlan Somers "OPEN",
799821f1d3SAlan Somers "READ",
809821f1d3SAlan Somers "WRITE",
819821f1d3SAlan Somers "STATFS",
829821f1d3SAlan Somers "RELEASE",
839821f1d3SAlan Somers "Unknown (opcode 19)",
849821f1d3SAlan Somers "FSYNC",
859821f1d3SAlan Somers "SETXATTR",
869821f1d3SAlan Somers "GETXATTR",
879821f1d3SAlan Somers "LISTXATTR",
889821f1d3SAlan Somers "REMOVEXATTR",
899821f1d3SAlan Somers "FLUSH",
909821f1d3SAlan Somers "INIT",
919821f1d3SAlan Somers "OPENDIR",
929821f1d3SAlan Somers "READDIR",
939821f1d3SAlan Somers "RELEASEDIR",
949821f1d3SAlan Somers "FSYNCDIR",
959821f1d3SAlan Somers "GETLK",
969821f1d3SAlan Somers "SETLK",
979821f1d3SAlan Somers "SETLKW",
989821f1d3SAlan Somers "ACCESS",
999821f1d3SAlan Somers "CREATE",
1009821f1d3SAlan Somers "INTERRUPT",
1019821f1d3SAlan Somers "BMAP",
10237df9d3bSAlan Somers "DESTROY",
10337df9d3bSAlan Somers "IOCTL",
10437df9d3bSAlan Somers "POLL",
10537df9d3bSAlan Somers "NOTIFY_REPLY",
10637df9d3bSAlan Somers "BATCH_FORGET",
10737df9d3bSAlan Somers "FALLOCATE",
10837df9d3bSAlan Somers "READDIRPLUS",
10937df9d3bSAlan Somers "RENAME2",
11037df9d3bSAlan Somers "LSEEK",
11192bbfe1fSAlan Somers "COPY_FILE_RANGE",
1129821f1d3SAlan Somers };
11337df9d3bSAlan Somers if (opcode >= nitems(table))
1149821f1d3SAlan Somers return ("Unknown (opcode > max)");
1159821f1d3SAlan Somers else
1169821f1d3SAlan Somers return (table[opcode]);
1179821f1d3SAlan Somers }
1189821f1d3SAlan Somers
1199821f1d3SAlan Somers ProcessMockerT
ReturnErrno(int error)1209821f1d3SAlan Somers ReturnErrno(int error)
1219821f1d3SAlan Somers {
1229821f1d3SAlan Somers return([=](auto in, auto &out) {
12329edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
12429edc611SAlan Somers out0->header.unique = in.header.unique;
1259821f1d3SAlan Somers out0->header.error = -error;
1269821f1d3SAlan Somers out0->header.len = sizeof(out0->header);
12729edc611SAlan Somers out.push_back(std::move(out0));
1289821f1d3SAlan Somers });
1299821f1d3SAlan Somers }
1309821f1d3SAlan Somers
1319821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1329821f1d3SAlan Somers ProcessMockerT
ReturnNegativeCache(const struct timespec * entry_valid)1339821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1349821f1d3SAlan Somers {
1359821f1d3SAlan Somers return([=](auto in, auto &out) {
1369821f1d3SAlan Somers /* nodeid means ENOENT and cache it */
13729edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
1389821f1d3SAlan Somers out0->body.entry.nodeid = 0;
13929edc611SAlan Somers out0->header.unique = in.header.unique;
1409821f1d3SAlan Somers out0->header.error = 0;
1419821f1d3SAlan Somers out0->body.entry.entry_valid = entry_valid->tv_sec;
1429821f1d3SAlan Somers out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
14329edc611SAlan Somers SET_OUT_HEADER_LEN(*out0, entry);
14429edc611SAlan Somers out.push_back(std::move(out0));
1459821f1d3SAlan Somers });
1469821f1d3SAlan Somers }
1479821f1d3SAlan Somers
1489821f1d3SAlan Somers ProcessMockerT
ReturnImmediate(std::function<void (const mockfs_buf_in & in,struct mockfs_buf_out & out)> f)14929edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in,
15029edc611SAlan Somers struct mockfs_buf_out &out)> f)
1519821f1d3SAlan Somers {
15229edc611SAlan Somers return([=](auto& in, auto &out) {
15329edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
15429edc611SAlan Somers out0->header.unique = in.header.unique;
15529edc611SAlan Somers f(in, *out0);
15629edc611SAlan Somers out.push_back(std::move(out0));
1579821f1d3SAlan Somers });
1589821f1d3SAlan Somers }
1599821f1d3SAlan Somers
sigint_handler(int __unused sig)1609821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1618b73a4c5SAlan Somers // Don't do anything except interrupt the daemon's read(2) call
1629821f1d3SAlan Somers }
1639821f1d3SAlan Somers
debug_request(const mockfs_buf_in & in,ssize_t buflen)1646c0c3620SAlan Somers void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
1659821f1d3SAlan Somers {
16629edc611SAlan Somers printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
16729edc611SAlan Somers in.header.nodeid);
1689821f1d3SAlan Somers if (verbosity > 1) {
1696c0c3620SAlan Somers printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
1706c0c3620SAlan Somers " buflen=%zd",
17129edc611SAlan Somers in.header.uid, in.header.gid, in.header.pid,
1726c0c3620SAlan Somers in.header.unique, in.header.len, buflen);
1739821f1d3SAlan Somers }
17429edc611SAlan Somers switch (in.header.opcode) {
17519ef317dSAlan Somers const char *name, *value;
17619ef317dSAlan Somers
177caf5f57dSAlan Somers case FUSE_ACCESS:
17829edc611SAlan Somers printf(" mask=%#x", in.body.access.mask);
179caf5f57dSAlan Somers break;
180a1c9f4adSAlan Somers case FUSE_BMAP:
18197b0512bSAlan Somers printf(" block=%" PRIx64 " blocksize=%#x",
18297b0512bSAlan Somers in.body.bmap.block, in.body.bmap.blocksize);
183a1c9f4adSAlan Somers break;
18492bbfe1fSAlan Somers case FUSE_COPY_FILE_RANGE:
18592bbfe1fSAlan Somers printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
18692bbfe1fSAlan Somers " off_out=%" PRIu64 " size=%" PRIu64,
18792bbfe1fSAlan Somers in.body.copy_file_range.off_in,
18892bbfe1fSAlan Somers in.body.copy_file_range.nodeid_out,
18992bbfe1fSAlan Somers in.body.copy_file_range.off_out,
19092bbfe1fSAlan Somers in.body.copy_file_range.len);
19192bbfe1fSAlan Somers if (verbosity > 1)
19292bbfe1fSAlan Somers printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
19392bbfe1fSAlan Somers " flags=%" PRIx64,
19492bbfe1fSAlan Somers in.body.copy_file_range.fh_in,
19592bbfe1fSAlan Somers in.body.copy_file_range.fh_out,
19692bbfe1fSAlan Somers in.body.copy_file_range.flags);
19792bbfe1fSAlan Somers break;
19819ef317dSAlan Somers case FUSE_CREATE:
199a4856c96SAlan Somers if (m_kernel_minor_version >= 12)
200a4856c96SAlan Somers name = (const char*)in.body.bytes +
201a4856c96SAlan Somers sizeof(fuse_create_in);
202a4856c96SAlan Somers else
20329edc611SAlan Somers name = (const char*)in.body.bytes +
20419ef317dSAlan Somers sizeof(fuse_open_in);
20519ef317dSAlan Somers printf(" flags=%#x name=%s",
20629edc611SAlan Somers in.body.open.flags, name);
20719ef317dSAlan Somers break;
208398c88c7SAlan Somers case FUSE_FALLOCATE:
209398c88c7SAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64
210398c88c7SAlan Somers " length=%" PRIx64 " mode=%#x",
211398c88c7SAlan Somers in.body.fallocate.fh,
212398c88c7SAlan Somers in.body.fallocate.offset,
213398c88c7SAlan Somers in.body.fallocate.length,
214398c88c7SAlan Somers in.body.fallocate.mode);
215398c88c7SAlan Somers break;
2169821f1d3SAlan Somers case FUSE_FLUSH:
217cc04566cSAlan Somers printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
21829edc611SAlan Somers in.body.flush.fh,
21929edc611SAlan Somers in.body.flush.lock_owner);
2209821f1d3SAlan Somers break;
2219821f1d3SAlan Somers case FUSE_FORGET:
22229edc611SAlan Somers printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
2239821f1d3SAlan Somers break;
2249821f1d3SAlan Somers case FUSE_FSYNC:
22529edc611SAlan Somers printf(" flags=%#x", in.body.fsync.fsync_flags);
2269821f1d3SAlan Somers break;
2279821f1d3SAlan Somers case FUSE_FSYNCDIR:
22829edc611SAlan Somers printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
2299821f1d3SAlan Somers break;
230f6e53195SAlan Somers case FUSE_GETLK:
231f6e53195SAlan Somers printf(" fh=%#" PRIx64
232f6e53195SAlan Somers " type=%u pid=%u",
233f6e53195SAlan Somers in.body.getlk.fh,
234f6e53195SAlan Somers in.body.getlk.lk.type,
235f6e53195SAlan Somers in.body.getlk.lk.pid);
236f6e53195SAlan Somers if (verbosity >= 2) {
237f6e53195SAlan Somers printf(" range=[%" PRIi64 ":%" PRIi64 "]",
238f6e53195SAlan Somers in.body.getlk.lk.start,
239f6e53195SAlan Somers in.body.getlk.lk.end);
240f6e53195SAlan Somers }
241f6e53195SAlan Somers break;
242723c7768SAlan Somers case FUSE_INTERRUPT:
24329edc611SAlan Somers printf(" unique=%" PRIu64, in.body.interrupt.unique);
244723c7768SAlan Somers break;
245002e54b0SAlan Somers case FUSE_LINK:
24629edc611SAlan Somers printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
247002e54b0SAlan Somers break;
2485e633330SAlan Somers case FUSE_LISTXATTR:
2495e633330SAlan Somers printf(" size=%" PRIu32, in.body.listxattr.size);
2505e633330SAlan Somers break;
2519821f1d3SAlan Somers case FUSE_LOOKUP:
25229edc611SAlan Somers printf(" %s", in.body.lookup);
2539821f1d3SAlan Somers break;
25437df9d3bSAlan Somers case FUSE_LSEEK:
25537df9d3bSAlan Somers switch (in.body.lseek.whence) {
25637df9d3bSAlan Somers case SEEK_HOLE:
2571d010cd3SCy Schubert printf(" SEEK_HOLE offset=%jd",
25837df9d3bSAlan Somers in.body.lseek.offset);
25937df9d3bSAlan Somers break;
26037df9d3bSAlan Somers case SEEK_DATA:
2611d010cd3SCy Schubert printf(" SEEK_DATA offset=%jd",
26237df9d3bSAlan Somers in.body.lseek.offset);
26337df9d3bSAlan Somers break;
26437df9d3bSAlan Somers default:
2651d010cd3SCy Schubert printf(" whence=%u offset=%jd",
26637df9d3bSAlan Somers in.body.lseek.whence, in.body.lseek.offset);
26737df9d3bSAlan Somers break;
26837df9d3bSAlan Somers }
26937df9d3bSAlan Somers break;
27099cf7bffSAlan Somers case FUSE_MKDIR:
27129edc611SAlan Somers name = (const char*)in.body.bytes +
27299cf7bffSAlan Somers sizeof(fuse_mkdir_in);
273a4856c96SAlan Somers printf(" name=%s mode=%#o umask=%#o", name,
274a4856c96SAlan Somers in.body.mkdir.mode, in.body.mkdir.umask);
27599cf7bffSAlan Somers break;
276bf4d7084SAlan Somers case FUSE_MKNOD:
277a4856c96SAlan Somers if (m_kernel_minor_version >= 12)
278a4856c96SAlan Somers name = (const char*)in.body.bytes +
279a4856c96SAlan Somers sizeof(fuse_mknod_in);
280a4856c96SAlan Somers else
281a4856c96SAlan Somers name = (const char*)in.body.bytes +
282a4856c96SAlan Somers FUSE_COMPAT_MKNOD_IN_SIZE;
283a4856c96SAlan Somers printf(" mode=%#o rdev=%x umask=%#o name=%s",
284a4856c96SAlan Somers in.body.mknod.mode, in.body.mknod.rdev,
285a4856c96SAlan Somers in.body.mknod.umask, name);
286bf4d7084SAlan Somers break;
2879821f1d3SAlan Somers case FUSE_OPEN:
288a4856c96SAlan Somers printf(" flags=%#x", in.body.open.flags);
2899821f1d3SAlan Somers break;
2909821f1d3SAlan Somers case FUSE_OPENDIR:
291a4856c96SAlan Somers printf(" flags=%#x", in.body.opendir.flags);
2929821f1d3SAlan Somers break;
2939821f1d3SAlan Somers case FUSE_READ:
294cc04566cSAlan Somers printf(" offset=%" PRIu64 " size=%u",
29529edc611SAlan Somers in.body.read.offset,
29629edc611SAlan Somers in.body.read.size);
297d4fd0c81SAlan Somers if (verbosity > 1)
298d4fd0c81SAlan Somers printf(" flags=%#x", in.body.read.flags);
2999821f1d3SAlan Somers break;
3009821f1d3SAlan Somers case FUSE_READDIR:
301cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
30229edc611SAlan Somers in.body.readdir.fh, in.body.readdir.offset,
30329edc611SAlan Somers in.body.readdir.size);
3049821f1d3SAlan Somers break;
3059821f1d3SAlan Somers case FUSE_RELEASE:
306cc04566cSAlan Somers printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
30729edc611SAlan Somers in.body.release.fh,
30829edc611SAlan Somers in.body.release.flags,
30929edc611SAlan Somers in.body.release.lock_owner);
3109821f1d3SAlan Somers break;
311c2d342c5SAlan Somers case FUSE_RENAME:
312c2d342c5SAlan Somers {
313c2d342c5SAlan Somers const char *src = (const char*)in.body.bytes +
314c2d342c5SAlan Somers sizeof(fuse_rename_in);
315c2d342c5SAlan Somers const char *dst = src + strlen(src) + 1;
316c2d342c5SAlan Somers printf(" src=%s newdir=%" PRIu64 " dst=%s",
317c2d342c5SAlan Somers src, in.body.rename.newdir, dst);
318c2d342c5SAlan Somers }
319c2d342c5SAlan Somers break;
3209821f1d3SAlan Somers case FUSE_SETATTR:
3219821f1d3SAlan Somers if (verbosity <= 1) {
32229edc611SAlan Somers printf(" valid=%#x", in.body.setattr.valid);
3239821f1d3SAlan Somers break;
3249821f1d3SAlan Somers }
32529edc611SAlan Somers if (in.body.setattr.valid & FATTR_MODE)
32629edc611SAlan Somers printf(" mode=%#o", in.body.setattr.mode);
32729edc611SAlan Somers if (in.body.setattr.valid & FATTR_UID)
32829edc611SAlan Somers printf(" uid=%u", in.body.setattr.uid);
32929edc611SAlan Somers if (in.body.setattr.valid & FATTR_GID)
33029edc611SAlan Somers printf(" gid=%u", in.body.setattr.gid);
33129edc611SAlan Somers if (in.body.setattr.valid & FATTR_SIZE)
33229edc611SAlan Somers printf(" size=%" PRIu64, in.body.setattr.size);
33329edc611SAlan Somers if (in.body.setattr.valid & FATTR_ATIME)
334cc04566cSAlan Somers printf(" atime=%" PRIu64 ".%u",
33529edc611SAlan Somers in.body.setattr.atime,
33629edc611SAlan Somers in.body.setattr.atimensec);
33729edc611SAlan Somers if (in.body.setattr.valid & FATTR_MTIME)
338cc04566cSAlan Somers printf(" mtime=%" PRIu64 ".%u",
33929edc611SAlan Somers in.body.setattr.mtime,
34029edc611SAlan Somers in.body.setattr.mtimensec);
34129edc611SAlan Somers if (in.body.setattr.valid & FATTR_FH)
34229edc611SAlan Somers printf(" fh=%" PRIu64 "", in.body.setattr.fh);
3439821f1d3SAlan Somers break;
344f067b609SAlan Somers case FUSE_SETLK:
345cc04566cSAlan Somers printf(" fh=%#" PRIx64 " owner=%" PRIu64
346cc04566cSAlan Somers " type=%u pid=%u",
34729edc611SAlan Somers in.body.setlk.fh, in.body.setlk.owner,
34829edc611SAlan Somers in.body.setlk.lk.type,
34929edc611SAlan Somers in.body.setlk.lk.pid);
350f067b609SAlan Somers if (verbosity >= 2) {
351f6e53195SAlan Somers printf(" range=[%" PRIi64 ":%" PRIi64 "]",
35229edc611SAlan Somers in.body.setlk.lk.start,
35329edc611SAlan Somers in.body.setlk.lk.end);
354f067b609SAlan Somers }
355f067b609SAlan Somers break;
3569821f1d3SAlan Somers case FUSE_SETXATTR:
3579821f1d3SAlan Somers /*
3589821f1d3SAlan Somers * In theory neither the xattr name and value need be
3599821f1d3SAlan Somers * ASCII, but in this test suite they always are.
3609821f1d3SAlan Somers */
36129edc611SAlan Somers name = (const char*)in.body.bytes +
3629821f1d3SAlan Somers sizeof(fuse_setxattr_in);
36319ef317dSAlan Somers value = name + strlen(name) + 1;
36419ef317dSAlan Somers printf(" %s=%s", name, value);
3659821f1d3SAlan Somers break;
3669821f1d3SAlan Somers case FUSE_WRITE:
367cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64
368d4fd0c81SAlan Somers " size=%u write_flags=%u",
36929edc611SAlan Somers in.body.write.fh,
37029edc611SAlan Somers in.body.write.offset, in.body.write.size,
37129edc611SAlan Somers in.body.write.write_flags);
372d4fd0c81SAlan Somers if (verbosity > 1)
373d4fd0c81SAlan Somers printf(" flags=%#x", in.body.write.flags);
3749821f1d3SAlan Somers break;
3759821f1d3SAlan Somers default:
3769821f1d3SAlan Somers break;
3779821f1d3SAlan Somers }
3789821f1d3SAlan Somers printf("\n");
3799821f1d3SAlan Somers }
3809821f1d3SAlan Somers
381c2d70d6eSAlan Somers /*
382c2d70d6eSAlan Somers * Debug a FUSE response.
383c2d70d6eSAlan Somers *
384c2d70d6eSAlan Somers * This is mostly useful for asynchronous notifications, which don't correspond
385c2d70d6eSAlan Somers * to any request
386c2d70d6eSAlan Somers */
debug_response(const mockfs_buf_out & out)387c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
388c2d70d6eSAlan Somers const char *name;
389c2d70d6eSAlan Somers
390c2d70d6eSAlan Somers if (verbosity == 0)
391c2d70d6eSAlan Somers return;
392c2d70d6eSAlan Somers
393c2d70d6eSAlan Somers switch (out.header.error) {
394c2d70d6eSAlan Somers case FUSE_NOTIFY_INVAL_ENTRY:
395c2d70d6eSAlan Somers name = (const char*)out.body.bytes +
396c2d70d6eSAlan Somers sizeof(fuse_notify_inval_entry_out);
397c2d70d6eSAlan Somers printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
398c2d70d6eSAlan Somers out.body.inval_entry.parent, name);
399c2d70d6eSAlan Somers break;
400eae1ae13SAlan Somers case FUSE_NOTIFY_INVAL_INODE:
401eae1ae13SAlan Somers printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
402eae1ae13SAlan Somers " len=%" PRIi64 "\n",
403eae1ae13SAlan Somers out.body.inval_inode.ino,
404eae1ae13SAlan Somers out.body.inval_inode.off,
405eae1ae13SAlan Somers out.body.inval_inode.len);
406eae1ae13SAlan Somers break;
4077cbb8e8aSAlan Somers case FUSE_NOTIFY_STORE:
4087cbb8e8aSAlan Somers printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
4097cbb8e8aSAlan Somers " size=%" PRIu32 "\n",
4107cbb8e8aSAlan Somers out.body.store.nodeid,
4117cbb8e8aSAlan Somers out.body.store.offset,
4127cbb8e8aSAlan Somers out.body.store.size);
4137cbb8e8aSAlan Somers break;
414c2d70d6eSAlan Somers default:
415c2d70d6eSAlan Somers break;
416c2d70d6eSAlan Somers }
417c2d70d6eSAlan Somers }
418c2d70d6eSAlan Somers
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,const char * fsname,const char * subtype)41991ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
42016bd2d47SAlan Somers bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
421402b609cSAlan Somers uint32_t kernel_minor_version, uint32_t max_write, bool async,
422616eaa66SAlan Somers bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
4232f636248SAlan Somers const char *fsname, const char *subtype)
4249821f1d3SAlan Somers {
4258b73a4c5SAlan Somers struct sigaction sa;
4269821f1d3SAlan Somers struct iovec *iov = NULL;
4279821f1d3SAlan Somers int iovlen = 0;
4289821f1d3SAlan Somers char fdstr[15];
42991ff3a0dSAlan Somers const bool trueval = true;
4309821f1d3SAlan Somers
4319821f1d3SAlan Somers m_daemon_id = NULL;
43216bd2d47SAlan Somers m_kernel_minor_version = kernel_minor_version;
4339821f1d3SAlan Somers m_maxreadahead = max_readahead;
434f928dbcbSAlan Somers m_maxwrite = MIN(max_write, max_max_write);
4350a7c63e0SAlan Somers m_nready = -1;
4363429092cSAlan Somers m_pm = pm;
437fef46454SAlan Somers m_time_gran = time_gran;
43881a619c4SAlan Somers m_quit = false;
4395403f2c1SAlan Somers m_last_unique = 0;
4403429092cSAlan Somers if (m_pm == KQ)
4413429092cSAlan Somers m_kq = kqueue();
4423429092cSAlan Somers else
4433429092cSAlan Somers m_kq = -1;
4449821f1d3SAlan Somers
4459821f1d3SAlan Somers /*
4469821f1d3SAlan Somers * Kyua sets pwd to a testcase-unique tempdir; no need to use
4479821f1d3SAlan Somers * mkdtemp
4489821f1d3SAlan Somers */
4499821f1d3SAlan Somers /*
4509821f1d3SAlan Somers * googletest doesn't allow ASSERT_ in constructors, so we must throw
4519821f1d3SAlan Somers * instead.
4529821f1d3SAlan Somers */
45391ff3a0dSAlan Somers if (mkdir("mountpoint" , 0755) && errno != EEXIST)
4549821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(),
4559821f1d3SAlan Somers "Couldn't make mountpoint directory"));
4569821f1d3SAlan Somers
4573429092cSAlan Somers switch (m_pm) {
4583429092cSAlan Somers case BLOCKING:
45991ff3a0dSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
4603429092cSAlan Somers break;
4613429092cSAlan Somers default:
4623429092cSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
4633429092cSAlan Somers break;
4643429092cSAlan Somers }
4659821f1d3SAlan Somers if (m_fuse_fd < 0)
4669821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(),
4679821f1d3SAlan Somers "Couldn't open /dev/fuse"));
4689821f1d3SAlan Somers
4699821f1d3SAlan Somers m_pid = getpid();
47091ff3a0dSAlan Somers m_child_pid = -1;
4719821f1d3SAlan Somers
4729821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4739821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fspath",
4749821f1d3SAlan Somers __DECONST(void *, "mountpoint"), -1);
4759821f1d3SAlan Somers build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4763429092cSAlan Somers sprintf(fdstr, "%d", m_fuse_fd);
4779821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fd", fdstr, -1);
47891ff3a0dSAlan Somers if (allow_other) {
47991ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "allow_other",
4809821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool));
4819821f1d3SAlan Somers }
4829821f1d3SAlan Somers if (default_permissions) {
4839821f1d3SAlan Somers build_iovec(&iov, &iovlen, "default_permissions",
4849821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool));
4859821f1d3SAlan Somers }
48691ff3a0dSAlan Somers if (push_symlinks_in) {
48791ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "push_symlinks_in",
48891ff3a0dSAlan Somers __DECONST(void*, &trueval), sizeof(bool));
48991ff3a0dSAlan Somers }
490140bb492SAlan Somers if (ro) {
491140bb492SAlan Somers build_iovec(&iov, &iovlen, "ro",
492140bb492SAlan Somers __DECONST(void*, &trueval), sizeof(bool));
493140bb492SAlan Somers }
4948eecd9ceSAlan Somers if (async) {
4958eecd9ceSAlan Somers build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4968eecd9ceSAlan Somers sizeof(bool));
4978eecd9ceSAlan Somers }
49891972cfcSAlan Somers if (noatime) {
49991972cfcSAlan Somers build_iovec(&iov, &iovlen, "noatime",
50091972cfcSAlan Somers __DECONST(void*, &trueval), sizeof(bool));
50191972cfcSAlan Somers }
502402b609cSAlan Somers if (noclusterr) {
503402b609cSAlan Somers build_iovec(&iov, &iovlen, "noclusterr",
504402b609cSAlan Somers __DECONST(void*, &trueval), sizeof(bool));
505402b609cSAlan Somers }
506ed74f781SAlan Somers if (nointr) {
507ed74f781SAlan Somers build_iovec(&iov, &iovlen, "nointr",
508ed74f781SAlan Somers __DECONST(void*, &trueval), sizeof(bool));
509ed74f781SAlan Somers } else {
510ed74f781SAlan Somers build_iovec(&iov, &iovlen, "intr",
511ed74f781SAlan Somers __DECONST(void*, &trueval), sizeof(bool));
512ed74f781SAlan Somers }
5132f636248SAlan Somers if (*fsname) {
5142f636248SAlan Somers build_iovec(&iov, &iovlen, "fsname=",
5152f636248SAlan Somers __DECONST(void*, fsname), -1);
5162f636248SAlan Somers }
517616eaa66SAlan Somers if (*subtype) {
518616eaa66SAlan Somers build_iovec(&iov, &iovlen, "subtype=",
519616eaa66SAlan Somers __DECONST(void*, subtype), -1);
520616eaa66SAlan Somers }
5219821f1d3SAlan Somers if (nmount(iov, iovlen, 0))
5229821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(),
5239821f1d3SAlan Somers "Couldn't mount filesystem"));
52439f5d8ddSAlan Somers free_iovec(&iov, &iovlen);
5259821f1d3SAlan Somers
5269821f1d3SAlan Somers // Setup default handler
5279821f1d3SAlan Somers ON_CALL(*this, process(_, _))
5289821f1d3SAlan Somers .WillByDefault(Invoke(this, &MockFS::process_default));
5299821f1d3SAlan Somers
5309821f1d3SAlan Somers init(flags);
5318b73a4c5SAlan Somers bzero(&sa, sizeof(sa));
5328b73a4c5SAlan Somers sa.sa_handler = sigint_handler;
5338b73a4c5SAlan Somers sa.sa_flags = 0; /* Don't set SA_RESTART! */
5348b73a4c5SAlan Somers if (0 != sigaction(SIGUSR1, &sa, NULL))
5358b73a4c5SAlan Somers throw(std::system_error(errno, std::system_category(),
5368b73a4c5SAlan Somers "Couldn't handle SIGUSR1"));
5379821f1d3SAlan Somers if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
5389821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(),
5399821f1d3SAlan Somers "Couldn't Couldn't start fuse thread"));
5409821f1d3SAlan Somers }
5419821f1d3SAlan Somers
~MockFS()5429821f1d3SAlan Somers MockFS::~MockFS() {
5439821f1d3SAlan Somers kill_daemon();
5449821f1d3SAlan Somers if (m_daemon_id != NULL) {
5459821f1d3SAlan Somers pthread_join(m_daemon_id, NULL);
5469821f1d3SAlan Somers m_daemon_id = NULL;
5479821f1d3SAlan Somers }
5488b73a4c5SAlan Somers ::unmount("mountpoint", MNT_FORCE);
5499821f1d3SAlan Somers rmdir("mountpoint");
5503429092cSAlan Somers if (m_kq >= 0)
5513429092cSAlan Somers close(m_kq);
5529821f1d3SAlan Somers }
5539821f1d3SAlan Somers
audit_request(const mockfs_buf_in & in,ssize_t buflen)5546c0c3620SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
555bf507497SAlan Somers uint32_t inlen = in.header.len;
556bf507497SAlan Somers size_t fih = sizeof(in.header);
557bf507497SAlan Somers switch (in.header.opcode) {
558bf507497SAlan Somers case FUSE_LOOKUP:
559bf507497SAlan Somers case FUSE_RMDIR:
560bf507497SAlan Somers case FUSE_SYMLINK:
561bf507497SAlan Somers case FUSE_UNLINK:
5626c0c3620SAlan Somers EXPECT_GT(inlen, fih) << "Missing request filename";
5636c0c3620SAlan Somers // No redundant information for checking buflen
564bf507497SAlan Somers break;
565bf507497SAlan Somers case FUSE_FORGET:
5666c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
5676c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
568bf507497SAlan Somers break;
569bf507497SAlan Somers case FUSE_GETATTR:
5706c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
5716c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
572bf507497SAlan Somers break;
573bf507497SAlan Somers case FUSE_SETATTR:
5746c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
5756c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
576bf507497SAlan Somers break;
577bf507497SAlan Somers case FUSE_READLINK:
5786c0c3620SAlan Somers EXPECT_EQ(inlen, fih) << "Unexpected request body";
5796c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
580bf507497SAlan Somers break;
581bf507497SAlan Somers case FUSE_MKNOD:
582bf507497SAlan Somers {
583bf507497SAlan Somers size_t s;
584bf507497SAlan Somers if (m_kernel_minor_version >= 12)
585bf507497SAlan Somers s = sizeof(in.body.mknod);
586bf507497SAlan Somers else
587bf507497SAlan Somers s = FUSE_COMPAT_MKNOD_IN_SIZE;
5886c0c3620SAlan Somers EXPECT_GE(inlen, fih + s) << "Missing request body";
5896c0c3620SAlan Somers EXPECT_GT(inlen, fih + s) << "Missing request filename";
5906c0c3620SAlan Somers // No redundant information for checking buflen
591bf507497SAlan Somers break;
592bf507497SAlan Somers }
593bf507497SAlan Somers case FUSE_MKDIR:
5946c0c3620SAlan Somers EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
595bf507497SAlan Somers "Missing request body";
5966c0c3620SAlan Somers EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
597bf507497SAlan Somers "Missing request filename";
5986c0c3620SAlan Somers // No redundant information for checking buflen
599bf507497SAlan Somers break;
600bf507497SAlan Somers case FUSE_RENAME:
6016c0c3620SAlan Somers EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
602bf507497SAlan Somers "Missing request body";
6036c0c3620SAlan Somers EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
604bf507497SAlan Somers "Missing request filename";
6056c0c3620SAlan Somers // No redundant information for checking buflen
606bf507497SAlan Somers break;
607bf507497SAlan Somers case FUSE_LINK:
6086c0c3620SAlan Somers EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
609bf507497SAlan Somers "Missing request body";
6106c0c3620SAlan Somers EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
611bf507497SAlan Somers "Missing request filename";
6126c0c3620SAlan Somers // No redundant information for checking buflen
613bf507497SAlan Somers break;
614bf507497SAlan Somers case FUSE_OPEN:
6156c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.open));
6166c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
617bf507497SAlan Somers break;
618bf507497SAlan Somers case FUSE_READ:
6196c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.read));
6206c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
621bf507497SAlan Somers break;
622bf507497SAlan Somers case FUSE_WRITE:
623bf507497SAlan Somers {
624bf507497SAlan Somers size_t s;
625bf507497SAlan Somers
626bf507497SAlan Somers if (m_kernel_minor_version >= 9)
627bf507497SAlan Somers s = sizeof(in.body.write);
628bf507497SAlan Somers else
629bf507497SAlan Somers s = FUSE_COMPAT_WRITE_IN_SIZE;
630bf507497SAlan Somers // I suppose a 0-byte write should be allowed
6316c0c3620SAlan Somers EXPECT_GE(inlen, fih + s) << "Missing request body";
6326c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
633bf507497SAlan Somers break;
634bf507497SAlan Somers }
635bf507497SAlan Somers case FUSE_DESTROY:
636bf507497SAlan Somers case FUSE_STATFS:
6376c0c3620SAlan Somers EXPECT_EQ(inlen, fih);
6386c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
639bf507497SAlan Somers break;
640bf507497SAlan Somers case FUSE_RELEASE:
6416c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.release));
6426c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
643bf507497SAlan Somers break;
644bf507497SAlan Somers case FUSE_FSYNC:
645bf507497SAlan Somers case FUSE_FSYNCDIR:
6466c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
6476c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
648bf507497SAlan Somers break;
649bf507497SAlan Somers case FUSE_SETXATTR:
6506c0c3620SAlan Somers EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
651bf507497SAlan Somers "Missing request body";
6526c0c3620SAlan Somers EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
653bf507497SAlan Somers "Missing request attribute name";
6546c0c3620SAlan Somers // No redundant information for checking buflen
655bf507497SAlan Somers break;
656bf507497SAlan Somers case FUSE_GETXATTR:
6576c0c3620SAlan Somers EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
658bf507497SAlan Somers "Missing request body";
6596c0c3620SAlan Somers EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
660bf507497SAlan Somers "Missing request attribute name";
6616c0c3620SAlan Somers // No redundant information for checking buflen
662bf507497SAlan Somers break;
663bf507497SAlan Somers case FUSE_LISTXATTR:
6646c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
6656c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
666bf507497SAlan Somers break;
667bf507497SAlan Somers case FUSE_REMOVEXATTR:
6686c0c3620SAlan Somers EXPECT_GT(inlen, fih) << "Missing request attribute name";
6696c0c3620SAlan Somers // No redundant information for checking buflen
670bf507497SAlan Somers break;
671bf507497SAlan Somers case FUSE_FLUSH:
6726c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
6736c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
674bf507497SAlan Somers break;
675bf507497SAlan Somers case FUSE_INIT:
6766c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.init));
6776c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
678bf507497SAlan Somers break;
679bf507497SAlan Somers case FUSE_OPENDIR:
6806c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
6816c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
682bf507497SAlan Somers break;
683bf507497SAlan Somers case FUSE_READDIR:
6846c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
6856c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
686bf507497SAlan Somers break;
687bf507497SAlan Somers case FUSE_RELEASEDIR:
6886c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
6896c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
690bf507497SAlan Somers break;
691bf507497SAlan Somers case FUSE_GETLK:
6926c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
6936c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
694bf507497SAlan Somers break;
695bf507497SAlan Somers case FUSE_SETLK:
696bf507497SAlan Somers case FUSE_SETLKW:
6976c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
6986c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
699bf507497SAlan Somers break;
700bf507497SAlan Somers case FUSE_ACCESS:
7016c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.access));
7026c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
703bf507497SAlan Somers break;
704bf507497SAlan Somers case FUSE_CREATE:
7056c0c3620SAlan Somers EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
706bf507497SAlan Somers "Missing request body";
7076c0c3620SAlan Somers EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
708bf507497SAlan Somers "Missing request filename";
7096c0c3620SAlan Somers // No redundant information for checking buflen
710bf507497SAlan Somers break;
711bf507497SAlan Somers case FUSE_INTERRUPT:
7126c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
7136c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
714bf507497SAlan Somers break;
715398c88c7SAlan Somers case FUSE_FALLOCATE:
716398c88c7SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
717398c88c7SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
718398c88c7SAlan Somers break;
719bf507497SAlan Somers case FUSE_BMAP:
7206c0c3620SAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
7216c0c3620SAlan Somers EXPECT_EQ((size_t)buflen, inlen);
722bf507497SAlan Somers break;
72337df9d3bSAlan Somers case FUSE_LSEEK:
72437df9d3bSAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
72537df9d3bSAlan Somers EXPECT_EQ((size_t)buflen, inlen);
72637df9d3bSAlan Somers break;
72792bbfe1fSAlan Somers case FUSE_COPY_FILE_RANGE:
72892bbfe1fSAlan Somers EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
72992bbfe1fSAlan Somers EXPECT_EQ(0ul, in.body.copy_file_range.flags);
73092bbfe1fSAlan Somers EXPECT_EQ((size_t)buflen, inlen);
73192bbfe1fSAlan Somers break;
732bf507497SAlan Somers case FUSE_NOTIFY_REPLY:
733bf507497SAlan Somers case FUSE_BATCH_FORGET:
734bf507497SAlan Somers case FUSE_IOCTL:
735bf507497SAlan Somers case FUSE_POLL:
736bf507497SAlan Somers case FUSE_READDIRPLUS:
737bf507497SAlan Somers FAIL() << "Unsupported opcode?";
738bf507497SAlan Somers default:
739bf507497SAlan Somers FAIL() << "Unknown opcode " << in.header.opcode;
740bf507497SAlan Somers }
7415403f2c1SAlan Somers /*
7425403f2c1SAlan Somers * Check that the ticket's unique value is sequential. Technically it
7435403f2c1SAlan Somers * doesn't need to be sequential, merely unique. But the current
7445403f2c1SAlan Somers * fusefs driver _does_ make it sequential, and that's easy to check
7455403f2c1SAlan Somers * for.
7465403f2c1SAlan Somers */
7475403f2c1SAlan Somers if (in.header.unique != ++m_last_unique)
7485403f2c1SAlan Somers FAIL() << "Non-sequential unique value";
749bf507497SAlan Somers }
750bf507497SAlan Somers
init(uint32_t flags)7519821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
7526c0c3620SAlan Somers ssize_t buflen;
7536c0c3620SAlan Somers
75429edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
75529edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7569821f1d3SAlan Somers
7576c0c3620SAlan Somers read_request(*in, buflen);
75877b040c9SAlan Somers if (verbosity > 0)
75977b040c9SAlan Somers debug_request(*in, buflen);
7606c0c3620SAlan Somers audit_request(*in, buflen);
7619821f1d3SAlan Somers ASSERT_EQ(FUSE_INIT, in->header.opcode);
7629821f1d3SAlan Somers
7639821f1d3SAlan Somers out->header.unique = in->header.unique;
7649821f1d3SAlan Somers out->header.error = 0;
7659821f1d3SAlan Somers out->body.init.major = FUSE_KERNEL_VERSION;
76616bd2d47SAlan Somers out->body.init.minor = m_kernel_minor_version;;
7679821f1d3SAlan Somers out->body.init.flags = in->body.init.flags & flags;
7688eecd9ceSAlan Somers out->body.init.max_write = m_maxwrite;
7699821f1d3SAlan Somers out->body.init.max_readahead = m_maxreadahead;
77087ff949aSAlan Somers
77187ff949aSAlan Somers if (m_kernel_minor_version < 23) {
77287ff949aSAlan Somers SET_OUT_HEADER_LEN(*out, init_7_22);
77387ff949aSAlan Somers } else {
774fef46454SAlan Somers out->body.init.time_gran = m_time_gran;
77529edc611SAlan Somers SET_OUT_HEADER_LEN(*out, init);
77687ff949aSAlan Somers }
77787ff949aSAlan Somers
77829edc611SAlan Somers write(m_fuse_fd, out.get(), out->header.len);
7799821f1d3SAlan Somers }
7809821f1d3SAlan Somers
kill_daemon()7819821f1d3SAlan Somers void MockFS::kill_daemon() {
78281a619c4SAlan Somers m_quit = true;
7838b73a4c5SAlan Somers if (m_daemon_id != NULL)
7849821f1d3SAlan Somers pthread_kill(m_daemon_id, SIGUSR1);
7858b73a4c5SAlan Somers // Closing the /dev/fuse file descriptor first allows unmount to
7868b73a4c5SAlan Somers // succeed even if the daemon doesn't correctly respond to commands
7878b73a4c5SAlan Somers // during the unmount sequence.
7889821f1d3SAlan Somers close(m_fuse_fd);
7898b73a4c5SAlan Somers m_fuse_fd = -1;
7909821f1d3SAlan Somers }
7919821f1d3SAlan Somers
loop()7929821f1d3SAlan Somers void MockFS::loop() {
79329edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> out;
7949821f1d3SAlan Somers
79529edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
7969821f1d3SAlan Somers ASSERT_TRUE(in != NULL);
79781a619c4SAlan Somers while (!m_quit) {
7986c0c3620SAlan Somers ssize_t buflen;
7996c0c3620SAlan Somers
80029edc611SAlan Somers bzero(in.get(), sizeof(*in));
8016c0c3620SAlan Somers read_request(*in, buflen);
80281a619c4SAlan Somers if (m_quit)
8039821f1d3SAlan Somers break;
8049821f1d3SAlan Somers if (verbosity > 0)
8056c0c3620SAlan Somers debug_request(*in, buflen);
8066c0c3620SAlan Somers audit_request(*in, buflen);
8079821f1d3SAlan Somers if (pid_ok((pid_t)in->header.pid)) {
80829edc611SAlan Somers process(*in, out);
8099821f1d3SAlan Somers } else {
8109821f1d3SAlan Somers /*
8119821f1d3SAlan Somers * Reject any requests from unknown processes. Because
8129821f1d3SAlan Somers * we actually do mount a filesystem, plenty of
8139821f1d3SAlan Somers * unrelated system daemons may try to access it.
8149821f1d3SAlan Somers */
81599cf7bffSAlan Somers if (verbosity > 1)
81699cf7bffSAlan Somers printf("\tREJECTED (wrong pid %d)\n",
81799cf7bffSAlan Somers in->header.pid);
81829edc611SAlan Somers process_default(*in, out);
8199821f1d3SAlan Somers }
82029edc611SAlan Somers for (auto &it: out)
82129edc611SAlan Somers write_response(*it);
8229821f1d3SAlan Somers out.clear();
8239821f1d3SAlan Somers }
8249821f1d3SAlan Somers }
8259821f1d3SAlan Somers
notify_inval_entry(ino_t parent,const char * name,size_t namelen)826c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
827c2d70d6eSAlan Somers {
828c2d70d6eSAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
829c2d70d6eSAlan Somers
830c2d70d6eSAlan Somers out->header.unique = 0; /* 0 means asynchronous notification */
831c2d70d6eSAlan Somers out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
832c2d70d6eSAlan Somers out->body.inval_entry.parent = parent;
833c2d70d6eSAlan Somers out->body.inval_entry.namelen = namelen;
834c2d70d6eSAlan Somers strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
835c2d70d6eSAlan Somers name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
836c2d70d6eSAlan Somers out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
837c2d70d6eSAlan Somers namelen;
838c2d70d6eSAlan Somers debug_response(*out);
839c2d70d6eSAlan Somers write_response(*out);
840c2d70d6eSAlan Somers return 0;
841c2d70d6eSAlan Somers }
842c2d70d6eSAlan Somers
notify_inval_inode(ino_t ino,off_t off,ssize_t len)843eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
844eae1ae13SAlan Somers {
845eae1ae13SAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
846eae1ae13SAlan Somers
847eae1ae13SAlan Somers out->header.unique = 0; /* 0 means asynchronous notification */
848eae1ae13SAlan Somers out->header.error = FUSE_NOTIFY_INVAL_INODE;
849eae1ae13SAlan Somers out->body.inval_inode.ino = ino;
850eae1ae13SAlan Somers out->body.inval_inode.off = off;
851eae1ae13SAlan Somers out->body.inval_inode.len = len;
852eae1ae13SAlan Somers out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
853eae1ae13SAlan Somers debug_response(*out);
854eae1ae13SAlan Somers write_response(*out);
855eae1ae13SAlan Somers return 0;
856eae1ae13SAlan Somers }
857eae1ae13SAlan Somers
notify_store(ino_t ino,off_t off,const void * data,ssize_t size)8585a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
8597cbb8e8aSAlan Somers {
8607cbb8e8aSAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
8617cbb8e8aSAlan Somers
8627cbb8e8aSAlan Somers out->header.unique = 0; /* 0 means asynchronous notification */
8637cbb8e8aSAlan Somers out->header.error = FUSE_NOTIFY_STORE;
8647cbb8e8aSAlan Somers out->body.store.nodeid = ino;
8657cbb8e8aSAlan Somers out->body.store.offset = off;
8667cbb8e8aSAlan Somers out->body.store.size = size;
8677cbb8e8aSAlan Somers bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
8687cbb8e8aSAlan Somers out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
8697cbb8e8aSAlan Somers debug_response(*out);
8707cbb8e8aSAlan Somers write_response(*out);
8717cbb8e8aSAlan Somers return 0;
8727cbb8e8aSAlan Somers }
8737cbb8e8aSAlan Somers
pid_ok(pid_t pid)8749821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
8759821f1d3SAlan Somers if (pid == m_pid) {
8769821f1d3SAlan Somers return (true);
87791ff3a0dSAlan Somers } else if (pid == m_child_pid) {
87891ff3a0dSAlan Somers return (true);
8799821f1d3SAlan Somers } else {
8809821f1d3SAlan Somers struct kinfo_proc *ki;
8819821f1d3SAlan Somers bool ok = false;
8829821f1d3SAlan Somers
8839821f1d3SAlan Somers ki = kinfo_getproc(pid);
8849821f1d3SAlan Somers if (ki == NULL)
8859821f1d3SAlan Somers return (false);
8869821f1d3SAlan Somers /*
8879821f1d3SAlan Somers * Allow access by the aio daemon processes so that our tests
8889821f1d3SAlan Somers * can use aio functions
8899821f1d3SAlan Somers */
8909821f1d3SAlan Somers if (0 == strncmp("aiod", ki->ki_comm, 4))
8919821f1d3SAlan Somers ok = true;
8929821f1d3SAlan Somers free(ki);
8939821f1d3SAlan Somers return (ok);
8949821f1d3SAlan Somers }
8959821f1d3SAlan Somers }
8969821f1d3SAlan Somers
process_default(const mockfs_buf_in & in,std::vector<std::unique_ptr<mockfs_buf_out>> & out)89729edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
89829edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> &out)
8999821f1d3SAlan Somers {
90029edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
90129edc611SAlan Somers out0->header.unique = in.header.unique;
9029821f1d3SAlan Somers out0->header.error = -EOPNOTSUPP;
9039821f1d3SAlan Somers out0->header.len = sizeof(out0->header);
90429edc611SAlan Somers out.push_back(std::move(out0));
9059821f1d3SAlan Somers }
9069821f1d3SAlan Somers
read_request(mockfs_buf_in & in,ssize_t & res)9076c0c3620SAlan Somers void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
90877fbe694SAlan Somers int nready = 0;
9093429092cSAlan Somers fd_set readfds;
9103429092cSAlan Somers pollfd fds[1];
9113429092cSAlan Somers struct kevent changes[1];
9123429092cSAlan Somers struct kevent events[1];
91377fbe694SAlan Somers struct timespec timeout_ts;
91477fbe694SAlan Somers struct timeval timeout_tv;
91577fbe694SAlan Somers const int timeout_ms = 999;
91677fbe694SAlan Somers int timeout_int, nfds;
917f44a4487SAlan Somers int fuse_fd;
9189821f1d3SAlan Somers
9193429092cSAlan Somers switch (m_pm) {
9203429092cSAlan Somers case BLOCKING:
9213429092cSAlan Somers break;
9223429092cSAlan Somers case KQ:
92377fbe694SAlan Somers timeout_ts.tv_sec = 0;
92477fbe694SAlan Somers timeout_ts.tv_nsec = timeout_ms * 1'000'000;
92577fbe694SAlan Somers while (nready == 0) {
9267b8622faSAlan Somers EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
9277b8622faSAlan Somers EV_ADD | EV_ONESHOT, 0, 0, 0);
92877fbe694SAlan Somers nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
92977fbe694SAlan Somers &timeout_ts);
9303429092cSAlan Somers if (m_quit)
9313429092cSAlan Somers return;
93277fbe694SAlan Somers }
9333429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno);
9343429092cSAlan Somers ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9353429092cSAlan Somers if (events[0].flags & EV_ERROR)
9363429092cSAlan Somers FAIL() << strerror(events[0].data);
9373429092cSAlan Somers else if (events[0].flags & EV_EOF)
9383429092cSAlan Somers FAIL() << strerror(events[0].fflags);
9390a7c63e0SAlan Somers m_nready = events[0].data;
9403429092cSAlan Somers break;
9413429092cSAlan Somers case POLL:
94277fbe694SAlan Somers timeout_int = timeout_ms;
9433429092cSAlan Somers fds[0].fd = m_fuse_fd;
9443429092cSAlan Somers fds[0].events = POLLIN;
94577fbe694SAlan Somers while (nready == 0) {
94677fbe694SAlan Somers nready = poll(fds, 1, timeout_int);
9473429092cSAlan Somers if (m_quit)
9483429092cSAlan Somers return;
94977fbe694SAlan Somers }
9503429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno);
9513429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLIN);
9523429092cSAlan Somers break;
9533429092cSAlan Somers case SELECT:
954f44a4487SAlan Somers fuse_fd = m_fuse_fd;
955f44a4487SAlan Somers if (fuse_fd < 0)
956f44a4487SAlan Somers break;
95777fbe694SAlan Somers timeout_tv.tv_sec = 0;
95877fbe694SAlan Somers timeout_tv.tv_usec = timeout_ms * 1'000;
959f44a4487SAlan Somers nfds = fuse_fd + 1;
96077fbe694SAlan Somers while (nready == 0) {
9613429092cSAlan Somers FD_ZERO(&readfds);
962f44a4487SAlan Somers FD_SET(fuse_fd, &readfds);
96377fbe694SAlan Somers nready = select(nfds, &readfds, NULL, NULL,
96477fbe694SAlan Somers &timeout_tv);
9653429092cSAlan Somers if (m_quit)
9663429092cSAlan Somers return;
96777fbe694SAlan Somers }
9683429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno);
969f44a4487SAlan Somers ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
9703429092cSAlan Somers break;
9713429092cSAlan Somers default:
9723429092cSAlan Somers FAIL() << "not yet implemented";
9733429092cSAlan Somers }
97429edc611SAlan Somers res = read(m_fuse_fd, &in, sizeof(in));
9753429092cSAlan Somers
976b690d120SAlan Somers if (res < 0 && !m_quit) {
977b690d120SAlan Somers m_quit = true;
9788e765737SAlan Somers FAIL() << "read: " << strerror(errno);
979b690d120SAlan Somers }
98029edc611SAlan Somers ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
981bf507497SAlan Somers /*
982bf507497SAlan Somers * Inconsistently, fuse_in_header.len is the size of the entire
983bf507497SAlan Somers * request,including header, even though fuse_out_header.len excludes
984bf507497SAlan Somers * the size of the header.
985bf507497SAlan Somers */
98638a3e0bdSAlan Somers ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
9879821f1d3SAlan Somers }
9889821f1d3SAlan Somers
write_response(const mockfs_buf_out & out)98929edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
9903429092cSAlan Somers fd_set writefds;
9913429092cSAlan Somers pollfd fds[1];
9927b8622faSAlan Somers struct kevent changes[1];
9937b8622faSAlan Somers struct kevent events[1];
9943429092cSAlan Somers int nready, nfds;
9953429092cSAlan Somers ssize_t r;
9963429092cSAlan Somers
9973429092cSAlan Somers switch (m_pm) {
9983429092cSAlan Somers case BLOCKING:
9997b8622faSAlan Somers break;
10007b8622faSAlan Somers case KQ:
10017b8622faSAlan Somers EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
10027b8622faSAlan Somers EV_ADD | EV_ONESHOT, 0, 0, 0);
10037b8622faSAlan Somers nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
10047b8622faSAlan Somers NULL);
10057b8622faSAlan Somers ASSERT_LE(0, nready) << strerror(errno);
10067b8622faSAlan Somers ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
10077b8622faSAlan Somers if (events[0].flags & EV_ERROR)
10087b8622faSAlan Somers FAIL() << strerror(events[0].data);
10097b8622faSAlan Somers else if (events[0].flags & EV_EOF)
10107b8622faSAlan Somers FAIL() << strerror(events[0].fflags);
10117b8622faSAlan Somers m_nready = events[0].data;
10123429092cSAlan Somers break;
10133429092cSAlan Somers case POLL:
10143429092cSAlan Somers fds[0].fd = m_fuse_fd;
10153429092cSAlan Somers fds[0].events = POLLOUT;
10163429092cSAlan Somers nready = poll(fds, 1, INFTIM);
10173429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno);
10183429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?";
10193429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLOUT);
10203429092cSAlan Somers break;
10213429092cSAlan Somers case SELECT:
10223429092cSAlan Somers FD_ZERO(&writefds);
10233429092cSAlan Somers FD_SET(m_fuse_fd, &writefds);
10243429092cSAlan Somers nfds = m_fuse_fd + 1;
10253429092cSAlan Somers nready = select(nfds, NULL, &writefds, NULL, NULL);
10263429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno);
10273429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?";
10283429092cSAlan Somers ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
10293429092cSAlan Somers break;
10303429092cSAlan Somers default:
10313429092cSAlan Somers FAIL() << "not yet implemented";
10323429092cSAlan Somers }
103329edc611SAlan Somers r = write(m_fuse_fd, &out, out.header.len);
10345f51c9c3SAlan Somers if (out.expected_errno) {
1035155ac516SAlan Somers ASSERT_EQ(-1, r);
10365f51c9c3SAlan Somers ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1037155ac516SAlan Somers } else {
10383429092cSAlan Somers ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
10393429092cSAlan Somers }
1040155ac516SAlan Somers }
10413429092cSAlan Somers
service(void * pthr_data)10429821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
10439821f1d3SAlan Somers MockFS *mock_fs = (MockFS*)pthr_data;
10449821f1d3SAlan Somers
10459821f1d3SAlan Somers mock_fs->loop();
10469821f1d3SAlan Somers
10479821f1d3SAlan Somers return (NULL);
10489821f1d3SAlan Somers }
10499821f1d3SAlan Somers
unmount()10509821f1d3SAlan Somers void MockFS::unmount() {
10519821f1d3SAlan Somers ::unmount("mountpoint", 0);
10529821f1d3SAlan Somers }
1053