xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 52f7eb31ae84b0a02cd79c857c3201cb7b65c00f)
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_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)4199f31c474SAlan Somers MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
4209f31c474SAlan Somers 	bool default_permissions,
42116bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
422402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
423616eaa66SAlan Somers 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
4242f636248SAlan Somers 	const char *fsname, const char *subtype)
4253f83f32dSAlan Somers 	: m_daemon_id(NULL),
4263f83f32dSAlan Somers 	  m_kernel_minor_version(kernel_minor_version),
4273f83f32dSAlan Somers 	  m_kq(pm == KQ ? kqueue() : -1),
4289f31c474SAlan Somers 	  m_maxread(max_read),
4293f83f32dSAlan Somers 	  m_maxreadahead(max_readahead),
4303f83f32dSAlan Somers 	  m_pid(getpid()),
4313f83f32dSAlan Somers 	  m_uniques(new std::unordered_set<uint64_t>),
4323f83f32dSAlan Somers 	  m_pm(pm),
4333f83f32dSAlan Somers 	  m_time_gran(time_gran),
4343f83f32dSAlan Somers 	  m_child_pid(-1),
4353f83f32dSAlan Somers 	  m_maxwrite(MIN(max_write, max_max_write)),
4363f83f32dSAlan Somers 	  m_nready(-1),
4373f83f32dSAlan Somers 	  m_quit(false)
4389821f1d3SAlan Somers {
4398b73a4c5SAlan Somers 	struct sigaction sa;
4409821f1d3SAlan Somers 	struct iovec *iov = NULL;
4419821f1d3SAlan Somers 	int iovlen = 0;
4429821f1d3SAlan Somers 	char fdstr[15];
44391ff3a0dSAlan Somers 	const bool trueval = true;
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 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4709821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4719821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4729821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4733429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4749821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
4759f31c474SAlan Somers 	if (m_maxread > 0) {
4769f31c474SAlan Somers 		char val[10];
4779f31c474SAlan Somers 
4789f31c474SAlan Somers 		snprintf(val, sizeof(val), "%d", m_maxread);
4799f31c474SAlan Somers 		build_iovec(&iov, &iovlen, "max_read=", &val, -1);
4809f31c474SAlan Somers 	}
48191ff3a0dSAlan Somers 	if (allow_other) {
48291ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4839821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4849821f1d3SAlan Somers 	}
4859821f1d3SAlan Somers 	if (default_permissions) {
4869821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4879821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4889821f1d3SAlan Somers 	}
48991ff3a0dSAlan Somers 	if (push_symlinks_in) {
49091ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
49191ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
49291ff3a0dSAlan Somers 	}
493140bb492SAlan Somers 	if (ro) {
494140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
495140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
496140bb492SAlan Somers 	}
4978eecd9ceSAlan Somers 	if (async) {
4988eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4998eecd9ceSAlan Somers 			sizeof(bool));
5008eecd9ceSAlan Somers 	}
50191972cfcSAlan Somers 	if (noatime) {
50291972cfcSAlan Somers 		build_iovec(&iov, &iovlen, "noatime",
50391972cfcSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
50491972cfcSAlan Somers 	}
505402b609cSAlan Somers 	if (noclusterr) {
506402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
507402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
508402b609cSAlan Somers 	}
509ed74f781SAlan Somers 	if (nointr) {
510ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "nointr",
511ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
512ed74f781SAlan Somers 	} else {
513ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "intr",
514ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
515ed74f781SAlan Somers 	}
5162f636248SAlan Somers 	if (*fsname) {
5172f636248SAlan Somers 		build_iovec(&iov, &iovlen, "fsname=",
5182f636248SAlan Somers 			__DECONST(void*, fsname), -1);
5192f636248SAlan Somers 	}
520616eaa66SAlan Somers 	if (*subtype) {
521616eaa66SAlan Somers 		build_iovec(&iov, &iovlen, "subtype=",
522616eaa66SAlan Somers 			__DECONST(void*, subtype), -1);
523616eaa66SAlan Somers 	}
5249821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
5259821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5269821f1d3SAlan Somers 			"Couldn't mount filesystem"));
52739f5d8ddSAlan Somers 	free_iovec(&iov, &iovlen);
5289821f1d3SAlan Somers 
5299821f1d3SAlan Somers 	// Setup default handler
5309821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
5319821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
5329821f1d3SAlan Somers 
5339821f1d3SAlan Somers 	init(flags);
5348b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
5358b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
5368b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
5378b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
5388b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5398b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
5409821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
5419821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5429821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
5439821f1d3SAlan Somers }
5449821f1d3SAlan Somers 
~MockFS()5459821f1d3SAlan Somers MockFS::~MockFS() {
5469821f1d3SAlan Somers 	kill_daemon();
5479821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
5489821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
5499821f1d3SAlan Somers 		m_daemon_id = NULL;
5509821f1d3SAlan Somers 	}
5518b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
5529821f1d3SAlan Somers 	rmdir("mountpoint");
5533429092cSAlan Somers 	if (m_kq >= 0)
5543429092cSAlan Somers 		close(m_kq);
5559821f1d3SAlan Somers }
5569821f1d3SAlan Somers 
audit_request(const mockfs_buf_in & in,ssize_t buflen)5576c0c3620SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
558bf507497SAlan Somers 	uint32_t inlen = in.header.len;
559bf507497SAlan Somers 	size_t fih = sizeof(in.header);
560bf507497SAlan Somers 	switch (in.header.opcode) {
561bf507497SAlan Somers 	case FUSE_LOOKUP:
562bf507497SAlan Somers 	case FUSE_RMDIR:
563bf507497SAlan Somers 	case FUSE_SYMLINK:
564bf507497SAlan Somers 	case FUSE_UNLINK:
5656c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request filename";
5666c0c3620SAlan Somers 		// No redundant information for checking buflen
567bf507497SAlan Somers 		break;
568bf507497SAlan Somers 	case FUSE_FORGET:
5696c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
5706c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
571bf507497SAlan Somers 		break;
572bf507497SAlan Somers 	case FUSE_GETATTR:
5736c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
5746c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
575bf507497SAlan Somers 		break;
576bf507497SAlan Somers 	case FUSE_SETATTR:
5776c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
5786c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
579bf507497SAlan Somers 		break;
580bf507497SAlan Somers 	case FUSE_READLINK:
5816c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
5826c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
583bf507497SAlan Somers 		break;
584bf507497SAlan Somers 	case FUSE_MKNOD:
585bf507497SAlan Somers 		{
586bf507497SAlan Somers 			size_t s;
587bf507497SAlan Somers 			if (m_kernel_minor_version >= 12)
588bf507497SAlan Somers 				s = sizeof(in.body.mknod);
589bf507497SAlan Somers 			else
590bf507497SAlan Somers 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
5916c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5926c0c3620SAlan Somers 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
5936c0c3620SAlan Somers 			// No redundant information for checking buflen
594bf507497SAlan Somers 			break;
595bf507497SAlan Somers 		}
596bf507497SAlan Somers 	case FUSE_MKDIR:
5976c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
598bf507497SAlan Somers 			"Missing request body";
5996c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
600bf507497SAlan Somers 			"Missing request filename";
6016c0c3620SAlan Somers 		// No redundant information for checking buflen
602bf507497SAlan Somers 		break;
603bf507497SAlan Somers 	case FUSE_RENAME:
6046c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
605bf507497SAlan Somers 			"Missing request body";
6066c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
607bf507497SAlan Somers 			"Missing request filename";
6086c0c3620SAlan Somers 		// No redundant information for checking buflen
609bf507497SAlan Somers 		break;
610bf507497SAlan Somers 	case FUSE_LINK:
6116c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
612bf507497SAlan Somers 			"Missing request body";
6136c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
614bf507497SAlan Somers 			"Missing request filename";
6156c0c3620SAlan Somers 		// No redundant information for checking buflen
616bf507497SAlan Somers 		break;
617bf507497SAlan Somers 	case FUSE_OPEN:
6186c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
6196c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
620bf507497SAlan Somers 		break;
621bf507497SAlan Somers 	case FUSE_READ:
6226c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
6236c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
624bf507497SAlan Somers 		break;
625bf507497SAlan Somers 	case FUSE_WRITE:
626bf507497SAlan Somers 		{
627bf507497SAlan Somers 			size_t s;
628bf507497SAlan Somers 
629bf507497SAlan Somers 			if (m_kernel_minor_version >= 9)
630bf507497SAlan Somers 				s = sizeof(in.body.write);
631bf507497SAlan Somers 			else
632bf507497SAlan Somers 				s = FUSE_COMPAT_WRITE_IN_SIZE;
633bf507497SAlan Somers 			// I suppose a 0-byte write should be allowed
6346c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
6356c0c3620SAlan Somers 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
636bf507497SAlan Somers 			break;
637bf507497SAlan Somers 		}
638bf507497SAlan Somers 	case FUSE_DESTROY:
639bf507497SAlan Somers 	case FUSE_STATFS:
6406c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih);
6416c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
642bf507497SAlan Somers 		break;
643bf507497SAlan Somers 	case FUSE_RELEASE:
6446c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
6456c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
646bf507497SAlan Somers 		break;
647bf507497SAlan Somers 	case FUSE_FSYNC:
648bf507497SAlan Somers 	case FUSE_FSYNCDIR:
6496c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
6506c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
651bf507497SAlan Somers 		break;
652bf507497SAlan Somers 	case FUSE_SETXATTR:
6536c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
654bf507497SAlan Somers 			"Missing request body";
6556c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
656bf507497SAlan Somers 			"Missing request attribute name";
6576c0c3620SAlan Somers 		// No redundant information for checking buflen
658bf507497SAlan Somers 		break;
659bf507497SAlan Somers 	case FUSE_GETXATTR:
6606c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
661bf507497SAlan Somers 			"Missing request body";
6626c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
663bf507497SAlan Somers 			"Missing request attribute name";
6646c0c3620SAlan Somers 		// No redundant information for checking buflen
665bf507497SAlan Somers 		break;
666bf507497SAlan Somers 	case FUSE_LISTXATTR:
6676c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
6686c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
669bf507497SAlan Somers 		break;
670bf507497SAlan Somers 	case FUSE_REMOVEXATTR:
6716c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
6726c0c3620SAlan Somers 		// No redundant information for checking buflen
673bf507497SAlan Somers 		break;
674bf507497SAlan Somers 	case FUSE_FLUSH:
6756c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
6766c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
677bf507497SAlan Somers 		break;
678bf507497SAlan Somers 	case FUSE_INIT:
6796c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
6806c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
681bf507497SAlan Somers 		break;
682bf507497SAlan Somers 	case FUSE_OPENDIR:
6836c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
6846c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
685bf507497SAlan Somers 		break;
686bf507497SAlan Somers 	case FUSE_READDIR:
6876c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
6886c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
689bf507497SAlan Somers 		break;
690bf507497SAlan Somers 	case FUSE_RELEASEDIR:
6916c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
6926c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
693bf507497SAlan Somers 		break;
694bf507497SAlan Somers 	case FUSE_GETLK:
6956c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
6966c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
697bf507497SAlan Somers 		break;
698bf507497SAlan Somers 	case FUSE_SETLK:
699bf507497SAlan Somers 	case FUSE_SETLKW:
7006c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
7016c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
702bf507497SAlan Somers 		break;
703bf507497SAlan Somers 	case FUSE_ACCESS:
7046c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
7056c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
706bf507497SAlan Somers 		break;
707bf507497SAlan Somers 	case FUSE_CREATE:
7086c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
709bf507497SAlan Somers 			"Missing request body";
7106c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
711bf507497SAlan Somers 			"Missing request filename";
7126c0c3620SAlan Somers 		// No redundant information for checking buflen
713bf507497SAlan Somers 		break;
714bf507497SAlan Somers 	case FUSE_INTERRUPT:
7156c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
7166c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
717bf507497SAlan Somers 		break;
718398c88c7SAlan Somers 	case FUSE_FALLOCATE:
719398c88c7SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
720398c88c7SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
721398c88c7SAlan Somers 		break;
722bf507497SAlan Somers 	case FUSE_BMAP:
7236c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
7246c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
725bf507497SAlan Somers 		break;
72637df9d3bSAlan Somers 	case FUSE_LSEEK:
72737df9d3bSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
72837df9d3bSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
72937df9d3bSAlan Somers 		break;
73092bbfe1fSAlan Somers 	case FUSE_COPY_FILE_RANGE:
73192bbfe1fSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
73292bbfe1fSAlan Somers 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
73392bbfe1fSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
73492bbfe1fSAlan Somers 		break;
735bf507497SAlan Somers 	case FUSE_NOTIFY_REPLY:
736bf507497SAlan Somers 	case FUSE_BATCH_FORGET:
737bf507497SAlan Somers 	case FUSE_IOCTL:
738bf507497SAlan Somers 	case FUSE_POLL:
739bf507497SAlan Somers 	case FUSE_READDIRPLUS:
740bf507497SAlan Somers 		FAIL() << "Unsupported opcode?";
741bf507497SAlan Somers 	default:
742bf507497SAlan Somers 		FAIL() << "Unknown opcode " << in.header.opcode;
743bf507497SAlan Somers 	}
744b1879975SAlan Somers 	/* Verify that the ticket's unique value is actually unique. */
745b1879975SAlan Somers 	if (m_uniques->find(in.header.unique) != m_uniques->end())
746b1879975SAlan Somers 		FAIL() << "Non-unique \"unique\" value";
747b1879975SAlan Somers 	m_uniques->insert(in.header.unique);
748bf507497SAlan Somers }
749bf507497SAlan Somers 
init(uint32_t flags)7509821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
7516c0c3620SAlan Somers 	ssize_t buflen;
7526c0c3620SAlan Somers 
75329edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
75429edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7559821f1d3SAlan Somers 
7566c0c3620SAlan Somers 	read_request(*in, buflen);
75777b040c9SAlan Somers 	if (verbosity > 0)
75877b040c9SAlan Somers 		debug_request(*in, buflen);
7596c0c3620SAlan Somers 	audit_request(*in, buflen);
7609821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
7619821f1d3SAlan Somers 
7629821f1d3SAlan Somers 	out->header.unique = in->header.unique;
7639821f1d3SAlan Somers 	out->header.error = 0;
7649821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
76516bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
7669821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
7678eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
7689821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
76987ff949aSAlan Somers 
77087ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
77187ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
77287ff949aSAlan Somers 	} else {
773fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
77429edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
77587ff949aSAlan Somers 	}
77687ff949aSAlan Somers 
77729edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
7789821f1d3SAlan Somers }
7799821f1d3SAlan Somers 
kill_daemon()7809821f1d3SAlan Somers void MockFS::kill_daemon() {
78181a619c4SAlan Somers 	m_quit = true;
7828b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
7839821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
7848b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
7858b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
7868b73a4c5SAlan Somers 	// during the unmount sequence.
7879821f1d3SAlan Somers 	close(m_fuse_fd);
7888b73a4c5SAlan Somers 	m_fuse_fd = -1;
7899821f1d3SAlan Somers }
7909821f1d3SAlan Somers 
loop()7919821f1d3SAlan Somers void MockFS::loop() {
79229edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
7939821f1d3SAlan Somers 
79429edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
7959821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
79681a619c4SAlan Somers 	while (!m_quit) {
7976c0c3620SAlan Somers 		ssize_t buflen;
7986c0c3620SAlan Somers 
79929edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
8006c0c3620SAlan Somers 		read_request(*in, buflen);
80181a619c4SAlan Somers 		if (m_quit)
8029821f1d3SAlan Somers 			break;
8039821f1d3SAlan Somers 		if (verbosity > 0)
8046c0c3620SAlan Somers 			debug_request(*in, buflen);
8056c0c3620SAlan Somers 		audit_request(*in, buflen);
8069821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
80729edc611SAlan Somers 			process(*in, out);
8089821f1d3SAlan Somers 		} else {
8099821f1d3SAlan Somers 			/*
8109821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
8119821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
8129821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
8139821f1d3SAlan Somers 			 */
81499cf7bffSAlan Somers 			if (verbosity > 1)
81599cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
81699cf7bffSAlan Somers 					in->header.pid);
81729edc611SAlan Somers 			process_default(*in, out);
8189821f1d3SAlan Somers 		}
81929edc611SAlan Somers 		for (auto &it: out)
82029edc611SAlan Somers 			write_response(*it);
8219821f1d3SAlan Somers 		out.clear();
8229821f1d3SAlan Somers 	}
8239821f1d3SAlan Somers }
8249821f1d3SAlan Somers 
notify_inval_entry(ino_t parent,const char * name,size_t namelen)825c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
826c2d70d6eSAlan Somers {
827c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
828c2d70d6eSAlan Somers 
829c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
830c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
831c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
832c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
833c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
834c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
835c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
836c2d70d6eSAlan Somers 		namelen;
837c2d70d6eSAlan Somers 	debug_response(*out);
838c2d70d6eSAlan Somers 	write_response(*out);
839c2d70d6eSAlan Somers 	return 0;
840c2d70d6eSAlan Somers }
841c2d70d6eSAlan Somers 
notify_inval_inode(ino_t ino,off_t off,ssize_t len)842eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
843eae1ae13SAlan Somers {
844eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
845eae1ae13SAlan Somers 
846eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
847eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
848eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
849eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
850eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
851eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
852eae1ae13SAlan Somers 	debug_response(*out);
853eae1ae13SAlan Somers 	write_response(*out);
854eae1ae13SAlan Somers 	return 0;
855eae1ae13SAlan Somers }
856eae1ae13SAlan Somers 
notify_store(ino_t ino,off_t off,const void * data,ssize_t size)8575a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
8587cbb8e8aSAlan Somers {
8597cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
8607cbb8e8aSAlan Somers 
8617cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
8627cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
8637cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
8647cbb8e8aSAlan Somers 	out->body.store.offset = off;
8657cbb8e8aSAlan Somers 	out->body.store.size = size;
8667cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
8677cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
8687cbb8e8aSAlan Somers 	debug_response(*out);
8697cbb8e8aSAlan Somers 	write_response(*out);
8707cbb8e8aSAlan Somers 	return 0;
8717cbb8e8aSAlan Somers }
8727cbb8e8aSAlan Somers 
pid_ok(pid_t pid)8739821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
8749821f1d3SAlan Somers 	if (pid == m_pid) {
8759821f1d3SAlan Somers 		return (true);
87691ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
87791ff3a0dSAlan Somers 		return (true);
8789821f1d3SAlan Somers 	} else {
8799821f1d3SAlan Somers 		struct kinfo_proc *ki;
8809821f1d3SAlan Somers 		bool ok = false;
8819821f1d3SAlan Somers 
8829821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
8839821f1d3SAlan Somers 		if (ki == NULL)
8849821f1d3SAlan Somers 			return (false);
8859821f1d3SAlan Somers 		/*
8869821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
8879821f1d3SAlan Somers 		 * can use aio functions
8889821f1d3SAlan Somers 		 */
8899821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
8909821f1d3SAlan Somers 			ok = true;
8919821f1d3SAlan Somers 		free(ki);
8929821f1d3SAlan Somers 		return (ok);
8939821f1d3SAlan Somers 	}
8949821f1d3SAlan Somers }
8959821f1d3SAlan Somers 
process_default(const mockfs_buf_in & in,std::vector<std::unique_ptr<mockfs_buf_out>> & out)89629edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
89729edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
8989821f1d3SAlan Somers {
89929edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
90029edc611SAlan Somers 	out0->header.unique = in.header.unique;
9019821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
9029821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
90329edc611SAlan Somers 	out.push_back(std::move(out0));
9049821f1d3SAlan Somers }
9059821f1d3SAlan Somers 
read_request(mockfs_buf_in & in,ssize_t & res)9066c0c3620SAlan Somers void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
90777fbe694SAlan Somers 	int nready = 0;
9083429092cSAlan Somers 	fd_set readfds;
9093429092cSAlan Somers 	pollfd fds[1];
9103429092cSAlan Somers 	struct kevent changes[1];
9113429092cSAlan Somers 	struct kevent events[1];
91277fbe694SAlan Somers 	struct timespec timeout_ts;
91377fbe694SAlan Somers 	struct timeval timeout_tv;
91477fbe694SAlan Somers 	const int timeout_ms = 999;
91577fbe694SAlan Somers 	int timeout_int, nfds;
916f44a4487SAlan Somers 	int fuse_fd;
9179821f1d3SAlan Somers 
9183429092cSAlan Somers 	switch (m_pm) {
9193429092cSAlan Somers 	case BLOCKING:
9203429092cSAlan Somers 		break;
9213429092cSAlan Somers 	case KQ:
92277fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
92377fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
92477fbe694SAlan Somers 		while (nready == 0) {
9257b8622faSAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
9267b8622faSAlan Somers 				EV_ADD | EV_ONESHOT, 0, 0, 0);
92777fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
92877fbe694SAlan Somers 				&timeout_ts);
9293429092cSAlan Somers 			if (m_quit)
9303429092cSAlan Somers 				return;
93177fbe694SAlan Somers 		}
9323429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9333429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9343429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
9353429092cSAlan Somers 			FAIL() << strerror(events[0].data);
9363429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
9373429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
9380a7c63e0SAlan Somers 		m_nready = events[0].data;
9393429092cSAlan Somers 		break;
9403429092cSAlan Somers 	case POLL:
94177fbe694SAlan Somers 		timeout_int = timeout_ms;
9423429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9433429092cSAlan Somers 		fds[0].events = POLLIN;
94477fbe694SAlan Somers 		while (nready == 0) {
94577fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
9463429092cSAlan Somers 			if (m_quit)
9473429092cSAlan Somers 				return;
94877fbe694SAlan Somers 		}
9493429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9503429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
9513429092cSAlan Somers 		break;
9523429092cSAlan Somers 	case SELECT:
953f44a4487SAlan Somers 		fuse_fd = m_fuse_fd;
954f44a4487SAlan Somers 		if (fuse_fd < 0)
955f44a4487SAlan Somers 			break;
95677fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
95777fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
958f44a4487SAlan Somers 		nfds = fuse_fd + 1;
95977fbe694SAlan Somers 		while (nready == 0) {
9603429092cSAlan Somers 			FD_ZERO(&readfds);
961f44a4487SAlan Somers 			FD_SET(fuse_fd, &readfds);
96277fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
96377fbe694SAlan Somers 				&timeout_tv);
9643429092cSAlan Somers 			if (m_quit)
9653429092cSAlan Somers 				return;
96677fbe694SAlan Somers 		}
9673429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
968f44a4487SAlan Somers 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
9693429092cSAlan Somers 		break;
9703429092cSAlan Somers 	default:
9713429092cSAlan Somers 		FAIL() << "not yet implemented";
9723429092cSAlan Somers 	}
97329edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
9743429092cSAlan Somers 
975b690d120SAlan Somers 	if (res < 0 && !m_quit) {
976b690d120SAlan Somers 		m_quit = true;
9778e765737SAlan Somers 		FAIL() << "read: " << strerror(errno);
978b690d120SAlan Somers 	}
97929edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
980bf507497SAlan Somers 	/*
981bf507497SAlan Somers 	 * Inconsistently, fuse_in_header.len is the size of the entire
982bf507497SAlan Somers 	 * request,including header, even though fuse_out_header.len excludes
983bf507497SAlan Somers 	 * the size of the header.
984bf507497SAlan Somers 	 */
98538a3e0bdSAlan Somers 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
9869821f1d3SAlan Somers }
9879821f1d3SAlan Somers 
write_response(const mockfs_buf_out & out)98829edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
9893429092cSAlan Somers 	fd_set writefds;
9903429092cSAlan Somers 	pollfd fds[1];
9917b8622faSAlan Somers 	struct kevent changes[1];
9927b8622faSAlan Somers 	struct kevent events[1];
9933429092cSAlan Somers 	int nready, nfds;
9943429092cSAlan Somers 	ssize_t r;
9953429092cSAlan Somers 
9963429092cSAlan Somers 	switch (m_pm) {
9973429092cSAlan Somers 	case BLOCKING:
9987b8622faSAlan Somers 		break;
9997b8622faSAlan Somers 	case KQ:
10007b8622faSAlan Somers 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
10017b8622faSAlan Somers 			EV_ADD | EV_ONESHOT, 0, 0, 0);
10027b8622faSAlan Somers 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
10037b8622faSAlan Somers 			NULL);
10047b8622faSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10057b8622faSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
10067b8622faSAlan Somers 		if (events[0].flags & EV_ERROR)
10077b8622faSAlan Somers 			FAIL() << strerror(events[0].data);
10087b8622faSAlan Somers 		else if (events[0].flags & EV_EOF)
10097b8622faSAlan Somers 			FAIL() << strerror(events[0].fflags);
10107b8622faSAlan Somers 		m_nready = events[0].data;
10113429092cSAlan Somers 		break;
10123429092cSAlan Somers 	case POLL:
10133429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
10143429092cSAlan Somers 		fds[0].events = POLLOUT;
10153429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
10163429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10173429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
10183429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
10193429092cSAlan Somers 		break;
10203429092cSAlan Somers 	case SELECT:
10213429092cSAlan Somers 		FD_ZERO(&writefds);
10223429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
10233429092cSAlan Somers 		nfds = m_fuse_fd + 1;
10243429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
10253429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10263429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
10273429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
10283429092cSAlan Somers 		break;
10293429092cSAlan Somers 	default:
10303429092cSAlan Somers 		FAIL() << "not yet implemented";
10313429092cSAlan Somers 	}
103229edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
10335f51c9c3SAlan Somers 	if (out.expected_errno) {
1034155ac516SAlan Somers 		ASSERT_EQ(-1, r);
10355f51c9c3SAlan Somers 		ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1036155ac516SAlan Somers 	} else {
1037564c732bSAlan Somers 		if (r <= 0 && errno == EINVAL) {
1038*52f7eb31SAlan Somers 			printf("Failed to write response.  unique=%" PRIu64
1039*52f7eb31SAlan Somers 			    ":\n", out.header.unique);
1040564c732bSAlan Somers 		}
10413429092cSAlan Somers 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
10423429092cSAlan Somers 	}
1043155ac516SAlan Somers }
10443429092cSAlan Somers 
service(void * pthr_data)10459821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
10469821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
10479821f1d3SAlan Somers 
10489821f1d3SAlan Somers 	mock_fs->loop();
10499821f1d3SAlan Somers 
10509821f1d3SAlan Somers 	return (NULL);
10519821f1d3SAlan Somers }
10529821f1d3SAlan Somers 
unmount()10539821f1d3SAlan Somers void MockFS::unmount() {
10549821f1d3SAlan Somers 	::unmount("mountpoint", 0);
10559821f1d3SAlan Somers }
1056