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