public inbox for linux-nfs@vger.kernel.org
 help / color / mirror / Atom feed
From: Jeff Layton <jlayton@kernel.org>
To: Steve Dickson <steved@redhat.com>
Cc: Chuck Lever <chuck.lever@oracle.com>, NeilBrown <neil@brown.name>,
	 Olga Kornievskaia <okorniev@redhat.com>,
	Dai Ngo <Dai.Ngo@oracle.com>,  Tom Talpey <tom@talpey.com>,
	Trond Myklebust <trondmy@kernel.org>,
	 Anna Schumaker <anna@kernel.org>,
	linux-nfs@vger.kernel.org,  Jeff Layton <jlayton@kernel.org>
Subject: [PATCH nfs-utils v2 08/16] exportd/mountd: add netlink support for the nfsd.fh cache
Date: Mon, 30 Mar 2026 09:38:28 -0400	[thread overview]
Message-ID: <20260330-exportd-netlink-v2-8-cc9bd5db2408@kernel.org> (raw)
In-Reply-To: <20260330-exportd-netlink-v2-0-cc9bd5db2408@kernel.org>

Refactor cache_nl_set_reqs() to take a socket parameter so it can be
reused for multiple genl families.

Rename cache_nl_process() to cache_nl_process_export() and factor out
the notification drain and auth_reload() into a new
cache_nfsd_nl_process() wrapper.

Add the expkey (nfsd.fh) netlink cache handler, which uses the same
nfsd genl family as svc_export. The handler resolves fsid-based
lookups including CROSSMOUNT submount iteration and responds via
NFSD_CMD_EXPKEY_SET_REQS.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 support/export/cache.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 314 insertions(+), 9 deletions(-)

diff --git a/support/export/cache.c b/support/export/cache.c
index 090ea0a6cc6da7e9c2ce255601103d2442526067..c350662fd97c33c40c1d59297b9638141a67befb 100644
--- a/support/export/cache.c
+++ b/support/export/cache.c
@@ -1477,7 +1477,7 @@ static struct nl_msg *cache_nl_new_msg(int family, int cmd, int flags)
 	return msg;
 }
 
-static int cache_nl_set_reqs(struct nl_msg *msg)
+static int cache_nl_set_reqs(struct nl_sock *sock, struct nl_msg *msg)
 {
 	struct nl_cb *cb;
 	int done = 0;
@@ -1490,14 +1490,14 @@ static int cache_nl_set_reqs(struct nl_msg *msg)
 	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_finish_cb, &done);
 	nl_cb_err(cb, NL_CB_CUSTOM, nl_error_cb, &done);
 
-	ret = nl_send_auto(nfsd_nl_cmd_sock, msg);
+	ret = nl_send_auto(sock, msg);
 	if (ret < 0) {
 		nl_cb_put(cb);
 		return ret;
 	}
 
 	while (!done) {
-		ret = nl_recvmsgs(nfsd_nl_cmd_sock, cb);
+		ret = nl_recvmsgs(sock, cb);
 		if (ret < 0)
 			break;
 	}
@@ -1515,9 +1515,6 @@ static void cache_nl_process_export(void)
 	struct nl_msg *msg;
 	int i;
 
-	/* Drain pending notifications */
-	cache_nfsd_nl_drain();
-
 	/* Fetch all pending requests from the kernel */
 	if (cache_nl_get_export_reqs(&reqs, &nreqs)) {
 		xlog(L_WARNING, "cache_nl_process_export: failed to get export requests");
@@ -1571,7 +1568,7 @@ static void cache_nl_process_export(void)
 		}
 
 		if (nfsd_nl_add_export(msg, dom, path, epp, ttl) < 0) {
-			cache_nl_set_reqs(msg);
+			cache_nl_set_reqs(nfsd_nl_cmd_sock, msg);
 			nlmsg_free(msg);
 			msg = cache_nl_new_msg(nfsd_nl_family,
 					       NFSD_CMD_SVC_EXPORT_SET_REQS, 0);
@@ -1588,7 +1585,7 @@ static void cache_nl_process_export(void)
 		nfs_freeaddrinfo(ai);
 	}
 
-	cache_nl_set_reqs(msg);
+	cache_nl_set_reqs(nfsd_nl_cmd_sock, msg);
 	nlmsg_free(msg);
 
 out_free:
@@ -1599,6 +1596,314 @@ out_free:
 	free(reqs);
 }
 
