git: 0f0a0bdff052 - stable/13 - fusefs: truncate write if it would exceed RLIMIT_FSIZE
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 12 Oct 2022 04:50:02 UTC
The branch stable/13 has been updated by asomers: URL: https://cgit.FreeBSD.org/src/commit/?id=0f0a0bdff052e4a61b20f6a0fc252e381c57c3ab commit 0f0a0bdff052e4a61b20f6a0fc252e381c57c3ab Author: Alan Somers <asomers@FreeBSD.org> AuthorDate: 2022-09-25 18:59:33 +0000 Commit: Alan Somers <asomers@FreeBSD.org> CommitDate: 2022-10-12 04:49:34 +0000 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) --- sys/fs/fuse/fuse_io.c | 16 ++++++--- tests/sys/fs/fusefs/write.cc | 78 ++++++++++++++++++++++++++++++++++++++------ 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.