mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-18 10:35:55 +00:00
fusefs: fix the FUSE_INTERRUPT tests when data_cache_mode==2
Replace most write operations with mkdir so they won't be affected by the setting of vfs.fusefs.data_cache_mode. Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
21d4686c5c
commit
9a17702912
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=346637
@ -45,6 +45,10 @@ using namespace testing;
|
||||
|
||||
/* Initial size of files used by these tests */
|
||||
const off_t FILESIZE = 1000;
|
||||
/* Access mode used by all directories in these tests */
|
||||
const mode_t MODE = 0755;
|
||||
const char FULLDIRPATH0[] = "mountpoint/some_dir";
|
||||
const char RELDIRPATH0[] = "some_dir";
|
||||
|
||||
static sem_t *signaled_semaphore;
|
||||
|
||||
@ -82,6 +86,23 @@ void expect_lookup(const char *relpath, uint64_t ino)
|
||||
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, FILESIZE, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a FUSE_MKDIR but don't reply. Instead, just record the unique value
|
||||
* to the provided pointer
|
||||
*/
|
||||
void expect_mkdir(uint64_t *mkdir_unique)
|
||||
{
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_MKDIR);
|
||||
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto &out __unused) {
|
||||
*mkdir_unique = in->header.unique;
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a FUSE_READ but don't reply. Instead, just record the unique value
|
||||
* to the provided pointer
|
||||
@ -149,32 +170,27 @@ void TearDown() {
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, already_complete)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique = 0;
|
||||
uint64_t mkdir_unique = 0;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_mkdir(&mkdir_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
in->body.interrupt.unique == mkdir_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in, auto &out) {
|
||||
// First complete the write request
|
||||
// First complete the mkdir request
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.unique = write_unique;
|
||||
SET_OUT_HEADER_LEN(out0, write);
|
||||
out0->body.write.size = bufsize;
|
||||
out0->header.unique = mkdir_unique;
|
||||
SET_OUT_HEADER_LEN(out0, entry);
|
||||
out0->body.create.entry.attr.mode = S_IFDIR | MODE;
|
||||
out0->body.create.entry.nodeid = ino;
|
||||
out.push_back(out0);
|
||||
|
||||
// Then, respond EAGAIN to the interrupt request
|
||||
@ -185,13 +201,8 @@ TEST_F(Interrupt, already_complete)
|
||||
out.push_back(out1);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
EXPECT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
EXPECT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -200,45 +211,31 @@ TEST_F(Interrupt, already_complete)
|
||||
*/
|
||||
TEST_F(Interrupt, fatal_signal)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
uint64_t ino = 42;
|
||||
int status;
|
||||
pthread_t self;
|
||||
uint64_t write_unique;
|
||||
uint64_t mkdir_unique;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_mkdir(&mkdir_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
in->body.interrupt.unique == mkdir_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
|
||||
/* Don't respond. The process should exit anyway */
|
||||
}));
|
||||
expect_flush(ino, 1, ReturnErrno(0));
|
||||
expect_release(ino, FH);
|
||||
|
||||
fork(false, &status, [&] {
|
||||
}, [&]() {
|
||||
struct sigaction sa;
|
||||
int fd, r;
|
||||
int r;
|
||||
pthread_t killer_th;
|
||||
pthread_t self;
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SIGUSR2 terminates the process by default */
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
@ -254,12 +251,10 @@ TEST_F(Interrupt, fatal_signal)
|
||||
return 1;
|
||||
}
|
||||
|
||||
write(fd, CONTENTS, bufsize);
|
||||
mkdir(FULLDIRPATH0, MODE);
|
||||
return 1;
|
||||
});
|
||||
ASSERT_EQ(SIGUSR2, WTERMSIG(status));
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -269,45 +264,45 @@ TEST_F(Interrupt, fatal_signal)
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, ignore)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique;
|
||||
uint64_t mkdir_unique;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_mkdir(&mkdir_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
in->body.interrupt.unique == mkdir_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in __unused, auto &out) {
|
||||
// Ignore FUSE_INTERRUPT; respond to the FUSE_WRITE
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.unique = write_unique;
|
||||
SET_OUT_HEADER_LEN(out0, write);
|
||||
out0->body.write.size = bufsize;
|
||||
out0->header.unique = mkdir_unique;
|
||||
SET_OUT_HEADER_LEN(out0, entry);
|
||||
out0->body.create.entry.attr.mode = S_IFDIR | MODE;
|
||||
out0->body.create.entry.nodeid = ino;
|
||||
out.push_back(out0);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
|
||||
}
|
||||
|
||||
void* write0(void* arg) {
|
||||
void* mkdir0(void* arg __unused) {
|
||||
ssize_t r;
|
||||
|
||||
r = mkdir(FULLDIRPATH0, MODE);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
else
|
||||
return (void*)(intptr_t)errno;
|
||||
}
|
||||
|
||||
void* setxattr0(void* arg) {
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
int fd = (int)(intptr_t)arg;
|
||||
@ -333,75 +328,6 @@ void* read1(void* arg) {
|
||||
return (void*)(intptr_t)errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* An operation that hasn't yet been sent to userland can be interrupted
|
||||
* without sending FUSE_INTERRUPT
|
||||
*/
|
||||
TEST_F(Interrupt, in_kernel)
|
||||
{
|
||||
const char FULLPATH0[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH0[] = "some_file.txt";
|
||||
const char FULLPATH1[] = "mountpoint/other_file.txt";
|
||||
const char RELPATH1[] = "other_file.txt";
|
||||
const char *CONTENTS = "ijklmnop";
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
uint64_t ino0 = 42, ino1 = 43;
|
||||
int fd0, fd1;
|
||||
pthread_t self, th0;
|
||||
sem_t sem0, sem1;
|
||||
void *thr0_value;
|
||||
|
||||
ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
|
||||
ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH0, ino0);
|
||||
expect_open(ino0, 0, 1);
|
||||
expect_lookup(RELPATH1, ino1);
|
||||
expect_open(ino1, 0, 1);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_WRITE &&
|
||||
in->header.nodeid == ino0);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) {
|
||||
/* Let the next write proceed */
|
||||
sem_post(&sem1);
|
||||
/* Pause the daemon thread so it won't read the next op */
|
||||
sem_wait(&sem0);
|
||||
|
||||
SET_OUT_HEADER_LEN(out, write);
|
||||
out->body.write.size = in->body.write.size;
|
||||
})));
|
||||
|
||||
fd0 = open(FULLPATH0, O_WRONLY);
|
||||
ASSERT_LE(0, fd0) << strerror(errno);
|
||||
fd1 = open(FULLPATH1, O_WRONLY);
|
||||
ASSERT_LE(0, fd1) << strerror(errno);
|
||||
|
||||
/* Use a separate thread for the first write */
|
||||
ASSERT_EQ(0, pthread_create(&th0, NULL, write0, (void*)(intptr_t)fd0))
|
||||
<< strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
|
||||
sem_wait(&sem1); /* Sequence the two writes */
|
||||
ASSERT_EQ(-1, write(fd1, CONTENTS, bufsize));
|
||||
EXPECT_EQ(EINTR, errno);
|
||||
|
||||
/* Unstick the daemon */
|
||||
ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
|
||||
|
||||
/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
|
||||
usleep(250'000);
|
||||
|
||||
pthread_join(th0, &thr0_value);
|
||||
EXPECT_EQ(0, (intptr_t)thr0_value);
|
||||
sem_destroy(&sem1);
|
||||
sem_destroy(&sem0);
|
||||
}
|
||||
|
||||
/*
|
||||
* A restartable operation (basically, anything except write or setextattr)
|
||||
* that hasn't yet been sent to userland can be interrupted without sending
|
||||
@ -409,12 +335,10 @@ TEST_F(Interrupt, in_kernel)
|
||||
*/
|
||||
TEST_F(Interrupt, in_kernel_restartable)
|
||||
{
|
||||
const char FULLPATH0[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH0[] = "some_file.txt";
|
||||
const char FULLPATH1[] = "mountpoint/other_file.txt";
|
||||
const char RELPATH1[] = "other_file.txt";
|
||||
uint64_t ino0 = 42, ino1 = 43;
|
||||
int fd0, fd1;
|
||||
int fd1;
|
||||
pthread_t self, th0, th1;
|
||||
sem_t sem0, sem1;
|
||||
void *thr0_value, *thr1_value;
|
||||
@ -423,34 +347,31 @@ TEST_F(Interrupt, in_kernel_restartable)
|
||||
ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH0, ino0);
|
||||
expect_open(ino0, 0, 1);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_lookup(RELPATH1, ino1);
|
||||
expect_open(ino1, 0, 1);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_WRITE &&
|
||||
in->header.nodeid == ino0);
|
||||
return (in->header.opcode == FUSE_MKDIR);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto out) {
|
||||
/* Let the next write proceed */
|
||||
sem_post(&sem1);
|
||||
/* Pause the daemon thread so it won't read the next op */
|
||||
sem_wait(&sem0);
|
||||
|
||||
SET_OUT_HEADER_LEN(out, write);
|
||||
out->body.write.size = in->body.write.size;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.create.entry.attr.mode = S_IFDIR | MODE;
|
||||
out->body.create.entry.nodeid = ino0;
|
||||
})));
|
||||
FuseTest::expect_read(ino1, 0, FILESIZE, 0, NULL);
|
||||
|
||||
fd0 = open(FULLPATH0, O_WRONLY);
|
||||
ASSERT_LE(0, fd0) << strerror(errno);
|
||||
fd1 = open(FULLPATH1, O_RDONLY);
|
||||
ASSERT_LE(0, fd1) << strerror(errno);
|
||||
|
||||
/* Use a separate thread for each operation */
|
||||
ASSERT_EQ(0, pthread_create(&th0, NULL, write0, (void*)(intptr_t)fd0))
|
||||
ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
|
||||
<< strerror(errno);
|
||||
|
||||
sem_wait(&sem1); /* Sequence the two operations */
|
||||
@ -476,21 +397,20 @@ TEST_F(Interrupt, in_kernel_restartable)
|
||||
sem_destroy(&sem0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Like FUSE_WRITE, FUSE_SETXATTR is non-restartable because it calls uiomove
|
||||
* before blocking in fticket_wait_answ
|
||||
/*
|
||||
* An operation that hasn't yet been sent to userland can be interrupted
|
||||
* without sending FUSE_INTERRUPT. If it's a non-restartable operation (write
|
||||
* or setextattr) it will return EINTR.
|
||||
*/
|
||||
TEST_F(Interrupt, in_kernel_setxattr)
|
||||
TEST_F(Interrupt, in_kernel_nonrestartable)
|
||||
{
|
||||
const char FULLPATH0[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH0[] = "some_file.txt";
|
||||
const char FULLPATH1[] = "mountpoint/other_file.txt";
|
||||
const char RELPATH1[] = "other_file.txt";
|
||||
const char value[] = "whatever";
|
||||
ssize_t value_len = strlen(value) + 1;
|
||||
uint64_t ino0 = 42, ino1 = 43;
|
||||
int ns = EXTATTR_NAMESPACE_USER;
|
||||
int fd0, fd1;
|
||||
int fd1;
|
||||
pthread_t self, th0;
|
||||
sem_t sem0, sem1;
|
||||
void *thr0_value;
|
||||
@ -500,33 +420,30 @@ TEST_F(Interrupt, in_kernel_setxattr)
|
||||
ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH0, ino0);
|
||||
expect_open(ino0, 0, 1);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_lookup(RELPATH1, ino1);
|
||||
expect_open(ino1, 0, 1);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_WRITE &&
|
||||
in->header.nodeid == ino0);
|
||||
return (in->header.opcode == FUSE_MKDIR);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto out) {
|
||||
/* Let the next write proceed */
|
||||
sem_post(&sem1);
|
||||
/* Pause the daemon thread so it won't read the next op */
|
||||
sem_wait(&sem0);
|
||||
|
||||
SET_OUT_HEADER_LEN(out, write);
|
||||
out->body.write.size = in->body.write.size;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.create.entry.attr.mode = S_IFDIR | MODE;
|
||||
out->body.create.entry.nodeid = ino0;
|
||||
})));
|
||||
|
||||
fd0 = open(FULLPATH0, O_WRONLY);
|
||||
ASSERT_LE(0, fd0) << strerror(errno);
|
||||
fd1 = open(FULLPATH1, O_WRONLY);
|
||||
ASSERT_LE(0, fd1) << strerror(errno);
|
||||
|
||||
/* Use a separate thread for the first write */
|
||||
ASSERT_EQ(0, pthread_create(&th0, NULL, write0, (void*)(intptr_t)fd0))
|
||||
ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
|
||||
<< strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
@ -556,42 +473,30 @@ TEST_F(Interrupt, in_kernel_setxattr)
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, in_progress)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique;
|
||||
uint64_t mkdir_unique;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_mkdir(&mkdir_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
in->body.interrupt.unique == mkdir_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in __unused, auto &out) {
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.error = -EINTR;
|
||||
out0->header.unique = write_unique;
|
||||
out0->header.unique = mkdir_unique;
|
||||
out0->header.len = sizeof(out0->header);
|
||||
out.push_back(out0);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
ASSERT_EQ(-1, write(fd, CONTENTS, bufsize));
|
||||
ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
|
||||
EXPECT_EQ(EINTR, errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
||||
/* Reads should also be interruptible */
|
||||
@ -638,16 +543,11 @@ TEST_F(Interrupt, in_progress_read)
|
||||
/* FUSE_INTERRUPT operations should take priority over other pending ops */
|
||||
TEST_F(Interrupt, priority)
|
||||
{
|
||||
const char FULLPATH0[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH0[] = "some_file.txt";
|
||||
const char FULLPATH1[] = "mountpoint/other_file.txt";
|
||||
const char RELPATH1[] = "other_file.txt";
|
||||
const char *CONTENTS = "ijklmnop";
|
||||
const char FULLPATH1[] = "mountpoint/other_dir";
|
||||
const char RELPATH1[] = "other_dir";
|
||||
Sequence seq;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
uint64_t ino0 = 42, ino1 = 43;
|
||||
int fd0, fd1;
|
||||
uint64_t write_unique;
|
||||
uint64_t ino1 = 43;
|
||||
uint64_t mkdir_unique;
|
||||
pthread_t self, th0;
|
||||
sem_t sem0, sem1;
|
||||
|
||||
@ -655,21 +555,19 @@ TEST_F(Interrupt, priority)
|
||||
ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH0, ino0);
|
||||
expect_open(ino0, 0, 1);
|
||||
expect_lookup(RELPATH1, ino1);
|
||||
expect_open(ino1, 0, 1);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
EXPECT_LOOKUP(1, RELPATH1).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
//expect_mkdir(&mkdir_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_WRITE &&
|
||||
in->header.nodeid == ino0);
|
||||
return (in->header.opcode == FUSE_MKDIR);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).InSequence(seq)
|
||||
.WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) {
|
||||
write_unique = in->header.unique;
|
||||
mkdir_unique = in->header.unique;
|
||||
|
||||
/* Let the next write proceed */
|
||||
/* Let the next mkdir proceed */
|
||||
sem_post(&sem1);
|
||||
|
||||
/* Pause the daemon thread so it won't read the next op */
|
||||
@ -677,47 +575,42 @@ TEST_F(Interrupt, priority)
|
||||
|
||||
/* Finally, interrupt the original op */
|
||||
out->header.error = -EINTR;
|
||||
out->header.unique = write_unique;
|
||||
out->header.unique = mkdir_unique;
|
||||
out->header.len = sizeof(out->header);
|
||||
})));
|
||||
/*
|
||||
* FUSE_INTERRUPT should be received before the second FUSE_WRITE, even
|
||||
* FUSE_INTERRUPT should be received before the second FUSE_MKDIR, even
|
||||
* though it was generated later
|
||||
*/
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
in->body.interrupt.unique == mkdir_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).InSequence(seq)
|
||||
.WillOnce(Invoke(ReturnErrno(EAGAIN)));
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_WRITE &&
|
||||
in->header.nodeid == ino1);
|
||||
return (in->header.opcode == FUSE_MKDIR);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).InSequence(seq)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in , auto out) {
|
||||
SET_OUT_HEADER_LEN(out, write);
|
||||
out->body.write.size = in->body.write.size;
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.create.entry.attr.mode = S_IFDIR | MODE;
|
||||
out->body.create.entry.nodeid = ino1;
|
||||
})));
|
||||
|
||||
fd0 = open(FULLPATH0, O_WRONLY);
|
||||
ASSERT_LE(0, fd0) << strerror(errno);
|
||||
fd1 = open(FULLPATH1, O_WRONLY);
|
||||
ASSERT_LE(0, fd1) << strerror(errno);
|
||||
|
||||
/* Use a separate thread for the first write */
|
||||
ASSERT_EQ(0, pthread_create(&th0, NULL, write0, (void*)(intptr_t)fd0))
|
||||
/* Use a separate thread for the first mkdir */
|
||||
ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
|
||||
<< strerror(errno);
|
||||
|
||||
signaled_semaphore = &sem0;
|
||||
|
||||
sem_wait(&sem1); /* Sequence the two writes */
|
||||
sem_wait(&sem1); /* Sequence the two mkdirs */
|
||||
setup_interruptor(th0);
|
||||
ASSERT_EQ(bufsize, write(fd1, CONTENTS, bufsize)) << strerror(errno);
|
||||
ASSERT_EQ(0, mkdir(FULLPATH1, MODE)) << strerror(errno);
|
||||
|
||||
/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
|
||||
usleep(250'000);
|
||||
@ -740,26 +633,19 @@ TEST_F(Interrupt, priority)
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, too_soon)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
Sequence seq;
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique;
|
||||
uint64_t mkdir_unique;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_LOOKUP(1, RELDIRPATH0).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_mkdir(&mkdir_unique);
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
in->body.interrupt.unique == mkdir_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).InSequence(seq)
|
||||
@ -768,23 +654,20 @@ TEST_F(Interrupt, too_soon)
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
in->body.interrupt.unique == mkdir_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).InSequence(seq)
|
||||
.WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.error = -EINTR;
|
||||
out0->header.unique = write_unique;
|
||||
out0->header.unique = mkdir_unique;
|
||||
out0->header.len = sizeof(out0->header);
|
||||
out.push_back(out0);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
ASSERT_EQ(-1, write(fd, CONTENTS, bufsize));
|
||||
ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
|
||||
EXPECT_EQ(EINTR, errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
|
Loading…
Reference in New Issue
Block a user