Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/fs/fusefs/read.cc
Show First 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) | ||||
<< strerror(errno); | << strerror(errno); | ||||
m_maxreadahead = val * get<1>(GetParam()); | m_maxreadahead = val * get<1>(GetParam()); | ||||
m_noclusterr = get<0>(GetParam()); | m_noclusterr = get<0>(GetParam()); | ||||
Read::SetUp(); | Read::SetUp(); | ||||
} | } | ||||
}; | }; | ||||
class ReadNoatime: public Read { | |||||
virtual void SetUp() { | |||||
m_noatime = true; | |||||
Read::SetUp(); | |||||
} | |||||
}; | |||||
class ReadSigbus: public Read | class ReadSigbus: public Read | ||||
{ | { | ||||
public: | public: | ||||
static jmp_buf s_jmpbuf; | static jmp_buf s_jmpbuf; | ||||
static void *s_si_addr; | static void *s_si_addr; | ||||
void TearDown() { | void TearDown() { | ||||
struct sigaction sa; | struct sigaction sa; | ||||
Show All 11 Lines | |||||
handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) { | handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) { | ||||
ReadSigbus::s_si_addr = info->si_addr; | ReadSigbus::s_si_addr = info->si_addr; | ||||
longjmp(ReadSigbus::s_jmpbuf, 1); | longjmp(ReadSigbus::s_jmpbuf, 1); | ||||
} | } | ||||
jmp_buf ReadSigbus::s_jmpbuf; | jmp_buf ReadSigbus::s_jmpbuf; | ||||
void *ReadSigbus::s_si_addr; | void *ReadSigbus::s_si_addr; | ||||
class TimeGran: public Read, public WithParamInterface<unsigned> { | |||||
public: | |||||
virtual void SetUp() { | |||||
m_time_gran = 1 << GetParam(); | |||||
Read::SetUp(); | |||||
} | |||||
}; | |||||
/* AIO reads need to set the header's pid field correctly */ | /* AIO reads need to set the header's pid field correctly */ | ||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ | /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ | ||||
TEST_F(AioRead, aio_read) | TEST_F(AioRead, aio_read) | ||||
{ | { | ||||
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"; | ||||
const char *CONTENTS = "abcdefgh"; | const char *CONTENTS = "abcdefgh"; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
▲ Show 20 Lines • Show All 175 Lines • ▼ Show 20 Lines | TEST_F(AsyncRead, async_read) | ||||
m_mock->kill_daemon(); | m_mock->kill_daemon(); | ||||
/* Wait for AIO activity to complete, but ignore errors */ | /* Wait for AIO activity to complete, but ignore errors */ | ||||
(void)aio_waitcomplete(NULL, NULL); | (void)aio_waitcomplete(NULL, NULL); | ||||
leak(fd); | leak(fd); | ||||
} | } | ||||
/* The kernel should update the cached atime attribute during a read */ | |||||
TEST_F(Read, atime) | |||||
{ | |||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | |||||
const char RELPATH[] = "some_file.txt"; | |||||
const char *CONTENTS = "abcdefgh"; | |||||
struct stat sb1, sb2; | |||||
uint64_t ino = 42; | |||||
int fd; | |||||
ssize_t bufsize = strlen(CONTENTS); | |||||
uint8_t buf[bufsize]; | |||||
expect_lookup(RELPATH, ino, bufsize); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, bufsize, bufsize, CONTENTS); | |||||
fd = open(FULLPATH, O_RDONLY); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb1)); | |||||
/* Ensure atime will be different than it was during lookup */ | |||||
nap(); | |||||
ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb2)); | |||||
/* The kernel should automatically update atime during read */ | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); | |||||
leak(fd); | |||||
} | |||||
/* The kernel should update the cached atime attribute during a cached read */ | |||||
TEST_F(Read, atime_cached) | |||||
{ | |||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | |||||
const char RELPATH[] = "some_file.txt"; | |||||
const char *CONTENTS = "abcdefgh"; | |||||
struct stat sb1, sb2; | |||||
uint64_t ino = 42; | |||||
int fd; | |||||
ssize_t bufsize = strlen(CONTENTS); | |||||
uint8_t buf[bufsize]; | |||||
expect_lookup(RELPATH, ino, bufsize); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, bufsize, bufsize, CONTENTS); | |||||
fd = open(FULLPATH, O_RDONLY); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb1)); | |||||
/* Ensure atime will be different than it was during the first read */ | |||||
nap(); | |||||
ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb2)); | |||||
/* The kernel should automatically update atime during read */ | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); | |||||
leak(fd); | |||||
} | |||||
/* dirty atime values should be flushed during close */ | |||||
TEST_F(Read, atime_during_close) | |||||
{ | |||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | |||||
const char RELPATH[] = "some_file.txt"; | |||||
const char *CONTENTS = "abcdefgh"; | |||||
struct stat sb; | |||||
uint64_t ino = 42; | |||||
const mode_t newmode = 0755; | |||||
int fd; | |||||
ssize_t bufsize = strlen(CONTENTS); | |||||
uint8_t buf[bufsize]; | |||||
expect_lookup(RELPATH, ino, bufsize); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, bufsize, bufsize, CONTENTS); | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([&](auto in) { | |||||
uint32_t valid = FATTR_ATIME; | |||||
return (in.header.opcode == FUSE_SETATTR && | |||||
in.header.nodeid == ino && | |||||
in.body.setattr.valid == valid && | |||||
(time_t)in.body.setattr.atime == | |||||
sb.st_atim.tv_sec && | |||||
in.body.setattr.atimensec == | |||||
sb.st_atim.tv_nsec); | |||||
}, 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 | newmode; | |||||
}))); | |||||
expect_flush(ino, 1, ReturnErrno(0)); | |||||
expect_release(ino, FuseTest::FH); | |||||
fd = open(FULLPATH, O_RDONLY); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
/* Ensure atime will be different than during lookup */ | |||||
nap(); | |||||
ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb)); | |||||
close(fd); | |||||
} | |||||
/* A cached atime should be flushed during FUSE_SETATTR */ | |||||
TEST_F(Read, atime_during_setattr) | |||||
{ | |||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | |||||
const char RELPATH[] = "some_file.txt"; | |||||
const char *CONTENTS = "abcdefgh"; | |||||
struct stat sb; | |||||
uint64_t ino = 42; | |||||
const mode_t newmode = 0755; | |||||
int fd; | |||||
ssize_t bufsize = strlen(CONTENTS); | |||||
uint8_t buf[bufsize]; | |||||
expect_lookup(RELPATH, ino, bufsize); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, bufsize, bufsize, CONTENTS); | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([&](auto in) { | |||||
uint32_t valid = FATTR_MODE | FATTR_ATIME; | |||||
return (in.header.opcode == FUSE_SETATTR && | |||||
in.header.nodeid == ino && | |||||
in.body.setattr.valid == valid && | |||||
(time_t)in.body.setattr.atime == | |||||
sb.st_atim.tv_sec && | |||||
in.body.setattr.atimensec == | |||||
sb.st_atim.tv_nsec); | |||||
}, 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 | newmode; | |||||
}))); | |||||
fd = open(FULLPATH, O_RDONLY); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
/* Ensure atime will be different than during lookup */ | |||||
nap(); | |||||
ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb)); | |||||
ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); | |||||
leak(fd); | |||||
} | |||||
/* The kernel should flush dirty atime values during close */ | |||||
/* 0-length reads shouldn't cause any confusion */ | /* 0-length reads shouldn't cause any confusion */ | ||||
TEST_F(Read, direct_io_read_nothing) | TEST_F(Read, direct_io_read_nothing) | ||||
{ | { | ||||
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; | ||||
int fd; | int fd; | ||||
uint64_t offset = 100; | uint64_t offset = 100; | ||||
▲ Show 20 Lines • Show All 274 Lines • ▼ Show 20 Lines | TEST_F(Read, mmap) | ||||
ASSERT_NE(MAP_FAILED, p) << strerror(errno); | ASSERT_NE(MAP_FAILED, p) << strerror(errno); | ||||
ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); | ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); | ||||
ASSERT_EQ(0, munmap(p, len)) << strerror(errno); | ASSERT_EQ(0, munmap(p, len)) << strerror(errno); | ||||
leak(fd); | leak(fd); | ||||
} | } | ||||
/* | |||||
* The kernel should not update the cached atime attribute during a read, if | |||||
* MNT_NOATIME is used. | |||||
*/ | |||||
TEST_F(ReadNoatime, atime) | |||||
{ | |||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | |||||
const char RELPATH[] = "some_file.txt"; | |||||
const char *CONTENTS = "abcdefgh"; | |||||
struct stat sb1, sb2; | |||||
uint64_t ino = 42; | |||||
int fd; | |||||
ssize_t bufsize = strlen(CONTENTS); | |||||
uint8_t buf[bufsize]; | |||||
expect_lookup(RELPATH, ino, bufsize); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, bufsize, bufsize, CONTENTS); | |||||
fd = open(FULLPATH, O_RDONLY); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb1)); | |||||
nap(); | |||||
ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb2)); | |||||
/* The kernel should not update atime during read */ | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); | |||||
leak(fd); | |||||
} | |||||
/* | |||||
* The kernel should not update the cached atime attribute during a cached | |||||
* read, if MNT_NOATIME is used. | |||||
*/ | |||||
TEST_F(ReadNoatime, atime_cached) | |||||
{ | |||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | |||||
const char RELPATH[] = "some_file.txt"; | |||||
const char *CONTENTS = "abcdefgh"; | |||||
struct stat sb1, sb2; | |||||
uint64_t ino = 42; | |||||
int fd; | |||||
ssize_t bufsize = strlen(CONTENTS); | |||||
uint8_t buf[bufsize]; | |||||
expect_lookup(RELPATH, ino, bufsize); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, bufsize, bufsize, CONTENTS); | |||||
fd = open(FULLPATH, O_RDONLY); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb1)); | |||||
nap(); | |||||
ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); | |||||
ASSERT_EQ(0, fstat(fd, &sb2)); | |||||
/* The kernel should automatically update atime during read */ | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); | |||||
EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); | |||||
leak(fd); | |||||
} | |||||
/* Read of an mmap()ed file fails */ | /* Read of an mmap()ed file fails */ | ||||
TEST_F(ReadSigbus, mmap_eio) | TEST_F(ReadSigbus, mmap_eio) | ||||
{ | { | ||||
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"; | ||||
const char *CONTENTS = "abcdefgh"; | const char *CONTENTS = "abcdefgh"; | ||||
struct sigaction sa; | struct sigaction sa; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
▲ Show 20 Lines • Show All 439 Lines • ▼ Show 20 Lines | |||||
INSTANTIATE_TEST_CASE_P(RA, ReadAhead, | INSTANTIATE_TEST_CASE_P(RA, ReadAhead, | ||||
Values(tuple<bool, int>(false, 0), | Values(tuple<bool, int>(false, 0), | ||||
tuple<bool, int>(false, 1), | tuple<bool, int>(false, 1), | ||||
tuple<bool, int>(false, 2), | tuple<bool, int>(false, 2), | ||||
tuple<bool, int>(false, 3), | tuple<bool, int>(false, 3), | ||||
tuple<bool, int>(true, 0), | tuple<bool, int>(true, 0), | ||||
tuple<bool, int>(true, 1), | tuple<bool, int>(true, 1), | ||||
tuple<bool, int>(true, 2))); | tuple<bool, int>(true, 2))); | ||||
/* fuse_init_out.time_gran controls the granularity of timestamps */ | |||||
TEST_P(TimeGran, atime_during_setattr) | |||||
{ | |||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | |||||
const char RELPATH[] = "some_file.txt"; | |||||
const char *CONTENTS = "abcdefgh"; | |||||
ssize_t bufsize = strlen(CONTENTS); | |||||
uint8_t buf[bufsize]; | |||||
uint64_t ino = 42; | |||||
const mode_t newmode = 0755; | |||||
int fd; | |||||
expect_lookup(RELPATH, ino, bufsize); | |||||
expect_open(ino, 0, 1); | |||||
expect_read(ino, 0, bufsize, bufsize, CONTENTS); | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
uint32_t valid = FATTR_MODE | FATTR_ATIME; | |||||
return (in.header.opcode == FUSE_SETATTR && | |||||
in.header.nodeid == ino && | |||||
in.body.setattr.valid == valid && | |||||
in.body.setattr.atimensec % m_time_gran == 0); | |||||
}, 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 | newmode; | |||||
}))); | |||||
fd = open(FULLPATH, O_RDWR); | |||||
ASSERT_LE(0, fd) << strerror(errno); | |||||
ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); | |||||
ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); | |||||
leak(fd); | |||||
} | |||||
INSTANTIATE_TEST_CASE_P(TG, TimeGran, Range(0u, 10u)); |