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 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright (c) 2018, Joyent, Inc.
  29  */
  30 
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <unistd.h>
  34 #include <strings.h>
  35 #include <string.h>
  36 #include <dirent.h>
  37 #include <sys/types.h>
  38 #include <sys/stat.h>
  39 #include <sys/param.h>
  40 #include <sys/errno.h>
  41 #include <limits.h>
  42 #include <libnvpair.h>
  43 #include <dlfcn.h>
  44 #include <libintl.h>
  45 #include <sys/systeminfo.h>
  46 #include <sys/fs_reparse.h>
  47 #include "rp_plugin.h"
  48 
  49 #define MAXISALEN       257     /* based on sysinfo(2) man page */
  50 
  51 static rp_proto_handle_t rp_proto_handle;
  52 static rp_proto_plugin_t *rp_proto_list;
  53 
  54 int rp_plugin_init(void);
  55 static void proto_plugin_fini(void);
  56 static rp_plugin_ops_t *rp_find_protocol(const char *svctype);
  57 
  58 extern int errno;
  59 static int rp_plugin_inited = 0;
  60 
  61 /*
  62  * reparse_create()
  63  *
  64  * Create a symlink at the specified 'path' as a reparse point.
  65  * This function will fail if path refers to an existing file system
  66  * object or an object named string already exists at the given path.
  67  *
  68  * return 0 if ok else return error code.
  69  */
  70 int
  71 reparse_create(const char *path, const char *data)
  72 {
  73         int err;
  74         struct stat sbuf;
  75 
  76         if (path == NULL || data == NULL)
  77                 return (EINVAL);
  78 
  79         if ((err = reparse_validate(data)) != 0)
  80                 return (err);
  81 
  82         /* check if object exists */
  83         if (lstat(path, &sbuf) == 0)
  84                 return (EEXIST);
  85 
  86         return (symlink(data, path) ? errno : 0);
  87 }
  88 
  89 /*
  90  * reparse_unparse()
  91  *
  92  * Convert an nvlist back to a string format suitable to write
  93  * to the reparse point symlink body.  The string returned is in
  94  * allocated memory and must be freed by the caller.
  95  *
  96  * return 0 if ok else return error code.
  97  */
  98 int
  99 reparse_unparse(nvlist_t *nvl, char **stringp)
 100 {
 101         int err, buflen;
 102         char *buf, *stype, *val;
 103         nvpair_t *curr;
 104 
 105         if (nvl == NULL || stringp == NULL ||
 106             ((curr = nvlist_next_nvpair(nvl, NULL)) == NULL))
 107                 return (EINVAL);
 108 
 109         buflen = SYMLINK_MAX;
 110         if ((buf = malloc(buflen)) == NULL)
 111                 return (ENOMEM);
 112 
 113         err = 0;
 114         (void) snprintf(buf, buflen, "%s", FS_REPARSE_TAG_STR);
 115         while (curr != NULL) {
 116                 if (!(stype = nvpair_name(curr))) {
 117                         err = EINVAL;
 118                         break;
 119                 }
 120                 if ((strlcat(buf, FS_TOKEN_START_STR, buflen) >= buflen) ||
 121                     (strlcat(buf, stype, buflen) >= buflen) ||
 122                     (strlcat(buf, ":", buflen) >= buflen) ||
 123                     (nvpair_value_string(curr, &val) != 0) ||
 124                     (strlcat(buf, val, buflen) >= buflen) ||
 125                     (strlcat(buf, FS_TOKEN_END_STR, buflen) >= buflen)) {
 126                         err = E2BIG;
 127                         break;
 128                 }
 129                 curr = nvlist_next_nvpair(nvl, curr);
 130         }
 131         if (err != 0) {
 132                 free(buf);
 133                 return (err);
 134         }
 135         if (strlcat(buf, FS_REPARSE_TAG_END_STR, buflen) >= buflen) {
 136                 free(buf);
 137                 return (E2BIG);
 138         }
 139 
 140         *stringp = buf;
 141         return (0);
 142 }
 143 
 144 /*
 145  * reparse_deref()
 146  *
 147  * Accepts the service-specific item from the reparse point and returns
 148  * the service-specific data requested.  The caller specifies the size
 149  * of the buffer provided via *bufsz.
 150  *
 151  * if ok return 0 and *bufsz is updated to contain the actual length of
 152  * the returned results, else return error code. If the error code is
 153  * EOVERFLOW; results do not fit in the buffer, *bufsz will be updated
 154  * to contain the number of bytes needed to hold the results.
 155  */
 156 int
 157 reparse_deref(const char *svc_type, const char *svc_data, char *buf,
 158     size_t *bufsz)
 159 {
 160         rp_plugin_ops_t *ops;
 161 
 162         if ((svc_type == NULL) || (svc_data == NULL) || (buf == NULL) ||
 163             (bufsz == NULL))
 164                 return (EINVAL);
 165 
 166         ops = rp_find_protocol(svc_type);
 167         if ((ops != NULL) && (ops->rpo_deref != NULL))
 168                 return (ops->rpo_deref(svc_type, svc_data, buf, bufsz));
 169 
 170         /* no plugin, return error */
 171         return (ENOTSUP);
 172 }
 173 
 174 /*
 175  * reparse_delete()
 176  *
 177  * Delete a reparse point at a given pathname.  It will fail if
 178  * a reparse point does not exist at the given path or the pathname
 179  * is not a symlink.
 180  *
 181  * return 0 if ok else return error code.
 182  */
 183 int
 184 reparse_delete(const char *path)
 185 {
 186         struct stat sbuf;
 187 
 188         if (path == NULL)
 189                 return (EINVAL);
 190 
 191         /* check if object exists */
 192         if (lstat(path, &sbuf) != 0)
 193                 return (errno);
 194 
 195         if ((sbuf.st_mode & S_IFLNK) != S_IFLNK)
 196                 return (EINVAL);
 197 
 198         return (unlink(path) ? errno : 0);
 199 }
 200 
 201 /*
 202  * reparse_add()
 203  *
 204  * Add a service type entry to a nvlist with a copy of svc_data,
 205  * replacing one of the same type if already present.
 206  *
 207  * return 0 if ok else return error code.
 208  */
 209 int
 210 reparse_add(nvlist_t *nvl, const char *svc_type, const char *svc_data)
 211 {
 212         int err;
 213         char *buf;
 214         size_t bufsz;
 215         rp_plugin_ops_t *ops;
 216 
 217         if ((nvl == NULL) || (svc_type == NULL) || (svc_data == NULL))
 218                 return (EINVAL);
 219 
 220         bufsz = SYMLINK_MAX;            /* no need to mess around */
 221         if ((buf = malloc(bufsz)) == NULL)
 222                 return (ENOMEM);
 223 
 224         ops = rp_find_protocol(svc_type);
 225         if ((ops != NULL) && (ops->rpo_form != NULL))
 226                 err = ops->rpo_form(svc_type, svc_data, buf, &bufsz);
 227         else
 228                 err = ENOTSUP;          /* no plugin */
 229 
 230         if (err != 0) {
 231                 free(buf);
 232                 return (err);
 233         }
 234 
 235         err =  nvlist_add_string(nvl, svc_type, buf);
 236         free(buf);
 237         return (err);
 238 }
 239 
 240 /*
 241  * reparse_remove()
 242  *
 243  * Remove a service type entry from the nvlist, if present.
 244  *
 245  * return 0 if ok else return error code.
 246  */
 247 int
 248 reparse_remove(nvlist_t *nvl, const char *svc_type)
 249 {
 250         if ((nvl == NULL) || (svc_type == NULL))
 251                 return (EINVAL);
 252 
 253         return (nvlist_remove_all(nvl, svc_type));
 254 }
 255 
 256 /*
 257  * Returns true if name is "." or "..", otherwise returns false.
 258  */
 259 static boolean_t
 260 rp_is_dot_or_dotdot(const char *name)
 261 {
 262         if (*name != '.')
 263                 return (B_FALSE);
 264 
 265         if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))
 266                 return (B_TRUE);
 267 
 268         return (B_FALSE);
 269 }
 270 
 271 static void
 272 proto_plugin_fini()
 273 {
 274         rp_proto_plugin_t *p;
 275 
 276         /*
 277          * Protocols may call this framework during _fini
 278          */
 279         for (p = rp_proto_list; p != NULL; p = p->plugin_next) {
 280                 if (p->plugin_ops->rpo_fini)
 281                         (void) p->plugin_ops->rpo_fini();
 282         }
 283         while ((p = rp_proto_list) != NULL) {
 284                 rp_proto_list = p->plugin_next;
 285                 if (p->plugin_handle != NULL)
 286                         (void) dlclose(p->plugin_handle);
 287                 free(p);
 288         }
 289 
 290         if (rp_proto_handle.rp_ops != NULL) {
 291                 free(rp_proto_handle.rp_ops);
 292                 rp_proto_handle.rp_ops = NULL;
 293         }
 294         rp_proto_handle.rp_num_proto = 0;
 295 }
 296 
 297 /*
 298  * rp_plugin_init()
 299  *
 300  * Initialize the service type specific plugin modules.
 301  * For each reparse service type, there should be a plugin library for it.
 302  * This function walks /usr/lib/reparse directory for plugin libraries.
 303  * For each plugin library found, initialize it and add it to the internal
 304  * list of service type plugin. These are used for service type specific
 305  * operations.
 306  */
 307 int
 308 rp_plugin_init()
 309 {
 310         int err, ret = RP_OK;
 311         char isa[MAXISALEN], dirpath[MAXPATHLEN], path[MAXPATHLEN];
 312         int num_protos = 0;
 313         rp_proto_handle_t *rp_hdl;
 314         rp_proto_plugin_t *proto, *tmp;
 315         rp_plugin_ops_t *plugin_ops;
 316         struct stat st;
 317         void *dlhandle;
 318         DIR *dir;
 319         struct dirent *dent;
 320 
 321 #if defined(_LP64)
 322         if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
 323                 isa[0] = '\0';
 324 #else
 325         isa[0] = '\0';
 326 #endif
 327 
 328         (void) snprintf(dirpath, MAXPATHLEN,
 329             "%s/%s", RP_LIB_DIR, isa);
 330 
 331         if ((dir = opendir(dirpath)) == NULL)
 332                 return (RP_NO_PLUGIN_DIR);
 333 
 334         while ((dent = readdir(dir)) != NULL) {
 335                 if (rp_is_dot_or_dotdot(dent->d_name))
 336                         continue;
 337 
 338                 (void) snprintf(path, MAXPATHLEN,
 339                     "%s/%s", dirpath, dent->d_name);
 340 
 341                 /*
 342                  * If file doesn't exist, don't try to map it
 343                  */
 344                 if (stat(path, &st) < 0)
 345                         continue;
 346                 if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL)
 347                         continue;
 348 
 349                 plugin_ops = (rp_plugin_ops_t *)
 350                     dlsym(dlhandle, "rp_plugin_ops");
 351                 if (plugin_ops == NULL) {
 352                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 353                             "Error in plugin ops for service type %s\n%s\n"),
 354                             dent->d_name, dlerror());
 355                         (void) dlclose(dlhandle);
 356                         continue;
 357                 }
 358                 proto = (rp_proto_plugin_t *)
 359                     calloc(1, sizeof (rp_proto_plugin_t));
 360                 if (proto == NULL) {
 361                         (void) dlclose(dlhandle);
 362                         (void) fprintf(stderr,
 363                             dgettext(TEXT_DOMAIN, "No memory for plugin %s\n"),
 364                             dent->d_name);
 365                         ret = RP_NO_MEMORY;
 366                         break;
 367                 }
 368 
 369                 proto->plugin_ops = plugin_ops;
 370                 proto->plugin_handle = dlhandle;
 371                 num_protos++;
 372                 proto->plugin_next = rp_proto_list;
 373                 rp_proto_list = proto;
 374         }
 375 
 376         (void) closedir(dir);
 377 
 378         if ((num_protos == 0) && (ret == 0))
 379                 ret = RP_NO_PLUGIN;
 380         /*
 381          * There was an error, so cleanup prior to return of failure.
 382          */
 383         if (ret != RP_OK) {
 384                 proto_plugin_fini();
 385                 return (ret);
 386         }
 387 
 388         rp_proto_handle.rp_ops = (rp_plugin_ops_t **)calloc(num_protos,
 389             sizeof (rp_plugin_ops_t *));
 390         if (!rp_proto_handle.rp_ops) {
 391                 proto_plugin_fini();
 392                 return (RP_NO_MEMORY);
 393         }
 394 
 395         rp_hdl = &rp_proto_handle;
 396         rp_hdl->rp_num_proto = 0;
 397         for (tmp = rp_proto_list; rp_hdl->rp_num_proto < num_protos &&
 398             tmp != NULL; tmp = tmp->plugin_next) {
 399 
 400                 err = RP_OK;
 401                 if (tmp->plugin_ops->rpo_init != NULL)
 402                         err = tmp->plugin_ops->rpo_init();
 403                 if (err != RP_OK)
 404                         continue;
 405                 rp_hdl->rp_ops[rp_hdl->rp_num_proto++] = tmp->plugin_ops;
 406         }
 407 
 408         return (rp_hdl->rp_num_proto > 0 ? RP_OK : RP_NO_PLUGIN);
 409 }
 410 
 411 
 412 /*
 413  * find_protocol()
 414  *
 415  * Search the plugin list for the specified protocol and return the
 416  * ops vector.  return NULL if protocol is not defined.
 417  */
 418 static rp_plugin_ops_t *
 419 rp_find_protocol(const char *svc_type)
 420 {
 421         int i;
 422         rp_plugin_ops_t *ops = NULL;
 423 
 424         if (svc_type == NULL)
 425                 return (NULL);
 426 
 427         if (rp_plugin_inited == 0) {
 428                 if (rp_plugin_init() == RP_OK)
 429                         rp_plugin_inited = 1;
 430                 else
 431                         return (NULL);
 432         }
 433 
 434         for (i = 0; i < rp_proto_handle.rp_num_proto; i++) {
 435                 ops = rp_proto_handle.rp_ops[i];
 436                 if (ops->rpo_supports_svc(svc_type))
 437                         return (ops);
 438 
 439         }
 440         return (NULL);
 441 }