aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2022-09-25 18:59:33 +0000
committerAlan Somers <asomers@FreeBSD.org>2022-10-12 04:49:34 +0000
commit0f0a0bdff052e4a61b20f6a0fc252e381c57c3ab (patch)
tree20aba7a31e05333c1a5238955f6b05a1ad19a848
parentb9f82f035af41476e5b73ab72292a84a346f0941 (diff)
downloadsrc-0f0a0bdff052e4a61b20f6a0fc252e381c57c3ab.tar.gz
src-0f0a0bdff052e4a61b20f6a0fc252e381c57c3ab.zip
fusefs: truncate write if it would exceed RLIMIT_FSIZE
PR: 164793 Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D36703 (cherry picked from commit be280f60dd8e8ef765a76966aac9c6ca7d6264d0)
-rw-r--r--sys/fs/fuse/fuse_io.c16
-rw-r--r--tests/sys/fs/fusefs/write.cc78
2 files changed, 80 insertions, 14 deletions
diff --git a/sys/fs/fuse/fuse_io.c b/sys/fs/fuse/fuse_io.c
index 179ee9e94f43..019093abf75a 100644
--- a/sys/fs/fuse/fuse_io.c
+++ b/sys/fs/fuse/fuse_io.c
@@ -303,6 +303,7 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
struct fuse_write_out *fwo;
struct fuse_dispatcher fdi;
size_t chunksize;
+ ssize_t r;
void *fwi_data;
off_t as_written_offset;
int diff;
@@ -338,9 +339,11 @@ fuse_write_directbackend(struct vnode *vp, struct uio *uio,
if (ioflag & IO_APPEND)
uio_setoffset(uio, filesize);
- err = vn_rlimit_fsize(vp, uio, uio->uio_td);
- if (err != 0)
+ err = vn_rlimit_fsizex(vp, uio, 0, &r, uio->uio_td);
+ if (err != 0) {
+ vn_rlimit_fsizex_res(uio, r);
return (err);
+ }
fdisp_init(&fdi, 0);
@@ -456,6 +459,7 @@ retry:
if (wrote_anything)
fuse_vnode_undirty_cached_timestamps(vp, false);
+ vn_rlimit_fsizex_res(uio, r);
return (err);
}
@@ -472,6 +476,7 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio,
struct buf *bp;
daddr_t lbn;
off_t filesize;
+ ssize_t r;
int bcount;
int n, on, seqcount, err = 0;
@@ -494,9 +499,11 @@ fuse_write_biobackend(struct vnode *vp, struct uio *uio,
if (ioflag & IO_APPEND)
uio_setoffset(uio, filesize);
- err = vn_rlimit_fsize(vp, uio, uio->uio_td);
- if (err != 0)
+ err = vn_rlimit_fsizex(vp, uio, 0, &r, uio->uio_td);
+ if (err != 0) {
+ vn_rlimit_fsizex_res(uio, r);
return (err);
+ }
do {
bool direct_append, extending;
@@ -723,6 +730,7 @@ again:
break;
} while (uio->uio_resid > 0 && n > 0);
+ vn_rlimit_fsizex_res(uio, r);
return (err);
}
diff --git a/tests/sys/fs/fusefs/write.cc b/tests/sys/fs/fusefs/write.cc
index d685bd13aa17..4e76414a601a 100644
--- a/tests/sys/fs/fusefs/write.cc
+++ b/tests/sys/fs/fusefs/write.cc
@@ -52,10 +52,7 @@ using namespace testing;
class Write: public FuseTest {
public:
-static sig_atomic_t s_sigxfsz;
-
void SetUp() {
- s_sigxfsz = 0;
FuseTest::SetUp();
}
@@ -118,8 +115,6 @@ void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
};
-sig_atomic_t Write::s_sigxfsz = 0;
-
class Write_7_8: public FuseTest {
public:
@@ -211,8 +206,28 @@ virtual void SetUp() {
class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int>
{};
+class WriteRlimitFsize: public Write, public WithParamInterface<int> {
+public:
+static sig_atomic_t s_sigxfsz;
+struct rlimit m_initial_limit;
+
+void SetUp() {
+ s_sigxfsz = 0;
+ getrlimit(RLIMIT_FSIZE, &m_initial_limit);
+ FuseTest::SetUp();
+}
+
+void TearDown() {
+ setrlimit(RLIMIT_FSIZE, &m_initial_limit);
+
+ FuseTest::TearDown();
+}
+};
+
+sig_atomic_t WriteRlimitFsize::s_sigxfsz = 0;
+
void sigxfsz_handler(int __unused sig) {
- Write::s_sigxfsz = 1;
+ WriteRlimitFsize::s_sigxfsz = 1;
}
/* AIO writes need to set the header's pid field correctly */
@@ -531,7 +546,7 @@ TEST_F(Write, direct_io_short_write_iov)
}
/* fusefs should respect RLIMIT_FSIZE */
-TEST_F(Write, rlimit_fsize)
+TEST_P(WriteRlimitFsize, rlimit_fsize)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
@@ -540,17 +555,19 @@ TEST_F(Write, rlimit_fsize)
ssize_t bufsize = strlen(CONTENTS);
off_t offset = 1'000'000'000;
uint64_t ino = 42;
- int fd;
+ int fd, oflag;
+
+ oflag = GetParam();
expect_lookup(RELPATH, ino, 0);
expect_open(ino, 0, 1);
rl.rlim_cur = offset;
- rl.rlim_max = 10 * offset;
+ rl.rlim_max = m_initial_limit.rlim_max;
ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
- fd = open(FULLPATH, O_WRONLY);
+ fd = open(FULLPATH, O_WRONLY | oflag);
ASSERT_LE(0, fd) << strerror(errno);
@@ -560,6 +577,47 @@ TEST_F(Write, rlimit_fsize)
leak(fd);
}
+/*
+ * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not
+ * aborted.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=164793
+ */
+TEST_P(WriteRlimitFsize, rlimit_fsize_truncate)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
+ struct rlimit rl;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ off_t offset = 1 << 30;
+ off_t limit = offset + strlen(CONTENTS) / 2;
+ int fd, oflag;
+
+ oflag = GetParam();
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, offset, bufsize / 2, bufsize / 2, CONTENTS);
+
+ rl.rlim_cur = limit;
+ rl.rlim_max = m_initial_limit.rlim_max;
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+ ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+ fd = open(FULLPATH, O_WRONLY | oflag);
+
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize / 2, pwrite(fd, CONTENTS, bufsize, offset))
+ << strerror(errno);
+ leak(fd);
+}
+
+INSTANTIATE_TEST_CASE_P(W, WriteRlimitFsize,
+ Values(0, O_DIRECT)
+);
+
/*
* A short read indicates EOF. Test that nothing bad happens if we get EOF
* during the R of a RMW operation.