Received: by 2002:a05:6a10:1a4d:0:0:0:0 with SMTP id nk13csp901267pxb; Tue, 1 Feb 2022 12:47:18 -0800 (PST) X-Google-Smtp-Source: ABdhPJz0lg/Bv8/4JE89i75fNKZ5OViaU1E7zcw3y+1WHp1nTl59Pmk73pHNFzp0DhigGwLFvmEx X-Received: by 2002:a17:90a:c68c:: with SMTP id n12mr4346985pjt.219.1643748438289; Tue, 01 Feb 2022 12:47:18 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1643748438; cv=none; d=google.com; s=arc-20160816; b=durbq8Yk5x76Nvsy8ZteCmiGEG/+uIoldTjQZkLbkoRn/gmEQlpAB74k3pQ25/3bsg 8qqJ+z4UKyfTTR9l8gFfCocRhmlmTFZ7i9WuadM/qofmzHIu+CffDdi+J/TD+MKmVwTU njbVf4fOfUrQG9d0fTW/xQ914Nt/pNDJ1KKR5XN7tTNLrO5HE0QT06Kp9Z8bE4KTG9mk 2f0kkA1301hndU9xW1QcYfMLK4SEEKhRPSYCjb4WRzIkHEgJRBsHrDOfgUnCOYruLQTw tCOcT1pl+O4d1z9lHDvDiZrw8RBHAP+eqbbvevX0kUGZaMRXrKge+YovSNcZwfIYEJHW WzFw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:to:from:subject; bh=YvW2DZJw5iC1HuCBUoT/iobL0q006erSZpt70bqxZGk=; b=XGYot7CwIEArceE4KRifjr6yA79DZkYntNyGlmtcyIItlxJ0Lx6IHAClYVo+hmDDeR BCdicJZvEvO+5TAoV7SiC/mg/4alHfDxXoUf05ssJIYjk9OY16ZDJQ3V7P0cS4lJVSHc 7C/B17xAs5XDT/wmNuoIJc4liaGTkLkagLDDoDOrIkUFy0bcC9QCt5XECf59zVIN2Hj4 9slM9f/9qto34HeTIDPNmJ1odRBsmUvz3655jxYnn6WQsW0SR/WP8m/aWTSnUIDpnQ+r 9fUQ5CgTCTSkP+piBx+Q8UGejagxHBUGsLbhv2mifGfMv7HdwHYPSCJBAAlbBt+V+vK2 QGxA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=oracle.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id b4si16900266plz.351.2022.02.01.12.47.06; Tue, 01 Feb 2022 12:47:18 -0800 (PST) Received-SPF: pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=oracle.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1355678AbiAaSZd (ORCPT + 99 others); Mon, 31 Jan 2022 13:25:33 -0500 Received: from dfw.source.kernel.org ([139.178.84.217]:49374 "EHLO dfw.source.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1356396AbiAaSZH (ORCPT ); Mon, 31 Jan 2022 13:25:07 -0500 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id D85F761210; Mon, 31 Jan 2022 18:25:06 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2AC21C340E8; Mon, 31 Jan 2022 18:25:06 +0000 (UTC) Subject: [PATCH v2 4/5] NFSD: COMMIT operations must not return NFS?ERR_INVAL From: Chuck Lever To: linux-nfs@vger.kernel.org, linux-fsdevel@vger.kernel.org Date: Mon, 31 Jan 2022 13:25:05 -0500 Message-ID: <164365350540.3304.5205980828341851849.stgit@bazille.1015granger.net> In-Reply-To: <164365324981.3304.4571955521912946906.stgit@bazille.1015granger.net> References: <164365324981.3304.4571955521912946906.stgit@bazille.1015granger.net> User-Agent: StGit/1.4 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org Since, well, forever, the Linux NFS server's nfsd_commit() function has returned nfserr_inval when the passed-in byte range arguments were non-sensical. However, according to RFC 1813 section 3.3.21, NFSv3 COMMIT requests are permitted to return only the following non-zero status codes: NFS3ERR_IO NFS3ERR_STALE NFS3ERR_BADHANDLE NFS3ERR_SERVERFAULT NFS3ERR_INVAL is not included in that list. Likewise, NFS4ERR_INVAL is not listed in the COMMIT row of Table 6 in RFC 8881. RFC 7530 does permit COMMIT to return NFS4ERR_INVAL, but does not specify when it can or should be used. Instead of dropping or failing a COMMIT request in a byte range that is not supported, turn it into a valid request by treating one or both arguments as zero. Offset zero means start-of-file, count zero means until-end-of-file, so we only ever extend the commit range. NFS servers are always allowed to commit more and sooner than requested. The range check is no longer bounded by NFS_OFFSET_MAX, but rather by the value that is returned in the maxfilesize field of the NFSv3 FSINFO procedure or the NFSv4 maxfilesize file attribute. Note that this change results in a new pynfs failure: CMT4 st_commit.testCommitOverflow : RUNNING CMT4 st_commit.testCommitOverflow : FAILURE COMMIT with offset + count overflow should return NFS4ERR_INVAL, instead got NFS4_OK IMO the test is not correct as written: RFC 8881 does not allow the COMMIT operation to return NFS4ERR_INVAL. Reported-by: Dan Aloni Signed-off-by: Chuck Lever Reviewed-by: Bruce Fields --- fs/nfsd/nfs3proc.c | 6 ------ fs/nfsd/vfs.c | 53 +++++++++++++++++++++++++++++++++++----------------- fs/nfsd/vfs.h | 4 ++-- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 4e939ebba5d5..79831da12462 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -666,15 +666,9 @@ nfsd3_proc_commit(struct svc_rqst *rqstp) argp->count, (unsigned long long) argp->offset); - if (argp->offset > NFS_OFFSET_MAX) { - resp->status = nfserr_inval; - goto out; - } - fh_copy(&resp->fh, &argp->fh); resp->status = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count, resp->verf); -out: return rpc_success; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 0cccceb105e7..91600e71be19 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1114,42 +1114,61 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, } #ifdef CONFIG_NFSD_V3 -/* - * Commit all pending writes to stable storage. +/** + * nfsd_commit - Commit pending writes to stable storage + * @rqstp: RPC request being processed + * @fhp: NFS filehandle + * @offset: raw offset from beginning of file + * @count: raw count of bytes to sync + * @verf: filled in with the server's current write verifier * - * Note: we only guarantee that data that lies within the range specified - * by the 'offset' and 'count' parameters will be synced. + * Note: we guarantee that data that lies within the range specified + * by the 'offset' and 'count' parameters will be synced. The server + * is permitted to sync data that lies outside this range at the + * same time. * * Unfortunately we cannot lock the file to make sure we return full WCC * data to the client, as locking happens lower down in the filesystem. + * + * Return values: + * An nfsstat value in network byte order. */ __be32 -nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, - loff_t offset, unsigned long count, __be32 *verf) +nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, u64 offset, + u32 count, __be32 *verf) { + u64 maxbytes; + loff_t start, end; struct nfsd_net *nn; struct nfsd_file *nf; - loff_t end = LLONG_MAX; - __be32 err = nfserr_inval; - - if (offset < 0) - goto out; - if (count != 0) { - end = offset + (loff_t)count - 1; - if (end < offset) - goto out; - } + __be32 err; err = nfsd_file_acquire(rqstp, fhp, NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &nf); if (err) goto out; + + /* + * Convert the client-provided (offset, count) range to a + * (start, end) range. If the client-provided range falls + * outside the maximum file size of the underlying FS, + * clamp the sync range appropriately. + */ + start = 0; + end = LLONG_MAX; + maxbytes = (u64)fhp->fh_dentry->d_sb->s_maxbytes; + if (offset < maxbytes) { + start = offset; + if (count && (offset + count - 1 < maxbytes)) + end = offset + count - 1; + } + nn = net_generic(nf->nf_net, nfsd_net_id); if (EX_ISSYNC(fhp->fh_export)) { errseq_t since = READ_ONCE(nf->nf_file->f_wb_err); int err2; - err2 = vfs_fsync_range(nf->nf_file, offset, end, 0); + err2 = vfs_fsync_range(nf->nf_file, start, end, 0); switch (err2) { case 0: nfsd_copy_write_verifier(verf, nn); diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 9f56dcb22ff7..2c43d10e3cab 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -74,8 +74,8 @@ __be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs, struct svc_fh *res, int createmode, u32 *verifier, bool *truncp, bool *created); -__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *, - loff_t, unsigned long, __be32 *verf); +__be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp, + u64 offset, u32 count, __be32 *verf); #endif /* CONFIG_NFSD_V3 */ #ifdef CONFIG_NFSD_V4 __be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,