/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #ifdef _KERNEL #include #include #else #include #include #include #define strfree(str) free((str)) #endif static char *reparse_skipspace(char *cp); static int reparse_create_nvlist(const char *string, nvlist_t *nvl); static int reparse_add_nvpair(char *token, nvlist_t *nvl); static boolean_t reparse_validate_svctype(char *svc_str); static int reparse_validate_create_nvlist(const char *string, nvlist_t *nvl); /* array of characters not allowed in service type string */ static char svctype_invalid_chars[] = { '{', '}', 0 }; /* * reparse_init() * * Function to allocate a new name-value pair list. * Caller needs to call reparse_free() to free memory * used by the list when done. * * Return pointer to new list else return NULL. */ nvlist_t * reparse_init(void) { nvlist_t *nvl; /* * Service type is unique, only one entry * of each service type is allowed */ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) return (NULL); return (nvl); } /* * reparse_free() * * Function to free memory of a nvlist allocated previously * by reparse_init(). */ void reparse_free(nvlist_t *nvl) { nvlist_free(nvl); } /* * reparse_parse() * * Parse the specified string and populate the nvlist with the svc_types * and data from the 'string'. The string could be read from the reparse * point symlink body. This routine will allocate memory that must be * freed by reparse_free(). * * If ok return 0 and the nvlist is populated, otherwise return error code. */ int reparse_parse(const char *string, nvlist_t *nvl) { int err; if (string == NULL || nvl == NULL) return (EINVAL); if ((err = reparse_validate(string)) != 0) return (err); if ((err = reparse_create_nvlist(string, nvl)) != 0) return (err); return (0); } static char * reparse_skipspace(char *cp) { while ((*cp) && (*cp == ' ' || *cp == '\t')) cp++; return (cp); } static boolean_t reparse_validate_svctype(char *svc_str) { int nx, ix, len; if (svc_str == NULL) return (B_FALSE); len = strlen(svc_str); for (ix = 0; ix < len; ix++) { for (nx = 0; nx < sizeof (svctype_invalid_chars); nx++) { if (svc_str[ix] == svctype_invalid_chars[nx]) return (B_FALSE); } } return (B_TRUE); } static boolean_t reparse_validate_svc_token(char *svc_token) { char save_c, *cp; if (svc_token == NULL) return (B_FALSE); if ((cp = strchr(svc_token, ':')) == NULL) return (B_FALSE); save_c = *cp; *cp = '\0'; /* * make sure service type and service data are non-empty string. */ if (strlen(svc_token) == 0 || strlen(cp + 1) == 0) { *cp = save_c; return (B_FALSE); } *cp = save_c; return (B_TRUE); } /* * Format of reparse data: * @{REPARSE@{servicetype:data} [@{servicetype:data}] ...} * REPARSE_TAG_STR@{REPARSE_TOKEN} [@{REPARSE_TOKEN}] ... REPARSE_TAG_END * * Validating reparse data: * . check for valid length of reparse data * . check for valid reparse data format * Return 0 if OK else return error code. */ int reparse_validate(const char *string) { return (reparse_validate_create_nvlist(string, NULL)); } /* * reparse_validate_create_nvlist * * dual-purpose function: * . Validate a reparse data string. * . Validate a reparse data string and parse the data * into a nvlist. */ static int reparse_validate_create_nvlist(const char *string, nvlist_t *nvl) { int err, tcnt; char *reparse_data, save_c, save_e, *save_e_ptr, *cp, *s_str, *e_str; if (string == NULL) return (EINVAL); if (strlen(string) >= MAXREPARSELEN) return (ENAMETOOLONG); if ((reparse_data = strdup(string)) == NULL) return (ENOMEM); /* check FS_REPARSE_TAG_STR */ if (strncmp(reparse_data, FS_REPARSE_TAG_STR, strlen(FS_REPARSE_TAG_STR))) { strfree(reparse_data); return (EINVAL); } /* locate FS_REPARSE_TAG_END_CHAR */ if ((cp = strrchr(reparse_data, FS_REPARSE_TAG_END_CHAR)) == NULL) { strfree(reparse_data); return (EINVAL); } save_e = *cp; save_e_ptr = cp; *cp = '\0'; e_str = cp; cp++; /* should point to NULL, or spaces */ cp = reparse_skipspace(cp); if (*cp) { *save_e_ptr = save_e; strfree(reparse_data); return (EINVAL); } /* skip FS_REPARSE_TAG_STR */ s_str = reparse_data + strlen(FS_REPARSE_TAG_STR); /* skip spaces after FS_REPARSE_TAG_STR */ s_str = reparse_skipspace(s_str); tcnt = 0; while (s_str < e_str) { /* check FS_TOKEN_START_STR */ if (strncmp(s_str, FS_TOKEN_START_STR, strlen(FS_TOKEN_START_STR))) { *save_e_ptr = save_e; strfree(reparse_data); return (EINVAL); } /* skip over FS_TOKEN_START_STR */ s_str += strlen(FS_TOKEN_START_STR); /* locate FS_TOKEN_END_STR */ if ((cp = strstr(s_str, FS_TOKEN_END_STR)) == NULL) { *save_e_ptr = save_e; strfree(reparse_data); return (EINVAL); } tcnt++; save_c = *cp; *cp = '\0'; /* check for valid characters in service type */ if (reparse_validate_svctype(s_str) == B_FALSE) { *cp = save_c; *save_e_ptr = save_e; strfree(reparse_data); return (EINVAL); } if (strlen(s_str) == 0) { *cp = save_c; *save_e_ptr = save_e; strfree(reparse_data); return (EINVAL); } if (reparse_validate_svc_token(s_str) == B_FALSE) { *cp = save_c; *save_e_ptr = save_e; strfree(reparse_data); return (EINVAL); } /* create a nvpair entry */ if (nvl != NULL && (err = reparse_add_nvpair(s_str, nvl)) != 0) { *cp = save_c; *save_e_ptr = save_e; strfree(reparse_data); return (err); } *cp = save_c; /* skip over FS_TOKEN_END_STR */ cp += strlen(FS_TOKEN_END_STR); cp = reparse_skipspace(cp); s_str = cp; } *save_e_ptr = save_e; strfree(reparse_data); return (tcnt ? 0 : EINVAL); } static int reparse_add_nvpair(char *token, nvlist_t *nvl) { int err; char save_c, *cp; if ((cp = strchr(token, ':')) == NULL) return (EINVAL); save_c = *cp; *cp = '\0'; err = nvlist_add_string(nvl, token, cp + 1); *cp = save_c; return (err); } static int reparse_create_nvlist(const char *string, nvlist_t *nvl) { if (nvl == NULL) return (EINVAL); return (reparse_validate_create_nvlist(string, nvl)); }