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 6B9763A782E for ; Tue, 24 Feb 2026 19:24:53 +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=1771961093; cv=none; b=LJMgG3yf1J3fChlikikn5qRjcZkI563aosZ14JF46YgoXv4yunzk5fYPS7YevAmwrNen0PIa/GIjMHEOeuQFva1uuI/ye0jAG0i5Gq7oZpCupRn8h/uWWcxGogyaPqMs5oB/U/bmOb6TYBxP5s6hpxVa9B26PcuysYPHs5DEvNw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771961093; c=relaxed/simple; bh=rjDdLqGu2jbLBv1GL0mFFzjMq19VF5FL6HRWMlyUSmk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cwSdk8ou7oVPWzAe7/4qpSLEyQnwMSORpAPzYZX/WEFUxaT0DRRwH+0DyRxfRnHL7fDRcwGaCa9Q5QtpnkdZ7dVVshu2lSZ9R0Xgo1Ggn1HZPVHCscQNbrae6SNpV8YbdMyuCb+rHxb0H6HgcTNKYqn37rgLK0XumtWM8li0bqk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ATdnPHUE; 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="ATdnPHUE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 12FDCC116D0; Tue, 24 Feb 2026 19:24:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1771961093; bh=rjDdLqGu2jbLBv1GL0mFFzjMq19VF5FL6HRWMlyUSmk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ATdnPHUE/ffVdJURL5+gnpZ48mSkZXR56UtwSPoJ1Z96ZMLOo8OJXSutePlEUglJP DkTKlJkyeoXIUe3RqHJyBGTkcndgCZFOhj7yABAJXo/v1Q6ixWYFZPz4C8OweeC+bu jPdxO1UVS7nZ5S5zg1nOJWTcu50gqDC78AaTIbdsestMEccoZnNdV8QEjWxKabjfR8 1Knq7k/T9WYzxsJovehm/pVXD9ua6JkzOMscComaTap3u3H0hyYGV+goAxKnDPwExX n6JWSJvdykj56b26f2l68Dzs3N02Hc3mDUsBWhupP5Oxl/SXKOLteST20FyW5pWeNt eSVqvbtzwbnZg== From: Mike Snitzer To: Chuck Lever , Jeff Layton , Trond Myklebust , Anna Schumaker Cc: linux-nfs@vger.kernel.org Subject: [RFC PATCH v2 10/11] NFSv4: add reexport support for GETACL nfs4_acl passthru Date: Tue, 24 Feb 2026 14:24:37 -0500 Message-ID: <20260224192438.25351-11-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 Wire up the export_operations .getacl hook and use the upper layer provided nfs4_acl's pages in the call to NFSPROC4_CLNT_GETACL. In this reexporting case (e.g. 4.1 reexporting 4.2), __nfs4_get_acl_uncached() is always used because the NFSv4 client's cache is designed to reply with the buffer that gets generated from the ACL's pages. So the frontend client (4.1) will cache the ACL returned by the backend (4.2) client. Signed-off-by: Mike Snitzer --- fs/nfs/export.c | 10 ++++++ fs/nfs/nfs4proc.c | 79 ++++++++++++++++++++++++++++------------- include/linux/nfs_xdr.h | 1 + 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/fs/nfs/export.c b/fs/nfs/export.c index 9a90eee3e433..6623eb13f4e6 100644 --- a/fs/nfs/export.c +++ b/fs/nfs/export.c @@ -161,11 +161,21 @@ static int nfs_set_nfs4_acl(struct inode *inode, struct nfs4_acl *acl) return rpc_ops->set_nfs4_acl(inode, acl); } +static int nfs_get_nfs4_acl(struct inode *inode, struct nfs4_acl *acl) +{ + const struct nfs_rpc_ops *rpc_ops = NFS_SERVER(inode)->nfs_client->rpc_ops; + + if (rpc_ops->get_nfs4_acl == NULL) + return -EOPNOTSUPP; + return rpc_ops->get_nfs4_acl(inode, acl); +} + const struct export_operations nfs_export_ops = { .encode_fh = nfs_encode_fh, .fh_to_dentry = nfs_fh_to_dentry, .get_parent = nfs_get_parent, .setacl = nfs_set_nfs4_acl, + .getacl = nfs_get_nfs4_acl, .flags = EXPORT_OP_NOWCC | EXPORT_OP_NOSUBTREECHK | EXPORT_OP_CLOSE_BEFORE_UNLINK | diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a71be8fcc893..7d535787d824 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6045,7 +6045,8 @@ static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, * the server, this time with the input buf of the required size. */ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, - size_t buflen, enum nfs4_acl_type type) + size_t buflen, enum nfs4_acl_type type, + struct nfs4_acl *acl) { struct page **pages; struct nfs_getaclargs args = { @@ -6069,26 +6070,29 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, if (buflen == 0) buflen = server->rsize; - npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1; - pages = kmalloc_objs(struct page *, npages); - if (!pages) - return -ENOMEM; + if (!acl) { + npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1; + pages = kmalloc_objs(struct page *, npages); + if (!pages) + return -ENOMEM; + for (i = 0; i < npages; i++) { + pages[i] = alloc_page(GFP_KERNEL); + if (!pages[i]) + goto out_free; + } + args.acl_len = npages * PAGE_SIZE; + } else { + npages = DIV_ROUND_UP(buflen, PAGE_SIZE); + pages = acl->pages; + } args.acl_pages = pages; - for (i = 0; i < npages; i++) { - pages[i] = alloc_page(GFP_KERNEL); - if (!pages[i]) - goto out_free; - } - /* for decoding across pages */ res.acl_scratch = folio_alloc(GFP_KERNEL, 0); if (!res.acl_scratch) goto out_free; - args.acl_len = npages * PAGE_SIZE; - dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", __func__, buf, buflen, npages, args.acl_len); ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), @@ -6104,8 +6108,18 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, ret = -ERANGE; goto out_free; } - nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len, - type); + + if (!acl) { + nfs4_write_cached_acl(inode, pages, res.acl_data_offset, + res.acl_len, type); + } else { + if (res.acl_len > buflen) { + ret = -ERANGE; + goto out_free; + } + acl->len = res.acl_len; + acl->pgbase = res.acl_data_offset; + } if (buf) { if (res.acl_len > buflen) { ret = -ERANGE; @@ -6116,23 +6130,26 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, out_ok: ret = res.acl_len; out_free: - while (--i >= 0) - __free_page(pages[i]); + if (!acl) { + while (--i >= 0) + __free_page(pages[i]); + kfree(pages); + } if (res.acl_scratch) folio_put(res.acl_scratch); - kfree(pages); return ret; } static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, - size_t buflen, enum nfs4_acl_type type) + size_t buflen, enum nfs4_acl_type type, + struct nfs4_acl *acl) { struct nfs4_exception exception = { .interruptible = true, }; ssize_t ret; do { - ret = __nfs4_get_acl_uncached(inode, buf, buflen, type); + ret = __nfs4_get_acl_uncached(inode, buf, buflen, type, acl); trace_nfs4_get_acl(inode, ret); if (ret >= 0) break; @@ -6142,7 +6159,7 @@ static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, } static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen, - enum nfs4_acl_type type) + enum nfs4_acl_type type, struct nfs4_acl *acl) { struct nfs_server *server = NFS_SERVER(inode); int ret; @@ -6156,12 +6173,23 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen, return ret; if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL) nfs_zap_acl_cache(inode); + /* Must get ACL if @acl != NULL, frontend NFS layer will cache it */ + if (acl != NULL) + goto get_acl_uncached; ret = nfs4_read_cached_acl(inode, buf, buflen, type); if (ret != -ENOENT) /* -ENOENT is returned if there is no ACL or if there is an ACL * but no cached acl data, just the acl length */ return ret; - return nfs4_get_acl_uncached(inode, buf, buflen, type); +get_acl_uncached: + return nfs4_get_acl_uncached(inode, buf, buflen, type, acl); +} + +static int nfs4_get_nfs4_acl(struct inode *inode, struct nfs4_acl *acl) +{ + if (!nfs4_server_supports_acls(NFS_SERVER(inode), acl->type)) + return -EOPNOTSUPP; + return nfs4_proc_get_acl(inode, NULL, acl->len, acl->type, acl); } static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, @@ -7831,7 +7859,7 @@ static int nfs4_xattr_get_nfs4_acl(const struct xattr_handler *handler, struct dentry *unused, struct inode *inode, const char *key, void *buf, size_t buflen) { - return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_ACL); + return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_ACL, NULL); } static bool nfs4_xattr_list_nfs4_acl(struct dentry *dentry) @@ -7854,7 +7882,7 @@ static int nfs4_xattr_get_nfs4_dacl(const struct xattr_handler *handler, struct dentry *unused, struct inode *inode, const char *key, void *buf, size_t buflen) { - return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_DACL); + return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_DACL, NULL); } static bool nfs4_xattr_list_nfs4_dacl(struct dentry *dentry) @@ -7877,7 +7905,7 @@ static int nfs4_xattr_get_nfs4_sacl(const struct xattr_handler *handler, struct dentry *unused, struct inode *inode, const char *key, void *buf, size_t buflen) { - return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_SACL); + return nfs4_proc_get_acl(inode, buf, buflen, NFS4ACL_SACL, NULL); } static bool nfs4_xattr_list_nfs4_sacl(struct dentry *dentry) @@ -10704,6 +10732,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .enable_swap = nfs4_enable_swap, .disable_swap = nfs4_disable_swap, .set_nfs4_acl = nfs4_set_nfs4_acl, + .get_nfs4_acl = nfs4_get_nfs4_acl, }; static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a86ed4532465..5d629ad9781f 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1853,6 +1853,7 @@ struct nfs_rpc_ops { void (*enable_swap)(struct inode *inode); void (*disable_swap)(struct inode *inode); int (*set_nfs4_acl)(struct inode *, struct nfs4_acl *); + int (*get_nfs4_acl)(struct inode *, struct nfs4_acl *); }; /* -- 2.44.0