xref: /illumos-gate/usr/src/common/fsreparse/fs_reparse.c (revision 42ed7838f131b8f58d6c95db1c7e3a6a3e6ea7e4)
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 *
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
81 reparse_free(nvlist_t *nvl)
82 {
83 	if (nvl)
84 		nvlist_free(nvl);
85 }
86 
87 /*
88  * reparse_parse()
89  *
90  * Parse the specified string and populate the nvlist with the svc_types
91  * and data from the 'string'.  The string could be read from the reparse
92  * point symlink body. This routine will allocate memory that must be
93  * freed by reparse_free().
94  *
95  * If ok return 0 and the nvlist is populated, otherwise return error code.
96  */
97 int
98 reparse_parse(const char *string, nvlist_t *nvl)
99 {
100 	int err;
101 
102 	if (string == NULL || nvl == NULL)
103 		return (EINVAL);
104 
105 	if ((err = reparse_validate(string)) != 0)
106 		return (err);
107 
108 	if ((err = reparse_create_nvlist(string, nvl)) != 0)
109 		return (err);
110 
111 	return (0);
112 }
113 
114 static char *
115 reparse_skipspace(char *cp)
116 {
117 	while ((*cp) && (*cp == ' ' || *cp == '\t'))
118 		cp++;
119 	return (cp);
120 }
121 
122 static boolean_t
123 reparse_validate_svctype(char *svc_str)
124 {
125 	int nx, ix, len;
126 
127 	if (svc_str == NULL)
128 		return (B_FALSE);
129 
130 	len = strlen(svc_str);
131 	for (ix = 0; ix < len; ix++) {
132 		for (nx = 0; nx < sizeof (svctype_invalid_chars); nx++) {
133 			if (svc_str[ix] == svctype_invalid_chars[nx])
134 				return (B_FALSE);
135 		}
136 	}
137 	return (B_TRUE);
138 }
139 
140 static boolean_t
141 reparse_validate_svc_token(char *svc_token)
142 {
143 	char save_c, *cp;
144 
145 	if (svc_token == NULL)
146 		return (B_FALSE);
147 	if ((cp = strchr(svc_token, ':')) == NULL)
148 		return (B_FALSE);
149 
150 	save_c = *cp;
151 	*cp = '\0';
152 
153 	/*
154 	 * make sure service type and service data are non-empty string.
155 	 */
156 	if (strlen(svc_token) == 0 || strlen(cp + 1) == 0) {
157 		*cp = save_c;
158 		return (B_FALSE);
159 	}
160 
161 	*cp = save_c;
162 	return (B_TRUE);
163 }
164 
165 /*
166  * Format of reparse data:
167  * @{REPARSE@{servicetype:data} [@{servicetype:data}] ...}
168  * REPARSE_TAG_STR@{REPARSE_TOKEN} [@{REPARSE_TOKEN}] ... REPARSE_TAG_END
169  *
170  * Validating reparse data:
171  *	. check for valid length of reparse data
172  *	. check for valid reparse data format
173  * Return 0 if OK else return error code.
174  */
175 int
176 reparse_validate(const char *string)
177 {
178 	return (reparse_validate_create_nvlist(string, NULL));
179 }
180 
181 /*
182  * reparse_validate_create_nvlist
183  *
184  * dual-purpose function:
185  *     . Validate a reparse data string.
186  *     . Validate a reparse data string and parse the data
187  *	 into a nvlist.
188  */
189 static int
190 reparse_validate_create_nvlist(const char *string, nvlist_t *nvl)
191 {
192 	int err, tcnt;
193 	char *reparse_data, save_c, save_e, *save_e_ptr, *cp, *s_str, *e_str;
194 
195 	if (string == NULL)
196 		return (EINVAL);
197 
198 	if (strlen(string) >= MAXREPARSELEN)
199 		return (ENAMETOOLONG);
200 
201 	if ((reparse_data = strdup(string)) == NULL)
202 		return (ENOMEM);
203 
204 	/* check FS_REPARSE_TAG_STR */
205 	if (strncmp(reparse_data, FS_REPARSE_TAG_STR,
206 	    strlen(FS_REPARSE_TAG_STR))) {
207 		strfree(reparse_data);
208 		return (EINVAL);
209 	}
210 
211 	/* locate FS_REPARSE_TAG_END_CHAR */
212 	if ((cp = strrchr(reparse_data, FS_REPARSE_TAG_END_CHAR)) == NULL) {
213 		strfree(reparse_data);
214 		return (EINVAL);
215 	}
216 	save_e = *cp;
217 	save_e_ptr = cp;
218 	*cp = '\0';
219 
220 	e_str = cp;
221 	cp++;		/* should point to NULL, or spaces */
222 
223 	cp = reparse_skipspace(cp);
224 	if (*cp) {
225 		*save_e_ptr = save_e;
226 		strfree(reparse_data);
227 		return (EINVAL);
228 	}
229 
230 	/* skip FS_REPARSE_TAG_STR */
231 	s_str = reparse_data + strlen(FS_REPARSE_TAG_STR);
232 
233 	/* skip spaces after FS_REPARSE_TAG_STR */
234 	s_str = reparse_skipspace(s_str);
235 
236 	tcnt = 0;
237 	while (s_str < e_str) {
238 		/* check FS_TOKEN_START_STR */
239 		if (strncmp(s_str, FS_TOKEN_START_STR,
240 		    strlen(FS_TOKEN_START_STR))) {
241 			*save_e_ptr = save_e;
242 			strfree(reparse_data);
243 			return (EINVAL);
244 		}
245 
246 		/* skip over FS_TOKEN_START_STR */
247 		s_str += strlen(FS_TOKEN_START_STR);
248 
249 		/* locate FS_TOKEN_END_STR */
250 		if ((cp = strstr(s_str, FS_TOKEN_END_STR)) == NULL) {
251 			*save_e_ptr = save_e;
252 			strfree(reparse_data);
253 			return (EINVAL);
254 		}
255 
256 		tcnt++;
257 		save_c = *cp;
258 		*cp = '\0';
259 
260 		/* check for valid characters in service type */
261 		if (reparse_validate_svctype(s_str) == B_FALSE) {
262 			*cp = save_c;
263 			*save_e_ptr = save_e;
264 			strfree(reparse_data);
265 			return (EINVAL);
266 		}
267 
268 		if (strlen(s_str) == 0) {
269 			*cp = save_c;
270 			*save_e_ptr = save_e;
271 			strfree(reparse_data);
272 			return (EINVAL);
273 		}
274 
275 		if (reparse_validate_svc_token(s_str) == B_FALSE) {
276 			*cp = save_c;
277 			*save_e_ptr = save_e;
278 			strfree(reparse_data);
279 			return (EINVAL);
280 		}
281 
282 		/* create a nvpair entry */
283 		if (nvl != NULL &&
284 		    (err = reparse_add_nvpair(s_str, nvl)) != 0) {
285 			*cp = save_c;
286 			*save_e_ptr = save_e;
287 			strfree(reparse_data);
288 			return (err);
289 		}
290 
291 		*cp = save_c;
292 
293 		/* skip over FS_TOKEN_END_STR */
294 		cp += strlen(FS_TOKEN_END_STR);
295 		cp = reparse_skipspace(cp);
296 		s_str = cp;
297 	}
298 	*save_e_ptr = save_e;
299 	strfree(reparse_data);
300 
301 	return (tcnt ? 0 : EINVAL);
302 }
303 
304 static int
305 reparse_add_nvpair(char *token, nvlist_t *nvl)
306 {
307 	int err;
308 	char save_c, *cp;
309 
310 	if ((cp = strchr(token, ':')) == NULL)
311 		return (EINVAL);
312 
313 	save_c = *cp;
314 	*cp = '\0';
315 	err = nvlist_add_string(nvl, token, cp + 1);
316 	*cp = save_c;
317 
318 	return (err);
319 }
320 
321 static int
322 reparse_create_nvlist(const char *string, nvlist_t *nvl)
323 {
324 	if (nvl == NULL)
325 		return (EINVAL);
326 
327 	return (reparse_validate_create_nvlist(string, nvl));
328 }
329