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