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