extern "C" {
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
}
#include "mockfs.hh"
#include "utils.hh"
using namespace testing;
class Execv: public FuseTest {
public:
virtual void SetUp() {
m_init_flags = FUSE_ASYNC_READ;
FuseTest::SetUp();
}
};
class Fexecv: public Execv {};
class FexecvDefaultPermissions: public Fexecv {
virtual void SetUp() {
m_default_permissions = true;
Fexecv::SetUp();
}
};
TEST_F(Execv, close)
{
const static char FULLPATH[] = "mountpoint/true";
const static char RELPATH[] = "true";
const static size_t BUFSIZE = 16384;
FILE *true_file;
uint64_t ino = 42;
size_t true_len;
int status;
char *buf;
buf = new char[BUFSIZE];
true_file = fopen("/usr/bin/true", "r");
ASSERT_TRUE(true_file) << strerror(errno);
true_len = fread(buf, 1, BUFSIZE, true_file);
ASSERT_LT(true_len, BUFSIZE) << "Must increase BUFSIZE";
fclose(true_file);
fork(false, &status, [&] {
expect_lookup(RELPATH, ino, S_IFREG | 0755, true_len, 1,
UINT64_MAX);
expect_open(ino, 0, 1);
expect_read(ino, 0, true_len, true_len, buf);
expect_flush(ino, 1, ReturnErrno(ENOSYS));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_RELEASE &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).Times(1)
.WillRepeatedly(Invoke(ReturnErrno(0)));
}, [&] {
char *const argv[] = {__DECONST(char *, "true"), NULL};
char *const env[] = {NULL};
execve(FULLPATH, argv, env);
fprintf(stderr, "execv: %s\n", strerror(errno));
return 1;
});
ASSERT_EQ(0, WEXITSTATUS(status));
delete[] buf;
}
TEST_F(Fexecv, close)
{
const static char FULLPATH[] = "mountpoint/true";
const static char RELPATH[] = "true";
const static size_t BUFSIZE = 16384;
FILE *true_file;
uint64_t ino = 42;
size_t true_len;
int status;
char *buf;
buf = new char[BUFSIZE];
true_file = fopen("/usr/bin/true", "r");
ASSERT_TRUE(true_file) << strerror(errno);
true_len = fread(buf, 1, BUFSIZE, true_file);
ASSERT_LT(true_len, BUFSIZE) << "Must increase BUFSIZE";
fclose(true_file);
fork(false, &status, [&] {
expect_lookup(RELPATH, ino, S_IFREG | 0755, true_len, 1,
UINT64_MAX);
expect_open(ino, 0, 2);
expect_read(ino, 0, true_len, true_len, buf);
expect_flush(ino, 1, ReturnErrno(ENOSYS));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_RELEASE &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).Times(2)
.WillRepeatedly(Invoke(ReturnErrno(0)));
}, [&] {
char *const argv[] = {__DECONST(char *, "true"), NULL};
char *const env[] = {NULL};
int fd;
fd = open(FULLPATH, O_EXEC);
if (fd < 0) {
fprintf(stderr, "open: %s\n", strerror(errno));
return 1;
}
fexecve(fd, argv, env);
fprintf(stderr, "execv: %s\n", strerror(errno));
return 1;
});
ASSERT_EQ(0, WEXITSTATUS(status));
delete[] buf;
}
TEST_F(Fexecv, close_noopen)
{
const static char FULLPATH[] = "mountpoint/true";
const static char RELPATH[] = "true";
const static size_t BUFSIZE = 16384;
FILE *true_file;
uint64_t ino = 42;
size_t true_len;
int status;
char *buf;
buf = new char[BUFSIZE];
true_file = fopen("/usr/bin/true", "r");
ASSERT_TRUE(true_file) << strerror(errno);
true_len = fread(buf, 1, BUFSIZE, true_file);
ASSERT_LT(true_len, BUFSIZE) << "Must increase BUFSIZE";
fclose(true_file);
fork(false, &status, [&] {
expect_lookup(RELPATH, ino, S_IFREG | 0755, true_len, 1,
UINT64_MAX);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_OPEN &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).Times(1)
.WillOnce(Invoke(ReturnErrno(ENOSYS)));
expect_read(ino, 0, true_len, true_len, buf, -1, 0);
expect_flush(ino, 1, ReturnErrno(ENOSYS));
}, [&] {
char *const argv[] = {__DECONST(char *, "true"), NULL};
char *const env[] = {NULL};
int fd;
fd = open(FULLPATH, O_EXEC);
if (fd < 0) {
fprintf(stderr, "open: %s\n", strerror(errno));
return 1;
}
fexecve(fd, argv, env);
fprintf(stderr, "execv: %s\n", strerror(errno));
return 1;
});
ASSERT_EQ(0, WEXITSTATUS(status));
delete[] buf;
}
TEST_F(FexecvDefaultPermissions, atime)
{
const static char FULLPATH[] = "mountpoint/true";
const static char RELPATH[] = "true";
const static size_t BUFSIZE = 16384;
FILE *true_file;
uint64_t ino = 42;
size_t true_len;
int status;
char *buf;
buf = new char[BUFSIZE];
true_file = fopen("/usr/bin/true", "r");
ASSERT_TRUE(true_file) << strerror(errno);
true_len = fread(buf, 1, BUFSIZE, true_file);
ASSERT_LT(true_len, BUFSIZE) << "Must increase BUFSIZE";
fclose(true_file);
fork(false, &status, [&] {
expect_lookup(RELPATH, ino, S_IFREG | 0777, true_len, 1,
UINT64_MAX);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
in.header.nodeid == FUSE_ROOT_ID);
}, Eq(true)),
_)
).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
SET_OUT_HEADER_LEN(out, attr);
out.body.attr.attr.ino = FUSE_ROOT_ID;
out.body.attr.attr.mode = S_IFDIR | 0777;
out.body.attr.attr.size = 0;
out.body.attr.attr_valid = UINT64_MAX;
})));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_OPEN &&
in.header.nodeid == ino);
}, Eq(true)),
_)
).Times(1)
.WillOnce(Invoke(ReturnErrno(ENOSYS)));
expect_read(ino, 0, true_len, true_len, buf, -1, 0);
expect_flush(ino, 1, ReturnErrno(ENOSYS));
EXPECT_CALL(*m_mock, process(
ResultOf([&](auto in) {
return (in.header.opcode == FUSE_SETATTR &&
in.header.nodeid == ino &&
in.body.setattr.valid == FATTR_ATIME);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, attr);
out.body.attr.attr.ino = ino;
out.body.attr.attr.mode = S_IFREG | 0777;
})));
}, [&] {
char *const argv[] = {__DECONST(char *, "true"), NULL};
char *const env[] = {NULL};
char cbuf[8];
int fd;
fd = open(FULLPATH, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open: %s\n", strerror(errno));
return 1;
}
if (read(fd, cbuf, sizeof(cbuf)) < 0) {
fprintf(stderr, "read: %s\n", strerror(errno));
return 1;
}
fexecve(fd, argv, env);
fprintf(stderr, "execv: %s\n", strerror(errno));
return 1;
});
ASSERT_EQ(0, WEXITSTATUS(status));
delete[] buf;
}