xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 5f51c9c328a63d7290f634b1742f03545eaa2c1c)
19821f1d3SAlan Somers /*-
29821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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.
291fa8ebfbSAlan Somers  *
301fa8ebfbSAlan Somers  * $FreeBSD$
319821f1d3SAlan Somers  */
329821f1d3SAlan Somers 
339821f1d3SAlan Somers extern "C" {
349821f1d3SAlan Somers #include <sys/param.h>
359821f1d3SAlan Somers 
369821f1d3SAlan Somers #include <sys/mount.h>
373429092cSAlan Somers #include <sys/select.h>
389821f1d3SAlan Somers #include <sys/stat.h>
399821f1d3SAlan Somers #include <sys/uio.h>
409821f1d3SAlan Somers #include <sys/user.h>
419821f1d3SAlan Somers 
429821f1d3SAlan Somers #include <fcntl.h>
439821f1d3SAlan Somers #include <libutil.h>
443429092cSAlan Somers #include <poll.h>
459821f1d3SAlan Somers #include <pthread.h>
469821f1d3SAlan Somers #include <signal.h>
479821f1d3SAlan Somers #include <stdlib.h>
489821f1d3SAlan Somers #include <unistd.h>
499821f1d3SAlan Somers 
509821f1d3SAlan Somers #include "mntopts.h"	// for build_iovec
519821f1d3SAlan Somers }
529821f1d3SAlan Somers 
53cc04566cSAlan Somers #include <cinttypes>
54cc04566cSAlan Somers 
559821f1d3SAlan Somers #include <gtest/gtest.h>
569821f1d3SAlan Somers 
579821f1d3SAlan Somers #include "mockfs.hh"
589821f1d3SAlan Somers 
599821f1d3SAlan Somers using namespace testing;
609821f1d3SAlan Somers 
619821f1d3SAlan Somers int verbosity = 0;
629821f1d3SAlan Somers 
639821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode)
649821f1d3SAlan Somers {
6537df9d3bSAlan Somers 	const char* table[] = {
669821f1d3SAlan Somers 		"Unknown (opcode 0)",
679821f1d3SAlan Somers 		"LOOKUP",
689821f1d3SAlan Somers 		"FORGET",
699821f1d3SAlan Somers 		"GETATTR",
709821f1d3SAlan Somers 		"SETATTR",
719821f1d3SAlan Somers 		"READLINK",
729821f1d3SAlan Somers 		"SYMLINK",
739821f1d3SAlan Somers 		"Unknown (opcode 7)",
749821f1d3SAlan Somers 		"MKNOD",
759821f1d3SAlan Somers 		"MKDIR",
769821f1d3SAlan Somers 		"UNLINK",
779821f1d3SAlan Somers 		"RMDIR",
789821f1d3SAlan Somers 		"RENAME",
799821f1d3SAlan Somers 		"LINK",
809821f1d3SAlan Somers 		"OPEN",
819821f1d3SAlan Somers 		"READ",
829821f1d3SAlan Somers 		"WRITE",
839821f1d3SAlan Somers 		"STATFS",
849821f1d3SAlan Somers 		"RELEASE",
859821f1d3SAlan Somers 		"Unknown (opcode 19)",
869821f1d3SAlan Somers 		"FSYNC",
879821f1d3SAlan Somers 		"SETXATTR",
889821f1d3SAlan Somers 		"GETXATTR",
899821f1d3SAlan Somers 		"LISTXATTR",
909821f1d3SAlan Somers 		"REMOVEXATTR",
919821f1d3SAlan Somers 		"FLUSH",
929821f1d3SAlan Somers 		"INIT",
939821f1d3SAlan Somers 		"OPENDIR",
949821f1d3SAlan Somers 		"READDIR",
959821f1d3SAlan Somers 		"RELEASEDIR",
969821f1d3SAlan Somers 		"FSYNCDIR",
979821f1d3SAlan Somers 		"GETLK",
989821f1d3SAlan Somers 		"SETLK",
999821f1d3SAlan Somers 		"SETLKW",
1009821f1d3SAlan Somers 		"ACCESS",
1019821f1d3SAlan Somers 		"CREATE",
1029821f1d3SAlan Somers 		"INTERRUPT",
1039821f1d3SAlan Somers 		"BMAP",
10437df9d3bSAlan Somers 		"DESTROY",
10537df9d3bSAlan Somers 		"IOCTL",
10637df9d3bSAlan Somers 		"POLL",
10737df9d3bSAlan Somers 		"NOTIFY_REPLY",
10837df9d3bSAlan Somers 		"BATCH_FORGET",
10937df9d3bSAlan Somers 		"FALLOCATE",
11037df9d3bSAlan Somers 		"READDIRPLUS",
11137df9d3bSAlan Somers 		"RENAME2",
11237df9d3bSAlan Somers 		"LSEEK",
11392bbfe1fSAlan Somers 		"COPY_FILE_RANGE",
1149821f1d3SAlan Somers 	};
11537df9d3bSAlan Somers 	if (opcode >= nitems(table))
1169821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
1179821f1d3SAlan Somers 	else
1189821f1d3SAlan Somers 		return (table[opcode]);
1199821f1d3SAlan Somers }
1209821f1d3SAlan Somers 
1219821f1d3SAlan Somers ProcessMockerT
1229821f1d3SAlan Somers ReturnErrno(int error)
1239821f1d3SAlan Somers {
1249821f1d3SAlan Somers 	return([=](auto in, auto &out) {
12529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
12629edc611SAlan Somers 		out0->header.unique = in.header.unique;
1279821f1d3SAlan Somers 		out0->header.error = -error;
1289821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
12929edc611SAlan Somers 		out.push_back(std::move(out0));
1309821f1d3SAlan Somers 	});
1319821f1d3SAlan Somers }
1329821f1d3SAlan Somers 
1339821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1349821f1d3SAlan Somers ProcessMockerT
1359821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1369821f1d3SAlan Somers {
1379821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1389821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
13929edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
1409821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
14129edc611SAlan Somers 		out0->header.unique = in.header.unique;
1429821f1d3SAlan Somers 		out0->header.error = 0;
1439821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
1449821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
14529edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
14629edc611SAlan Somers 		out.push_back(std::move(out0));
1479821f1d3SAlan Somers 	});
1489821f1d3SAlan Somers }
1499821f1d3SAlan Somers 
1509821f1d3SAlan Somers ProcessMockerT
15129edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in,
15229edc611SAlan Somers 				   struct mockfs_buf_out &out)> f)
1539821f1d3SAlan Somers {
15429edc611SAlan Somers 	return([=](auto& in, auto &out) {
15529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
15629edc611SAlan Somers 		out0->header.unique = in.header.unique;
15729edc611SAlan Somers 		f(in, *out0);
15829edc611SAlan Somers 		out.push_back(std::move(out0));
1599821f1d3SAlan Somers 	});
1609821f1d3SAlan Somers }
1619821f1d3SAlan Somers 
1629821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1638b73a4c5SAlan Somers 	// Don't do anything except interrupt the daemon's read(2) call
1649821f1d3SAlan Somers }
1659821f1d3SAlan Somers 
1666c0c3620SAlan Somers void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
1679821f1d3SAlan Somers {
16829edc611SAlan Somers 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
16929edc611SAlan Somers 		in.header.nodeid);
1709821f1d3SAlan Somers 	if (verbosity > 1) {
1716c0c3620SAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
1726c0c3620SAlan Somers 			" buflen=%zd",
17329edc611SAlan Somers 			in.header.uid, in.header.gid, in.header.pid,
1746c0c3620SAlan Somers 			in.header.unique, in.header.len, buflen);
1759821f1d3SAlan Somers 	}
17629edc611SAlan Somers 	switch (in.header.opcode) {
17719ef317dSAlan Somers 		const char *name, *value;
17819ef317dSAlan Somers 
179caf5f57dSAlan Somers 		case FUSE_ACCESS:
18029edc611SAlan Somers 			printf(" mask=%#x", in.body.access.mask);
181caf5f57dSAlan Somers 			break;
182a1c9f4adSAlan Somers 		case FUSE_BMAP:
18397b0512bSAlan Somers 			printf(" block=%" PRIx64 " blocksize=%#x",
18497b0512bSAlan Somers 				in.body.bmap.block, in.body.bmap.blocksize);
185a1c9f4adSAlan Somers 			break;
18692bbfe1fSAlan Somers 		case FUSE_COPY_FILE_RANGE:
18792bbfe1fSAlan Somers 			printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
18892bbfe1fSAlan Somers 			       " off_out=%" PRIu64 " size=%" PRIu64,
18992bbfe1fSAlan Somers 			       in.body.copy_file_range.off_in,
19092bbfe1fSAlan Somers 			       in.body.copy_file_range.nodeid_out,
19192bbfe1fSAlan Somers 			       in.body.copy_file_range.off_out,
19292bbfe1fSAlan Somers 			       in.body.copy_file_range.len);
19392bbfe1fSAlan Somers 			if (verbosity > 1)
19492bbfe1fSAlan Somers 				printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
19592bbfe1fSAlan Somers 				       " flags=%" PRIx64,
19692bbfe1fSAlan Somers 				       in.body.copy_file_range.fh_in,
19792bbfe1fSAlan Somers 				       in.body.copy_file_range.fh_out,
19892bbfe1fSAlan Somers 				       in.body.copy_file_range.flags);
19992bbfe1fSAlan Somers 			break;
20019ef317dSAlan Somers 		case FUSE_CREATE:
201a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
202a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
203a4856c96SAlan Somers 					sizeof(fuse_create_in);
204a4856c96SAlan Somers 			else
20529edc611SAlan Somers 				name = (const char*)in.body.bytes +
20619ef317dSAlan Somers 					sizeof(fuse_open_in);
20719ef317dSAlan Somers 			printf(" flags=%#x name=%s",
20829edc611SAlan Somers 				in.body.open.flags, name);
20919ef317dSAlan Somers 			break;
210398c88c7SAlan Somers 		case FUSE_FALLOCATE:
211398c88c7SAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
212398c88c7SAlan Somers 				" length=%" PRIx64 " mode=%#x",
213398c88c7SAlan Somers 				in.body.fallocate.fh,
214398c88c7SAlan Somers 				in.body.fallocate.offset,
215398c88c7SAlan Somers 				in.body.fallocate.length,
216398c88c7SAlan Somers 				in.body.fallocate.mode);
217398c88c7SAlan Somers 			break;
2189821f1d3SAlan Somers 		case FUSE_FLUSH:
219cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
22029edc611SAlan Somers 				in.body.flush.fh,
22129edc611SAlan Somers 				in.body.flush.lock_owner);
2229821f1d3SAlan Somers 			break;
2239821f1d3SAlan Somers 		case FUSE_FORGET:
22429edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
2259821f1d3SAlan Somers 			break;
2269821f1d3SAlan Somers 		case FUSE_FSYNC:
22729edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
2289821f1d3SAlan Somers 			break;
2299821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
23029edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
2319821f1d3SAlan Somers 			break;
232f6e53195SAlan Somers 		case FUSE_GETLK:
233f6e53195SAlan Somers 			printf(" fh=%#" PRIx64
234f6e53195SAlan Somers 				" type=%u pid=%u",
235f6e53195SAlan Somers 				in.body.getlk.fh,
236f6e53195SAlan Somers 				in.body.getlk.lk.type,
237f6e53195SAlan Somers 				in.body.getlk.lk.pid);
238f6e53195SAlan Somers 			if (verbosity >= 2) {
239f6e53195SAlan Somers 				printf(" range=[%" PRIi64 ":%" PRIi64 "]",
240f6e53195SAlan Somers 					in.body.getlk.lk.start,
241f6e53195SAlan Somers 					in.body.getlk.lk.end);
242f6e53195SAlan Somers 			}
243f6e53195SAlan Somers 			break;
244723c7768SAlan Somers 		case FUSE_INTERRUPT:
24529edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
246723c7768SAlan Somers 			break;
247002e54b0SAlan Somers 		case FUSE_LINK:
24829edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
249002e54b0SAlan Somers 			break;
2505e633330SAlan Somers 		case FUSE_LISTXATTR:
2515e633330SAlan Somers 			printf(" size=%" PRIu32, in.body.listxattr.size);
2525e633330SAlan Somers 			break;
2539821f1d3SAlan Somers 		case FUSE_LOOKUP:
25429edc611SAlan Somers 			printf(" %s", in.body.lookup);
2559821f1d3SAlan Somers 			break;
25637df9d3bSAlan Somers 		case FUSE_LSEEK:
25737df9d3bSAlan Somers 			switch (in.body.lseek.whence) {
25837df9d3bSAlan Somers 			case SEEK_HOLE:
2591d010cd3SCy Schubert 				printf(" SEEK_HOLE offset=%jd",
26037df9d3bSAlan Somers 				    in.body.lseek.offset);
26137df9d3bSAlan Somers 				break;
26237df9d3bSAlan Somers 			case SEEK_DATA:
2631d010cd3SCy Schubert 				printf(" SEEK_DATA offset=%jd",
26437df9d3bSAlan Somers 				    in.body.lseek.offset);
26537df9d3bSAlan Somers 				break;
26637df9d3bSAlan Somers 			default:
2671d010cd3SCy Schubert 				printf(" whence=%u offset=%jd",
26837df9d3bSAlan Somers 				    in.body.lseek.whence, in.body.lseek.offset);
26937df9d3bSAlan Somers 				break;
27037df9d3bSAlan Somers 			}
27137df9d3bSAlan Somers 			break;
27299cf7bffSAlan Somers 		case FUSE_MKDIR:
27329edc611SAlan Somers 			name = (const char*)in.body.bytes +
27499cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
275a4856c96SAlan Somers 			printf(" name=%s mode=%#o umask=%#o", name,
276a4856c96SAlan Somers 				in.body.mkdir.mode, in.body.mkdir.umask);
27799cf7bffSAlan Somers 			break;
278bf4d7084SAlan Somers 		case FUSE_MKNOD:
279a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
280a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
281a4856c96SAlan Somers 					sizeof(fuse_mknod_in);
282a4856c96SAlan Somers 			else
283a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
284a4856c96SAlan Somers 					FUSE_COMPAT_MKNOD_IN_SIZE;
285a4856c96SAlan Somers 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
286a4856c96SAlan Somers 				in.body.mknod.mode, in.body.mknod.rdev,
287a4856c96SAlan Somers 				in.body.mknod.umask, name);
288bf4d7084SAlan Somers 			break;
2899821f1d3SAlan Somers 		case FUSE_OPEN:
290a4856c96SAlan Somers 			printf(" flags=%#x", in.body.open.flags);
2919821f1d3SAlan Somers 			break;
2929821f1d3SAlan Somers 		case FUSE_OPENDIR:
293a4856c96SAlan Somers 			printf(" flags=%#x", in.body.opendir.flags);
2949821f1d3SAlan Somers 			break;
2959821f1d3SAlan Somers 		case FUSE_READ:
296cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
29729edc611SAlan Somers 				in.body.read.offset,
29829edc611SAlan Somers 				in.body.read.size);
299d4fd0c81SAlan Somers 			if (verbosity > 1)
300d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
3019821f1d3SAlan Somers 			break;
3029821f1d3SAlan Somers 		case FUSE_READDIR:
303cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
30429edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
30529edc611SAlan Somers 				in.body.readdir.size);
3069821f1d3SAlan Somers 			break;
3079821f1d3SAlan Somers 		case FUSE_RELEASE:
308cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
30929edc611SAlan Somers 				in.body.release.fh,
31029edc611SAlan Somers 				in.body.release.flags,
31129edc611SAlan Somers 				in.body.release.lock_owner);
3129821f1d3SAlan Somers 			break;
313c2d342c5SAlan Somers 		case FUSE_RENAME:
314c2d342c5SAlan Somers 			{
315c2d342c5SAlan Somers 				const char *src = (const char*)in.body.bytes +
316c2d342c5SAlan Somers 					sizeof(fuse_rename_in);
317c2d342c5SAlan Somers 				const char *dst = src + strlen(src) + 1;
318c2d342c5SAlan Somers 				printf(" src=%s newdir=%" PRIu64 " dst=%s",
319c2d342c5SAlan Somers 					src, in.body.rename.newdir, dst);
320c2d342c5SAlan Somers 			}
321c2d342c5SAlan Somers 			break;
3229821f1d3SAlan Somers 		case FUSE_SETATTR:
3239821f1d3SAlan Somers 			if (verbosity <= 1) {
32429edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
3259821f1d3SAlan Somers 				break;
3269821f1d3SAlan Somers 			}
32729edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
32829edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
32929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
33029edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
33129edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
33229edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
33329edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
33429edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
33529edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
336cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
33729edc611SAlan Somers 					in.body.setattr.atime,
33829edc611SAlan Somers 					in.body.setattr.atimensec);
33929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
340cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
34129edc611SAlan Somers 					in.body.setattr.mtime,
34229edc611SAlan Somers 					in.body.setattr.mtimensec);
34329edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
34429edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
3459821f1d3SAlan Somers 			break;
346f067b609SAlan Somers 		case FUSE_SETLK:
347cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
348cc04566cSAlan Somers 				" type=%u pid=%u",
34929edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
35029edc611SAlan Somers 				in.body.setlk.lk.type,
35129edc611SAlan Somers 				in.body.setlk.lk.pid);
352f067b609SAlan Somers 			if (verbosity >= 2) {
353f6e53195SAlan Somers 				printf(" range=[%" PRIi64 ":%" PRIi64 "]",
35429edc611SAlan Somers 					in.body.setlk.lk.start,
35529edc611SAlan Somers 					in.body.setlk.lk.end);
356f067b609SAlan Somers 			}
357f067b609SAlan Somers 			break;
3589821f1d3SAlan Somers 		case FUSE_SETXATTR:
3599821f1d3SAlan Somers 			/*
3609821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
3619821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
3629821f1d3SAlan Somers 			 */
36329edc611SAlan Somers 			name = (const char*)in.body.bytes +
3649821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
36519ef317dSAlan Somers 			value = name + strlen(name) + 1;
36619ef317dSAlan Somers 			printf(" %s=%s", name, value);
3679821f1d3SAlan Somers 			break;
3689821f1d3SAlan Somers 		case FUSE_WRITE:
369cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
370d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
37129edc611SAlan Somers 				in.body.write.fh,
37229edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
37329edc611SAlan Somers 				in.body.write.write_flags);
374d4fd0c81SAlan Somers 			if (verbosity > 1)
375d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
3769821f1d3SAlan Somers 			break;
3779821f1d3SAlan Somers 		default:
3789821f1d3SAlan Somers 			break;
3799821f1d3SAlan Somers 	}
3809821f1d3SAlan Somers 	printf("\n");
3819821f1d3SAlan Somers }
3829821f1d3SAlan Somers 
383c2d70d6eSAlan Somers /*
384c2d70d6eSAlan Somers  * Debug a FUSE response.
385c2d70d6eSAlan Somers  *
386c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
387c2d70d6eSAlan Somers  * to any request
388c2d70d6eSAlan Somers  */
389c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
390c2d70d6eSAlan Somers 	const char *name;
391c2d70d6eSAlan Somers 
392c2d70d6eSAlan Somers 	if (verbosity == 0)
393c2d70d6eSAlan Somers 		return;
394c2d70d6eSAlan Somers 
395c2d70d6eSAlan Somers 	switch (out.header.error) {
396c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
397c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
398c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
399c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
400c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
401c2d70d6eSAlan Somers 			break;
402eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
403eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
404eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
405eae1ae13SAlan Somers 				out.body.inval_inode.ino,
406eae1ae13SAlan Somers 				out.body.inval_inode.off,
407eae1ae13SAlan Somers 				out.body.inval_inode.len);
408eae1ae13SAlan Somers 			break;
4097cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
4107cbb8e8aSAlan Somers 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
4117cbb8e8aSAlan Somers 				" size=%" PRIu32 "\n",
4127cbb8e8aSAlan Somers 				out.body.store.nodeid,
4137cbb8e8aSAlan Somers 				out.body.store.offset,
4147cbb8e8aSAlan Somers 				out.body.store.size);
4157cbb8e8aSAlan Somers 			break;
416c2d70d6eSAlan Somers 		default:
417c2d70d6eSAlan Somers 			break;
418c2d70d6eSAlan Somers 	}
419c2d70d6eSAlan Somers }
420c2d70d6eSAlan Somers 
42191ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
42216bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
423402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
424616eaa66SAlan Somers 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
4252f636248SAlan Somers 	const char *fsname, const char *subtype)
4269821f1d3SAlan Somers {
4278b73a4c5SAlan Somers 	struct sigaction sa;
4289821f1d3SAlan Somers 	struct iovec *iov = NULL;
4299821f1d3SAlan Somers 	int iovlen = 0;
4309821f1d3SAlan Somers 	char fdstr[15];
43191ff3a0dSAlan Somers 	const bool trueval = true;
4329821f1d3SAlan Somers 
4339821f1d3SAlan Somers 	m_daemon_id = NULL;
43416bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
4359821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
436f928dbcbSAlan Somers 	m_maxwrite = MIN(max_write, max_max_write);
4370a7c63e0SAlan Somers 	m_nready = -1;
4383429092cSAlan Somers 	m_pm = pm;
439fef46454SAlan Somers 	m_time_gran = time_gran;
44081a619c4SAlan Somers 	m_quit = false;
4415403f2c1SAlan Somers 	m_last_unique = 0;
4423429092cSAlan Somers 	if (m_pm == KQ)
4433429092cSAlan Somers 		m_kq = kqueue();
4443429092cSAlan Somers 	else
4453429092cSAlan Somers 		m_kq = -1;
4469821f1d3SAlan Somers 
4479821f1d3SAlan Somers 	/*
4489821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
4499821f1d3SAlan Somers 	 * mkdtemp
4509821f1d3SAlan Somers 	 */
4519821f1d3SAlan Somers 	/*
4529821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
4539821f1d3SAlan Somers 	 * instead.
4549821f1d3SAlan Somers 	 */
45591ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
4569821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4579821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
4589821f1d3SAlan Somers 
4593429092cSAlan Somers 	switch (m_pm) {
4603429092cSAlan Somers 	case BLOCKING:
46191ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
4623429092cSAlan Somers 		break;
4633429092cSAlan Somers 	default:
4643429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
4653429092cSAlan Somers 		break;
4663429092cSAlan Somers 	}
4679821f1d3SAlan Somers 	if (m_fuse_fd < 0)
4689821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4699821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
4709821f1d3SAlan Somers 
4719821f1d3SAlan Somers 	m_pid = getpid();
47291ff3a0dSAlan Somers 	m_child_pid = -1;
4739821f1d3SAlan Somers 
4749821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4759821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4769821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4779821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4783429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4799821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
48091ff3a0dSAlan Somers 	if (allow_other) {
48191ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4829821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4839821f1d3SAlan Somers 	}
4849821f1d3SAlan Somers 	if (default_permissions) {
4859821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4869821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4879821f1d3SAlan Somers 	}
48891ff3a0dSAlan Somers 	if (push_symlinks_in) {
48991ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
49091ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
49191ff3a0dSAlan Somers 	}
492140bb492SAlan Somers 	if (ro) {
493140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
494140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
495140bb492SAlan Somers 	}
4968eecd9ceSAlan Somers 	if (async) {
4978eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4988eecd9ceSAlan Somers 			sizeof(bool));
4998eecd9ceSAlan Somers 	}
50091972cfcSAlan Somers 	if (noatime) {
50191972cfcSAlan Somers 		build_iovec(&iov, &iovlen, "noatime",
50291972cfcSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
50391972cfcSAlan Somers 	}
504402b609cSAlan Somers 	if (noclusterr) {
505402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
506402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
507402b609cSAlan Somers 	}
508ed74f781SAlan Somers 	if (nointr) {
509ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "nointr",
510ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
511ed74f781SAlan Somers 	} else {
512ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "intr",
513ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
514ed74f781SAlan Somers 	}
5152f636248SAlan Somers 	if (*fsname) {
5162f636248SAlan Somers 		build_iovec(&iov, &iovlen, "fsname=",
5172f636248SAlan Somers 			__DECONST(void*, fsname), -1);
5182f636248SAlan Somers 	}
519616eaa66SAlan Somers 	if (*subtype) {
520616eaa66SAlan Somers 		build_iovec(&iov, &iovlen, "subtype=",
521616eaa66SAlan Somers 			__DECONST(void*, subtype), -1);
522616eaa66SAlan Somers 	}
5239821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
5249821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5259821f1d3SAlan Somers 			"Couldn't mount filesystem"));
5269821f1d3SAlan Somers 
5279821f1d3SAlan Somers 	// Setup default handler
5289821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
5299821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
5309821f1d3SAlan Somers 
5319821f1d3SAlan Somers 	init(flags);
5328b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
5338b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
5348b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
5358b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
5368b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5378b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
5389821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
5399821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5409821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
5419821f1d3SAlan Somers }
5429821f1d3SAlan Somers 
5439821f1d3SAlan Somers MockFS::~MockFS() {
5449821f1d3SAlan Somers 	kill_daemon();
5459821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
5469821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
5479821f1d3SAlan Somers 		m_daemon_id = NULL;
5489821f1d3SAlan Somers 	}
5498b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
5509821f1d3SAlan Somers 	rmdir("mountpoint");
5513429092cSAlan Somers 	if (m_kq >= 0)
5523429092cSAlan Somers 		close(m_kq);
5539821f1d3SAlan Somers }
5549821f1d3SAlan Somers 
5556c0c3620SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
556bf507497SAlan Somers 	uint32_t inlen = in.header.len;
557bf507497SAlan Somers 	size_t fih = sizeof(in.header);
558bf507497SAlan Somers 	switch (in.header.opcode) {
559bf507497SAlan Somers 	case FUSE_LOOKUP:
560bf507497SAlan Somers 	case FUSE_RMDIR:
561bf507497SAlan Somers 	case FUSE_SYMLINK:
562bf507497SAlan Somers 	case FUSE_UNLINK:
5636c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request filename";
5646c0c3620SAlan Somers 		// No redundant information for checking buflen
565bf507497SAlan Somers 		break;
566bf507497SAlan Somers 	case FUSE_FORGET:
5676c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
5686c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
569bf507497SAlan Somers 		break;
570bf507497SAlan Somers 	case FUSE_GETATTR:
5716c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
5726c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
573bf507497SAlan Somers 		break;
574bf507497SAlan Somers 	case FUSE_SETATTR:
5756c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
5766c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
577bf507497SAlan Somers 		break;
578bf507497SAlan Somers 	case FUSE_READLINK:
5796c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
5806c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
581bf507497SAlan Somers 		break;
582bf507497SAlan Somers 	case FUSE_MKNOD:
583bf507497SAlan Somers 		{
584bf507497SAlan Somers 			size_t s;
585bf507497SAlan Somers 			if (m_kernel_minor_version >= 12)
586bf507497SAlan Somers 				s = sizeof(in.body.mknod);
587bf507497SAlan Somers 			else
588bf507497SAlan Somers 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
5896c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5906c0c3620SAlan Somers 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
5916c0c3620SAlan Somers 			// No redundant information for checking buflen
592bf507497SAlan Somers 			break;
593bf507497SAlan Somers 		}
594bf507497SAlan Somers 	case FUSE_MKDIR:
5956c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
596bf507497SAlan Somers 			"Missing request body";
5976c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
598bf507497SAlan Somers 			"Missing request filename";
5996c0c3620SAlan Somers 		// No redundant information for checking buflen
600bf507497SAlan Somers 		break;
601bf507497SAlan Somers 	case FUSE_RENAME:
6026c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
603bf507497SAlan Somers 			"Missing request body";
6046c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
605bf507497SAlan Somers 			"Missing request filename";
6066c0c3620SAlan Somers 		// No redundant information for checking buflen
607bf507497SAlan Somers 		break;
608bf507497SAlan Somers 	case FUSE_LINK:
6096c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
610bf507497SAlan Somers 			"Missing request body";
6116c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
612bf507497SAlan Somers 			"Missing request filename";
6136c0c3620SAlan Somers 		// No redundant information for checking buflen
614bf507497SAlan Somers 		break;
615bf507497SAlan Somers 	case FUSE_OPEN:
6166c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
6176c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
618bf507497SAlan Somers 		break;
619bf507497SAlan Somers 	case FUSE_READ:
6206c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
6216c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
622bf507497SAlan Somers 		break;
623bf507497SAlan Somers 	case FUSE_WRITE:
624bf507497SAlan Somers 		{
625bf507497SAlan Somers 			size_t s;
626bf507497SAlan Somers 
627bf507497SAlan Somers 			if (m_kernel_minor_version >= 9)
628bf507497SAlan Somers 				s = sizeof(in.body.write);
629bf507497SAlan Somers 			else
630bf507497SAlan Somers 				s = FUSE_COMPAT_WRITE_IN_SIZE;
631bf507497SAlan Somers 			// I suppose a 0-byte write should be allowed
6326c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
6336c0c3620SAlan Somers 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
634bf507497SAlan Somers 			break;
635bf507497SAlan Somers 		}
636bf507497SAlan Somers 	case FUSE_DESTROY:
637bf507497SAlan Somers 	case FUSE_STATFS:
6386c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih);
6396c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
640bf507497SAlan Somers 		break;
641bf507497SAlan Somers 	case FUSE_RELEASE:
6426c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
6436c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
644bf507497SAlan Somers 		break;
645bf507497SAlan Somers 	case FUSE_FSYNC:
646bf507497SAlan Somers 	case FUSE_FSYNCDIR:
6476c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
6486c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
649bf507497SAlan Somers 		break;
650bf507497SAlan Somers 	case FUSE_SETXATTR:
6516c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
652bf507497SAlan Somers 			"Missing request body";
6536c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
654bf507497SAlan Somers 			"Missing request attribute name";
6556c0c3620SAlan Somers 		// No redundant information for checking buflen
656bf507497SAlan Somers 		break;
657bf507497SAlan Somers 	case FUSE_GETXATTR:
6586c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
659bf507497SAlan Somers 			"Missing request body";
6606c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
661bf507497SAlan Somers 			"Missing request attribute name";
6626c0c3620SAlan Somers 		// No redundant information for checking buflen
663bf507497SAlan Somers 		break;
664bf507497SAlan Somers 	case FUSE_LISTXATTR:
6656c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
6666c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
667bf507497SAlan Somers 		break;
668bf507497SAlan Somers 	case FUSE_REMOVEXATTR:
6696c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
6706c0c3620SAlan Somers 		// No redundant information for checking buflen
671bf507497SAlan Somers 		break;
672bf507497SAlan Somers 	case FUSE_FLUSH:
6736c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
6746c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
675bf507497SAlan Somers 		break;
676bf507497SAlan Somers 	case FUSE_INIT:
6776c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
6786c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
679bf507497SAlan Somers 		break;
680bf507497SAlan Somers 	case FUSE_OPENDIR:
6816c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
6826c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
683bf507497SAlan Somers 		break;
684bf507497SAlan Somers 	case FUSE_READDIR:
6856c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
6866c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
687bf507497SAlan Somers 		break;
688bf507497SAlan Somers 	case FUSE_RELEASEDIR:
6896c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
6906c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
691bf507497SAlan Somers 		break;
692bf507497SAlan Somers 	case FUSE_GETLK:
6936c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
6946c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
695bf507497SAlan Somers 		break;
696bf507497SAlan Somers 	case FUSE_SETLK:
697bf507497SAlan Somers 	case FUSE_SETLKW:
6986c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
6996c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
700bf507497SAlan Somers 		break;
701bf507497SAlan Somers 	case FUSE_ACCESS:
7026c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
7036c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
704bf507497SAlan Somers 		break;
705bf507497SAlan Somers 	case FUSE_CREATE:
7066c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
707bf507497SAlan Somers 			"Missing request body";
7086c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
709bf507497SAlan Somers 			"Missing request filename";
7106c0c3620SAlan Somers 		// No redundant information for checking buflen
711bf507497SAlan Somers 		break;
712bf507497SAlan Somers 	case FUSE_INTERRUPT:
7136c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
7146c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
715bf507497SAlan Somers 		break;
716398c88c7SAlan Somers 	case FUSE_FALLOCATE:
717398c88c7SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
718398c88c7SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
719398c88c7SAlan Somers 		break;
720bf507497SAlan Somers 	case FUSE_BMAP:
7216c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
7226c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
723bf507497SAlan Somers 		break;
72437df9d3bSAlan Somers 	case FUSE_LSEEK:
72537df9d3bSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
72637df9d3bSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
72737df9d3bSAlan Somers 		break;
72892bbfe1fSAlan Somers 	case FUSE_COPY_FILE_RANGE:
72992bbfe1fSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
73092bbfe1fSAlan Somers 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
73192bbfe1fSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
73292bbfe1fSAlan Somers 		break;
733bf507497SAlan Somers 	case FUSE_NOTIFY_REPLY:
734bf507497SAlan Somers 	case FUSE_BATCH_FORGET:
735bf507497SAlan Somers 	case FUSE_IOCTL:
736bf507497SAlan Somers 	case FUSE_POLL:
737bf507497SAlan Somers 	case FUSE_READDIRPLUS:
738bf507497SAlan Somers 		FAIL() << "Unsupported opcode?";
739bf507497SAlan Somers 	default:
740bf507497SAlan Somers 		FAIL() << "Unknown opcode " << in.header.opcode;
741bf507497SAlan Somers 	}
7425403f2c1SAlan Somers 	/*
7435403f2c1SAlan Somers 	 * Check that the ticket's unique value is sequential.  Technically it
7445403f2c1SAlan Somers 	 * doesn't need to be sequential, merely unique.  But the current
7455403f2c1SAlan Somers 	 * fusefs driver _does_ make it sequential, and that's easy to check
7465403f2c1SAlan Somers 	 * for.
7475403f2c1SAlan Somers 	 */
7485403f2c1SAlan Somers 	if (in.header.unique != ++m_last_unique)
7495403f2c1SAlan Somers 		FAIL() << "Non-sequential unique value";
750bf507497SAlan Somers }
751bf507497SAlan Somers 
7529821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
7536c0c3620SAlan Somers 	ssize_t buflen;
7546c0c3620SAlan Somers 
75529edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
75629edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7579821f1d3SAlan Somers 
7586c0c3620SAlan Somers 	read_request(*in, buflen);
75977b040c9SAlan Somers 	if (verbosity > 0)
76077b040c9SAlan Somers 		debug_request(*in, buflen);
7616c0c3620SAlan Somers 	audit_request(*in, buflen);
7629821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
7639821f1d3SAlan Somers 
7649821f1d3SAlan Somers 	out->header.unique = in->header.unique;
7659821f1d3SAlan Somers 	out->header.error = 0;
7669821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
76716bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
7689821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
7698eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
7709821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
77187ff949aSAlan Somers 
77287ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
77387ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
77487ff949aSAlan Somers 	} else {
775fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
77629edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
77787ff949aSAlan Somers 	}
77887ff949aSAlan Somers 
77929edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
7809821f1d3SAlan Somers }
7819821f1d3SAlan Somers 
7829821f1d3SAlan Somers void MockFS::kill_daemon() {
78381a619c4SAlan Somers 	m_quit = true;
7848b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
7859821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
7868b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
7878b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
7888b73a4c5SAlan Somers 	// during the unmount sequence.
7899821f1d3SAlan Somers 	close(m_fuse_fd);
7908b73a4c5SAlan Somers 	m_fuse_fd = -1;
7919821f1d3SAlan Somers }
7929821f1d3SAlan Somers 
7939821f1d3SAlan Somers void MockFS::loop() {
79429edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
7959821f1d3SAlan Somers 
79629edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
7979821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
79881a619c4SAlan Somers 	while (!m_quit) {
7996c0c3620SAlan Somers 		ssize_t buflen;
8006c0c3620SAlan Somers 
80129edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
8026c0c3620SAlan Somers 		read_request(*in, buflen);
80381a619c4SAlan Somers 		if (m_quit)
8049821f1d3SAlan Somers 			break;
8059821f1d3SAlan Somers 		if (verbosity > 0)
8066c0c3620SAlan Somers 			debug_request(*in, buflen);
8076c0c3620SAlan Somers 		audit_request(*in, buflen);
8089821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
80929edc611SAlan Somers 			process(*in, out);
8109821f1d3SAlan Somers 		} else {
8119821f1d3SAlan Somers 			/*
8129821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
8139821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
8149821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
8159821f1d3SAlan Somers 			 */
81699cf7bffSAlan Somers 			if (verbosity > 1)
81799cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
81899cf7bffSAlan Somers 					in->header.pid);
81929edc611SAlan Somers 			process_default(*in, out);
8209821f1d3SAlan Somers 		}
82129edc611SAlan Somers 		for (auto &it: out)
82229edc611SAlan Somers 			write_response(*it);
8239821f1d3SAlan Somers 		out.clear();
8249821f1d3SAlan Somers 	}
8259821f1d3SAlan Somers }
8269821f1d3SAlan Somers 
827c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
828c2d70d6eSAlan Somers {
829c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
830c2d70d6eSAlan Somers 
831c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
832c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
833c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
834c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
835c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
836c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
837c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
838c2d70d6eSAlan Somers 		namelen;
839c2d70d6eSAlan Somers 	debug_response(*out);
840c2d70d6eSAlan Somers 	write_response(*out);
841c2d70d6eSAlan Somers 	return 0;
842c2d70d6eSAlan Somers }
843c2d70d6eSAlan Somers 
844eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
845eae1ae13SAlan Somers {
846eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
847eae1ae13SAlan Somers 
848eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
849eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
850eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
851eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
852eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
853eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
854eae1ae13SAlan Somers 	debug_response(*out);
855eae1ae13SAlan Somers 	write_response(*out);
856eae1ae13SAlan Somers 	return 0;
857eae1ae13SAlan Somers }
858eae1ae13SAlan Somers 
8595a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
8607cbb8e8aSAlan Somers {
8617cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
8627cbb8e8aSAlan Somers 
8637cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
8647cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
8657cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
8667cbb8e8aSAlan Somers 	out->body.store.offset = off;
8677cbb8e8aSAlan Somers 	out->body.store.size = size;
8687cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
8697cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
8707cbb8e8aSAlan Somers 	debug_response(*out);
8717cbb8e8aSAlan Somers 	write_response(*out);
8727cbb8e8aSAlan Somers 	return 0;
8737cbb8e8aSAlan Somers }
8747cbb8e8aSAlan Somers 
8759821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
8769821f1d3SAlan Somers 	if (pid == m_pid) {
8779821f1d3SAlan Somers 		return (true);
87891ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
87991ff3a0dSAlan Somers 		return (true);
8809821f1d3SAlan Somers 	} else {
8819821f1d3SAlan Somers 		struct kinfo_proc *ki;
8829821f1d3SAlan Somers 		bool ok = false;
8839821f1d3SAlan Somers 
8849821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
8859821f1d3SAlan Somers 		if (ki == NULL)
8869821f1d3SAlan Somers 			return (false);
8879821f1d3SAlan Somers 		/*
8889821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
8899821f1d3SAlan Somers 		 * can use aio functions
8909821f1d3SAlan Somers 		 */
8919821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
8929821f1d3SAlan Somers 			ok = true;
8939821f1d3SAlan Somers 		free(ki);
8949821f1d3SAlan Somers 		return (ok);
8959821f1d3SAlan Somers 	}
8969821f1d3SAlan Somers }
8979821f1d3SAlan Somers 
89829edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
89929edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
9009821f1d3SAlan Somers {
90129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
90229edc611SAlan Somers 	out0->header.unique = in.header.unique;
9039821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
9049821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
90529edc611SAlan Somers 	out.push_back(std::move(out0));
9069821f1d3SAlan Somers }
9079821f1d3SAlan Somers 
9086c0c3620SAlan Somers void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
90977fbe694SAlan Somers 	int nready = 0;
9103429092cSAlan Somers 	fd_set readfds;
9113429092cSAlan Somers 	pollfd fds[1];
9123429092cSAlan Somers 	struct kevent changes[1];
9133429092cSAlan Somers 	struct kevent events[1];
91477fbe694SAlan Somers 	struct timespec timeout_ts;
91577fbe694SAlan Somers 	struct timeval timeout_tv;
91677fbe694SAlan Somers 	const int timeout_ms = 999;
91777fbe694SAlan Somers 	int timeout_int, nfds;
918f44a4487SAlan Somers 	int fuse_fd;
9199821f1d3SAlan Somers 
9203429092cSAlan Somers 	switch (m_pm) {
9213429092cSAlan Somers 	case BLOCKING:
9223429092cSAlan Somers 		break;
9233429092cSAlan Somers 	case KQ:
92477fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
92577fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
92677fbe694SAlan Somers 		while (nready == 0) {
9277b8622faSAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
9287b8622faSAlan Somers 				EV_ADD | EV_ONESHOT, 0, 0, 0);
92977fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
93077fbe694SAlan Somers 				&timeout_ts);
9313429092cSAlan Somers 			if (m_quit)
9323429092cSAlan Somers 				return;
93377fbe694SAlan Somers 		}
9343429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9353429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9363429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
9373429092cSAlan Somers 			FAIL() << strerror(events[0].data);
9383429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
9393429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
9400a7c63e0SAlan Somers 		m_nready = events[0].data;
9413429092cSAlan Somers 		break;
9423429092cSAlan Somers 	case POLL:
94377fbe694SAlan Somers 		timeout_int = timeout_ms;
9443429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9453429092cSAlan Somers 		fds[0].events = POLLIN;
94677fbe694SAlan Somers 		while (nready == 0) {
94777fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
9483429092cSAlan Somers 			if (m_quit)
9493429092cSAlan Somers 				return;
95077fbe694SAlan Somers 		}
9513429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9523429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
9533429092cSAlan Somers 		break;
9543429092cSAlan Somers 	case SELECT:
955f44a4487SAlan Somers 		fuse_fd = m_fuse_fd;
956f44a4487SAlan Somers 		if (fuse_fd < 0)
957f44a4487SAlan Somers 			break;
95877fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
95977fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
960f44a4487SAlan Somers 		nfds = fuse_fd + 1;
96177fbe694SAlan Somers 		while (nready == 0) {
9623429092cSAlan Somers 			FD_ZERO(&readfds);
963f44a4487SAlan Somers 			FD_SET(fuse_fd, &readfds);
96477fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
96577fbe694SAlan Somers 				&timeout_tv);
9663429092cSAlan Somers 			if (m_quit)
9673429092cSAlan Somers 				return;
96877fbe694SAlan Somers 		}
9693429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
970f44a4487SAlan Somers 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
9713429092cSAlan Somers 		break;
9723429092cSAlan Somers 	default:
9733429092cSAlan Somers 		FAIL() << "not yet implemented";
9743429092cSAlan Somers 	}
97529edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
9763429092cSAlan Somers 
977b690d120SAlan Somers 	if (res < 0 && !m_quit) {
978b690d120SAlan Somers 		m_quit = true;
9798e765737SAlan Somers 		FAIL() << "read: " << strerror(errno);
980b690d120SAlan Somers 	}
98129edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
982bf507497SAlan Somers 	/*
983bf507497SAlan Somers 	 * Inconsistently, fuse_in_header.len is the size of the entire
984bf507497SAlan Somers 	 * request,including header, even though fuse_out_header.len excludes
985bf507497SAlan Somers 	 * the size of the header.
986bf507497SAlan Somers 	 */
98738a3e0bdSAlan Somers 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
9889821f1d3SAlan Somers }
9899821f1d3SAlan Somers 
99029edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
9913429092cSAlan Somers 	fd_set writefds;
9923429092cSAlan Somers 	pollfd fds[1];
9937b8622faSAlan Somers 	struct kevent changes[1];
9947b8622faSAlan Somers 	struct kevent events[1];
9953429092cSAlan Somers 	int nready, nfds;
9963429092cSAlan Somers 	ssize_t r;
9973429092cSAlan Somers 
9983429092cSAlan Somers 	switch (m_pm) {
9993429092cSAlan Somers 	case BLOCKING:
10007b8622faSAlan Somers 		break;
10017b8622faSAlan Somers 	case KQ:
10027b8622faSAlan Somers 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
10037b8622faSAlan Somers 			EV_ADD | EV_ONESHOT, 0, 0, 0);
10047b8622faSAlan Somers 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
10057b8622faSAlan Somers 			NULL);
10067b8622faSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10077b8622faSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
10087b8622faSAlan Somers 		if (events[0].flags & EV_ERROR)
10097b8622faSAlan Somers 			FAIL() << strerror(events[0].data);
10107b8622faSAlan Somers 		else if (events[0].flags & EV_EOF)
10117b8622faSAlan Somers 			FAIL() << strerror(events[0].fflags);
10127b8622faSAlan Somers 		m_nready = events[0].data;
10133429092cSAlan Somers 		break;
10143429092cSAlan Somers 	case POLL:
10153429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
10163429092cSAlan Somers 		fds[0].events = POLLOUT;
10173429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
10183429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10193429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
10203429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
10213429092cSAlan Somers 		break;
10223429092cSAlan Somers 	case SELECT:
10233429092cSAlan Somers 		FD_ZERO(&writefds);
10243429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
10253429092cSAlan Somers 		nfds = m_fuse_fd + 1;
10263429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
10273429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10283429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
10293429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
10303429092cSAlan Somers 		break;
10313429092cSAlan Somers 	default:
10323429092cSAlan Somers 		FAIL() << "not yet implemented";
10333429092cSAlan Somers 	}
103429edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
1035*5f51c9c3SAlan Somers 	if (out.expected_errno) {
1036155ac516SAlan Somers 		ASSERT_EQ(-1, r);
1037*5f51c9c3SAlan Somers 		ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1038155ac516SAlan Somers 	} else {
10393429092cSAlan Somers 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
10403429092cSAlan Somers 	}
1041155ac516SAlan Somers }
10423429092cSAlan Somers 
10439821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
10449821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
10459821f1d3SAlan Somers 
10469821f1d3SAlan Somers 	mock_fs->loop();
10479821f1d3SAlan Somers 
10489821f1d3SAlan Somers 	return (NULL);
10499821f1d3SAlan Somers }
10509821f1d3SAlan Somers 
10519821f1d3SAlan Somers void MockFS::unmount() {
10529821f1d3SAlan Somers 	::unmount("mountpoint", 0);
10539821f1d3SAlan Somers }
1054