diff options
Diffstat (limited to 'usr/src/common/fsreparse/fs_reparse.c')
-rw-r--r-- | usr/src/common/fsreparse/fs_reparse.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/usr/src/common/fsreparse/fs_reparse.c b/usr/src/common/fsreparse/fs_reparse.c new file mode 100644 index 0000000000..82da0349fd --- /dev/null +++ b/usr/src/common/fsreparse/fs_reparse.c @@ -0,0 +1,328 @@ +/* + * 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 <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> + +#ifdef _KERNEL +#include <sys/sunddi.h> +#include <fs/fs_reparse.h> +#else +#include <string.h> +#include <limits.h> +#include <sys/fs_reparse.h> + +#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) +{ + if (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)); +} |