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
This commit is contained in:
parent
4a6d5507f7
commit
363a74163b
@ -121,12 +121,8 @@ fuse_filehandle_open(struct vnode *vp, fufh_type_t fufh_type,
|
||||
|
||||
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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,14 +99,7 @@ TEST_F(Opendir, eperm)
|
||||
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(ReturnErrno(EPERM)));
|
||||
expect_opendir(ino, O_RDONLY, ReturnErrno(EPERM));
|
||||
|
||||
EXPECT_NE(0, open(FULLPATH, O_DIRECTORY));
|
||||
EXPECT_EQ(EPERM, errno);
|
||||
@ -102,20 +112,32 @@ 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);
|
||||
}
|
||||
|
||||
/* 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_opendir(ino, O_EXEC,
|
||||
ReturnImmediate([=](auto in __unused, auto out) {
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_EXEC | O_DIRECTORY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
}
|
||||
|
||||
TEST_F(Opendir, opendir)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_dir";
|
||||
@ -123,24 +145,10 @@ TEST_F(Opendir, opendir)
|
||||
uint64_t ino = 42;
|
||||
|
||||
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_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);
|
||||
})));
|
||||
}));
|
||||
|
||||
errno = 0;
|
||||
EXPECT_NE(NULL, opendir(FULLPATH)) << strerror(errno);
|
||||
|
@ -52,6 +52,7 @@ void expect_readdir(uint64_t ino, uint64_t off, vector<struct dirent> &ents)
|
||||
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)),
|
||||
_)
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
extern "C" {
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
}
|
||||
|
||||
#include "mockfs.hh"
|
||||
@ -107,3 +108,21 @@ TEST_F(ReleaseDir, ok)
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -166,6 +166,7 @@ void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
|
||||
|
||||
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user