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 }