svn commit: r344831 - projects/fuse2/tests/sys/fs/fuse

Alan Somers asomers at FreeBSD.org
Wed Mar 6 00:38:13 UTC 2019


Author: asomers
Date: Wed Mar  6 00:38:10 2019
New Revision: 344831
URL: https://svnweb.freebsd.org/changeset/base/344831

Log:
  fuse(4): add tests for unlink, rmdir, and statfs
  
  Also, combine some common code for sending cacheable negative lookup
  responses.
  
  Sponsored by:	The FreeBSD Foundation

Added:
  projects/fuse2/tests/sys/fs/fuse/rmdir.cc   (contents, props changed)
  projects/fuse2/tests/sys/fs/fuse/statfs.cc   (contents, props changed)
  projects/fuse2/tests/sys/fs/fuse/unlink.cc   (contents, props changed)
Modified:
  projects/fuse2/tests/sys/fs/fuse/Makefile
  projects/fuse2/tests/sys/fs/fuse/create.cc
  projects/fuse2/tests/sys/fs/fuse/lookup.cc
  projects/fuse2/tests/sys/fs/fuse/mkdir.cc
  projects/fuse2/tests/sys/fs/fuse/mockfs.cc
  projects/fuse2/tests/sys/fs/fuse/mockfs.hh
  projects/fuse2/tests/sys/fs/fuse/rename.cc

Modified: projects/fuse2/tests/sys/fs/fuse/Makefile
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/Makefile	Wed Mar  6 00:01:06 2019	(r344830)
+++ projects/fuse2/tests/sys/fs/fuse/Makefile	Wed Mar  6 00:38:10 2019	(r344831)
@@ -14,8 +14,11 @@ ATF_TESTS_CXX+=	mknod
 ATF_TESTS_CXX+=	open
 ATF_TESTS_CXX+=	readlink
 ATF_TESTS_CXX+=	rename
+ATF_TESTS_CXX+=	rmdir
 ATF_TESTS_CXX+=	setattr
+ATF_TESTS_CXX+=	statfs
 ATF_TESTS_CXX+=	symlink
+ATF_TESTS_CXX+=	unlink
 
 SRCS.access+=	access.cc
 SRCS.access+=	getmntopts.c
@@ -67,15 +70,30 @@ SRCS.rename+=	mockfs.cc
 SRCS.rename+=	rename.cc
 SRCS.rename+=	utils.cc
 
+SRCS.rmdir+=	getmntopts.c
+SRCS.rmdir+=	mockfs.cc
+SRCS.rmdir+=	rmdir.cc
+SRCS.rmdir+=	utils.cc
+
 SRCS.setattr+=	getmntopts.c
 SRCS.setattr+=	mockfs.cc
 SRCS.setattr+=	setattr.cc
 SRCS.setattr+=	utils.cc
 
+SRCS.statfs+=	getmntopts.c
+SRCS.statfs+=	mockfs.cc
+SRCS.statfs+=	statfs.cc
+SRCS.statfs+=	utils.cc
+
 SRCS.symlink+=	getmntopts.c
 SRCS.symlink+=	mockfs.cc
 SRCS.symlink+=	symlink.cc
 SRCS.symlink+=	utils.cc
+
+SRCS.unlink+=	getmntopts.c
+SRCS.unlink+=	mockfs.cc
+SRCS.unlink+=	unlink.cc
+SRCS.unlink+=	utils.cc
 
 # TODO: drastically increase timeout after test development is mostly complete
 TEST_METADATA+= timeout=10

Modified: projects/fuse2/tests/sys/fs/fuse/create.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/create.cc	Wed Mar  6 00:01:06 2019	(r344830)
+++ projects/fuse2/tests/sys/fs/fuse/create.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -195,21 +195,15 @@ TEST_F(Create, DISABLED_entry_cache_negative)
 	mode_t mode = 0755;
 	uint64_t ino = 42;
 	int fd;
+	/* 
+	 * Set entry_valid = 0 because this test isn't concerned with whether
+	 * or not we actually cache negative entries, only with whether we
+	 * interpret negative cache responses correctly.
+	 */
+	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
 
 	/* create will first do a LOOKUP, adding a negative cache entry */
