From nobody Wed Oct 12 04:50:03 2022 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4MnKvb6QS3z4fLgf; Wed, 12 Oct 2022 04:50:03 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4MnKvb5gZpz3C4M; Wed, 12 Oct 2022 04:50:03 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1665550203; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=88agFSyiErjhYScVwCuY0ONVOQ3OIfLs3lLEgMAvWts=; b=soMv86JBn8+rD3wymF2PlsAnt/tEhAI6csGvxC7VT2XjRLIkhlLQj/t/9ygAP59+NMBpPg iQMl7H45a1JRre5fiPrITGzraMNU8xzz/wIAxQ+ZOhXVYmEWC2vfcfZ0twzF3XXlie8+Lm WxyxEfo4zXmYGPvAXrvPYMx4n0kZqJiWdVmX4a5JfFCmmCXGkpeVnZYX7dKemTZgHJs2jY hfzAnQUEEVLSleW44E0tVHBens4efMfCCR6GLS1Tk33VoJCH5uFmRIFJPmD+f1GsNQ2Zir jBO3kDnaxze9cMt+Qlp54nmUhcGtq4rOyYfl50vA2MsTyIo80BzWbiz0oqIVdA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4MnKvb4lLlz1C7B; Wed, 12 Oct 2022 04:50:03 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 29C4o3Hs010015; Wed, 12 Oct 2022 04:50:03 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 29C4o3NZ010010; Wed, 12 Oct 2022 04:50:03 GMT (envelope-from git) Date: Wed, 12 Oct 2022 04:50:03 GMT Message-Id: <202210120450.29C4o3NZ010010@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Alan Somers Subject: git: 0733dd8a9353 - stable/13 - copy_file_range: truncate write if it would exceed RLIMIT_FSIZE List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: asomers X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: 0733dd8a9353a1b9796ffeca0120c5c3642df48b Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1665550203; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=88agFSyiErjhYScVwCuY0ONVOQ3OIfLs3lLEgMAvWts=; b=BKjQe/z4Izy9KH4dBAS2QOSAygnm1AyVhNiD2iXMjTAd814H9Tvbu98WY/o0Kw6kcn7KXB lAf0MHwg1YUwTJy7fHpNOAgCUYL0gXSZOLL+pp5NJ2znjmZMkNl/ylLyL2X3lweuV1kQCo kul0jMYiP5rIgUES1FFDmmR8Ql98bsKgi4qN2nbG4Nk4hLYTlj07VqRSYXGMip7HCi2rZN M5P0AC9S6ElMqIaEPVipkHxXAUMG8X/tIoT9M4IGc99ZCOYldVeZulstbz5uhmf9wA4z0w ezelU2U8ffOWT1ePu1XDuokXubzyfSPZj4zZki/dBIzbLKKF4w9CNzx+ZXItVg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1665550203; a=rsa-sha256; cv=none; b=XNgSbPphIn+dX5ErsY/FwGKQvCznwPIT+Z2VgT++hQUToJyOmCIXRKTmZciyoPPSpbayts zdQOXOFQhp/LKiK3lMcjCejufOJHo6uWYF7OCWPl+uJ+gb6v1dMkLB1gcz0c8ghl9C1e4/ 2jQdPvnETpmwbZiI29/84j3Fwhy1ujjXTmD9y+ucKnYqnKgpOlyaWqk7URAPp8SDpvUF82 dTEIjRc7t4UbfWz6PM7zOnwyGk5qobuYfQih1vkMQxOkJrZfLrWzOwbR5Zjjq6WCQWQWyl N5REREHlstjxAeBXNx7PuNIqFXOq+YAG5bmnZ8EQHc3h37vRzrXKhi7esB3Nkw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by asomers: URL: https://cgit.FreeBSD.org/src/commit/?id=0733dd8a9353a1b9796ffeca0120c5c3642df48b commit 0733dd8a9353a1b9796ffeca0120c5c3642df48b Author: Alan Somers AuthorDate: 2022-09-25 22:53:36 +0000 Commit: Alan Somers CommitDate: 2022-10-12 04:49:39 +0000 copy_file_range: truncate write if it would exceed RLIMIT_FSIZE PR: 266611 Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D36706 (cherry picked from commit 52360ca32ff90b605ac7481fd79e6a251e8b5116) --- sys/fs/fuse/fuse_vnops.c | 15 +++-- sys/fs/nfsclient/nfs_clvnops.c | 8 ++- sys/kern/vfs_vnops.c | 14 ++-- tests/sys/fs/fusefs/copy_file_range.cc | 113 +++++++++++++++++++++++++-------- 4 files changed, 115 insertions(+), 35 deletions(-) diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c index 925d4ff16232..f68bbb358ca2 100644 --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -812,6 +812,7 @@ fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap) struct thread *td; struct uio io; off_t outfilesize; + ssize_t r = 0; pid_t pid; int err; @@ -859,11 +860,11 @@ fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap) if (err) goto unlock; + io.uio_resid = *ap->a_lenp; if (ap->a_fsizetd) { io.uio_offset = *ap->a_outoffp; - io.uio_resid = *ap->a_lenp; - err = vn_rlimit_fsize(outvp, &io, ap->a_fsizetd); - if (err) + err = vn_rlimit_fsizex(outvp, &io, 0, &r, ap->a_fsizetd); + if (err != 0) goto unlock; } @@ -872,7 +873,7 @@ fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap) goto unlock; err = fuse_inval_buf_range(outvp, outfilesize, *ap->a_outoffp, - *ap->a_outoffp + *ap->a_lenp); + *ap->a_outoffp + io.uio_resid); if (err) goto unlock; @@ -884,7 +885,7 @@ fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap) fcfri->nodeid_out = VTOI(outvp); fcfri->fh_out = outfufh->fh_id; fcfri->off_out = *ap->a_outoffp; - fcfri->len = *ap->a_lenp; + fcfri->len = io.uio_resid; fcfri->flags = 0; err = fdisp_wait_answ(&fdi); @@ -916,6 +917,10 @@ fallback: ap->a_incred, ap->a_outcred, ap->a_fsizetd); } + /* + * No need to call vn_rlimit_fsizex_res before return, since the uio is + * local. + */ return (err); } diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index 155028a81716..4edfee540cc9 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -3803,6 +3803,7 @@ nfs_copy_file_range(struct vop_copy_file_range_args *ap) struct uio io; struct nfsmount *nmp; size_t len, len2; + ssize_t r; int error, inattrflag, outattrflag, ret, ret2; off_t inoff, outoff; bool consecutive, must_commit, tryoutcred; @@ -3862,7 +3863,12 @@ generic_copy: */ io.uio_offset = *ap->a_outoffp; io.uio_resid = *ap->a_lenp; - error = vn_rlimit_fsize(outvp, &io, ap->a_fsizetd); + error = vn_rlimit_fsizex(outvp, &io, 0, &r, ap->a_fsizetd); + *ap->a_lenp = io.uio_resid; + /* + * No need to call vn_rlimit_fsizex_res before return, since the uio is + * local. + */ /* * Flush the input file so that the data is up to date before diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index af4320f44eb5..190a48291517 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -3236,12 +3236,11 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, { struct vattr va, inva; struct mount *mp; - struct uio io; off_t startoff, endoff, xfer, xfer2; u_long blksize; int error, interrupted; bool cantseek, readzeros, eof, lastblock, holetoeof; - ssize_t aresid; + ssize_t aresid, r = 0; size_t copylen, len, rem, savlen; char *dat; long holein, holeout; @@ -3270,13 +3269,20 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp, error = vn_lock(outvp, LK_EXCLUSIVE); if (error == 0) { /* - * If fsize_td != NULL, do a vn_rlimit_fsize() call, + * If fsize_td != NULL, do a vn_rlimit_fsizex() call, * now that outvp is locked. */ if (fsize_td != NULL) { + struct uio io; + io.uio_offset = *outoffp; io.uio_resid = len; - error = vn_rlimit_fsize(outvp, &io, fsize_td); + error = vn_rlimit_fsizex(outvp, &io, 0, &r, fsize_td); + len = savlen = io.uio_resid; + /* + * No need to call vn_rlimit_fsizex_res before return, + * since the uio is local. + */ } if (VOP_PATHCONF(outvp, _PC_MIN_HOLE_SIZE, &holeout) != 0) holeout = 0; diff --git a/tests/sys/fs/fusefs/copy_file_range.cc b/tests/sys/fs/fusefs/copy_file_range.cc index 7e1189648de3..84101ca6c984 100644 --- a/tests/sys/fs/fusefs/copy_file_range.cc +++ b/tests/sys/fs/fusefs/copy_file_range.cc @@ -44,22 +44,6 @@ using namespace testing; class CopyFileRange: public FuseTest { public: -static sig_atomic_t s_sigxfsz; - -void SetUp() { - s_sigxfsz = 0; - FuseTest::SetUp(); -} - -void TearDown() { - struct sigaction sa; - - bzero(&sa, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGXFSZ, &sa, NULL); - - FuseTest::TearDown(); -} void expect_maybe_lseek(uint64_t ino) { @@ -114,12 +98,6 @@ void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, }; -sig_atomic_t CopyFileRange::s_sigxfsz = 0; - -void sigxfsz_handler(int __unused sig) { - CopyFileRange::s_sigxfsz = 1; -} - class CopyFileRange_7_27: public CopyFileRange { public: @@ -137,6 +115,37 @@ virtual void SetUp() { } }; +class CopyFileRangeRlimitFsize: public CopyFileRange { +public: +static sig_atomic_t s_sigxfsz; +struct rlimit m_initial_limit; + +virtual void SetUp() { + s_sigxfsz = 0; + getrlimit(RLIMIT_FSIZE, &m_initial_limit); + CopyFileRange::SetUp(); +} + +void TearDown() { + struct sigaction sa; + + setrlimit(RLIMIT_FSIZE, &m_initial_limit); + + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(SIGXFSZ, &sa, NULL); + + FuseTest::TearDown(); +} + +}; + +sig_atomic_t CopyFileRangeRlimitFsize::s_sigxfsz = 0; + +void sigxfsz_handler(int __unused sig) { + CopyFileRangeRlimitFsize::s_sigxfsz = 1; +} + TEST_F(CopyFileRange, eio) { const char FULLPATH1[] = "mountpoint/src.txt"; @@ -313,8 +322,11 @@ TEST_F(CopyFileRange, fallback) ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0)); } -/* fusefs should respect RLIMIT_FSIZE */ -TEST_F(CopyFileRange, rlimit_fsize) +/* + * copy_file_range should send SIGXFSZ and return EFBIG when the operation + * would exceed the limit imposed by RLIMIT_FSIZE. + */ +TEST_F(CopyFileRangeRlimitFsize, signal) { const char FULLPATH1[] = "mountpoint/src.txt"; const char RELPATH1[] = "src.txt"; @@ -344,7 +356,7 @@ TEST_F(CopyFileRange, rlimit_fsize) ).Times(0); rl.rlim_cur = fsize2; - rl.rlim_max = 10 * fsize2; + 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); @@ -355,6 +367,57 @@ TEST_F(CopyFileRange, rlimit_fsize) EXPECT_EQ(1, s_sigxfsz); } +/* + * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not + * aborted. + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=266611 + */ +TEST_F(CopyFileRangeRlimitFsize, truncate) +{ + const char FULLPATH1[] = "mountpoint/src.txt"; + const char RELPATH1[] = "src.txt"; + const char FULLPATH2[] = "mountpoint/dst.txt"; + const char RELPATH2[] = "dst.txt"; + struct rlimit rl; + const uint64_t ino1 = 42; + const uint64_t ino2 = 43; + const uint64_t fh1 = 0xdeadbeef1a7ebabe; + const uint64_t fh2 = 0xdeadc0de88c0ffee; + off_t fsize1 = 1 << 20; /* 1 MiB */ + off_t fsize2 = 1 << 19; /* 512 KiB */ + off_t start1 = 1 << 18; + off_t start2 = fsize2; + ssize_t len = 65536; + off_t limit = start2 + len / 2; + int fd1, fd2; + + expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1); + expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1); + expect_open(ino1, 0, 1, fh1); + expect_open(ino2, 0, 1, fh2); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_COPY_FILE_RANGE && + (off_t)in.body.copy_file_range.off_out == start2 && + in.body.copy_file_range.len == (size_t)len / 2 + ); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { + SET_OUT_HEADER_LEN(out, write); + out.body.write.size = len / 2; + }))); + + 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); + + fd1 = open(FULLPATH1, O_RDONLY); + fd2 = open(FULLPATH2, O_WRONLY); + ASSERT_EQ(len / 2, copy_file_range(fd1, &start1, fd2, &start2, len, 0)); +} + TEST_F(CopyFileRange, ok) { const char FULLPATH1[] = "mountpoint/src.txt";