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