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