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

Alan Somers asomers at FreeBSD.org
Mon Apr 1 16:36:05 UTC 2019


Author: asomers
Date: Mon Apr  1 16:36:02 2019
New Revision: 345768
URL: https://svnweb.freebsd.org/changeset/base/345768

Log:
  fusefs: allow opening files O_EXEC
  
  O_EXEC is useful for fexecve(2) and fchdir(2).  Treat it as another fufh
  type alongside the existing RDONLY, WRONLY, and RDWR.  Prior to r345742 this
  would've caused a memory and performance penalty.
  
  PR:		236329
  Sponsored by:	The FreeBSD Foundation

Modified:
  projects/fuse2/sys/fs/fuse/fuse_file.c
  projects/fuse2/sys/fs/fuse/fuse_file.h
  projects/fuse2/sys/fs/fuse/fuse_vnops.c
  projects/fuse2/tests/sys/fs/fusefs/mockfs.cc
  projects/fuse2/tests/sys/fs/fusefs/open.cc
  projects/fuse2/tests/sys/fs/fusefs/opendir.cc
  projects/fuse2/tests/sys/fs/fusefs/readdir.cc
  projects/fuse2/tests/sys/fs/fusefs/releasedir.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.cc

Modified: projects/fuse2/sys/fs/fuse/fuse_file.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_file.c	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/sys/fs/fuse/fuse_file.c	Mon Apr  1 16:36:02 2019	(r345768)
@@ -121,12 +121,8 @@ fuse_filehandle_open(struct vnode *vp, fufh_type_t fuf
 
 	if (vnode_isdir(vp)) {
 		op = FUSE_OPENDIR;
-		if (fufh_type != FUFH_RDONLY) {
-			SDT_PROBE2(fuse, , file, trace, 1,
-				"non-rdonly fh requested for a directory?");
-			printf("FUSE:non-rdonly fh requested for a directory?\n");
-			fufh_type = FUFH_RDONLY;
-		}
+		/* vn_open_vnode already rejects FWRITE on directories */
+		MPASS(fufh_type == FUFH_RDONLY || fufh_type == FUFH_EXEC);
 	}
 	fdisp_init(&fdi, sizeof(*foi));
 	fdisp_make_vp(&fdi, op, vp, td, cred);

Modified: projects/fuse2/sys/fs/fuse/fuse_file.h
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_file.h	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/sys/fs/fuse/fuse_file.h	Mon Apr  1 16:36:02 2019	(r345768)
@@ -72,14 +72,11 @@
  */
 typedef enum fufh_type {
 	FUFH_INVALID = -1,
-	FUFH_RDONLY  = 0,
-	FUFH_WRONLY  = 1,
-	FUFH_RDWR    = 2,
-	/* TODO: add FUFH_EXEC */
+	FUFH_RDONLY  = O_RDONLY,
+	FUFH_WRONLY  = O_WRONLY,
+	FUFH_RDWR    = O_RDWR,
+	FUFH_EXEC    = O_EXEC,
 } fufh_type_t;
-_Static_assert(FUFH_RDONLY == O_RDONLY, "RDONLY");
-_Static_assert(FUFH_WRONLY == O_WRONLY, "WRONLY");
-_Static_assert(FUFH_RDWR == O_RDWR, "RDWR");
 
 struct fuse_filehandle {
 	LIST_ENTRY(fuse_filehandle) next;
@@ -110,6 +107,8 @@ fuse_filehandle_xlate_from_fflags(int fflags)
 		return FUFH_WRONLY;
 	else if (fflags & (FREAD))
 		return FUFH_RDONLY;
+	else if (fflags & (FEXEC))
+		return FUFH_EXEC;
 	else
 		panic("FUSE: What kind of a flag is this (%x)?", fflags);
 }
@@ -123,6 +122,7 @@ fuse_filehandle_xlate_to_oflags(fufh_type_t type)
 	case FUFH_RDONLY:
 	case FUFH_WRONLY:
 	case FUFH_RDWR:
+	case FUFH_EXEC:
 		oflags = type;
 		break;
 	default:

Modified: projects/fuse2/sys/fs/fuse/fuse_vnops.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_vnops.c	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/sys/fs/fuse/fuse_vnops.c	Mon Apr  1 16:36:02 2019	(r345768)
@@ -285,7 +285,8 @@ fuse_vnop_close(struct vop_close_args *ap)
 	if (vnode_isdir(vp)) {
 		struct fuse_filehandle *fufh;
 
-		if (fuse_filehandle_get(vp, O_RDONLY, &fufh) == 0)
+		if ((fuse_filehandle_get(vp, O_RDONLY, &fufh) == 0) ||
+		    (fuse_filehandle_get(vp, O_EXEC, &fufh) == 0))
 			fuse_filehandle_close(vp, fufh, NULL, cred);
 		return 0;
 	}
@@ -1201,16 +1202,12 @@ fuse_vnop_open(struct vop_open_args *ap)
 		return ENXIO;
 	if (vp->v_type == VCHR || vp->v_type == VBLK || vp->v_type == VFIFO)
 		return (EOPNOTSUPP);
-	if ((mode & (FREAD | FWRITE)) == 0)
+	if ((mode & (FREAD | FWRITE | FEXEC)) == 0)
 		return EINVAL;
 
 	fvdat = VTOFUD(vp);
 
-	if (vnode_isdir(vp)) {
-		fufh_type = FUFH_RDONLY;
-	} else {
-		fufh_type = fuse_filehandle_xlate_from_fflags(mode);
-	}
+	fufh_type = fuse_filehandle_xlate_from_fflags(mode);
 
 	if (fuse_filehandle_validrw(vp, fufh_type) != FUFH_INVALID) {
 		fuse_vnode_open(vp, 0, td);
@@ -1303,7 +1300,7 @@ fuse_vnop_readdir(struct vop_readdir_args *ap)
 		return EINVAL;
 	}
 
-	if ((err = fuse_filehandle_get(vp, O_RDONLY, &fufh)) != 0) {
+	if ((err = fuse_filehandle_get(vp, FUFH_RDONLY, &fufh)) != 0) {
 		SDT_PROBE2(fuse, , vnops, trace, 1,
 			"calling readdir() before open()");
 		err = fuse_filehandle_open(vp, O_RDONLY, &fufh, NULL, cred);

Modified: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/mockfs.cc	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/tests/sys/fs/fusefs/mockfs.cc	Mon Apr  1 16:36:02 2019	(r345768)
@@ -200,7 +200,8 @@ void debug_fuseop(const mockfs_buf_in *in)
 				in->body.read.size);
 			break;
 		case FUSE_READDIR:
-			printf(" offset=%lu size=%u", in->body.readdir.offset,
+			printf(" fh=%#lx offset=%lu size=%u",
+				in->body.readdir.fh, in->body.readdir.offset,
 				in->body.readdir.size);
 			break;
 		case FUSE_RELEASE:

Modified: projects/fuse2/tests/sys/fs/fusefs/open.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/open.cc	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/tests/sys/fs/fusefs/open.cc	Mon Apr  1 16:36:02 2019	(r345768)
@@ -250,8 +250,7 @@ TEST_F(Open, o_excl)
 	test_ok(O_WRONLY | O_EXCL, O_WRONLY);
 }
 
-/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236329 */
-TEST_F(Open, DISABLED_o_exec)
+TEST_F(Open, o_exec)
 {
 	test_ok(O_EXEC, O_EXEC);
 }

Modified: projects/fuse2/tests/sys/fs/fusefs/opendir.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/opendir.cc	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/tests/sys/fs/fusefs/opendir.cc	Mon Apr  1 16:36:02 2019	(r345768)
@@ -44,6 +44,29 @@ void expect_lookup(const char *relpath, uint64_t ino)
 {
 	FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1);
 }
+
+void expect_opendir(uint64_t ino, uint32_t flags, ProcessMockerT r)
+{
+	/* opendir(3) calls fstatfs */
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([](auto in) {
+			return (in->header.opcode == FUSE_STATFS);
+		}, Eq(true)),
+		_)
+	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
+		SET_OUT_HEADER_LEN(out, statfs);
+	})));
+
+	EXPECT_CALL(*m_mock, process(
+		ResultOf([=](auto in) {
+			return (in->header.opcode == FUSE_OPENDIR &&
+				in->header.nodeid == ino &&
+				in->body.opendir.flags == flags);
+		}, Eq(true)),
+		_)
+	).WillOnce(Invoke(r));
+}
+
 };
 
 
