xref: /illumos-gate/usr/src/cmd/fs.d/nfs/rp_basic/libnfs_basic.c (revision 5400c85432c0a689ecefb8c19b8f9861809ca093)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2026 Oxide Computer Company
26  */
27 
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <strings.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/errno.h>
35 #include <limits.h>
36 #include <libnvpair.h>
37 #include <dlfcn.h>
38 #include <link.h>
39 #include <rp_plugin.h>
40 #include <fcntl.h>
41 #include <uuid/uuid.h>
42 #include <rpc/types.h>
43 #include <rpc/xdr.h>
44 #include <rpc/auth.h>
45 #include <rpc/clnt.h>
46 #include <rpc/rpc_msg.h>
47 #include <sys/param.h>
48 #include <nfs/nfs4.h>
49 #include <rpcsvc/nfs4_prot.h>
50 #include "ref_subr.h"
51 
52 extern int errno;
53 
54 #define	SERVICE_TYPE	"nfs-basic"
55 
56 char *nfs_basic_service_type(void);
57 boolean_t nfs_basic_supports_svc(const char *);
58 int nfs_basic_deref(const char *, const char *, char *, size_t *);
59 int nfs_basic_form(const char *, const char *, char *, size_t *);
60 
61 struct rp_plugin_ops rp_plugin_ops = {
62 	RP_PLUGIN_V1,
63 	NULL,			/* rpo_init */
64 	NULL,			/* rpo_fini */
65 	nfs_basic_service_type,
66 	nfs_basic_supports_svc,
67 	nfs_basic_form,
68 	nfs_basic_deref
69 };
70 
71 /*
72  * What service type does this module support?
73  */
74 char *
75 nfs_basic_service_type()
76 {
77 	return (SERVICE_TYPE);
78 }
79 
80 /*
81  * Does this module support a particular service type?
82  */
83 boolean_t
84 nfs_basic_supports_svc(const char *svc_type)
85 {
86 	if (!svc_type)
87 		return (0);
88 	return (!strncasecmp(svc_type, SERVICE_TYPE, strlen(SERVICE_TYPE)));
89 }
90 
91 /*
92  * Take a string with a set of locations like this:
93  *   host1:/path1 host2:/path2 host3:/path3
94  * and convert it to an fs_locations4 for the deref routine.
95  */
96 static fs_locations4 *
97 get_fs_locations(char *buf)
98 {
99 	fs_locations4 *result = NULL;
100 	fs_location4 *fsl_array;
101 	int i, gothost;
102 	int fsl_count = 0, escape = 0, delimiter = 0;
103 	int len;
104 	char *p, *sp, *dp, buf2[SYMLINK_MAX];
105 
106 	if (buf == NULL)
107 		return (NULL);
108 
109 	/*
110 	 * Each host and path is copied into buf2 below so neither can be
111 	 * longer than the input; reject any input that would not fit.
112 	 * Data from a real reparse point is always shorter than this.
113 	 */
114 	if (strlen(buf) >= sizeof (buf2))
115 		return (NULL);
116 #ifdef DEBUG
117 	printf("get_fs_locations: input %s\n", buf);
118 #endif
119 	/*
120 	 * Count fs_location entries by counting spaces.
121 	 * Remember that escaped spaces ("\ ") may exist.
122 	 * We mark the location boundaries with null bytes.
123 	 * Variable use:
124 	 *   escape -   set if we have found a backspace,
125 	 *		part of either "\ " or "\\"
126 	 *   delimiter - set if we have found a space and
127 	 *		 used to skip multiple spaces
128 	 */
129 	for (sp = buf; sp && *sp; sp++) {
130 		if (*sp == '\\') {
131 			escape = 1;
132 			delimiter = 0;
133 			continue;
134 		}
135 		if (*sp == ' ') {
136 			if (delimiter == 1)
137 				continue;
138 			if (escape == 0) {
139 				delimiter = 1;
140 				fsl_count++;
141 				*sp = '\0';
142 			} else
143 				escape = 0;
144 		} else
145 			delimiter = 0;
146 	}
147 	len = sp - buf;
148 	sp--;
149 	if (escape == 0 && *sp != '\0')
150 		fsl_count++;
151 #ifdef DEBUG
152 	printf("get_fs_locations: fsl_count %d\n", fsl_count);
153 #endif
154 	if (fsl_count == 0)
155 		goto out;
156 
157 	/* Alloc space for everything */
158 	result = calloc(1, sizeof (fs_locations4));
159 	if (result == NULL)
160 		goto out;
161 	fsl_array = calloc(fsl_count, sizeof (fs_location4));
162 	if (fsl_array == NULL) {
163 		free(result);
164 		result = NULL;
165 		goto out;
166 	}
167 	result->locations.locations_len = fsl_count;
168 	result->locations.locations_val = fsl_array;
169 	result->fs_root.pathname4_len = 0;
170 	result->fs_root.pathname4_val = NULL;
171 
172 	/*
173 	 * Copy input, removing escapes from host:/path/to/my\ files
174 	 */
175 	sp = buf;
176 	dp = buf2;
177 	bzero(buf2, sizeof (buf2));
178 
179 	i = gothost = 0;
180 	while ((sp && *sp && (sp - buf < len)) || gothost) {
181 
182 		if (!gothost) {
183 			/* Drop leading spaces */
184 			if (*sp == ' ') {
185 				sp++;
186 				continue;
187 			}
188 
189 			/* Look for the rightmost colon for host */
190 			p = strrchr(sp, ':');
191 			if (!p) {
192 #ifdef DEBUG
193 				printf("get_fs_locations: skipping %s\n", sp);
194 #endif
195 				fsl_count--;
196 				sp += strlen(sp) + 1;
197 			} else {
198 				bcopy(sp, dp, p - sp);
199 				sp = p + 1;
200 #ifdef DEBUG
201 				printf("get_fs_locations: host %s\n", buf2);
202 #endif
203 				fsl_array[i].server.server_len = 1;
204 				fsl_array[i].server.server_val =
205 				    malloc(sizeof (utf8string));
206 				if (fsl_array[i].server.server_val == NULL) {
207 					int j;
208 
209 					free(result);
210 					result = NULL;
211 					for (j = 0; j < i; j++)
212 						free(fsl_array[j].
213 						    server.server_val);
214 					free(fsl_array);
215 					goto out;
216 				}
217 				str_to_utf8(buf2,
218 				    fsl_array[i].server.server_val);
219 				gothost = 1;
220 				dp = buf2;
221 				bzero(buf2, sizeof (buf2));
222 			}
223 			continue;
224 		}
225 
226 		/* End of string should mean a pathname */
227 		if (*sp == '\0' && gothost) {
228 #ifdef DEBUG
229 			printf("get_fs_locations: path %s\n", buf2);
230 #endif
231 			(void) make_pathname4(buf2, &fsl_array[i].rootpath);
232 			i++;
233 			gothost = 0;
234 			dp = buf2;
235 			bzero(buf2, sizeof (buf2));
236 			if (sp - buf < len)
237 				sp++;
238 			continue;
239 		}
240 
241 		/* Skip a single escape character */
242 		if (*sp == '\\')
243 			sp++;
244 
245 		/* Plain char, just copy it */
246 		*dp++ = *sp++;
247 	}
248 
249 	/*
250 	 * If we're still expecting a path name, we don't have a
251 	 * server:/path pair and should discard the server and
252 	 * note that we got fewer locations than expected.
253 	 */
254 	if (gothost) {
255 		fsl_count--;
256 		free(fsl_array[i].server.server_val);
257 		fsl_array[i].server.server_val = NULL;
258 		fsl_array[i].server.server_len = 0;
259 	}
260 
261 	/*
262 	 * If we have zero entries, we never got a whole server:/path
263 	 * pair, and so cannot have anything else allocated.
264 	 */
265 	if (fsl_count <= 0) {
266 		free(result);
267 		free(fsl_array);
268 		return (NULL);
269 	}
270 
271 	/*
272 	 * Make sure we reflect the right number of locations.
273 	 */
274 	if (fsl_count < result->locations.locations_len)
275 		result->locations.locations_len = fsl_count;
276 
277 out:
278 	return (result);
279 }
280 
281 /*
282  * Deref function for nfs-basic service type returns an fs_locations4.
283  */
284 int
285 nfs_basic_deref(const char *svc_type, const char *svc_data, char *buf,
286     size_t *bufsz)
287 {
288 	int slen, err;
289 	fs_locations4 *fsl;
290 	XDR xdr;
291 
292 	if ((!svc_type) || (!svc_data) || (!buf) || (!bufsz) || (*bufsz == 0))
293 		return (EINVAL);
294 
295 	if (strcasecmp(svc_type, SERVICE_TYPE))
296 		return (ENOTSUP);
297 
298 	fsl = get_fs_locations((char *)svc_data);
299 	if (fsl == NULL)
300 		return (ENOENT);
301 #ifdef DEBUG
302 	printf("nfs_basic_deref: past get_fs_locations()\n");
303 #endif
304 	slen = xdr_sizeof(xdr_fs_locations4, (void *)fsl);
305 	if (slen > *bufsz) {
306 		*bufsz = slen;
307 		xdr_free(xdr_fs_locations4, (char *)fsl);
308 		return (EOVERFLOW);
309 	}
310 #ifdef DEBUG
311 	printf("nfs_basic_deref: past buffer check\n");
312 	print_referral_summary(fsl);
313 #endif
314 	xdrmem_create(&xdr, buf, *bufsz, XDR_ENCODE);
315 	err = xdr_fs_locations4(&xdr, fsl);
316 	XDR_DESTROY(&xdr);
317 	xdr_free(xdr_fs_locations4, (char *)fsl);
318 	if (err != TRUE)
319 		return (EINVAL);
320 	*bufsz = slen;
321 #ifdef DEBUG
322 	printf("nfs_basic_deref: past xdr_fs_locations4() and done\n");
323 #endif
324 	return (0);
325 }
326 
327 /*
328  * Form function for nfs-basic service type.
329  */
330 int
331 nfs_basic_form(const char *svc_type, const char *svc_data, char *buf,
332     size_t *bufsz)
333 {
334 	int slen;
335 
336 	if ((!svc_type) || (!svc_data) || (!buf) || (*bufsz == 0))
337 		return (EINVAL);
338 
339 	if (strcmp(svc_type, SERVICE_TYPE))
340 		return (ENOTSUP);
341 
342 	slen = strlen(svc_data) + 1;
343 	if (slen > *bufsz) {
344 		*bufsz = slen;
345 		return (EOVERFLOW);
346 	}
347 	*bufsz = slen;
348 	strncpy(buf, svc_data, slen);
349 	return (0);
350 }
351