From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 32DDF3A782E for ; Tue, 24 Feb 2026 19:24:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771961088; cv=none; b=ABQC67CNrdKiZRBOOT/s0Bnvc4m1JV3FMr+WvYWVyfihkHLN4rDhQVlNxCFB1E9XKbQ16BqNYZxNGNPY0dZX0T0GfV16qPVIgKHCpePLjwcn/oNH7iZiJn1SePXO36T1s2paoKJvpQrDyl5W88dnspvJ5EyVT+CYBJVzMRW/JgA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771961088; c=relaxed/simple; bh=4woLTZqqsftdd5sX55RLnBecqq8eLAWs3R81sNVp2n8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=a50sQbhX33Kl1Cihy03glCFFxDBph4rZZZM66SxXfhqoTQowx1I37BmFh8LDU8Nj/FOO0V3AKML8C5nNOB+nN4sDNs8ma6bfsGuQf+8a8qvRSn9W9PGG9eT2K+ueDmzyi4YySTwtJ4N9fT3PlCl9fuir3IENyQW4EKWE2y8Wnx8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=m+sliJhj; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="m+sliJhj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AB825C19422; Tue, 24 Feb 2026 19:24:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771961087; bh=4woLTZqqsftdd5sX55RLnBecqq8eLAWs3R81sNVp2n8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m+sliJhjgB4gVRFAVStphUrlk8bpv24sNirwZp/k/HEkdknp7IpdhA+lly+acwUtQ whrnZ8hWI4eJOFUOeYxuQJ85yJyDaxR7jVCx0ws+oBFIE0JVmP+pJSr9h/u8ZvwoWX BmzcygV0QefOZMAHKuJvWjZQvtUlDTbix6LHLfw5t36tId4oejHC+tw8S5/f5dZnBk b778hXBR6eUk2+tbPx29yWM383ZjPMSFAJaklZH0U3TutsXhgVaCqhNnp/K9QvgT4v iEhpurviVzY0E+8EVehP+fexM0laVr2GzRRAN2hQ/e8OC5u/UDXNTvI7X4cGGOFg3N ZcOTvXSY1ufZg== From: Mike Snitzer To: Chuck Lever , Jeff Layton , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org Subject: [RFC PATCH v2 06/11] NFSD: add NFS4 reexport support for GETACL nfs4_acl passthru Date: Tue, 24 Feb 2026 14:24:33 -0500 Message-ID: <20260224192438.25351-7-snitzer@kernel.org> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20260224192438.25351-1-snitzer@kernel.org> References: <20260224192438.25351-1-snitzer@kernel.org> Precedence: bulk X-Mailing-List: linux-nfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Mike Snitzer Allow NFSD's 4.1 reexport of a 4.2 mount to perform GETACL by passing thru nfs4_acl whose pages are allocated in nfsd4_get_nfs4_acl_passthru and then passed down to exported filesystem's ops->getacl(). Once nfs4_acl is retrieved nfsd4_encode_fattr4_acl() will send the ACL payload to the client using nfsd4_encode_nfs4_acl_passthru(). Signed-off-by: Mike Snitzer --- fs/nfsd/acl.h | 3 ++- fs/nfsd/nfs4acl.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++- fs/nfsd/nfs4xdr.c | 33 ++++++++++++++++++++++- 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h index 699a3b19bdb8..488be04551e4 100644 --- a/fs/nfsd/acl.h +++ b/fs/nfsd/acl.h @@ -42,13 +42,14 @@ struct svc_fh; struct svc_rqst; struct nfsd_attrs; enum nfs_ftype4; +enum nfs4_acl_type; int nfs4_acl_bytes(int entries); int nfs4_acl_get_whotype(char *, u32); __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who); int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, - struct nfs4_acl **acl); + enum nfs4_acl_type acl_type, struct nfs4_acl **acl); __be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, struct nfsd_attrs *attr); void sort_pacl_range(struct posix_acl *pacl, int start, int end); diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 2c2f2fd89e87..2d494909e63a 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "nfsfh.h" #include "nfsd.h" @@ -125,9 +126,62 @@ static short ace2type(struct nfs4_ace *); static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int); +static int +nfsd4_get_nfs4_acl_passthru(struct inode *inode, + const struct export_operations *ops, + enum nfs4_acl_type acl_type, + u32 acl_len, struct nfs4_acl **acl) +{ + int error = 0; + int i = 0; + unsigned int npages; + + npages = DIV_ROUND_UP(acl_len, PAGE_SIZE); + *acl = kmalloc(sizeof(struct nfs4_acl) + + npages * sizeof(struct page *), GFP_KERNEL); + if (*acl == NULL) + return -ENOMEM; + + (*acl)->type = acl_type; + (*acl)->len = acl_len = npages * PAGE_SIZE; + (*acl)->pgbase = 0; + + for (; i < npages; i++) { + (*acl)->pages[i] = alloc_page(GFP_KERNEL); + if (!(*acl)->pages[i]) { + error = -ENOMEM; + goto out; + } + } + + if (unlikely(!ops->getacl)) { + error = -EOPNOTSUPP; + goto out; + } + + error = ops->getacl(inode, *acl); + if (likely(error > 0)) { + error = 0; /* don't error out below */ + if ((*acl)->len < acl_len) { + /* free any unused pages */ + npages = DIV_ROUND_UP((*acl)->len, PAGE_SIZE); + while (--i >= npages) + __free_page((*acl)->pages[i]); + } + } +out: + if (error) { + while (--i >= 0) + __free_page((*acl)->pages[i]); + kfree(*acl); + *acl = NULL; + } + return error; +} + int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, - struct nfs4_acl **acl) + enum nfs4_acl_type acl_type, struct nfs4_acl **acl) { struct inode *inode = d_inode(dentry); int error = 0; @@ -157,6 +211,19 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, size += 2 * dpacl->a_count; } + if (!IS_POSIXACL(inode) && + exportfs_may_passthru_nfs4acl(dentry->d_sb->s_export_op)) { + /* Ensure NFSv4 ACL has adequate space based on POSIX ACL size */ + u32 acl_len = min_t(u32, svc_max_payload(rqstp), + (2 * nfs4_acl_bytes(size) - + 2 * sizeof(struct nfs4_acl))); + const struct export_operations *ops = dentry->d_sb->s_export_op; + + error = nfsd4_get_nfs4_acl_passthru(inode, ops, acl_type, + acl_len, acl); + goto out; + } + *acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL); if (*acl == NULL) { error = -ENOMEM; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 62b1d79f80cb..80d2e7ea8cc9 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3391,6 +3391,33 @@ static __be32 nfsd4_encode_fattr4_aclsupport(struct xdr_stream *xdr, return nfsd4_encode_uint32_t(xdr, mask); } +static __be32 nfsd4_encode_nfs4_acl_passthru(struct xdr_stream *xdr, + struct nfs4_acl *acl) +{ + uint32_t pgbase = acl->pgbase; + uint32_t remaining = acl->len; + unsigned int npages = DIV_ROUND_UP(remaining, PAGE_SIZE); + + for (int i = 0; i < npages; i++) { + void *vaddr = page_address(acl->pages[i]); + size_t len = (remaining < PAGE_SIZE) ? remaining : PAGE_SIZE; + + if (pgbase) { + vaddr += pgbase; + pgbase = 0; + } + WARN_ON_ONCE(xdr_stream_encode_opaque_fixed(xdr, vaddr, len) < 0); + remaining -= len; + /* + * Free each page that was allocated using alloc_page() + * in nfsd4_get_nfs4_acl_passthru(). + */ + __free_page(acl->pages[i]); + } + + return nfs_ok; +} + static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr, const struct nfsd4_fattr_args *args) { @@ -3403,6 +3430,10 @@ static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr, if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) return nfserr_resource; } else { + if (!IS_POSIXACL(d_inode(args->dentry)) && + exportfs_may_passthru_nfs4acl(args->dentry->d_sb->s_export_op)) + return nfsd4_encode_nfs4_acl_passthru(xdr, acl); + if (xdr_stream_encode_u32(xdr, acl->naces) != XDR_UNIT) return nfserr_resource; for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) { @@ -4028,7 +4059,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, args.fhp = fhp; if (attrmask[0] & FATTR4_WORD0_ACL) { - err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl); + err = nfsd4_get_nfs4_acl(rqstp, dentry, NFS4ACL_ACL, &args.acl); if (err == -EOPNOTSUPP) attrmask[0] &= ~FATTR4_WORD0_ACL; else if (err == -EINVAL) { -- 2.44.0