@@ -59,14 +82,8 @@ TEST_F(Opendir, enoent)
 	uint64_t ino = 42;
 
 	expect_lookup(RELPATH, ino);
+	expect_opendir(ino, O_RDONLY, ReturnErrno(ENOENT));
 
-	EXPECT_CALL(*m_mock, process(
-		ResultOf([=](auto in) {
-			return (in->header.opcode == FUSE_OPENDIR &&
-				in->header.nodeid == ino);
-		}, Eq(true)),
-		_)
-	).WillOnce(Invoke(ReturnErrno(ENOENT)));
 	EXPECT_NE(0, open(FULLPATH, O_DIRECTORY));
 	EXPECT_EQ(ENOENT, errno);
 }
@@ -82,15 +99,8 @@ TEST_F(Opendir, eperm)
 	uint64_t ino = 42;
 
 	expect_lookup(RELPATH, ino);
+	expect_opendir(ino, O_RDONLY, ReturnErrno(EPERM));
 
-	EXPECT_CALL(*m_mock, process(
-		ResultOf([=](auto in) {
-			return (in->header.opcode == FUSE_OPENDIR &&
-				in->header.nodeid == ino);
-		}, Eq(true)),
-		_)
-	).WillOnce(Invoke(ReturnErrno(EPERM)));
-
 	EXPECT_NE(0, open(FULLPATH, O_DIRECTORY));
 	EXPECT_EQ(EPERM, errno);
 }
