Changeset View
Changeset View
Standalone View
Standalone View
head/tests/sys/fs/fusefs/unlink.cc
Show All 35 Lines | |||||
#include "mockfs.hh" | #include "mockfs.hh" | ||||
#include "utils.hh" | #include "utils.hh" | ||||
using namespace testing; | using namespace testing; | ||||
class Unlink: public FuseTest { | class Unlink: public FuseTest { | ||||
public: | public: | ||||
void expect_getattr(uint64_t ino, mode_t mode) | |||||
{ | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
return (in.header.opcode == FUSE_GETATTR && | |||||
in.header.nodeid == ino); | |||||
}, Eq(true)), | |||||
_) | |||||
).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { | |||||
SET_OUT_HEADER_LEN(out, attr); | |||||
out.body.attr.attr.ino = ino; // Must match nodeid | |||||
out.body.attr.attr.mode = mode; | |||||
out.body.attr.attr_valid = UINT64_MAX; | |||||
}))); | |||||
} | |||||
void expect_lookup(const char *relpath, uint64_t ino, int times, int nlink=1) | void expect_lookup(const char *relpath, uint64_t ino, int times, int nlink=1) | ||||
{ | { | ||||
EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) | EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) | ||||
.Times(times) | .Times(times) | ||||
.WillRepeatedly(Invoke( | .WillRepeatedly(Invoke( | ||||
ReturnImmediate([=](auto in __unused, auto& out) { | ReturnImmediate([=](auto in __unused, auto& out) { | ||||
SET_OUT_HEADER_LEN(out, entry); | SET_OUT_HEADER_LEN(out, entry); | ||||
out.body.entry.attr.mode = S_IFREG | 0644; | out.body.entry.attr.mode = S_IFREG | 0644; | ||||
Show All 16 Lines | TEST_F(Unlink, attr_cache) | ||||
const char FULLPATH0[] = "mountpoint/some_file.txt"; | const char FULLPATH0[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH0[] = "some_file.txt"; | const char RELPATH0[] = "some_file.txt"; | ||||
const char FULLPATH1[] = "mountpoint/other_file.txt"; | const char FULLPATH1[] = "mountpoint/other_file.txt"; | ||||
const char RELPATH1[] = "other_file.txt"; | const char RELPATH1[] = "other_file.txt"; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
struct stat sb_old, sb_new; | struct stat sb_old, sb_new; | ||||
int fd1; | int fd1; | ||||
expect_getattr(1, S_IFDIR | 0755); | |||||
expect_lookup(RELPATH0, ino, 1, 2); | expect_lookup(RELPATH0, ino, 1, 2); | ||||
expect_lookup(RELPATH1, ino, 1, 2); | expect_lookup(RELPATH1, ino, 1, 2); | ||||
expect_open(ino, 0, 1); | expect_open(ino, 0, 1); | ||||
expect_unlink(1, RELPATH0, 0); | expect_unlink(1, RELPATH0, 0); | ||||
fd1 = open(FULLPATH1, O_RDONLY); | fd1 = open(FULLPATH1, O_RDONLY); | ||||
ASSERT_LE(0, fd1) << strerror(errno); | ASSERT_LE(0, fd1) << strerror(errno); | ||||
Show All 11 Lines | |||||
* because the fuse daemon should update its mtime and ctime | * because the fuse daemon should update its mtime and ctime | ||||
*/ | */ | ||||
TEST_F(Unlink, parent_attr_cache) | TEST_F(Unlink, parent_attr_cache) | ||||
{ | { | ||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | const char FULLPATH[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH[] = "some_file.txt"; | const char RELPATH[] = "some_file.txt"; | ||||
struct stat sb; | struct stat sb; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
Sequence seq; | |||||
/* Use nlink=2 so we don't get a FUSE_FORGET */ | |||||
expect_lookup(RELPATH, ino, 1, 2); | |||||
EXPECT_CALL(*m_mock, process( | EXPECT_CALL(*m_mock, process( | ||||
ResultOf([=](auto in) { | ResultOf([=](auto in) { | ||||
return (in.header.opcode == FUSE_UNLINK && | |||||
0 == strcmp(RELPATH, in.body.unlink) && | |||||
in.header.nodeid == FUSE_ROOT_ID); | |||||
}, Eq(true)), | |||||
_) | |||||
).InSequence(seq) | |||||
.WillOnce(Invoke(ReturnErrno(0))); | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
return (in.header.opcode == FUSE_GETATTR && | return (in.header.opcode == FUSE_GETATTR && | ||||
in.header.nodeid == FUSE_ROOT_ID); | in.header.nodeid == FUSE_ROOT_ID); | ||||
}, Eq(true)), | }, Eq(true)), | ||||
_) | _) | ||||
).Times(2) | ).InSequence(seq) | ||||
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { | .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { | ||||
SET_OUT_HEADER_LEN(out, attr); | SET_OUT_HEADER_LEN(out, attr); | ||||
out.body.attr.attr.ino = ino; // Must match nodeid | out.body.attr.attr.ino = FUSE_ROOT_ID; | ||||
out.body.attr.attr.mode = S_IFDIR | 0755; | out.body.attr.attr.mode = S_IFDIR | 0755; | ||||
out.body.attr.attr_valid = UINT64_MAX; | out.body.attr.attr_valid = UINT64_MAX; | ||||
}))); | }))); | ||||
/* Use nlink=2 so we don't get a FUSE_FORGET */ | |||||
expect_lookup(RELPATH, ino, 1, 2); | |||||
expect_unlink(1, RELPATH, 0); | |||||
ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); | ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); | ||||
EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno); | EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno); | ||||
} | } | ||||
TEST_F(Unlink, eperm) | TEST_F(Unlink, eperm) | ||||
{ | { | ||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | const char FULLPATH[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH[] = "some_file.txt"; | const char RELPATH[] = "some_file.txt"; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
expect_getattr(1, S_IFDIR | 0755); | |||||
expect_lookup(RELPATH, ino, 1); | expect_lookup(RELPATH, ino, 1); | ||||
expect_unlink(1, RELPATH, EPERM); | expect_unlink(1, RELPATH, EPERM); | ||||
ASSERT_NE(0, unlink(FULLPATH)); | ASSERT_NE(0, unlink(FULLPATH)); | ||||
ASSERT_EQ(EPERM, errno); | ASSERT_EQ(EPERM, errno); | ||||
} | } | ||||
/* | /* | ||||
* Unlinking a file should expire its entry cache, even if it's multiply linked | * Unlinking a file should expire its entry cache, even if it's multiply linked | ||||
*/ | */ | ||||
TEST_F(Unlink, entry_cache) | TEST_F(Unlink, entry_cache) | ||||
{ | { | ||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | const char FULLPATH[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH[] = "some_file.txt"; | const char RELPATH[] = "some_file.txt"; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
expect_getattr(1, S_IFDIR | 0755); | |||||
expect_lookup(RELPATH, ino, 2, 2); | expect_lookup(RELPATH, ino, 2, 2); | ||||
expect_unlink(1, RELPATH, 0); | expect_unlink(1, RELPATH, 0); | ||||
ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); | ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); | ||||
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); | ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); | ||||
} | } | ||||
/* | /* | ||||
* Unlink a multiply-linked file. There should be no FUSE_FORGET because the | * Unlink a multiply-linked file. There should be no FUSE_FORGET because the | ||||
* file is still linked. | * file is still linked. | ||||
*/ | */ | ||||
TEST_F(Unlink, multiply_linked) | TEST_F(Unlink, multiply_linked) | ||||
{ | { | ||||
const char FULLPATH0[] = "mountpoint/some_file.txt"; | const char FULLPATH0[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH0[] = "some_file.txt"; | const char RELPATH0[] = "some_file.txt"; | ||||
const char FULLPATH1[] = "mountpoint/other_file.txt"; | const char FULLPATH1[] = "mountpoint/other_file.txt"; | ||||
const char RELPATH1[] = "other_file.txt"; | const char RELPATH1[] = "other_file.txt"; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
expect_getattr(1, S_IFDIR | 0755); | |||||
expect_lookup(RELPATH0, ino, 1, 2); | expect_lookup(RELPATH0, ino, 1, 2); | ||||
expect_unlink(1, RELPATH0, 0); | expect_unlink(1, RELPATH0, 0); | ||||
EXPECT_CALL(*m_mock, process( | EXPECT_CALL(*m_mock, process( | ||||
ResultOf([=](auto in) { | ResultOf([=](auto in) { | ||||
return (in.header.opcode == FUSE_FORGET && | return (in.header.opcode == FUSE_FORGET && | ||||
in.header.nodeid == ino); | in.header.nodeid == ino); | ||||
}, Eq(true)), | }, Eq(true)), | ||||
_) | _) | ||||
Show All 14 Lines | |||||
{ | { | ||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | const char FULLPATH[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH[] = "some_file.txt"; | const char RELPATH[] = "some_file.txt"; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
sem_t sem; | sem_t sem; | ||||
ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); | ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); | ||||
expect_getattr(1, S_IFDIR | 0755); | |||||
expect_lookup(RELPATH, ino, 1); | expect_lookup(RELPATH, ino, 1); | ||||
expect_unlink(1, RELPATH, 0); | expect_unlink(1, RELPATH, 0); | ||||
expect_forget(ino, 1, &sem); | expect_forget(ino, 1, &sem); | ||||
ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); | ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); | ||||
sem_wait(&sem); | sem_wait(&sem); | ||||
sem_destroy(&sem); | sem_destroy(&sem); | ||||
} | } | ||||
/* Unlink an open file */ | /* Unlink an open file */ | ||||
TEST_F(Unlink, open_but_deleted) | TEST_F(Unlink, open_but_deleted) | ||||
{ | { | ||||
const char FULLPATH0[] = "mountpoint/some_file.txt"; | const char FULLPATH0[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH0[] = "some_file.txt"; | const char RELPATH0[] = "some_file.txt"; | ||||
const char FULLPATH1[] = "mountpoint/other_file.txt"; | const char FULLPATH1[] = "mountpoint/other_file.txt"; | ||||
const char RELPATH1[] = "other_file.txt"; | const char RELPATH1[] = "other_file.txt"; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
int fd; | int fd; | ||||
expect_getattr(1, S_IFDIR | 0755); | |||||
expect_lookup(RELPATH0, ino, 2); | expect_lookup(RELPATH0, ino, 2); | ||||
expect_open(ino, 0, 1); | expect_open(ino, 0, 1); | ||||
expect_unlink(1, RELPATH0, 0); | expect_unlink(1, RELPATH0, 0); | ||||
expect_lookup(RELPATH1, ino, 1, 1); | expect_lookup(RELPATH1, ino, 1, 1); | ||||
fd = open(FULLPATH0, O_RDWR); | fd = open(FULLPATH0, O_RDWR); | ||||
ASSERT_LE(0, fd) << strerror(errno); | ASSERT_LE(0, fd) << strerror(errno); | ||||
ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno); | ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno); | ||||
Show All 9 Lines |