svn commit: r349250 - in projects/fuse2: sys/fs/fuse tests/sys/fs/fusefs

Alan Somers asomers at FreeBSD.org
Thu Jun 20 23:32:28 UTC 2019


Author: asomers
Date: Thu Jun 20 23:32:25 2019
New Revision: 349250
URL: https://svnweb.freebsd.org/changeset/base/349250

Log:
  fusefs: raise protocol level to 7.15
  
  This protocol level adds two new features: the ability for the server to
  store or retrieve data into/from the client's cache.  But the messages
  aren't defined soundly since they identify the file only by its inode,
  without the generation number.  So it's possible for them to modify the
  wrong file's cache.  Also, I don't know of any file systems in ports that
  use these messages.  So I'm not implementing them.  I did add a (disabled)
  test for the store message, however.
  
  Sponsored by:	The FreeBSD Foundation

Modified:
  projects/fuse2/sys/fs/fuse/fuse_device.c
  projects/fuse2/sys/fs/fuse/fuse_kernel.h
  projects/fuse2/tests/sys/fs/fusefs/mockfs.cc
  projects/fuse2/tests/sys/fs/fusefs/mockfs.hh
  projects/fuse2/tests/sys/fs/fusefs/notify.cc

Modified: projects/fuse2/sys/fs/fuse/fuse_device.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_device.c	Thu Jun 20 23:12:19 2019	(r349249)
+++ projects/fuse2/sys/fs/fuse/fuse_device.c	Thu Jun 20 23:32:25 2019	(r349250)
@@ -518,7 +518,19 @@ fuse_device_write(struct cdev *dev, struct uio *uio, i
 		case FUSE_NOTIFY_INVAL_INODE:
 			err = fuse_internal_invalidate_inode(mp, uio);
 			break;
+		case FUSE_NOTIFY_RETRIEVE:
+		case FUSE_NOTIFY_STORE:
+			/*
+			 * Unimplemented.  I don't know of any file systems
+			 * that use them, and the protocol isn't sound anyway,
+			 * since the notification messages don't include the
+			 * inode's generation number.  Without that, it's
+			 * possible to manipulate the cache of the wrong vnode.
+			 * Finally, it's not defined what this message should
+			 * do for a file with dirty cache.
+			 */
 		case FUSE_NOTIFY_POLL:
+			/* Unimplemented.  See comments in fuse_vnops */
 		default:
 			/* Not implemented */
 			err = ENOSYS;

Modified: projects/fuse2/sys/fs/fuse/fuse_kernel.h
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_kernel.h	Thu Jun 20 23:12:19 2019	(r349249)
+++ projects/fuse2/sys/fs/fuse/fuse_kernel.h	Thu Jun 20 23:32:25 2019	(r349250)
@@ -64,6 +64,10 @@
  *
  * 7.14
  *  - add splice support to fuse device
+ *
+ * 7.15
+ *  - add store notify
+ *  - add retrieve notify
  */
 
 #ifndef _FUSE_FUSE_KERNEL_H
@@ -84,7 +88,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 14
+#define FUSE_KERNEL_MINOR_VERSION 15
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -269,6 +273,7 @@ enum fuse_opcode {
 	FUSE_DESTROY       = 38,
 	FUSE_IOCTL         = 39,
 	FUSE_POLL          = 40,
+	FUSE_NOTIFY_REPLY  = 41,
 
 #ifdef linux
 	/* CUSE specific operations */
@@ -280,6 +285,8 @@ enum fuse_notify_code {
 	FUSE_NOTIFY_POLL   = 1,
 	FUSE_NOTIFY_INVAL_INODE = 2,
 	FUSE_NOTIFY_INVAL_ENTRY = 3,
+	FUSE_NOTIFY_STORE = 4,
+	FUSE_NOTIFY_RETRIEVE = 5,
 	FUSE_NOTIFY_CODE_MAX,
 };
 
@@ -556,6 +563,31 @@ struct fuse_poll_out {
 
 struct fuse_notify_poll_wakeup_out {
 	__u64	kh;
+};
+
+struct fuse_notify_store_out {
+	__u64	nodeid;
+	__u64	offset;
+	__u32	size;
+	__u32	padding;
+};
+
+struct fuse_notify_retrieve_out {
+	__u64	notify_unique;
+	__u64	nodeid;
+	__u64	offset;
+	__u32	size;
+	__u32	padding;
+};
+
+/* Matches the size of fuse_write_in */
+struct fuse_notify_retrieve_in {
+	__u64	dummy1;
+	__u64	offset;
+	__u32	size;
+	__u32	dummy2;
+	__u64	dummy3;
+	__u64	dummy4;
 };
 
 struct fuse_in_header {

Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/mockfs.cc	Thu Jun 20 23:12:19 2019	(r349249)
+++ projects/fuse2/tests/sys/fs/fusefs/mockfs.cc	Thu Jun 20 23:32:25 2019	(r349250)
@@ -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;
 	}
@@ -539,6 +546,22 @@ int MockFS::notify_inval_inode(ino_t ino, off_t off, s
 	out->body.inval_inode.off = off;
 	out->body.inval_inode.len = len;
 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
+	debug_response(*out);
+	write_response(*out);
+	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;

Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.hh
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/mockfs.hh	Thu Jun 20 23:12:19 2019	(r349249)
+++ projects/fuse2/tests/sys/fs/fusefs/mockfs.hh	Thu Jun 20 23:32:25 2019	(r349250)
@@ -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;
@@ -345,6 +347,20 @@ class MockFS {
 	 * 			to invalidate all cached data.
 	 */
 	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

Modified: projects/fuse2/tests/sys/fs/fusefs/notify.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/notify.cc	Thu Jun 20 23:12:19 2019	(r349249)
+++ projects/fuse2/tests/sys/fs/fusefs/notify.cc	Thu Jun 20 23:32:25 2019	(r349250)
@@ -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)
 {
@@ -370,6 +389,65 @@ TEST_F(Notify, inval_inode_with_clean_cache)
 
 	/* This read should not be serviced by cache */
 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+	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 */
+}
+
+/* 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));
 


More information about the svn-src-projects mailing list