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 }