+/*
+ * Netlink-based expkey (nfsd.fh) cache support.
+ *
+ * Uses the same nfsd genl family as svc_export. The kernel sends
+ * NFSD_CMD_CACHE_NOTIFY with NFSD_CACHE_TYPE_EXPKEY to signal
+ * pending expkey cache requests.
+ */
+struct expkey_req {
+	char	*client;
+	int	fsidtype;
+	char	*fsid;
+	int	fsidlen;
+};
+
+struct get_expkey_reqs_data {
+	struct expkey_req	*reqs;
+	int			nreqs;
+	int			maxreqs;
+	int			err;
+};
+
+static int get_expkey_reqs_cb(struct nl_msg *msg, void *arg)
+{
+	struct get_expkey_reqs_data *data = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *attr;
+	int rem;
+
+	nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0),
+			  genlmsg_attrlen(gnlh, 0), rem) {
+		struct nlattr *tb[NFSD_A_EXPKEY_PATH + 1];
+		struct expkey_req *req;
+
+		if (nla_type(attr) != NFSD_A_EXPKEY_REQS_REQUESTS)
+			continue;
+
+		if (nla_parse_nested(tb, NFSD_A_EXPKEY_PATH, attr, NULL))
+			continue;
+
+		if (!tb[NFSD_A_EXPKEY_CLIENT] ||
+		    !tb[NFSD_A_EXPKEY_FSIDTYPE] ||
+		    !tb[NFSD_A_EXPKEY_FSID])
+			continue;
+
+		if (data->nreqs >= data->maxreqs) {
+			int newmax = data->maxreqs ? data->maxreqs * 2 : 16;
+			struct expkey_req *tmp;
+
+			tmp = realloc(data->reqs, newmax * sizeof(*tmp));
+			if (!tmp) {
+				data->err = -ENOMEM;
+				return NL_STOP;
+			}
+			data->reqs = tmp;
+			data->maxreqs = newmax;
+		}
+
+		req = &data->reqs[data->nreqs++];
+		req->client = strdup(nla_get_string(tb[NFSD_A_EXPKEY_CLIENT]));
+		req->fsidtype = nla_get_u8(tb[NFSD_A_EXPKEY_FSIDTYPE]);
+		req->fsidlen = nla_len(tb[NFSD_A_EXPKEY_FSID]);
+		req->fsid = malloc(req->fsidlen);
+
+		if (!req->client || !req->fsid) {
+			data->err = -ENOMEM;
+			return NL_STOP;
+		}
+		memcpy(req->fsid, nla_data(tb[NFSD_A_EXPKEY_FSID]),
+		       req->fsidlen);
+	}
+
+	return NL_OK;
+}
+
+static int cache_nl_get_expkey_reqs(struct expkey_req **reqs_out,
+				    int *nreqs_out)
+{
+	struct get_expkey_reqs_data data = { };
+	struct nl_msg *msg;
+	struct nl_cb *cb;
+	int done = 0;
+	int ret;
+
+	msg = cache_nl_new_msg(nfsd_nl_family,
+			       NFSD_CMD_EXPKEY_GET_REQS, NLM_F_DUMP);
+	if (!msg)
+		return -ENOMEM;
+
+	cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!cb) {
+		nlmsg_free(msg);
+		return -ENOMEM;
+	}
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_expkey_reqs_cb, &data);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_cb, &done);
+	nl_cb_err(cb, NL_CB_CUSTOM, nl_error_cb, &done);
+
+	ret = nl_send_auto(nfsd_nl_cmd_sock, msg);
+	nlmsg_free(msg);
+	if (ret < 0) {
+		nl_cb_put(cb);
+		return ret;
+	}
+
+	while (!done) {
+		ret = nl_recvmsgs(nfsd_nl_cmd_sock, cb);
+		if (ret < 0)
+			break;
+	}
+
+	nl_cb_put(cb);
+
+	if (data.err) {
+		int i;
+		for (i = 0; i < data.nreqs; i++) {
+			free(data.reqs[i].client);
+			free(data.reqs[i].fsid);
+		}
+		free(data.reqs);
+		return data.err;
+	}
+
+	*reqs_out = data.reqs;
+	*nreqs_out = data.nreqs;
+	return 0;
+}
+
+static int nfsd_nl_add_expkey(struct nl_msg *msg, char *dom, int fsidtype,
+			 char *fsid, int fsidlen, char *found_path)
+{
+	struct nlattr *nest;
+
+	nest = nla_nest_start(msg, NFSD_A_EXPKEY_REQS_REQUESTS);
+	if (!nest)
+		return -1;
+
+	if (nla_put_string(msg, NFSD_A_EXPKEY_CLIENT, dom) < 0 ||
+	    nla_put_u8(msg, NFSD_A_EXPKEY_FSIDTYPE, fsidtype) < 0 ||
+	    nla_put(msg, NFSD_A_EXPKEY_FSID, fsidlen, fsid) < 0 ||
+	    nla_put_u64(msg, NFSD_A_EXPKEY_EXPIRY, 0x7fffffff) < 0)
+		goto nla_failure;
+
+	if (found_path) {
+		if (nla_put_string(msg, NFSD_A_EXPKEY_PATH,
+				   found_path) < 0)
+			goto nla_failure;
+	} else {
+		if (nla_put_flag(msg, NFSD_A_EXPKEY_NEGATIVE) < 0)
+			goto nla_failure;
+	}
+
+	nla_nest_end(msg, nest);
+	return 0;
+
+nla_failure:
+	nla_nest_cancel(msg, nest);
+	return -1;
+}
+
+static void cache_nl_process_expkey(void)
+{
+	struct expkey_req *reqs = NULL;
+	int nreqs = 0;
+	struct nl_msg *msg;
+	int i;
+
+	if (cache_nl_get_expkey_reqs(&reqs, &nreqs)) {
+		xlog(L_WARNING, "cache_nl_process_expkey: failed to get expkey requests");
+		return;
+	}
+
+	if (!nreqs)
+		return;
+
+	xlog(D_CALL, "cache_nl_process_expkey: %d pending expkey requests", nreqs);
+
+	msg = cache_nl_new_msg(nfsd_nl_family, NFSD_CMD_EXPKEY_SET_REQS, 0);
+	if (!msg)
+		goto out_free;
+
+	for (i = 0; i < nreqs; i++) {
+		char *dom = reqs[i].client;
+		int fsidtype = reqs[i].fsidtype;
+		char *fsid = reqs[i].fsid;
+		int fsidlen = reqs[i].fsidlen;
+		struct parsed_fsid parsed;
+		struct addrinfo *ai = NULL;
+		struct exportent *found = NULL;
+		char *found_path = NULL;
+		nfs_export *exp;
+		int j;
+
+		if (parse_fsid(fsidtype, fsidlen, fsid, &parsed))
+			goto do_add_expkey;
+
+		if (is_ipaddr_client(dom)) {
+			ai = lookup_client_addr(dom);
+			if (!ai)
+				goto do_add_expkey;
+		}
+
+		for (j = 0; j < MCL_MAXTYPES; j++) {
+			nfs_export *prev = NULL;
+			nfs_export *next_exp;
+			void *mnt = NULL;
+
+			for (exp = exportlist[j].p_head; exp;
+			     exp = next_exp) {
+				char *path;
+
+				if (exp->m_export.e_flags &
+				    NFSEXP_CROSSMOUNT) {
+					if (prev == exp) {
+						path = next_mnt(&mnt,
+							exp->m_export.e_path);
+						if (!path) {
+							next_exp = exp->m_next;
+							prev = NULL;
+							continue;
+						}
+						next_exp = exp;
+					} else {
+						prev = exp;
+						mnt = NULL;
+						path = exp->m_export.e_path;
+						next_exp = exp;
+					}
+				} else {
+					path = exp->m_export.e_path;
+					next_exp = exp->m_next;
+				}
+
+				if (!is_ipaddr_client(dom) &&
+				    !namelist_client_matches(exp, dom))
+					continue;
+
+				switch (match_fsid(&parsed, exp, path)) {
+				case 0:
+					continue;
+				case -1:
+					continue;
+				}
+
+				if (is_ipaddr_client(dom) &&
+				    !ipaddr_client_matches(exp, ai))
+					continue;
+
+				if (!found ||
+				    subexport(&exp->m_export, found)) {
+					found = &exp->m_export;
+					free(found_path);
+					found_path = strdup(path);
+					if (!found_path)
+						goto do_add_expkey;
+				}
+			}
+		}
+
+do_add_expkey:
+		if (nfsd_nl_add_expkey(msg, dom, fsidtype, fsid,
+				       fsidlen, found_path) < 0) {
+			cache_nl_set_reqs(nfsd_nl_cmd_sock, msg);
+			nlmsg_free(msg);
+			msg = cache_nl_new_msg(nfsd_nl_family,
+					       NFSD_CMD_EXPKEY_SET_REQS, 0);
+			if (!msg) {
+				free(found_path);
+				nfs_freeaddrinfo(ai);
+				goto out_free;
+			}
+			if (nfsd_nl_add_expkey(msg, dom, fsidtype, fsid,
+					       fsidlen, found_path) < 0)
+				xlog(L_WARNING, "%s: skipping oversized "
+				     "entry", __func__);
+		}
+		if (!found)
+			xlog(D_AUTH, "denied access to %s",
+			     *dom == '$' ? dom + 1 : dom);
+		free(found_path);
+		nfs_freeaddrinfo(ai);
+	}
+
+	cache_nl_set_reqs(nfsd_nl_cmd_sock, msg);
+	nlmsg_free(msg);
+
+out_free:
+	for (i = 0; i < nreqs; i++) {
+		free(reqs[i].client);
+		free(reqs[i].fsid);
+	}
+	free(reqs);
+}
+
+static void cache_nfsd_nl_process(void)
+{
+	/* Drain pending nfsd notifications */
+	cache_nfsd_nl_drain();
+
+	auth_reload();
+
+	/* Handle any pending svc_export requests */
+	cache_nl_process_export();
+
+	/* Handle any pending expkey requests */
+	cache_nl_process_expkey();
+}
+
 static int can_reexport_via_fsidnum(struct exportent *exp, struct statfs *st)
 {
 	if (st->f_type != 0x6969 /* NFS_SUPER_MAGIC */)
@@ -2207,7 +2512,7 @@ int cache_process_req(fd_set *readfds)
 	if (nfsd_nl_notify_sock &&
 	    FD_ISSET(nl_socket_get_fd(nfsd_nl_notify_sock), readfds)) {
 		cnt++;
-		cache_nl_process_export();
+		cache_nfsd_nl_process();
 		FD_CLR(nl_socket_get_fd(nfsd_nl_notify_sock), readfds);
 	}
 	return cnt;

-- 
2.53.0


  parent reply	other threads:[~2026-03-30 13:38 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-30 13:38 [PATCH nfs-utils v2 00/16] exportfs/exportd/mountd: allow them to use netlink for up/downcalls Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 01/16] nfsdctl: move *_netlink.h to support/include/ Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 02/16] support/export: remove unnecessary static variables in nfsd_fh expkey lookup Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 03/16] exportfs: remove obsolete legacy mode documentation from manpage Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 04/16] support/include: update netlink headers for all cache upcalls Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 05/16] build: add libnl3 and netlink header support for exportd and mountd Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 06/16] xlog: claim D_FAC3 as D_NETLINK Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 07/16] exportd/mountd: add netlink support for svc_export cache Jeff Layton
