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