From owner-svn-src-all@freebsd.org Fri Nov 8 23:39:17 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id F2FCF1648E0; Fri, 8 Nov 2019 23:39:17 +0000 (UTC) (envelope-from rmacklem@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) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 478xYx6D3Wz44mJ; Fri, 8 Nov 2019 23:39:17 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id B9AE15DF1; Fri, 8 Nov 2019 23:39:17 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xA8NdHCR056811; Fri, 8 Nov 2019 23:39:17 GMT (envelope-from rmacklem@FreeBSD.org) Received: (from rmacklem@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xA8NdHm7056810; Fri, 8 Nov 2019 23:39:17 GMT (envelope-from rmacklem@FreeBSD.org) Message-Id: <201911082339.xA8NdHm7056810@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: rmacklem set sender to rmacklem@FreeBSD.org using -f From: Rick Macklem Date: Fri, 8 Nov 2019 23:39:17 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r354564 - head/sys/kern X-SVN-Group: head X-SVN-Commit-Author: rmacklem X-SVN-Commit-Paths: head/sys/kern X-SVN-Commit-Revision: 354564 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 08 Nov 2019 23:39:18 -0000 Author: rmacklem Date: Fri Nov 8 23:39:17 2019 New Revision: 354564 URL: https://svnweb.freebsd.org/changeset/base/354564 Log: Update copy_file_range(2) to be Linux5 compatible. The current linux man page and testing done on a fairly recent linux5.n kernel have identified two changes to the semantics of the linux copy_file_range system call. Since the copy_file_range(2) system call is intended to be linux compatible and is only currently in head/current and not used by any commands, it seems appropriate to update the system call to be compatible with the current linux one. The old linux man page stated that, if the offset + len exceeded file_size for the input file, EINVAL should be returned. Now, the semantics is to copy up to at most file_size bytes and return that number of bytes copied. If the offset is at or beyond file_size, a return of 0 bytes is done. This patch modifies copy_file_range(2) to be linux compatible for this semantic change. A separate patch will change copy_file_range(2) for the other semantic change, which allows the infd and outfd to refer to the same file, so long as the byte ranges do not overlap. Modified: head/sys/kern/vfs_vnops.c Modified: head/sys/kern/vfs_vnops.c ============================================================================== --- head/sys/kern/vfs_vnops.c Fri Nov 8 20:53:56 2019 (r354563) +++ head/sys/kern/vfs_vnops.c Fri Nov 8 23:39:17 2019 (r354564) @@ -2679,7 +2679,6 @@ vn_copy_file_range(struct vnode *invp, off_t *inoffp, off_t *outoffp, size_t *lenp, unsigned int flags, struct ucred *incred, struct ucred *outcred, struct thread *fsize_td) { - struct vattr va; int error; size_t len; uint64_t uvalin, uvalout; @@ -2705,17 +2704,6 @@ vn_copy_file_range(struct vnode *invp, off_t *inoffp, if (error != 0) goto out; - error = vn_lock(invp, LK_SHARED); - if (error != 0) - goto out; - /* Check that the offset + len does not go past EOF of invp. */ - error = VOP_GETATTR(invp, &va, incred); - if (error == 0 && va.va_size < *inoffp + len) - error = EINVAL; - VOP_UNLOCK(invp, 0); - if (error != 0) - goto out; - /* * If the two vnode are for the same file system, call * VOP_COPY_FILE_RANGE(), otherwise call vn_generic_copy_file_range() @@ -2917,7 +2905,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t * off_t startoff, endoff, xfer, xfer2; u_long blksize; int error; - bool cantseek, readzeros; + bool cantseek, readzeros, eof, lastblock; ssize_t aresid; size_t copylen, len, savlen; char *dat; @@ -3004,7 +2992,8 @@ vn_generic_copy_file_range(struct vnode *invp, off_t * * Note that some file systems such as NFSv3, NFSv4.0 and NFSv4.1 may * support holes on the server, but do not support FIOSEEKHOLE. */ - while (len > 0 && error == 0) { + eof = false; + while (len > 0 && error == 0 && !eof) { endoff = 0; /* To shut up compilers. */ cantseek = true; startoff = *inoffp; @@ -3086,7 +3075,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t * xfer -= (*inoffp % blksize); } /* Loop copying the data block. */ - while (copylen > 0 && error == 0) { + while (copylen > 0 && error == 0 && !eof) { if (copylen < xfer) xfer = copylen; error = vn_lock(invp, LK_SHARED); @@ -3097,12 +3086,13 @@ vn_generic_copy_file_range(struct vnode *invp, off_t * curthread->td_ucred, incred, &aresid, curthread); VOP_UNLOCK(invp, 0); - /* - * Linux considers a range that exceeds EOF to - * be an error, so we will too. - */ - if (error == 0 && aresid > 0) - error = EINVAL; + lastblock = false; + if (error == 0 && aresid > 0) { + /* Stop the copy at EOF on the input file. */ + xfer -= aresid; + eof = true; + lastblock = true; + } if (error == 0) { /* * Skip the write for holes past the initial EOF @@ -3111,11 +3101,13 @@ vn_generic_copy_file_range(struct vnode *invp, off_t * */ readzeros = cantseek ? mem_iszero(dat, xfer) : false; + if (xfer == len) + lastblock = true; if (!cantseek || *outoffp < va.va_size || - xfer == len || !readzeros) + lastblock || !readzeros) error = vn_write_outvp(outvp, dat, *outoffp, xfer, blksize, - readzeros && xfer == len && + readzeros && lastblock && *outoffp >= va.va_size, false, outcred); if (error == 0) {