xref: /linux/fs/nfsd/localio.c (revision c7546e2c3cb739a3c1a2f5acaf9bb629d401afe5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * NFS server support for local clients to bypass network stack
4  *
5  * Copyright (C) 2014 Weston Andros Adamson <dros@primarydata.com>
6  * Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com>
7  * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
8  * Copyright (C) 2024 NeilBrown <neilb@suse.de>
9  */
10 
11 #include <linux/exportfs.h>
12 #include <linux/sunrpc/svcauth.h>
13 #include <linux/sunrpc/clnt.h>
14 #include <linux/nfs.h>
15 #include <linux/nfs_common.h>
16 #include <linux/nfslocalio.h>
17 #include <linux/nfs_fs.h>
18 #include <linux/nfs_xdr.h>
19 #include <linux/string.h>
20 
21 #include "nfsd.h"
22 #include "vfs.h"
23 #include "netns.h"
24 #include "filecache.h"
25 #include "cache.h"
26 
27 static const struct nfsd_localio_operations nfsd_localio_ops = {
28 	.nfsd_serv_try_get  = nfsd_serv_try_get,
29 	.nfsd_serv_put  = nfsd_serv_put,
30 	.nfsd_open_local_fh = nfsd_open_local_fh,
31 	.nfsd_file_put_local = nfsd_file_put_local,
32 	.nfsd_file_file = nfsd_file_file,
33 };
34 
35 void nfsd_localio_ops_init(void)
36 {
37 	nfs_to = &nfsd_localio_ops;
38 }
39 
40 /**
41  * nfsd_open_local_fh - lookup a local filehandle @nfs_fh and map to nfsd_file
42  *
43  * @net: 'struct net' to get the proper nfsd_net required for LOCALIO access
44  * @dom: 'struct auth_domain' required for LOCALIO access
45  * @rpc_clnt: rpc_clnt that the client established
46  * @cred: cred that the client established
47  * @nfs_fh: filehandle to lookup
48  * @fmode: fmode_t to use for open
49  *
50  * This function maps a local fh to a path on a local filesystem.
51  * This is useful when the nfs client has the local server mounted - it can
52  * avoid all the NFS overhead with reads, writes and commits.
53  *
54  * On successful return, returned nfsd_file will have its nf_net member
55  * set. Caller (NFS client) is responsible for calling nfsd_serv_put and
56  * nfsd_file_put (via nfs_to->nfsd_file_put_local).
57  */
58 struct nfsd_file *
59 nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
60 		   struct rpc_clnt *rpc_clnt, const struct cred *cred,
61 		   const struct nfs_fh *nfs_fh, const fmode_t fmode)
62 {
63 	int mayflags = NFSD_MAY_LOCALIO;
64 	struct svc_cred rq_cred;
65 	struct svc_fh fh;
66 	struct nfsd_file *localio;
67 	__be32 beres;
68 
69 	if (nfs_fh->size > NFS4_FHSIZE)
70 		return ERR_PTR(-EINVAL);
71 
72 	/* nfs_fh -> svc_fh */
73 	fh_init(&fh, NFS4_FHSIZE);
74 	fh.fh_handle.fh_size = nfs_fh->size;
75 	memcpy(fh.fh_handle.fh_raw, nfs_fh->data, nfs_fh->size);
76 
77 	if (fmode & FMODE_READ)
78 		mayflags |= NFSD_MAY_READ;
79 	if (fmode & FMODE_WRITE)
80 		mayflags |= NFSD_MAY_WRITE;
81 
82 	svcauth_map_clnt_to_svc_cred_local(rpc_clnt, cred, &rq_cred);
83 
84 	beres = nfsd_file_acquire_local(net, &rq_cred, dom,
85 					&fh, mayflags, &localio);
86 	if (beres)
87 		localio = ERR_PTR(nfs_stat_to_errno(be32_to_cpu(beres)));
88 
89 	fh_put(&fh);
90 	if (rq_cred.cr_group_info)
91 		put_group_info(rq_cred.cr_group_info);
92 
93 	return localio;
94 }
95 EXPORT_SYMBOL_GPL(nfsd_open_local_fh);
96 
97 /*
98  * UUID_IS_LOCAL XDR functions
99  */
100 
101 static __be32 localio_proc_null(struct svc_rqst *rqstp)
102 {
103 	return rpc_success;
104 }
105 
106 struct localio_uuidarg {
107 	uuid_t			uuid;
108 };
109 
110 static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp)
111 {
112 	struct localio_uuidarg *argp = rqstp->rq_argp;
113 	struct net *net = SVC_NET(rqstp);
114 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
115 
116 	nfs_uuid_is_local(&argp->uuid, &nn->local_clients,
117 			  net, rqstp->rq_client, THIS_MODULE);
118 
119 	return rpc_success;
120 }
121 
122 static bool localio_decode_uuidarg(struct svc_rqst *rqstp,
123 				   struct xdr_stream *xdr)
124 {
125 	struct localio_uuidarg *argp = rqstp->rq_argp;
126 	u8 uuid[UUID_SIZE];
127 
128 	if (decode_opaque_fixed(xdr, uuid, UUID_SIZE))
129 		return false;
130 	import_uuid(&argp->uuid, uuid);
131 
132 	return true;
133 }
134 
135 static const struct svc_procedure localio_procedures1[] = {
136 	[LOCALIOPROC_NULL] = {
137 		.pc_func = localio_proc_null,
138 		.pc_decode = nfssvc_decode_voidarg,
139 		.pc_encode = nfssvc_encode_voidres,
140 		.pc_argsize = sizeof(struct nfsd_voidargs),
141 		.pc_ressize = sizeof(struct nfsd_voidres),
142 		.pc_cachetype = RC_NOCACHE,
143 		.pc_xdrressize = 0,
144 		.pc_name = "NULL",
145 	},
146 	[LOCALIOPROC_UUID_IS_LOCAL] = {
147 		.pc_func = localio_proc_uuid_is_local,
148 		.pc_decode = localio_decode_uuidarg,
149 		.pc_encode = nfssvc_encode_voidres,
150 		.pc_argsize = sizeof(struct localio_uuidarg),
151 		.pc_argzero = sizeof(struct localio_uuidarg),
152 		.pc_ressize = sizeof(struct nfsd_voidres),
153 		.pc_cachetype = RC_NOCACHE,
154 		.pc_name = "UUID_IS_LOCAL",
155 	},
156 };
157 
158 #define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1)
159 static DEFINE_PER_CPU_ALIGNED(unsigned long,
160 			      localio_count[LOCALIO_NR_PROCEDURES]);
161 const struct svc_version localio_version1 = {
162 	.vs_vers	= 1,
163 	.vs_nproc	= LOCALIO_NR_PROCEDURES,
164 	.vs_proc	= localio_procedures1,
165 	.vs_dispatch	= nfsd_dispatch,
166 	.vs_count	= localio_count,
167 	.vs_xdrsize	= XDR_QUADLEN(UUID_SIZE),
168 	.vs_hidden	= true,
169 };
170