-	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
-		/* nodeid means ENOENT and cache it */
-		out->body.entry.nodeid = 0;
-		out->header.unique = in->header.unique;
-		out->header.error = 0;
-		/* 
-		 * Set entry_valid = 0 because this test isn't concerned with
-		 * whether or not we actually cache negative entries, only with
-		 * whether we interpret negative cache responses correctly.
-		 */
-		out->body.entry.entry_valid = 0;
-		SET_OUT_HEADER_LEN(out, entry);
-	}));
+	EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid));
 
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([=](auto in) {
@@ -258,17 +252,12 @@ TEST_F(Create, DISABLED_entry_cache_negative_purge)
 	mode_t mode = 0755;
 	uint64_t ino = 42;
 	int fd;
+	struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
 
 	/* create will first do a LOOKUP, adding a negative cache entry */
 	EXPECT_LOOKUP(1, RELPATH).Times(1)
-	.WillOnce(Invoke([=](auto in, auto out) {
-		/* nodeid means ENOENT and cache it */
-		out->body.entry.nodeid = 0;
-		out->header.unique = in->header.unique;
-		out->header.error = 0;
-		out->body.entry.entry_valid = UINT64_MAX;
-		SET_OUT_HEADER_LEN(out, entry);
-	})).RetiresOnSaturation();
+	.WillOnce(Invoke(ReturnNegativeCache(&entry_valid)))
+	.RetiresOnSaturation();
 
 	/* Then the CREATE should purge the negative cache entry */
 	EXPECT_CALL(*m_mock, process(

Modified: projects/fuse2/tests/sys/fs/fuse/lookup.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/lookup.cc	Wed Mar  6 00:01:06 2019	(r344830)
+++ projects/fuse2/tests/sys/fs/fuse/lookup.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -181,14 +181,11 @@ TEST_F(Lookup, entry_cache)
 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236226 */
 TEST_F(Lookup, DISABLED_entry_cache_negative)
 {
+	struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
+
 	EXPECT_LOOKUP(1, "does_not_exist").Times(1)
-	.WillOnce(Invoke([](auto in, auto out) {
-		out->header.unique = in->header.unique;
-		out->header.error = 0;
-		out->body.entry.nodeid = 0;
-		out->body.entry.entry_valid = UINT64_MAX;
-		SET_OUT_HEADER_LEN(out, entry);
-	}));
+	.WillOnce(Invoke(ReturnNegativeCache(&entry_valid)));
+
 	EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK));
 	EXPECT_EQ(ENOENT, errno);
 	EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK));
@@ -204,20 +201,15 @@ TEST_F(Lookup, entry_cache_negative_timeout)
 	 * The timeout should be longer than the longest plausible time the
 	 * daemon would take to complete a write(2) to /dev/fuse, but no longer.
 	 */
-	long timeout_ns = 250'000'000;
+	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 250'000'000};
 
 	EXPECT_LOOKUP(1, RELPATH).Times(2)
-	.WillRepeatedly(Invoke([=](auto in, auto out) {
-		out->header.unique = in->header.unique;
-		out->header.error = 0;
-		out->body.entry.nodeid = 0;
-		out->body.entry.entry_valid_nsec = timeout_ns;
-		SET_OUT_HEADER_LEN(out, entry);
-	}));
+	.WillRepeatedly(Invoke(ReturnNegativeCache(&entry_valid)));
+
 	EXPECT_NE(0, access(FULLPATH, F_OK));
 	EXPECT_EQ(ENOENT, errno);
 
-	usleep(2 * timeout_ns / 1000);
+	usleep(2 * entry_valid.tv_nsec / 1000);
 
 	/* The cache has timed out; VOP_LOOKUP should requery the daemon*/
 	EXPECT_NE(0, access(FULLPATH, F_OK));

Modified: projects/fuse2/tests/sys/fs/fuse/mkdir.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/mkdir.cc	Wed Mar  6 00:01:06 2019	(r344830)
+++ projects/fuse2/tests/sys/fs/fuse/mkdir.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -75,21 +75,15 @@ TEST_F(Mkdir, DISABLED_entry_cache_negative)
 	const char RELPATH[] = "some_file.txt";
 	mode_t mode = 0755;
 	uint64_t ino = 42;
+	/* 
+	 * Set entry_valid = 0 because this test isn't concerned with whether
+	 * or not we actually cache negative entries, only with whether we
+	 * interpret negative cache responses correctly.
+	 */
+	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
 
 	/* mkdir will first do a LOOKUP, adding a negative cache entry */
-	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
-		/* nodeid means ENOENT and cache it */
-		out->body.entry.nodeid = 0;
-		out->header.unique = in->header.unique;
-		out->header.error = 0;
-		/* 
-		 * Set entry_valid = 0 because this test isn't concerned with
-		 * whether or not we actually cache negative entries, only with
-		 * whether we interpret negative cache responses correctly.
-		 */
-		out->body.entry.entry_valid = 0;
-		SET_OUT_HEADER_LEN(out, entry);
-	}));
+	EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid));
 
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([=](auto in) {
@@ -122,17 +116,12 @@ TEST_F(Mkdir, DISABLED_entry_cache_negative_purge)
 	const char RELPATH[] = "some_file.txt";
 	mode_t mode = 0755;
 	uint64_t ino = 42;
+	struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
 
 	/* mkdir will first do a LOOKUP, adding a negative cache entry */
 	EXPECT_LOOKUP(1, RELPATH).Times(1)
-	.WillOnce(Invoke([=](auto in, auto out) {
-		/* nodeid means ENOENT and cache it */
-		out->body.entry.nodeid = 0;
-		out->header.unique = in->header.unique;
-		out->header.error = 0;
-		out->body.entry.entry_valid = UINT64_MAX;
-		SET_OUT_HEADER_LEN(out, entry);
-	})).RetiresOnSaturation();
+	.WillOnce(Invoke(ReturnNegativeCache(&entry_valid)))
+	.RetiresOnSaturation();
 
 	/* Then the MKDIR should purge the negative cache entry */
 	EXPECT_CALL(*m_mock, process(

Modified: projects/fuse2/tests/sys/fs/fuse/mockfs.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/mockfs.cc	Wed Mar  6 00:01:06 2019	(r344830)
+++ projects/fuse2/tests/sys/fs/fuse/mockfs.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -112,6 +112,21 @@ ReturnErrno(int error)
 	});
 }
 
+/* Helper function used for returning negative cache entries for LOOKUP */
+std::function<void (const struct mockfs_buf_in *in, struct mockfs_buf_out *out)>
+ReturnNegativeCache(const struct timespec *entry_valid)
+{
+	return([=](auto in, auto out) {
+		/* nodeid means ENOENT and cache it */
+		out->body.entry.nodeid = 0;
+		out->header.unique = in->header.unique;
+		out->header.error = 0;
+		out->body.entry.entry_valid = entry_valid->tv_sec;
+		out->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
+		SET_OUT_HEADER_LEN(out, entry);
+	});
+}
+
 void sigint_handler(int __unused sig) {
 	quit = 1;
 }
@@ -135,6 +150,9 @@ MockFS::MockFS() {
 	int iovlen = 0;
 	char fdstr[15];
 
+	m_daemon_id = NULL;
+	quit = 0;
+
 	/*
 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
 	 * mkdtemp
@@ -169,18 +187,14 @@ MockFS::MockFS() {
 		.WillByDefault(Invoke(this, &MockFS::process_default));
 
 	init();
-	if (pthread_create(&m_thr, NULL, service, (void*)this))
+	signal(SIGUSR1, sigint_handler);
+	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
 		throw(std::system_error(errno, std::system_category(),
 			"Couldn't Couldn't start fuse thread"));
 }
 
 MockFS::~MockFS() {
-	pthread_kill(m_daemon_id, SIGUSR1);
-	// Closing the /dev/fuse file descriptor first allows unmount to
-	// succeed even if the daemon doesn't correctly respond to commands
-	// during the unmount sequence.
-	close(m_fuse_fd);
-	pthread_join(m_daemon_id, NULL);
+	kill_daemon();
 	::unmount("mountpoint", MNT_FORCE);
 	rmdir("mountpoint");
 }
@@ -206,6 +220,18 @@ void MockFS::init() {
 	free(in);
 }
 
+void MockFS::kill_daemon() {
+	if (m_daemon_id != NULL) {
+		pthread_kill(m_daemon_id, SIGUSR1);
+		// Closing the /dev/fuse file descriptor first allows unmount
+		// to succeed even if the daemon doesn't correctly respond to
+		// commands during the unmount sequence.
+		close(m_fuse_fd);
+		pthread_join(m_daemon_id, NULL);
+		m_daemon_id = NULL;
+	}
+}
+
 void MockFS::loop() {
 	mockfs_buf_in *in;
 	mockfs_buf_out out;
@@ -258,10 +284,6 @@ void MockFS::read_request(mockfs_buf_in *in) {
 
 void* MockFS::service(void *pthr_data) {
 	MockFS *mock_fs = (MockFS*)pthr_data;
-	mock_fs->m_daemon_id = pthread_self();
-
-	quit = 0;
-	signal(SIGUSR1, sigint_handler);
 
 	mock_fs->loop();
 

Modified: projects/fuse2/tests/sys/fs/fuse/mockfs.hh
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/mockfs.hh	Wed Mar  6 00:01:06 2019	(r344830)
+++ projects/fuse2/tests/sys/fs/fuse/mockfs.hh	Wed Mar  6 00:38:10 2019	(r344831)
@@ -37,6 +37,8 @@ extern "C" {
 
 #include <gmock/gmock.h>
 
+#define TIME_T_MAX (std::numeric_limits<time_t>::max())
+
 #define SET_OUT_HEADER_LEN(out, variant) { \
 	(out)->header.len = (sizeof((out)->header) + \
 			     sizeof((out)->body.variant)); \
@@ -79,7 +81,9 @@ union fuse_payloads_in {
 	fuse_mknod_in	mknod;
 	fuse_open_in	open;
 	fuse_rename_in	rename;
+	char		rmdir[0];
 	fuse_setattr_in	setattr;
+	char		unlink[0];
 };
 
 struct mockfs_buf_in {
@@ -93,6 +97,7 @@ union fuse_payloads_out {
 	fuse_entry_out		entry;
 	fuse_init_out		init;
 	fuse_open_out		open;
+	fuse_statfs_out		statfs;
 	/*
 	 * The protocol places no limits on the length of the string.  This is
 	 * merely convenient for testing.
@@ -112,6 +117,10 @@ struct mockfs_buf_out {
 std::function<void (const struct mockfs_buf_in *in, struct mockfs_buf_out *out)>
 ReturnErrno(int error);
 
+/* Helper function used for returning negative cache entries for LOOKUP */
+std::function<void (const struct mockfs_buf_in *in, struct mockfs_buf_out *out)>
+ReturnNegativeCache(const struct timespec *entry_valid);
+
 /*
  * Fake FUSE filesystem
  *
@@ -122,7 +131,12 @@ ReturnErrno(int error);
  */
 class MockFS {
 	public:
-	/* thread id of the fuse daemon thread */
+	/*
+	 * thread id of the fuse daemon thread
+	 *
+	 * It must run in a separate thread so it doesn't deadlock with the
+	 * client test code.
+	 */
 	pthread_t m_daemon_id;
 
 	private:
@@ -132,14 +146,6 @@ class MockFS {
 	/* pid of the test process */
 	pid_t m_pid;
 
-	/* 
-	 * Thread that's running the mockfs daemon.
-	 *
-	 * It must run in a separate thread so it doesn't deadlock with the
-	 * client test code.
-	 */
-	pthread_t m_thr;
-
 	/* Initialize a session after mounting */
 	void init();
 
@@ -156,6 +162,9 @@ class MockFS {
 	/* Create a new mockfs and mount it to a tempdir */
 	MockFS();
 	virtual ~MockFS();
+
+	/* Kill the filesystem daemon without unmounting the filesystem */
+	void kill_daemon();
 
 	/* Process FUSE requests endlessly */
 	void loop();

Modified: projects/fuse2/tests/sys/fs/fuse/rename.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/rename.cc	Wed Mar  6 00:01:06 2019	(r344830)
+++ projects/fuse2/tests/sys/fs/fuse/rename.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -87,6 +87,111 @@ TEST_F(Rename, enoent)
 	ASSERT_EQ(ENOENT, errno);
 }
 
+/*
+ * Renaming a file after FUSE_LOOKUP returned a negative cache entry for dst
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */
+TEST_F(Rename, DISABLED_entry_cache_negative)
+{
+	const char FULLDST[] = "mountpoint/dst";
+	const char RELDST[] = "dst";
+	const char FULLSRC[] = "mountpoint/src";
+	const char RELSRC[] = "src";
+	// FUSE hardcodes the mountpoint to inocde 1
+	uint64_t dst_dir_ino = 1;
+	uint64_t ino = 42;
+	/* 
+	 * Set entry_valid = 0 because this test isn't concerned with whether
+	 * or not we actually cache negative entries, only with whether we
+	 * interpret negative cache responses correctly.
+	 */
+	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
+
+	EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		out->body.entry.attr.mode = S_IFREG | 0644;
+		out->body.entry.nodeid = ino;
+		SET_OUT_HEADER_LEN(out, entry);
+	}));
+
+	/* LOOKUP returns a negative cache entry for dst */
+	EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid));
+
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([=](auto in) {
+			const char *src = (const char*)in->body.bytes +
+				sizeof(fuse_rename_in);
+			const char *dst = src + strlen(src) + 1;
+			return (in->header.opcode == FUSE_RENAME &&
+				in->body.rename.newdir == dst_dir_ino &&
+				(0 == strcmp(RELDST, dst)) &&
+				(0 == strcmp(RELSRC, src)));
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(ReturnErrno(0)));
+
+	ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+}
+
+/*
+ * Renaming a file should purge any negative namecache entries for the dst
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */
+TEST_F(Rename, DISABLED_entry_cache_negative_purge)
+{
+	const char FULLDST[] = "mountpoint/dst";
+	const char RELDST[] = "dst";
+	const char FULLSRC[] = "mountpoint/src";
+	const char RELSRC[] = "src";
+	// FUSE hardcodes the mountpoint to inocde 1
+	uint64_t dst_dir_ino = 1;
+	uint64_t ino = 42;
+	/* 
+	 * Set entry_valid = 0 because this test isn't concerned with whether
+	 * or not we actually cache negative entries, only with whether we
+	 * interpret negative cache responses correctly.
+	 */
+	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
+
+	EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		out->body.entry.attr.mode = S_IFREG | 0644;
+		out->body.entry.nodeid = ino;
+		SET_OUT_HEADER_LEN(out, entry);
+	}));
+
+	/* LOOKUP returns a negative cache entry for dst */
+	EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid))
+	.RetiresOnSaturation();
+
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([=](auto in) {
+			const char *src = (const char*)in->body.bytes +
+				sizeof(fuse_rename_in);
+			const char *dst = src + strlen(src) + 1;
+			return (in->header.opcode == FUSE_RENAME &&
+				in->body.rename.newdir == dst_dir_ino &&
+				(0 == strcmp(RELDST, dst)) &&
+				(0 == strcmp(RELSRC, src)));
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(ReturnErrno(0)));
+
+	ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+
+	/* Finally, a subsequent lookup should query the daemon */
+	EXPECT_LOOKUP(1, RELDST).Times(1)
+	.WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		out->header.error = 0;
+		out->body.entry.nodeid = ino;
+		out->body.entry.attr.mode = S_IFREG | 0644;
+		SET_OUT_HEADER_LEN(out, entry);
+	}));
+
+	ASSERT_EQ(0, access(FULLDST, F_OK)) << strerror(errno);
+}
+
 TEST_F(Rename, exdev)
 {
 	const char FULLB[] = "mountpoint/src";

Added: projects/fuse2/tests/sys/fs/fuse/rmdir.cc
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/fuse2/tests/sys/fs/fuse/rmdir.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 2019 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Rmdir: public FuseTest {};
+
+TEST_F(Rmdir, enotempty)
+{
+	const char FULLPATH[] = "mountpoint/some_dir";
+	const char RELPATH[] = "some_dir";
+	uint64_t ino = 42;
+
+	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		SET_OUT_HEADER_LEN(out, entry);
+		out->body.entry.attr.mode = S_IFDIR | 0755;
+		out->body.entry.nodeid = ino;
+		out->body.entry.attr.nlink = 2;
+	}));
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([=](auto in) {
+			return (in->header.opcode == FUSE_RMDIR &&
+				0 == strcmp(RELPATH, in->body.rmdir) &&
+				in->header.nodeid == 1);
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(ReturnErrno(ENOTEMPTY)));
+
+	ASSERT_NE(0, rmdir(FULLPATH));
+	ASSERT_EQ(ENOTEMPTY, errno);
+}
+
+TEST_F(Rmdir, ok)
+{
+	const char FULLPATH[] = "mountpoint/some_dir";
+	const char RELPATH[] = "some_dir";
+	uint64_t ino = 42;
+
+	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		SET_OUT_HEADER_LEN(out, entry);
+		out->body.entry.attr.mode = S_IFDIR | 0755;
+		out->body.entry.nodeid = ino;
+		out->body.entry.attr.nlink = 2;
+	}));
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([=](auto in) {
+			return (in->header.opcode == FUSE_RMDIR &&
+				0 == strcmp(RELPATH, in->body.rmdir) &&
+				in->header.nodeid == 1);
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(ReturnErrno(0)));
+
+	ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno);
+}

Added: projects/fuse2/tests/sys/fs/fuse/statfs.cc
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/fuse2/tests/sys/fs/fuse/statfs.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2019 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Statfs: public FuseTest {};
+
+TEST_F(Statfs, eio)
+{
+	struct statfs statbuf;
+
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([](auto in) {
+			return (in->header.opcode == FUSE_STATFS);
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(ReturnErrno(EIO)));
+
+	ASSERT_NE(0, statfs("mountpoint", &statbuf));
+	ASSERT_EQ(EIO, errno);
+}
+
+/*
+ * When the daemon is dead but the filesystem is still mounted, fuse(4) fakes
+ * the statfs(2) response, which is necessary for unmounting.
+ */
+TEST_F(Statfs, enotconn)
+{
+	struct statfs statbuf;
+	char mp[PATH_MAX];
+
+	m_mock->kill_daemon();
+
+	ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno);
+	strlcat(mp, "/mountpoint", PATH_MAX);
+	ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+
+	EXPECT_EQ(getuid(), statbuf.f_owner);
+	EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
+	EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
+	EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
+}
+
+TEST_F(Statfs, ok)
+{
+	struct statfs statbuf;
+	char mp[PATH_MAX];
+
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([](auto in) {
+			return (in->header.opcode == FUSE_STATFS);
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		SET_OUT_HEADER_LEN(out, statfs);
+		out->body.statfs.st.blocks = 1000;
+		out->body.statfs.st.bfree = 100;
+		out->body.statfs.st.bavail = 200;
+		out->body.statfs.st.files = 5;
+		out->body.statfs.st.ffree = 6;
+		out->body.statfs.st.namelen = 128;
+		out->body.statfs.st.frsize = 1024;
+	}));
+
+	ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno);
+	strlcat(mp, "/mountpoint", PATH_MAX);
+	ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+	EXPECT_EQ(1024ul, statbuf.f_bsize);
+	/* 
+	 * fuse(4) ignores the filesystem's reported optimal transfer size, and
+	 * chooses a size that works well with the rest of the system instead
+	 */
+	EXPECT_EQ(1000ul, statbuf.f_blocks);
+	EXPECT_EQ(100ul, statbuf.f_bfree);
+	EXPECT_EQ(200l, statbuf.f_bavail);
+	EXPECT_EQ(5ul, statbuf.f_files);
+	EXPECT_EQ(6l, statbuf.f_ffree);
+	EXPECT_EQ(128u, statbuf.f_namemax);
+	EXPECT_EQ(getuid(), statbuf.f_owner);
+	EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
+	EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
+	EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
+}

Added: projects/fuse2/tests/sys/fs/fuse/unlink.cc
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/fuse2/tests/sys/fs/fuse/unlink.cc	Wed Mar  6 00:38:10 2019	(r344831)
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 2019 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Unlink: public FuseTest {};
+
+TEST_F(Unlink, eperm)
+{
+	const char FULLPATH[] = "mountpoint/some_file.txt";
+	const char RELPATH[] = "some_file.txt";
+	uint64_t ino = 42;
+
+	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		SET_OUT_HEADER_LEN(out, entry);
+		out->body.entry.attr.mode = S_IFREG | 0644;
+		out->body.entry.nodeid = ino;
+		out->body.entry.attr.nlink = 1;
+	}));
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([=](auto in) {
+			return (in->header.opcode == FUSE_UNLINK &&
+				0 == strcmp(RELPATH, in->body.unlink) &&
+				in->header.nodeid == 1);
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(ReturnErrno(EPERM)));
+
+	ASSERT_NE(0, unlink(FULLPATH));
+	ASSERT_EQ(EPERM, errno);
+}
+
+TEST_F(Unlink, ok)
+{
+	const char FULLPATH[] = "mountpoint/some_file.txt";
+	const char RELPATH[] = "some_file.txt";
+	uint64_t ino = 42;
+
+	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
+		out->header.unique = in->header.unique;
+		SET_OUT_HEADER_LEN(out, entry);
+		out->body.entry.attr.mode = S_IFREG | 0644;
+		out->body.entry.nodeid = ino;
+		out->body.entry.attr.nlink = 1;
+	}));
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([=](auto in) {
+			return (in->header.opcode == FUSE_UNLINK &&
+				0 == strcmp(RELPATH, in->body.unlink) &&
+				in->header.nodeid == 1);
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(ReturnErrno(0)));
+
+	ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
+}


More information about the svn-src-projects mailing list