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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/errno.h>
29 
30 #ifdef _KERNEL
31 #include <sys/sunddi.h>
32 #include <fs/fs_reparse.h>
33 #else
34 #include <string.h>
35 #include <limits.h>
36 #include <sys/fs_reparse.h>
37 
38 #define	strfree(str)		free((str))
39 #endif
40 
41 static char *reparse_skipspace(char *cp);
42 static int reparse_create_nvlist(const char *string, nvlist_t *nvl);
43 static int reparse_add_nvpair(char *token, nvlist_t *nvl);
44 static boolean_t reparse_validate_svctype(char *svc_str);
45 static int reparse_validate_create_nvlist(const char *string, nvlist_t *nvl);
46 
47 /* array of characters not allowed in service type string */
48 static char svctype_invalid_chars[] = { '{', '}', 0 };
49 
50 /*
51  * reparse_init()
52  *
53  * Function to allocate a new name-value pair list.
54  * Caller needs to call reparse_free() to free memory
55  * used by the list when done.
56  *
57  * Return pointer to new list else return NULL.
58  */
59 nvlist_t *
reparse_init(void)60 reparse_init(void)
61 {
62 	nvlist_t *nvl;
63 
64 	/*
65 	 * Service type is unique, only one entry
66 	 * of each service type is allowed
67 	 */
68 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0))
69 		return (NULL);
70 
71 	return (nvl);
72 }
73 
74 /*
75  * reparse_free()
76  *
77  * Function to free memory of a nvlist allocated previously
78  * by reparse_init().
79  */
80 void
reparse_free(nvlist_t * nvl)81 reparse_free(nvlist_t *nvl)
82 {
83 	nvlist_free(nvl);
84 }
85 
86 /*
87  * reparse_parse()
88  *
89  * Parse the specified string and populate the nvlist with the svc_types
90  * and data from the 'string'.  The string could be read from the reparse
91  * point symlink body. This routine will allocate memory that must be
92  * freed by reparse_free().
93  *
94  * If ok return 0 and the nvlist is populated, otherwise return error code.
95  */
96 int
reparse_parse(const char * string,nvlist_t * nvl)97 reparse_parse(const char *string, nvlist_t *nvl)
98 {
99 	int err;
100 
101 	if (string == NULL || nvl == NULL)
102 		return (EINVAL);
103 
104 	if ((err = reparse_validate(string)) != 0)
105 		return (err);
106 
107 	if ((err = reparse_create_nvlist(string, nvl)) != 0)
108 		return (err);
109 
110 	return (0);
111 }
112 
113 static char *
reparse_skipspace(char * cp)114 reparse_skipspace(char *cp)
115 {
116 	while ((*cp) && (*cp == ' ' || *cp == '\t'))
117 		cp++;
118 	return (cp);
119 }
120 
121 static boolean_t
reparse_validate_svctype(char * svc_str)122 reparse_validate_svctype(char *svc_str)
123 {
124 	int nx, ix, len;
125 
126 	if (svc_str == NULL)
127 		return (B_FALSE);
128 
129 	len = strlen(svc_str);
130 	for (ix = 0; ix < len; ix++) {
131 		for (nx = 0; nx < sizeof (svctype_invalid_chars); nx++) {
132 			if (svc_str[ix] == svctype_invalid_chars[nx])
133 				return (B_FALSE);
134 		}
135 	}
136 	return (B_TRUE);
137 }
138 
139 static boolean_t
reparse_validate_svc_token(char * svc_token)140 reparse_validate_svc_token(char *svc_token)
141 {
142 	char save_c, *cp;
143 
144 	if (svc_token == NULL)
145 		return (B_FALSE);
146 	if ((cp = strchr(svc_token, ':')) == NULL)
147 		return (B_FALSE);
148 
149 	save_c = *cp;
150 	*cp = '\0';
151 
152 	/*
153 	 * make sure service type and service data are non-empty string.
154 	 */
155 	if (strlen(svc_token) == 0 || strlen(cp + 1) == 0) {
156 		*cp = save_c;
157 		return (B_FALSE);
158 	}
159 
160 	*cp = save_c;
161 	return (B_TRUE);
162 }
163 
164 /*
165  * Format of reparse data:
166  * @{REPARSE@{servicetype:data} [@{servicetype:data}] ...}
167  * REPARSE_TAG_STR@{REPARSE_TOKEN} [@{REPARSE_TOKEN}] ... REPARSE_TAG_END
168  *
169  * Validating reparse data:
170  *	. check for valid length of reparse data
171  *	. check for valid reparse data format
172  * Return 0 if OK else return error code.
173  */
174 int
reparse_validate(const char * string)175 reparse_validate(const char *string)
176 {
177 	return (reparse_validate_create_nvlist(string, NULL));
178 }
179 
180 /*
181  * reparse_validate_create_nvlist
182  *
183  * dual-purpose function:
184  *     . Validate a reparse data string.
185  *     . Validate a reparse data string and parse the data
186  *	 into a nvlist.
187  */
188 static int
reparse_validate_create_nvlist(const char * string,nvlist_t * nvl)189 reparse_validate_create_nvlist(const char *string, nvlist_t *nvl)
190 {
191 	int err, tcnt;
192 	char *reparse_data, save_c, save_e, *save_e_ptr, *cp, *s_str, *e_str;
193 
194 	if (string == NULL)
195 		return (EINVAL);
196 
197 	if (strlen(string) >= MAXREPARSELEN)
198 		return (ENAMETOOLONG);
199 
200 	if ((reparse_data = strdup(string)) == NULL)
201 		return (ENOMEM);
202 
203 	/* check FS_REPARSE_TAG_STR */
204 	if (strncmp(reparse_data, FS_REPARSE_TAG_STR,
205 	    strlen(FS_REPARSE_TAG_STR))) {
206 		strfree(reparse_data);
207 		return (EINVAL);
208 	}
209 
210 	/* locate FS_REPARSE_TAG_END_CHAR */
211 	if ((cp = strrchr(reparse_data, FS_REPARSE_TAG_END_CHAR)) == NULL) {
212 		strfree(reparse_data);
213 		return (EINVAL);
214 	}
215 	save_e = *cp;
216 	save_e_ptr = cp;
217 	*cp = '\0';
218 
219 	e_str = cp;
220 	cp++;		/* should point to NULL, or spaces */
221 
222 	cp = reparse_skipspace(cp);
223 	if (*cp) {
224 		*save_e_ptr = save_e;
225 		strfree(reparse_data);
226 		return (EINVAL);
227 	}
228 
229 	/* skip FS_REPARSE_TAG_STR */
230 	s_str = reparse_data + strlen(FS_REPARSE_TAG_STR);
231 
232 	/* skip spaces after FS_REPARSE_TAG_STR */
233 	s_str = reparse_skipspace(s_str);
234 
235 	tcnt = 0;
236 	while (s_str < e_str) {
237 		/* check FS_TOKEN_START_STR */
238 		if (strncmp(s_str, FS_TOKEN_START_STR,
239 		    strlen(FS_TOKEN_START_STR))) {
240 			*save_e_ptr = save_e;
241 			strfree(reparse_data);
242 			return (EINVAL);
243 		}
244 
245 		/* skip over FS_TOKEN_START_STR */
246 		s_str += strlen(FS_TOKEN_START_STR);
247 
248 		/* locate FS_TOKEN_END_STR */
249 		if ((cp = strstr(s_str, FS_TOKEN_END_STR)) == NULL) {
250 			*save_e_ptr = save_e;
251 			strfree(reparse_data);
252 			return (EINVAL);
253 		}
254 
255 		tcnt++;
256 		save_c = *cp;
257 		*cp = '\0';
258 
259 		/* check for valid characters in service type */
260 		if (reparse_validate_svctype(s_str) == B_FALSE) {
261 			*cp = save_c;
262 			*save_e_ptr = save_e;
263 			strfree(reparse_data);
264 			return (EINVAL);
265 		}
266 
267 		if (strlen(s_str) == 0) {
268 			*cp = save_c;
269 			*save_e_ptr = save_e;
270 			strfree(reparse_data);
271 			return (EINVAL);
272 		}
273 
274 		if (reparse_validate_svc_token(s_str) == B_FALSE) {
275 			*cp = save_c;
276 			*save_e_ptr = save_e;
277 			strfree(reparse_data);
278 			return (EINVAL);
279 		}
280 
281 		/* create a nvpair entry */
282 		if (nvl != NULL &&
283 		    (err = reparse_add_nvpair(s_str, nvl)) != 0) {
284 			*cp = save_c;
285 			*save_e_ptr = save_e;
286 			strfree(reparse_data);
287 			return (err);
288 		}
289 
290 		*cp = save_c;
291 
292 		/* skip over FS_TOKEN_END_STR */
293 		cp += strlen(FS_TOKEN_END_STR);
294 		cp = reparse_skipspace(cp);
295 		s_str = cp;
296 	}
297 	*save_e_ptr = save_e;
298 	strfree(reparse_data);
299 
300 	return (tcnt ? 0 : EINVAL);
301 }
302 
303 static int
reparse_add_nvpair(char * token,nvlist_t * nvl)304 reparse_add_nvpair(char *token, nvlist_t *nvl)
305 {
306 	int err;
307 	char save_c, *cp;
308 
309 	if ((cp = strchr(token, ':')) == NULL)
310 		return (EINVAL);
311 
312 	save_c = *cp;
313 	*cp = '\0';
314 	err = nvlist_add_string(nvl, token, cp + 1);
315 	*cp = save_c;
316 
317 	return (err);
318 }
319 
320 static int
reparse_create_nvlist(const char * string,nvlist_t * nvl)321 reparse_create_nvlist(const char *string, nvlist_t *nvl)
322 {
323 	if (nvl == NULL)
324 		return (EINVAL);
325 
326 	return (reparse_validate_create_nvlist(string, nvl));
327 }
328