aboutsummaryrefslogtreecommitdiff
path: root/tests/sys
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2019-06-25 17:24:43 +0000
committerAlan Somers <asomers@FreeBSD.org>2019-06-25 17:24:43 +0000
commitb9e20197551d547fc3ebcb747a568d50ca98c47d (patch)
tree9605f046a53b0b17587aca84aecaebe1a16e3cee /tests/sys
parent48417ae0ba1b0b42d8ad04679a0b7c9dd43eb1f2 (diff)
Notes
Diffstat (limited to 'tests/sys')
-rw-r--r--tests/sys/fs/fusefs/read.cc62
-rw-r--r--tests/sys/fs/fusefs/write.cc33
2 files changed, 92 insertions, 3 deletions
diff --git a/tests/sys/fs/fusefs/read.cc b/tests/sys/fs/fusefs/read.cc
index 2ef9f6be55ace..5694a8f5f79fb 100644
--- a/tests/sys/fs/fusefs/read.cc
+++ b/tests/sys/fs/fusefs/read.cc
@@ -413,7 +413,8 @@ TEST_F(Read, eio)
/*
* If the server returns a short read when direct io is not in use, that
- * indicates EOF and we should update the file size.
+ * indicates EOF, because of a server-side truncation. We should invalidate
+ * all cached attributes. We may update the file size,
*/
TEST_F(ReadCacheable, eof)
{
@@ -425,18 +426,21 @@ TEST_F(ReadCacheable, eof)
uint64_t offset = 100;
ssize_t bufsize = strlen(CONTENTS);
ssize_t partbufsize = 3 * bufsize / 4;
+ ssize_t r;
char buf[bufsize];
struct stat sb;
expect_lookup(RELPATH, ino, offset + bufsize);
expect_open(ino, 0, 1);
expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS);
+ expect_getattr(ino, offset + partbufsize);
fd = open(FULLPATH, O_RDONLY);
ASSERT_LE(0, fd) << strerror(errno);
- ASSERT_EQ(partbufsize, pread(fd, buf, bufsize, offset))
- << strerror(errno);
+ r = pread(fd, buf, bufsize, offset);
+ ASSERT_LE(0, r) << strerror(errno);
+ EXPECT_EQ(partbufsize, r) << strerror(errno);
ASSERT_EQ(0, fstat(fd, &sb));
EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size);
/* Deliberately leak fd. close(2) will be tested in release.cc */
@@ -459,6 +463,7 @@ TEST_F(ReadCacheable, eof_of_whole_buffer)
expect_open(ino, 0, 1);
expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS);
expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS);
+ expect_getattr(ino, m_maxbcachebuf);
fd = open(FULLPATH, O_RDONLY);
ASSERT_LE(0, fd) << strerror(errno);
@@ -586,6 +591,57 @@ TEST_F(ReadCacheable, mmap)
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
+/*
+ * A read via mmap comes up short, indicating that the file was truncated
+ * server-side.
+ */
+TEST_F(ReadCacheable, mmap_eof)
+{
+ 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 len;
+ size_t bufsize = strlen(CONTENTS);
+ struct stat sb;
+ void *p;
+
+ len = getpagesize();
+
+ expect_lookup(RELPATH, ino, 100000);
+ expect_open(ino, 0, 1);
+ /* mmap may legitimately try to read more data than is available */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == Read::FH &&
+ in.body.read.offset == 0 &&
+ in.body.read.size >= bufsize);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(struct fuse_out_header) + bufsize;
+ memmove(out.body.bytes, CONTENTS, bufsize);
+ })));
+ expect_getattr(ino, bufsize);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ /* The file size should be automatically truncated */
+ ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ((off_t)bufsize, sb.st_size);
+
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ /* Deliberately leak fd. close(2) will be tested in release.cc */
+}
+
/*
* Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
* cache and to straight to the daemon
diff --git a/tests/sys/fs/fusefs/write.cc b/tests/sys/fs/fusefs/write.cc
index b59273385b390..df6ef36366f2a 100644
--- a/tests/sys/fs/fusefs/write.cc
+++ b/tests/sys/fs/fusefs/write.cc
@@ -528,6 +528,39 @@ TEST_F(Write, rlimit_fsize)
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
+/*
+ * A short read indicates EOF. Test that nothing bad happens if we get EOF
+ * during the R of a RMW operation.
+ */
+TEST_F(WriteCacheable, eof_during_rmw)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ const char *INITIAL = "XXXXXXXXXX";
+ uint64_t ino = 42;
+ uint64_t offset = 1;
+ ssize_t bufsize = strlen(CONTENTS);
+ off_t orig_fsize = 10;
+ off_t truncated_fsize = 5;
+ off_t final_fsize = bufsize;
+ int fd;
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
+ expect_getattr(ino, truncated_fsize);
+ expect_read(ino, 0, final_fsize, final_fsize, INITIAL, O_RDWR);
+ maybe_expect_write(ino, offset, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR);
+ EXPECT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
+ << strerror(errno);
+ /* Deliberately leak fd. close(2) will be tested in release.cc */
+}
+
/*
* If the kernel cannot be sure which uid, gid, or pid was responsible for a
* write, then it must set the FUSE_WRITE_CACHE bit