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