#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <limits.h>
#include <libnvpair.h>
#include <dlfcn.h>
#include <libintl.h>
#include <sys/systeminfo.h>
#include <sys/fs_reparse.h>
#include "rp_plugin.h"
#define MAXISALEN 257
static rp_proto_handle_t rp_proto_handle;
static rp_proto_plugin_t *rp_proto_list;
int rp_plugin_init(void);
static void proto_plugin_fini(void);
static rp_plugin_ops_t *rp_find_protocol(const char *svctype);
extern int errno;
static int rp_plugin_inited = 0;
int
reparse_create(const char *path, const char *data)
{
int err;
struct stat sbuf;
if (path == NULL || data == NULL)
return (EINVAL);
if ((err = reparse_validate(data)) != 0)
return (err);
if (lstat(path, &sbuf) == 0)
return (EEXIST);
return (symlink(data, path) ? errno : 0);
}
int
reparse_unparse(nvlist_t *nvl, char **stringp)
{
int err, buflen;
char *buf, *stype, *val;
nvpair_t *curr;
if (nvl == NULL || stringp == NULL ||
((curr = nvlist_next_nvpair(nvl, NULL)) == NULL))
return (EINVAL);
buflen = SYMLINK_MAX;
if ((buf = malloc(buflen)) == NULL)
return (ENOMEM);
err = 0;
(void) snprintf(buf, buflen, "%s", FS_REPARSE_TAG_STR);
while (curr != NULL) {
if (!(stype = nvpair_name(curr))) {
err = EINVAL;
break;
}
if ((strlcat(buf, FS_TOKEN_START_STR, buflen) >= buflen) ||
(strlcat(buf, stype, buflen) >= buflen) ||
(strlcat(buf, ":", buflen) >= buflen) ||
(nvpair_value_string(curr, &val) != 0) ||
(strlcat(buf, val, buflen) >= buflen) ||
(strlcat(buf, FS_TOKEN_END_STR, buflen) >= buflen)) {
err = E2BIG;
break;
}
curr = nvlist_next_nvpair(nvl, curr);
}
if (err != 0) {
free(buf);
return (err);
}
if (strlcat(buf, FS_REPARSE_TAG_END_STR, buflen) >= buflen) {
free(buf);
return (E2BIG);
}
*stringp = buf;
return (0);
}
int
reparse_deref(const char *svc_type, const char *svc_data, char *buf,
size_t *bufsz)
{
rp_plugin_ops_t *ops;
if ((svc_type == NULL) || (svc_data == NULL) || (buf == NULL) ||
(bufsz == NULL))
return (EINVAL);
ops = rp_find_protocol(svc_type);
if ((ops != NULL) && (ops->rpo_deref != NULL))
return (ops->rpo_deref(svc_type, svc_data, buf, bufsz));
return (ENOTSUP);
}
int
reparse_delete(const char *path)
{
struct stat sbuf;
if (path == NULL)
return (EINVAL);
if (lstat(path, &sbuf) != 0)
return (errno);
if ((sbuf.st_mode & S_IFLNK) != S_IFLNK)
return (EINVAL);
return (unlink(path) ? errno : 0);
}
int
reparse_add(nvlist_t *nvl, const char *svc_type, const char *svc_data)
{
int err;
char *buf;
size_t bufsz;
rp_plugin_ops_t *ops;
if ((nvl == NULL) || (svc_type == NULL) || (svc_data == NULL))
return (EINVAL);
bufsz = SYMLINK_MAX;
if ((buf = malloc(bufsz)) == NULL)
return (ENOMEM);
ops = rp_find_protocol(svc_type);
if ((ops != NULL) && (ops->rpo_form != NULL))
err = ops->rpo_form(svc_type, svc_data, buf, &bufsz);
else
err = ENOTSUP;
if (err != 0) {
free(buf);
return (err);
}
err = nvlist_add_string(nvl, svc_type, buf);
free(buf);
return (err);
}
int
reparse_remove(nvlist_t *nvl, const char *svc_type)
{
if ((nvl == NULL) || (svc_type == NULL))
return (EINVAL);
return (nvlist_remove_all(nvl, svc_type));
}
static boolean_t
rp_is_dot_or_dotdot(const char *name)
{
if (*name != '.')
return (B_FALSE);
if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))
return (B_TRUE);
return (B_FALSE);
}
static void
proto_plugin_fini()
{
rp_proto_plugin_t *p;
for (p = rp_proto_list; p != NULL; p = p->plugin_next) {
if (p->plugin_ops->rpo_fini)
(void) p->plugin_ops->rpo_fini();
}
while ((p = rp_proto_list) != NULL) {
rp_proto_list = p->plugin_next;
if (p->plugin_handle != NULL)
(void) dlclose(p->plugin_handle);
free(p);
}
if (rp_proto_handle.rp_ops != NULL) {
free(rp_proto_handle.rp_ops);
rp_proto_handle.rp_ops = NULL;
}
rp_proto_handle.rp_num_proto = 0;
}
int
rp_plugin_init()
{
int err, ret = RP_OK;
char isa[MAXISALEN], dirpath[MAXPATHLEN], path[MAXPATHLEN];
int num_protos = 0;
rp_proto_handle_t *rp_hdl;
rp_proto_plugin_t *proto, *tmp;
rp_plugin_ops_t *plugin_ops;
struct stat st;
void *dlhandle;
DIR *dir;
struct dirent *dent;
#if defined(_LP64)
if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
isa[0] = '\0';
#else
isa[0] = '\0';
#endif
(void) snprintf(dirpath, MAXPATHLEN,
"%s/%s", RP_LIB_DIR, isa);
if ((dir = opendir(dirpath)) == NULL)
return (RP_NO_PLUGIN_DIR);
while ((dent = readdir(dir)) != NULL) {
if (rp_is_dot_or_dotdot(dent->d_name))
continue;
(void) snprintf(path, MAXPATHLEN,
"%s/%s", dirpath, dent->d_name);
if (stat(path, &st) < 0)
continue;
if ((dlhandle = dlopen(path, RTLD_FIRST|RTLD_LAZY)) == NULL)
continue;
plugin_ops = (rp_plugin_ops_t *)
dlsym(dlhandle, "rp_plugin_ops");
if (plugin_ops == NULL) {
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"Error in plugin ops for service type %s\n%s\n"),
dent->d_name, dlerror());
(void) dlclose(dlhandle);
continue;
}
proto = (rp_proto_plugin_t *)
calloc(1, sizeof (rp_proto_plugin_t));
if (proto == NULL) {
(void) dlclose(dlhandle);
(void) fprintf(stderr,
dgettext(TEXT_DOMAIN, "No memory for plugin %s\n"),
dent->d_name);
ret = RP_NO_MEMORY;
break;
}
proto->plugin_ops = plugin_ops;
proto->plugin_handle = dlhandle;
num_protos++;
proto->plugin_next = rp_proto_list;
rp_proto_list = proto;
}
(void) closedir(dir);
if ((num_protos == 0) && (ret == 0))
ret = RP_NO_PLUGIN;
if (ret != RP_OK) {
proto_plugin_fini();
return (ret);
}
rp_proto_handle.rp_ops = (rp_plugin_ops_t **)calloc(num_protos,
sizeof (rp_plugin_ops_t *));
if (!rp_proto_handle.rp_ops) {
proto_plugin_fini();
return (RP_NO_MEMORY);
}
rp_hdl = &rp_proto_handle;
rp_hdl->rp_num_proto = 0;
for (tmp = rp_proto_list; rp_hdl->rp_num_proto < num_protos &&
tmp != NULL; tmp = tmp->plugin_next) {
err = RP_OK;
if (tmp->plugin_ops->rpo_init != NULL)
err = tmp->plugin_ops->rpo_init();
if (err != RP_OK)
continue;
rp_hdl->rp_ops[rp_hdl->rp_num_proto++] = tmp->plugin_ops;
}
return (rp_hdl->rp_num_proto > 0 ? RP_OK : RP_NO_PLUGIN);
}
static rp_plugin_ops_t *
rp_find_protocol(const char *svc_type)
{
int i;
rp_plugin_ops_t *ops = NULL;
if (svc_type == NULL)
return (NULL);
if (rp_plugin_inited == 0) {
if (rp_plugin_init() == RP_OK)
rp_plugin_inited = 1;
else
return (NULL);
}
for (i = 0; i < rp_proto_handle.rp_num_proto; i++) {
ops = rp_proto_handle.rp_ops[i];
if (ops->rpo_supports_svc(svc_type))
return (ops);
}
return (NULL);
}