git: b9f82f035af4 - stable/13 - fusefs: respect RLIMIT_FSIZE during truncate

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Wed, 12 Oct 2022 04:50:01 UTC
The branch stable/13 has been updated by asomers:

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

commit b9f82f035af41476e5b73ab72292a84a346f0941
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2022-09-25 17:56:11 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2022-10-12 04:49:26 +0000

    fusefs: respect RLIMIT_FSIZE during truncate
    
    PR:             164793
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D36703
    
    (cherry picked from commit 0a192b3abab19deac70f762cd1ec45ba09ec47ca)
---
 sys/fs/fuse/fuse_vnops.c       |  3 +++
 tests/sys/fs/fusefs/setattr.cc | 44 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
index 57d896cea079..925d4ff16232 100644
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -2263,6 +2263,9 @@ fuse_vnop_setattr(struct vop_setattr_args *ap)
 		case VREG:
 			if (vfs_isrdonly(mp))
 				return (EROFS);
+			err = vn_rlimit_trunc(vap->va_size, td);
+			if (err)
+				return (err);
 			break;
 		default:
 			/*
diff --git a/tests/sys/fs/fusefs/setattr.cc b/tests/sys/fs/fusefs/setattr.cc
index 00b37ec7965a..e245c274ba07 100644
--- a/tests/sys/fs/fusefs/setattr.cc
+++ b/tests/sys/fs/fusefs/setattr.cc
@@ -31,10 +31,14 @@
  */
 
 extern "C" {
+#include <sys/types.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 
 #include <fcntl.h>
 #include <semaphore.h>
+#include <signal.h>
 }
 
 #include "mockfs.hh"
@@ -42,11 +46,15 @@ extern "C" {
 
 using namespace testing;
 
-class Setattr : public FuseTest {};
+class Setattr : public FuseTest {
+public:
+static sig_atomic_t s_sigxfsz;
+};
 
 class RofsSetattr: public Setattr {
 public:
 virtual void SetUp() {
+	s_sigxfsz = 0;
 	m_ro = true;
 	Setattr::SetUp();
 }
@@ -61,6 +69,12 @@ virtual void SetUp() {
 };
 
 
+sig_atomic_t Setattr::s_sigxfsz = 0;
+
+void sigxfsz_handler(int __unused sig) {
+	Setattr::s_sigxfsz = 1;
+}
+
 /*
  * If setattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs
  * should use the cached attributes, rather than query the daemon
@@ -553,6 +567,34 @@ TEST_F(Setattr, truncate_discards_cached_data) {
 	leak(fd);
 }
 
+/* truncate should fail if it would cause the file to exceed RLIMIT_FSIZE */
+TEST_F(Setattr, truncate_rlimit_rsize)
+{
+	const char FULLPATH[] = "mountpoint/some_file.txt";
+	const char RELPATH[] = "some_file.txt";
+	struct rlimit rl;
+	const uint64_t ino = 42;
+	const uint64_t oldsize = 0;
+	const uint64_t newsize = 100'000'000;
+
+	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+		SET_OUT_HEADER_LEN(out, entry);
+		out.body.entry.attr.mode = S_IFREG | 0644;
+		out.body.entry.nodeid = ino;
+		out.body.entry.attr.size = oldsize;
+	})));
+
+	rl.rlim_cur = newsize / 2;
+	rl.rlim_max = 10 * newsize;
+	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+	EXPECT_EQ(-1, truncate(FULLPATH, newsize));
+	EXPECT_EQ(EFBIG, errno);
+	EXPECT_EQ(1, s_sigxfsz);
+}
+
 /* Change a file's timestamps */
 TEST_F(Setattr, utimensat) {
 	const char FULLPATH[] = "mountpoint/some_file.txt";