The branch main has been updated by asomers:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=be280f60dd8e8ef765a76966aac9c6ca7d6264d0

commit be280f60dd8e8ef765a76966aac9c6ca7d6264d0
Author:     Alan Somers <[email protected]>
AuthorDate: 2022-09-25 18:59:33 +0000
Commit:     Alan Somers <[email protected]>
CommitDate: 2022-09-25 21:02:59 +0000

    fusefs: truncate write if it would exceed RLIMIT_FSIZE
    
    PR:             164793
    MFC after:      2 weeks
    Reviewed by:    kib
    Differential Revision: https://reviews.freebsd.org/D36703
---
 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 252fabb1c7d6..155406bc488f 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;
@@ -724,6 +731,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.

Reply via email to