2026-03-30 13:38 ` Jeff Layton [this message]
2026-03-30 13:38 ` [PATCH nfs-utils v2 09/16] exportd/mountd: add netlink support for the auth.unix.ip cache Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 10/16] exportd/mountd: add netlink support for the auth.unix.gid cache Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 11/16] mountd/exportd: only use /proc interfaces if netlink setup fails Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 12/16] support/export: check for pending requests after opening netlink sockets Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 13/16] exportd/mountd: use cache type from notifications to target scanning Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 14/16] exportfs: add netlink support for cache flush with /proc fallback Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 15/16] exportfs: use netlink to probe kernel support, skip export_test Jeff Layton
2026-03-30 13:38 ` [PATCH nfs-utils v2 16/16] mountd/exportd/exportfs: add --no-netlink option to disable netlink Jeff Layton
2026-04-03 12:55 ` [PATCH nfs-utils v2 00/16] exportfs/exportd/mountd: allow them to use netlink for up/downcalls Steve Dickson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260330-exportd-netlink-v2-8-cc9bd5db2408@kernel.org \
    --to=jlayton@kernel.org \
    --cc=Dai.Ngo@oracle.com \
    --cc=anna@kernel.org \
    --cc=chuck.lever@oracle.com \
    --cc=linux-nfs@vger.kernel.org \
    --cc=neil@brown.name \
    --cc=okorniev@redhat.com \
    --cc=steved@redhat.com \
    --cc=tom@talpey.com \
    --cc=trondmy@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox