17a286c47SDai Ngo /*
27a286c47SDai Ngo  * CDDL HEADER START
37a286c47SDai Ngo  *
47a286c47SDai Ngo  * The contents of this file are subject to the terms of the
57a286c47SDai Ngo  * Common Development and Distribution License (the "License").
67a286c47SDai Ngo  * You may not use this file except in compliance with the License.
77a286c47SDai Ngo  *
87a286c47SDai Ngo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97a286c47SDai Ngo  * or http://www.opensolaris.org/os/licensing.
107a286c47SDai Ngo  * See the License for the specific language governing permissions
117a286c47SDai Ngo  * and limitations under the License.
127a286c47SDai Ngo  *
137a286c47SDai Ngo  * When distributing Covered Code, include this CDDL HEADER in each
147a286c47SDai Ngo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157a286c47SDai Ngo  * If applicable, add the following below this CDDL HEADER, with the
167a286c47SDai Ngo  * fields enclosed by brackets "[]" replaced with your own identifying
177a286c47SDai Ngo  * information: Portions Copyright [yyyy] [name of copyright owner]
187a286c47SDai Ngo  *
197a286c47SDai Ngo  * CDDL HEADER END
207a286c47SDai Ngo  */
217a286c47SDai Ngo /*
227a286c47SDai Ngo  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237a286c47SDai Ngo  * Use is subject to license terms.
247a286c47SDai Ngo  */
257a286c47SDai Ngo 
267a286c47SDai Ngo #include <sys/types.h>
277a286c47SDai Ngo #include <sys/param.h>
287a286c47SDai Ngo #include <sys/errno.h>
297a286c47SDai Ngo 
307a286c47SDai Ngo #ifdef _KERNEL
317a286c47SDai Ngo #include <sys/sunddi.h>
327a286c47SDai Ngo #include <fs/fs_reparse.h>
337a286c47SDai Ngo #else
347a286c47SDai Ngo #include <string.h>
357a286c47SDai Ngo #include <limits.h>
367a286c47SDai Ngo #include <sys/fs_reparse.h>
377a286c47SDai Ngo 
387a286c47SDai Ngo #define	strfree(str)		free((str))
397a286c47SDai Ngo #endif
407a286c47SDai Ngo 
417a286c47SDai Ngo static char *reparse_skipspace(char *cp);
427a286c47SDai Ngo static int reparse_create_nvlist(const char *string, nvlist_t *nvl);
437a286c47SDai Ngo static int reparse_add_nvpair(char *token, nvlist_t *nvl);
447a286c47SDai Ngo static boolean_t reparse_validate_svctype(char *svc_str);
457a286c47SDai Ngo static int reparse_validate_create_nvlist(const char *string, nvlist_t *nvl);
467a286c47SDai Ngo 
477a286c47SDai Ngo /* array of characters not allowed in service type string */
487a286c47SDai Ngo static char svctype_invalid_chars[] = { '{', '}', 0 };
497a286c47SDai Ngo 
507a286c47SDai Ngo /*
517a286c47SDai Ngo  * reparse_init()
527a286c47SDai Ngo  *
537a286c47SDai Ngo  * Function to allocate a new name-value pair list.
547a286c47SDai Ngo  * Caller needs to call reparse_free() to free memory
557a286c47SDai Ngo  * used by the list when done.
567a286c47SDai Ngo  *
577a286c47SDai Ngo  * Return pointer to new list else return NULL.
587a286c47SDai Ngo  */
597a286c47SDai Ngo nvlist_t *
reparse_init(void)607a286c47SDai Ngo reparse_init(void)
617a286c47SDai Ngo {
627a286c47SDai Ngo 	nvlist_t *nvl;
637a286c47SDai Ngo 
647a286c47SDai Ngo 	/*
657a286c47SDai Ngo 	 * Service type is unique, only one entry
667a286c47SDai Ngo 	 * of each service type is allowed
677a286c47SDai Ngo 	 */
687a286c47SDai Ngo 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0))
697a286c47SDai Ngo 		return (NULL);
707a286c47SDai Ngo 
717a286c47SDai Ngo 	return (nvl);
727a286c47SDai Ngo }
737a286c47SDai Ngo 
747a286c47SDai Ngo /*
757a286c47SDai Ngo  * reparse_free()
767a286c47SDai Ngo  *
777a286c47SDai Ngo  * Function to free memory of a nvlist allocated previously
787a286c47SDai Ngo  * by reparse_init().
797a286c47SDai Ngo  */
807a286c47SDai Ngo void
reparse_free(nvlist_t * nvl)817a286c47SDai Ngo reparse_free(nvlist_t *nvl)
827a286c47SDai Ngo {
83*aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(nvl);
847a286c47SDai Ngo }
857a286c47SDai Ngo 
867a286c47SDai Ngo /*
877a286c47SDai Ngo  * reparse_parse()
887a286c47SDai Ngo  *
897a286c47SDai Ngo  * Parse the specified string and populate the nvlist with the svc_types
907a286c47SDai Ngo  * and data from the 'string'.  The string could be read from the reparse
917a286c47SDai Ngo  * point symlink body. This routine will allocate memory that must be
927a286c47SDai Ngo  * freed by reparse_free().
937a286c47SDai Ngo  *
947a286c47SDai Ngo  * If ok return 0 and the nvlist is populated, otherwise return error code.
957a286c47SDai Ngo  */
967a286c47SDai Ngo int
reparse_parse(const char * string,nvlist_t * nvl)977a286c47SDai Ngo reparse_parse(const char *string, nvlist_t *nvl)
987a286c47SDai Ngo {
997a286c47SDai Ngo 	int err;
1007a286c47SDai Ngo 
1017a286c47SDai Ngo 	if (string == NULL || nvl == NULL)
1027a286c47SDai Ngo 		return (EINVAL);
1037a286c47SDai Ngo 
1047a286c47SDai Ngo 	if ((err = reparse_validate(string)) != 0)
1057a286c47SDai Ngo 		return (err);
1067a286c47SDai Ngo 
1077a286c47SDai Ngo 	if ((err = reparse_create_nvlist(string, nvl)) != 0)
1087a286c47SDai Ngo 		return (err);
1097a286c47SDai Ngo 
1107a286c47SDai Ngo 	return (0);
1117a286c47SDai Ngo }
1127a286c47SDai Ngo 
1137a286c47SDai Ngo static char *
reparse_skipspace(char * cp)1147a286c47SDai Ngo reparse_skipspace(char *cp)
1157a286c47SDai Ngo {
1167a286c47SDai Ngo 	while ((*cp) && (*cp == ' ' || *cp == '\t'))
1177a286c47SDai Ngo 		cp++;
1187a286c47SDai Ngo 	return (cp);
1197a286c47SDai Ngo }
1207a286c47SDai Ngo 
1217a286c47SDai Ngo static boolean_t
reparse_validate_svctype(char * svc_str)1227a286c47SDai Ngo reparse_validate_svctype(char *svc_str)
1237a286c47SDai Ngo {
1247a286c47SDai Ngo 	int nx, ix, len;
1257a286c47SDai Ngo 
1267a286c47SDai Ngo 	if (svc_str == NULL)
1277a286c47SDai Ngo 		return (B_FALSE);
1287a286c47SDai Ngo 
1297a286c47SDai Ngo 	len = strlen(svc_str);
1307a286c47SDai Ngo 	for (ix = 0; ix < len; ix++) {
1317a286c47SDai Ngo 		for (nx = 0; nx < sizeof (svctype_invalid_chars); nx++) {
1327a286c47SDai Ngo 			if (svc_str[ix] == svctype_invalid_chars[nx])
1337a286c47SDai Ngo 				return (B_FALSE);
1347a286c47SDai Ngo 		}
1357a286c47SDai Ngo 	}
1367a286c47SDai Ngo 	return (B_TRUE);
1377a286c47SDai Ngo }
1387a286c47SDai Ngo 
1397a286c47SDai Ngo static boolean_t
reparse_validate_svc_token(char * svc_token)1407a286c47SDai Ngo reparse_validate_svc_token(char *svc_token)
1417a286c47SDai Ngo {
1427a286c47SDai Ngo 	char save_c, *cp;
1437a286c47SDai Ngo 
1447a286c47SDai Ngo 	if (svc_token == NULL)
1457a286c47SDai Ngo 		return (B_FALSE);
1467a286c47SDai Ngo 	if ((cp = strchr(svc_token, ':')) == NULL)
1477a286c47SDai Ngo 		return (B_FALSE);
1487a286c47SDai Ngo 
1497a286c47SDai Ngo 	save_c = *cp;
1507a286c47SDai Ngo 	*cp = '\0';
1517a286c47SDai Ngo 
1527a286c47SDai Ngo 	/*
1537a286c47SDai Ngo 	 * make sure service type and service data are non-empty string.
1547a286c47SDai Ngo 	 */
1557a286c47SDai Ngo 	if (strlen(svc_token) == 0 || strlen(cp + 1) == 0) {
1567a286c47SDai Ngo 		*cp = save_c;
1577a286c47SDai Ngo 		return (B_FALSE);
1587a286c47SDai Ngo 	}
1597a286c47SDai Ngo 
1607a286c47SDai Ngo 	*cp = save_c;
1617a286c47SDai Ngo 	return (B_TRUE);
1627a286c47SDai Ngo }
1637a286c47SDai Ngo 
1647a286c47SDai Ngo /*
1657a286c47SDai Ngo  * Format of reparse data:
1667a286c47SDai Ngo  * @{REPARSE@{servicetype:data} [@{servicetype:data}] ...}
1677a286c47SDai Ngo  * REPARSE_TAG_STR@{REPARSE_TOKEN} [@{REPARSE_TOKEN}] ... REPARSE_TAG_END
1687a286c47SDai Ngo  *
1697a286c47SDai Ngo  * Validating reparse data:
1707a286c47SDai Ngo  *	. check for valid length of reparse data
1717a286c47SDai Ngo  *	. check for valid reparse data format
1727a286c47SDai Ngo  * Return 0 if OK else return error code.
1737a286c47SDai Ngo  */
1747a286c47SDai Ngo int
reparse_validate(const char * string)1757a286c47SDai Ngo reparse_validate(const char *string)
1767a286c47SDai Ngo {
1777a286c47SDai Ngo 	return (reparse_validate_create_nvlist(string, NULL));
1787a286c47SDai Ngo }
1797a286c47SDai Ngo 
1807a286c47SDai Ngo /*
1817a286c47SDai Ngo  * reparse_validate_create_nvlist
1827a286c47SDai Ngo  *
1837a286c47SDai Ngo  * dual-purpose function:
1847a286c47SDai Ngo  *     . Validate a reparse data string.
1857a286c47SDai Ngo  *     . Validate a reparse data string and parse the data
1867a286c47SDai Ngo  *	 into a nvlist.
1877a286c47SDai Ngo  */
1887a286c47SDai Ngo static int
reparse_validate_create_nvlist(const char * string,nvlist_t * nvl)1897a286c47SDai Ngo reparse_validate_create_nvlist(const char *string, nvlist_t *nvl)
1907a286c47SDai Ngo {
1917a286c47SDai Ngo 	int err, tcnt;
1927a286c47SDai Ngo 	char *reparse_data, save_c, save_e, *save_e_ptr, *cp, *s_str, *e_str;
1937a286c47SDai Ngo 
1947a286c47SDai Ngo 	if (string == NULL)
1957a286c47SDai Ngo 		return (EINVAL);
1967a286c47SDai Ngo 
1977a286c47SDai Ngo 	if (strlen(string) >= MAXREPARSELEN)
1987a286c47SDai Ngo 		return (ENAMETOOLONG);
1997a286c47SDai Ngo 
2007a286c47SDai Ngo 	if ((reparse_data = strdup(string)) == NULL)
2017a286c47SDai Ngo 		return (ENOMEM);
2027a286c47SDai Ngo 
2037a286c47SDai Ngo 	/* check FS_REPARSE_TAG_STR */
2047a286c47SDai Ngo 	if (strncmp(reparse_data, FS_REPARSE_TAG_STR,
2057a286c47SDai Ngo 	    strlen(FS_REPARSE_TAG_STR))) {
2067a286c47SDai Ngo 		strfree(reparse_data);
2077a286c47SDai Ngo 		return (EINVAL);
2087a286c47SDai Ngo 	}
2097a286c47SDai Ngo 
2107a286c47SDai Ngo 	/* locate FS_REPARSE_TAG_END_CHAR */
2117a286c47SDai Ngo 	if ((cp = strrchr(reparse_data, FS_REPARSE_TAG_END_CHAR)) == NULL) {
2127a286c47SDai Ngo 		strfree(reparse_data);
2137a286c47SDai Ngo 		return (EINVAL);
2147a286c47SDai Ngo 	}
2157a286c47SDai Ngo 	save_e = *cp;
2167a286c47SDai Ngo 	save_e_ptr = cp;
2177a286c47SDai Ngo 	*cp = '\0';
2187a286c47SDai Ngo 
2197a286c47SDai Ngo 	e_str = cp;
2207a286c47SDai Ngo 	cp++;		/* should point to NULL, or spaces */
2217a286c47SDai Ngo 
2227a286c47SDai Ngo 	cp = reparse_skipspace(cp);
2237a286c47SDai Ngo 	if (*cp) {
2247a286c47SDai Ngo 		*save_e_ptr = save_e;
2257a286c47SDai Ngo 		strfree(reparse_data);
2267a286c47SDai Ngo 		return (EINVAL);
2277a286c47SDai Ngo 	}
2287a286c47SDai Ngo 
2297a286c47SDai Ngo 	/* skip FS_REPARSE_TAG_STR */
2307a286c47SDai Ngo 	s_str = reparse_data + strlen(FS_REPARSE_TAG_STR);
2317a286c47SDai Ngo 
2327a286c47SDai Ngo 	/* skip spaces after FS_REPARSE_TAG_STR */
2337a286c47SDai Ngo 	s_str = reparse_skipspace(s_str);
2347a286c47SDai Ngo 
2357a286c47SDai Ngo 	tcnt = 0;
2367a286c47SDai Ngo 	while (s_str < e_str) {
2377a286c47SDai Ngo 		/* check FS_TOKEN_START_STR */
2387a286c47SDai Ngo 		if (strncmp(s_str, FS_TOKEN_START_STR,
2397a286c47SDai Ngo 		    strlen(FS_TOKEN_START_STR))) {
2407a286c47SDai Ngo 			*save_e_ptr = save_e;
2417a286c47SDai Ngo 			strfree(reparse_data);
2427a286c47SDai Ngo 			return (EINVAL);
2437a286c47SDai Ngo 		}
2447a286c47SDai Ngo 
2457a286c47SDai Ngo 		/* skip over FS_TOKEN_START_STR */
2467a286c47SDai Ngo 		s_str += strlen(FS_TOKEN_START_STR);
2477a286c47SDai Ngo 
2487a286c47SDai Ngo 		/* locate FS_TOKEN_END_STR */
2497a286c47SDai Ngo 		if ((cp = strstr(s_str, FS_TOKEN_END_STR)) == NULL) {
2507a286c47SDai Ngo 			*save_e_ptr = save_e;
2517a286c47SDai Ngo 			strfree(reparse_data);
2527a286c47SDai Ngo 			return (EINVAL);
2537a286c47SDai Ngo 		}
2547a286c47SDai Ngo 
2557a286c47SDai Ngo 		tcnt++;
2567a286c47SDai Ngo 		save_c = *cp;
2577a286c47SDai Ngo 		*cp = '\0';
2587a286c47SDai Ngo 
2597a286c47SDai Ngo 		/* check for valid characters in service type */
2607a286c47SDai Ngo 		if (reparse_validate_svctype(s_str) == B_FALSE) {
2617a286c47SDai Ngo 			*cp = save_c;
2627a286c47SDai Ngo 			*save_e_ptr = save_e;
2637a286c47SDai Ngo 			strfree(reparse_data);
2647a286c47SDai Ngo 			return (EINVAL);
2657a286c47SDai Ngo 		}
2667a286c47SDai Ngo 
2677a286c47SDai Ngo 		if (strlen(s_str) == 0) {
2687a286c47SDai Ngo 			*cp = save_c;
2697a286c47SDai Ngo 			*save_e_ptr = save_e;
2707a286c47SDai Ngo 			strfree(reparse_data);
2717a286c47SDai Ngo 			return (EINVAL);
2727a286c47SDai Ngo 		}
2737a286c47SDai Ngo 
2747a286c47SDai Ngo 		if (reparse_validate_svc_token(s_str) == B_FALSE) {
2757a286c47SDai Ngo 			*cp = save_c;
2767a286c47SDai Ngo 			*save_e_ptr = save_e;
2777a286c47SDai Ngo 			strfree(reparse_data);
2787a286c47SDai Ngo 			return (EINVAL);
2797a286c47SDai Ngo 		}
2807a286c47SDai Ngo 
2817a286c47SDai Ngo 		/* create a nvpair entry */
2827a286c47SDai Ngo 		if (nvl != NULL &&
2837a286c47SDai Ngo 		    (err = reparse_add_nvpair(s_str, nvl)) != 0) {
2847a286c47SDai Ngo 			*cp = save_c;
2857a286c47SDai Ngo 			*save_e_ptr = save_e;
2867a286c47SDai Ngo 			strfree(reparse_data);
2877a286c47SDai Ngo 			return (err);
2887a286c47SDai Ngo 		}
2897a286c47SDai Ngo 
2907a286c47SDai Ngo 		*cp = save_c;
2917a286c47SDai Ngo 
2927a286c47SDai Ngo 		/* skip over FS_TOKEN_END_STR */
2937a286c47SDai Ngo 		cp += strlen(FS_TOKEN_END_STR);
2947a286c47SDai Ngo 		cp = reparse_skipspace(cp);
2957a286c47SDai Ngo 		s_str = cp;
2967a286c47SDai Ngo 	}
2977a286c47SDai Ngo 	*save_e_ptr = save_e;
2987a286c47SDai Ngo 	strfree(reparse_data);
2997a286c47SDai Ngo 
3007a286c47SDai Ngo 	return (tcnt ? 0 : EINVAL);
3017a286c47SDai Ngo }
3027a286c47SDai Ngo 
3037a286c47SDai Ngo static int
reparse_add_nvpair(char * token,nvlist_t * nvl)3047a286c47SDai Ngo reparse_add_nvpair(char *token, nvlist_t *nvl)
3057a286c47SDai Ngo {
3067a286c47SDai Ngo 	int err;
3077a286c47SDai Ngo 	char save_c, *cp;
3087a286c47SDai Ngo 
3097a286c47SDai Ngo 	if ((cp = strchr(token, ':')) == NULL)
3107a286c47SDai Ngo 		return (EINVAL);
3117a286c47SDai Ngo 
3127a286c47SDai Ngo 	save_c = *cp;
3137a286c47SDai Ngo 	*cp = '\0';
3147a286c47SDai Ngo 	err = nvlist_add_string(nvl, token, cp + 1);
3157a286c47SDai Ngo 	*cp = save_c;
3167a286c47SDai Ngo 
3177a286c47SDai Ngo 	return (err);
3187a286c47SDai Ngo }
3197a286c47SDai Ngo 
3207a286c47SDai Ngo static int
reparse_create_nvlist(const char * string,nvlist_t * nvl)3217a286c47SDai Ngo reparse_create_nvlist(const char *string, nvlist_t *nvl)
3227a286c47SDai Ngo {
3237a286c47SDai Ngo 	if (nvl == NULL)
3247a286c47SDai Ngo 		return (EINVAL);
3257a286c47SDai Ngo 
3267a286c47SDai Ngo 	return (reparse_validate_create_nvlist(string, nvl));
3277a286c47SDai Ngo }
328