2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <strings.h>
2N/A#include <string.h>
2N/A#include <dirent.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/param.h>
2N/A#include <errno.h>
2N/A#include <limits.h>
2N/A#include <libnvpair.h>
2N/A#include <dlfcn.h>
2N/A#include <libintl.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/fs_reparse.h>
2N/A#include <syslog.h>
2N/A#include "rp_plugin.h"
2N/A
2N/A#define MAXISALEN 257 /* based on sysinfo(2) man page */
2N/A
2N/Astatic rp_proto_handle_t rp_proto_handle;
2N/Astatic rp_proto_plugin_t *rp_proto_list = NULL;
2N/A
2N/Aint rp_plugin_init(void);
2N/Astatic void proto_plugin_fini(void);
2N/Astatic rp_plugin_ops_t *rp_find_protocol(const char *svctype);
2N/A
2N/Astatic int rp_plugin_inited = 0;
2N/A
2N/A/*
2N/A * reparse_create()
2N/A *
2N/A * Create a symlink at the specified 'path' as a reparse point.
2N/A * This function will fail if path refers to an existing file system
2N/A * object or an object named string already exists at the given path.
2N/A *
2N/A * return 0 if ok else return error code.
2N/A */
2N/Aint
2N/Areparse_create(const char *path, const char *string)
2N/A{
2N/A int err;
2N/A struct stat sbuf;
2N/A
2N/A if (path == NULL || string == NULL)
2N/A return (EINVAL);
2N/A
2N/A if ((err = reparse_validate(string)) != 0)
2N/A return (err);
2N/A
2N/A /* check if object exists */
2N/A if (lstat(path, &sbuf) == 0)
2N/A return (EEXIST);
2N/A
2N/A return (symlink(string, path) ? errno : 0);
2N/A}
2N/A
2N/A/*
2N/A * reparse_unparse()
2N/A *
2N/A * Convert an nvlist back to a string format suitable to write
2N/A * to the reparse point symlink body. The string returned is in
2N/A * allocated memory and must be freed by the caller.
2N/A *
2N/A * return 0 if ok else return error code.
2N/A */
2N/Aint
2N/Areparse_unparse(nvlist_t *nvl, char **stringp)
2N/A{
2N/A int err, buflen;
2N/A char *buf, *stype, *val;
2N/A nvpair_t *curr;
2N/A
2N/A if (nvl == NULL || stringp == NULL ||
2N/A ((curr = nvlist_next_nvpair(nvl, NULL)) == NULL))
2N/A return (EINVAL);
2N/A
2N/A buflen = SYMLINK_MAX;
2N/A if ((buf = malloc(buflen)) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A err = 0;
2N/A (void) snprintf(buf, buflen, "%s", FS_REPARSE_TAG_STR);
2N/A while (curr != NULL) {
2N/A if (!(stype = nvpair_name(curr))) {
2N/A err = EINVAL;
2N/A break;
2N/A }
2N/A if ((strlcat(buf, FS_TOKEN_START_STR, buflen) >= buflen) ||
2N/A (strlcat(buf, stype, buflen) >= buflen) ||
2N/A (strlcat(buf, ":", buflen) >= buflen) ||
2N/A (nvpair_value_string(curr, &val) != 0) ||
2N/A (strlcat(buf, val, buflen) >= buflen) ||
2N/A (strlcat(buf, FS_TOKEN_END_STR, buflen) >= buflen)) {
2N/A err = E2BIG;
2N/A break;
2N/A }
2N/A curr = nvlist_next_nvpair(nvl, curr);
2N/A }
2N/A if (err != 0) {
2N/A free(buf);
2N/A return (err);
2N/A }
2N/A if (strlcat(buf, FS_REPARSE_TAG_END_STR, buflen) >= buflen) {
2N/A free(buf);
2N/A return (E2BIG);
2N/A }
2N/A
2N/A *stringp = buf;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * reparse_deref()
2N/A *
2N/A * Accepts the service-specific item from the reparse point and returns
2N/A * the service-specific data requested. The caller specifies the size
2N/A * of the buffer provided via *bufsz.
2N/A *
2N/A * if ok return 0 and *bufsz is updated to contain the actual length of
2N/A * the returned results, else return error code. If the error code is
2N/A * EOVERFLOW; results do not fit in the buffer, *bufsz will be updated
2N/A * to contain the number of bytes needed to hold the results.
2N/A */
2N/Aint
2N/Areparse_deref(const char *svc_type, const char *svc_data, char *buf,
2N/A size_t *bufsz)
2N/A{
2N/A rp_plugin_ops_t *ops;
2N/A
2N/A if ((svc_type == NULL) || (svc_data == NULL) || (buf == NULL) ||
2N/A (bufsz == NULL))
2N/A return (EINVAL);
2N/A
2N/A#ifdef DEBUG
2N/A syslog(LOG_NOTICE, "reparse_deref: svc_type: %s, data: %s, size: %d",
2N/A svc_type, svc_data, *bufsz);
2N/A#endif
2N/A ops = rp_find_protocol(svc_type);
2N/A#ifdef DEBUG
2N/A syslog(LOG_NOTICE, "reparse_deref: found %s",
2N/A (ops && ops->rpo_svc_types()) ? ops->rpo_svc_types() :
2N/A "unknown plugin");
2N/A#endif
2N/A if ((ops != NULL) && (ops->rpo_deref != NULL))
2N/A return (ops->rpo_deref(svc_type, svc_data, buf, bufsz));
2N/A
2N/A /* no plugin, return error */
2N/A return (ENOTSUP);
2N/A}
2N/A
2N/A/*
2N/A * reparse_delete()
2N/A *
2N/A * Delete a reparse point at a given pathname. It will fail if
2N/A * a reparse point does not exist at the given path or the pathname
2N/A * is not a symlink.
2N/A *
2N/A * return 0 if ok else return error code.
2N/A */
2N/Aint
2N/Areparse_delete(const char *path)
2N/A{
2N/A struct stat sbuf;
2N/A
2N/A if (path == NULL)
2N/A return (EINVAL);
2N/A
2N/A /* check if object exists */
2N/A if (lstat(path, &sbuf) != 0)
2N/A return (errno);
2N/A
2N/A if ((sbuf.st_mode & S_IFMT) != S_IFLNK)
2N/A return (EINVAL);
2N/A
2N/A return (unlink(path) ? errno : 0);
2N/A}
2N/A
2N/A/*
2N/A * reparse_add()
2N/A *
2N/A * Add a service type entry to a nvlist with a copy of svc_data,
2N/A * replacing one of the same type if already present.
2N/A *
2N/A * return 0 if ok else return error code.
2N/A */
2N/Aint
2N/Areparse_add(nvlist_t *nvl, const char *svc_type, const char *svc_data)
2N/A{
2N/A int err;
2N/A char *buf;
2N/A size_t bufsz;
2N/A rp_plugin_ops_t *ops;
2N/A
2N/A if ((nvl == NULL) || (svc_type == NULL) || (svc_data == NULL))
2N/A return (EINVAL);
2N/A
2N/A bufsz = SYMLINK_MAX; /* no need to mess around */
2N/A if ((buf = malloc(bufsz)) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A ops = rp_find_protocol(svc_type);
2N/A if ((ops != NULL) && (ops->rpo_form != NULL))
2N/A err = ops->rpo_form(svc_type, svc_data, buf, &bufsz);
2N/A else
2N/A err = ENOTSUP; /* no plugin */
2N/A
2N/A if (err != 0) {
2N/A free(buf);
2N/A return (err);
2N/A }
2N/A
2N/A err = nvlist_add_string(nvl, svc_type, buf);
2N/A free(buf);
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * reparse_remove()
2N/A *
2N/A * Remove a service type entry from the nvlist, if present.
2N/A *
2N/A * return 0 if ok else return error code.
2N/A */
2N/Aint
2N/Areparse_remove(nvlist_t *nvl, const char *svc_type)
2N/A{
2N/A if ((nvl == NULL) || (svc_type == NULL))
2N/A return (EINVAL);
2N/A
2N/A return (nvlist_remove_all(nvl, svc_type));
2N/A}
2N/A
2N/A/*
2N/A * Returns true if name is "." or "..", otherwise returns false.
2N/A */
2N/Astatic boolean_t
2N/Arp_is_dot_or_dotdot(const char *name)
2N/A{
2N/A if (*name != '.')
2N/A return (B_FALSE);
2N/A
2N/A if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))
2N/A return (B_TRUE);
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/Astatic void
2N/Aproto_plugin_fini()
2N/A{
2N/A rp_proto_plugin_t *p;
2N/A
2N/A /*
2N/A * Protocols may call this framework during _fini
2N/A */
2N/A for (p = rp_proto_list; p != NULL; p = p->plugin_next) {
2N/A if (p->plugin_ops->rpo_fini)
2N/A p->plugin_ops->rpo_fini();
2N/A }
2N/A while ((p = rp_proto_list) != NULL) {
2N/A rp_proto_list = p->plugin_next;
2N/A if (p->plugin_handle != NULL)
2N/A (void) dlclose(p->plugin_handle);
2N/A free(p);
2N/A }
2N/A
2N/A if (rp_proto_handle.rp_ops != NULL) {
2N/A free(rp_proto_handle.rp_ops);
2N/A rp_proto_handle.rp_ops = NULL;
2N/A }
2N/A rp_proto_handle.rp_num_proto = 0;
2N/A}
2N/A
2N/A/*
2N/A * rp_plugin_init()
2N/A *
2N/A * Initialize the service type specific plugin modules.
2N/A * For each reparse service type, there should be a plugin library for it.
2N/A * This function walks /usr/lib/reparse directory for plugin libraries.
2N/A * For each plugin library found, initialize it and add it to the internal
2N/A * list of service type plugin. These are used for service type specific
2N/A * operations.
2N/A */
2N/Aint
2N/Arp_plugin_init(void)
2N/A{
2N/A int err, ret = RP_OK;
2N/A char isa[MAXISALEN], dirpath[MAXPATHLEN], path[MAXPATHLEN];
2N/A int num_protos = 0;
2N/A rp_proto_handle_t *rp_hdl;
2N/A rp_proto_plugin_t *proto, *tmp;
2N/A rp_plugin_ops_t *plugin_ops;
2N/A struct stat st;
2N/A void *dlhandle;
2N/A DIR *dir;
2N/A struct dirent *dent;
2N/A
2N/A#if defined(_LP64)
2N/A if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
2N/A isa[0] = '\0';
2N/A#else
2N/A isa[0] = '\0';
2N/A#endif
2N/A
2N/A (void) snprintf(dirpath, MAXPATHLEN,
2N/A "%s/%s", RP_LIB_DIR, isa);
2N/A
2N/A if ((dir = opendir(dirpath)) == NULL)
2N/A return (RP_NO_PLUGIN_DIR);
2N/A
2N/A while ((dent = readdir(dir)) != NULL) {
2N/A if (rp_is_dot_or_dotdot(dent->d_name))
2N/A continue;
2N/A
2N/A (void) snprintf(path, MAXPATHLEN,
2N/A "%s/%s", dirpath, dent->d_name);
2N/A
2N/A /*
2N/A * If file doesn't exist, or it's a link, don't try to map it
2N/A */
2N/A if (lstat(path, &st) < 0)
2N/A continue;
2N/A if ((st.st_mode & S_IFMT) == S_IFLNK)
2N/A continue;
2N/A if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL)
2N/A continue;
2N/A
2N/A plugin_ops = (rp_plugin_ops_t *)
2N/A dlsym(dlhandle, "rp_plugin_ops");
2N/A if (plugin_ops == NULL) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "Error in plugin ops for service type %s\n%s\n"),
2N/A dent->d_name, dlerror());
2N/A (void) dlclose(dlhandle);
2N/A continue;
2N/A }
2N/A proto = (rp_proto_plugin_t *)
2N/A calloc(1, sizeof (rp_proto_plugin_t));
2N/A if (proto == NULL) {
2N/A (void) dlclose(dlhandle);
2N/A (void) fprintf(stderr,
2N/A dgettext(TEXT_DOMAIN, "No memory for plugin %s\n"),
2N/A dent->d_name);
2N/A ret = RP_NO_MEMORY;
2N/A break;
2N/A }
2N/A
2N/A proto->plugin_ops = plugin_ops;
2N/A proto->plugin_handle = dlhandle;
2N/A num_protos++;
2N/A proto->plugin_next = rp_proto_list;
2N/A rp_proto_list = proto;
2N/A }
2N/A
2N/A (void) closedir(dir);
2N/A
2N/A if ((num_protos == 0) && (ret == 0))
2N/A ret = RP_NO_PLUGIN;
2N/A /*
2N/A * There was an error, so cleanup prior to return of failure.
2N/A */
2N/A if (ret != RP_OK) {
2N/A proto_plugin_fini();
2N/A return (ret);
2N/A }
2N/A
2N/A rp_proto_handle.rp_ops = (rp_plugin_ops_t **)calloc(num_protos,
2N/A sizeof (rp_plugin_ops_t *));
2N/A if (!rp_proto_handle.rp_ops) {
2N/A proto_plugin_fini();
2N/A return (RP_NO_MEMORY);
2N/A }
2N/A
2N/A rp_hdl = &rp_proto_handle;
2N/A rp_hdl->rp_num_proto = 0;
2N/A for (tmp = rp_proto_list; rp_hdl->rp_num_proto < num_protos &&
2N/A tmp != NULL; tmp = tmp->plugin_next) {
2N/A
2N/A err = RP_OK;
2N/A if (tmp->plugin_ops->rpo_init != NULL)
2N/A err = tmp->plugin_ops->rpo_init();
2N/A if (err != RP_OK)
2N/A continue;
2N/A rp_hdl->rp_ops[rp_hdl->rp_num_proto++] = tmp->plugin_ops;
2N/A }
2N/A
2N/A if (rp_hdl->rp_num_proto == 0)
2N/A return (RP_NO_PLUGIN);
2N/A rp_plugin_inited = 1;
2N/A return (RP_OK);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * find_protocol()
2N/A *
2N/A * Search the plugin list for the specified protocol and return the
2N/A * ops vector. return NULL if protocol is not defined.
2N/A */
2N/Astatic rp_plugin_ops_t *
2N/Arp_find_protocol(const char *svc_type)
2N/A{
2N/A int i;
2N/A rp_plugin_ops_t *ops = NULL;
2N/A
2N/A if (svc_type == NULL)
2N/A return (NULL);
2N/A
2N/A if (rp_plugin_inited == 0) {
2N/A if (rp_plugin_init() != RP_OK)
2N/A return (NULL);
2N/A }
2N/A
2N/A for (i = 0; i < rp_proto_handle.rp_num_proto; i++) {
2N/A ops = rp_proto_handle.rp_ops[i];
2N/A if (ops->rpo_supports_svc(svc_type))
2N/A return (ops);
2N/A }
2N/A return (NULL);
2N/A}