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
This commit is contained in:
Alan Somers 2022-09-25 12:59:33 -06:00
parent 0a192b3aba
commit be280f60dd
2 changed files with 80 additions and 14 deletions

View file

@ -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);
}

View file

@ -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.