@@ -102,45 +112,43 @@ TEST_F(Opendir, open)
 	uint64_t ino = 42;
 
 	expect_lookup(RELPATH, ino);
-
-	EXPECT_CALL(*m_mock, process(
-		ResultOf([=](auto in) {
-			return (in->header.opcode == FUSE_OPENDIR &&
-				in->header.nodeid == ino);
-		}, Eq(true)),
-		_)
-	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
+	expect_opendir(ino, O_RDONLY,
+	ReturnImmediate([=](auto in __unused, auto out) {
 		SET_OUT_HEADER_LEN(out, open);
-	})));
+	}));
 
 	EXPECT_LE(0, open(FULLPATH, O_DIRECTORY)) << strerror(errno);
 }
 
-TEST_F(Opendir, opendir)
+/* Directories can be opened O_EXEC for stuff like fchdir(2) */
+TEST_F(Opendir, open_exec)
 {
 	const char FULLPATH[] = "mountpoint/some_dir";
 	const char RELPATH[] = "some_dir";
 	uint64_t ino = 42;
+	int fd;
 
 	expect_lookup(RELPATH, ino);
-	EXPECT_CALL(*m_mock, process(
-		ResultOf([](auto in) {
-			return (in->header.opcode == FUSE_STATFS);
-		}, Eq(true)),
-		_)
-	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
-		SET_OUT_HEADER_LEN(out, statfs);
-	})));
+	expect_opendir(ino, O_EXEC,
+	ReturnImmediate([=](auto in __unused, auto out) {
+		SET_OUT_HEADER_LEN(out, open);
+	}));
 
-	EXPECT_CALL(*m_mock, process(
-		ResultOf([=](auto in) {
-			return (in->header.opcode == FUSE_OPENDIR &&
-				in->header.nodeid == ino);
-		}, Eq(true)),
-		_)
-	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
+	fd = open(FULLPATH, O_EXEC | O_DIRECTORY);
+	ASSERT_LE(0, fd) << strerror(errno);
+}
+
+TEST_F(Opendir, opendir)
+{
+	const char FULLPATH[] = "mountpoint/some_dir";
+	const char RELPATH[] = "some_dir";
+	uint64_t ino = 42;
+
+	expect_lookup(RELPATH, ino);
+	expect_opendir(ino, O_RDONLY,
+	ReturnImmediate([=](auto in __unused, auto out) {
 		SET_OUT_HEADER_LEN(out, open);
-	})));
+	}));
 
 	errno = 0;
 	EXPECT_NE(NULL, opendir(FULLPATH)) << strerror(errno);

Modified: projects/fuse2/tests/sys/fs/fusefs/readdir.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/readdir.cc	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/tests/sys/fs/fusefs/readdir.cc	Mon Apr  1 16:36:02 2019	(r345768)
@@ -52,6 +52,7 @@ void expect_readdir(uint64_t ino, uint64_t off, vector
 		ResultOf([=](auto in) {
 			return (in->header.opcode == FUSE_READDIR &&
 				in->header.nodeid == ino &&
+				in->body.readdir.fh == FH &&
 				in->body.readdir.offset == off);
 		}, Eq(true)),
 		_)

Modified: projects/fuse2/tests/sys/fs/fusefs/releasedir.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/releasedir.cc	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/tests/sys/fs/fusefs/releasedir.cc	Mon Apr  1 16:36:02 2019	(r345768)
@@ -30,6 +30,7 @@
 
 extern "C" {
 #include <dirent.h>
+#include <fcntl.h>
 }
 
 #include "mockfs.hh"
@@ -106,4 +107,22 @@ TEST_F(ReleaseDir, ok)
 	ASSERT_NE(NULL, dir) << strerror(errno);
 
 	ASSERT_EQ(0, closedir(dir)) << strerror(errno);
+}
+
+/* Directories opened O_EXEC should be properly released, too */
+TEST_F(ReleaseDir, o_exec)
+{
+	const char FULLPATH[] = "mountpoint/some_dir";
+	const char RELPATH[] = "some_dir";
+	uint64_t ino = 42;
+	int fd;
+
+	expect_lookup(RELPATH, ino);
+	expect_opendir(ino);
+	expect_releasedir(ino, ReturnErrno(0));
+	
+	fd = open(FULLPATH, O_EXEC | O_DIRECTORY);
+	EXPECT_LE(0, fd) << strerror(errno);
+
+	ASSERT_EQ(0, close(fd)) << strerror(errno);
 }

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.cc	Mon Apr  1 16:15:29 2019	(r345767)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.cc	Mon Apr  1 16:36:02 2019	(r345768)
@@ -166,6 +166,7 @@ void FuseTest::expect_open(uint64_t ino, uint32_t flag
 
 void FuseTest::expect_opendir(uint64_t ino)
 {
+	/* opendir(3) calls fstatfs */
 	EXPECT_CALL(*m_mock, process(
 		ResultOf([](auto in) {
 			return (in->header.opcode == FUSE_STATFS);


More information about the svn-src-projects mailing list