aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2019-06-20 23:32:25 +0000
committerAlan Somers <asomers@FreeBSD.org>2019-06-20 23:32:25 +0000
commit7cbb8e8a06904f6bf4a7547c2a5d40b53f0041ff (patch)
tree56fef042066368804694df14215a3aeff9143c70 /tests
parentbb23d43901b27cf218b8c516322dc2129ed34b6a (diff)
Notes
Diffstat (limited to 'tests')
-rw-r--r--tests/sys/fs/fusefs/mockfs.cc23
-rw-r--r--tests/sys/fs/fusefs/mockfs.hh16
-rw-r--r--tests/sys/fs/fusefs/notify.cc78
3 files changed, 117 insertions, 0 deletions
diff --git a/tests/sys/fs/fusefs/mockfs.cc b/tests/sys/fs/fusefs/mockfs.cc
index 7a9e8b66a5c40..d0dc5c1ef095f 100644
--- a/tests/sys/fs/fusefs/mockfs.cc
+++ b/tests/sys/fs/fusefs/mockfs.cc
@@ -333,6 +333,13 @@ void MockFS::debug_response(const mockfs_buf_out &out) {
out.body.inval_inode.off,
out.body.inval_inode.len);
break;
+ case FUSE_NOTIFY_STORE:
+ printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
+ " size=%" PRIu32 "\n",
+ out.body.store.nodeid,
+ out.body.store.offset,
+ out.body.store.size);
+ break;
default:
break;
}
@@ -544,6 +551,22 @@ int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
return 0;
}
+int MockFS::notify_store(ino_t ino, off_t off, void* data, ssize_t size)
+{
+ std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
+
+ out->header.unique = 0; /* 0 means asynchronous notification */
+ out->header.error = FUSE_NOTIFY_STORE;
+ out->body.store.nodeid = ino;
+ out->body.store.offset = off;
+ out->body.store.size = size;
+ bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
+ out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
+ debug_response(*out);
+ write_response(*out);
+ return 0;
+}
+
bool MockFS::pid_ok(pid_t pid) {
if (pid == m_pid) {
return (true);
diff --git a/tests/sys/fs/fusefs/mockfs.hh b/tests/sys/fs/fusefs/mockfs.hh
index 162df62819329..94029748107ed 100644
--- a/tests/sys/fs/fusefs/mockfs.hh
+++ b/tests/sys/fs/fusefs/mockfs.hh
@@ -181,6 +181,8 @@ union fuse_payloads_out {
/* The inval_entry structure should be followed by the entry's name */
fuse_notify_inval_entry_out inval_entry;
fuse_notify_inval_inode_out inval_inode;
+ /* The store structure should be followed by the data to store */
+ fuse_notify_store_out store;
fuse_listxattr_out listxattr;
fuse_open_out open;
fuse_statfs_out statfs;
@@ -346,6 +348,20 @@ class MockFS {
*/
int notify_inval_inode(ino_t ino, off_t off, ssize_t len);
+ /*
+ * Send an asynchronous notification to store data directly into an
+ * inode's cache. Similar to libfuse's fuse_lowlevel_notify_store.
+ *
+ * This method will block until the client has responded, so it should
+ * generally be run in a separate thread from request processing.
+ *
+ * @param ino File's inode number
+ * @param off Offset at which to store data
+ * @param data Pointer to the data to cache
+ * @param len Size of data
+ */
+ int notify_store(ino_t ino, off_t off, void* data, ssize_t size);
+
/*
* Request handler
*
diff --git a/tests/sys/fs/fusefs/notify.cc b/tests/sys/fs/fusefs/notify.cc
index 4198c182178c7..8131e1f69636c 100644
--- a/tests/sys/fs/fusefs/notify.cc
+++ b/tests/sys/fs/fusefs/notify.cc
@@ -133,6 +133,14 @@ struct inval_inode_args {
ssize_t len;
};
+struct store_args {
+ MockFS *mock;
+ ino_t nodeid;
+ off_t offset;
+ ssize_t size;
+ void* data;
+};
+
static void* inval_inode(void* arg) {
const struct inval_inode_args *iia = (struct inval_inode_args*)arg;
ssize_t r;
@@ -144,6 +152,17 @@ static void* inval_inode(void* arg) {
return (void*)(intptr_t)errno;
}
+static void* store(void* arg) {
+ const struct store_args *sa = (struct store_args*)arg;
+ ssize_t r;
+
+ r = sa->mock->notify_store(sa->nodeid, sa->offset, sa->data, sa->size);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
/* Invalidate a nonexistent entry */
TEST_F(Notify, inval_entry_nonexistent)
{
@@ -376,6 +395,65 @@ TEST_F(Notify, inval_inode_with_clean_cache)
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
+/* FUSE_NOTIFY_STORE with a file that's not in the entry cache */
+/* disabled because FUSE_NOTIFY_STORE is not yet implemented */
+TEST_F(Notify, DISABLED_store_nonexistent)
+{
+ struct store_args sa;
+ ino_t ino = 42;
+ void *thr0_value;
+ pthread_t th0;
+
+ sa.mock = m_mock;
+ sa.nodeid = ino;
+ sa.offset = 0;
+ sa.size = 0;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, store, &sa)) << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ /* It's not an error for a file to be unknown to the kernel */
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+}
+
+/* Store data into for a file that does not yet have anything cached */
+/* disabled because FUSE_NOTIFY_STORE is not yet implemented */
+TEST_F(Notify, DISABLED_store_with_blank_cache)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ const char CONTENTS1[] = "ijklmnopqrstuvwxyz";
+ struct store_args sa;
+ ino_t ino = 42;
+ void *thr0_value;
+ Sequence seq;
+ pthread_t th0;
+ ssize_t size1 = sizeof(CONTENTS1);
+ char buf[80];
+ int fd;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size1, seq);
+ expect_open(ino, 0, 1);
+
+ /* Fill the data cache */
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Evict the data cache */
+ sa.mock = m_mock;
+ sa.nodeid = ino;
+ sa.offset = 0;
+ sa.size = size1;
+ sa.data = (void*)CONTENTS1;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, store, &sa)) << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* This read should be serviced by cache */
+ ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
+
+ /* Deliberately leak fd. close(2) will be tested in release.cc */
+}
+
TEST_F(NotifyWriteback, inval_inode_with_dirty_cache)
{
const static char FULLPATH[] = "mountpoint/foo";