libshare: use AVL tree with static data, pass all data in arguments

This makes it so we don't leak a consistent 64 bytes anymore,
makes the searches simpler and faster, removes /all allocations/
from the driver (quite trivially, since they were absolutely needless),
and makes libshare thread-safe (except, maybe, linux/smb, but that only
does pointer-width loads/stores so it's also mostly fine, except for
leaking smb_shares)

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
Closes #13165
This commit is contained in:
наб
2022-02-28 14:50:28 +01:00
committed by Brian Behlendorf
parent 4ccbb23971
commit 63ce6dd988
8 changed files with 119 additions and 413 deletions
+87 -235
View File
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <libintl.h>
@@ -37,139 +38,84 @@
#include <libzfs.h>
#include <libshare.h>
#include "libshare_impl.h"
#include "nfs.h"
#include "smb.h"
static sa_share_impl_t alloc_share(const char *zfsname, const char *path);
static void free_share(sa_share_impl_t share);
#define init_share(zfsname, path, shareopts) \
{ \
.sa_zfsname = zfsname, \
.sa_mountpoint = path, \
.sa_shareopts = shareopts, \
}
#define find_proto(pcol) \
/* CSTYLED */ \
({ \
sa_fstype_t prot = { \
.protocol = pcol, \
}; \
avl_find(&fstypes, &prot, NULL); \
})
static int fstypes_count;
static sa_fstype_t *fstypes;
static avl_tree_t fstypes;
sa_fstype_t *
register_fstype(const char *name, const sa_share_ops_t *ops)
static int
fstypes_compar(const void *lhs, const void *rhs)
{
sa_fstype_t *fstype;
fstype = calloc(1, sizeof (sa_fstype_t));
if (fstype == NULL)
return (NULL);
fstype->name = name;
fstype->ops = ops;
fstype->fsinfo_index = fstypes_count;
fstypes_count++;
fstype->next = fstypes;
fstypes = fstype;
return (fstype);
const sa_fstype_t *l = lhs, *r = rhs;
int cmp = strcmp(l->protocol, r->protocol);
return ((0 < cmp) - (cmp < 0));
}
__attribute__((constructor)) static void
libshare_init(void)
{
libshare_nfs_init();
libshare_smb_init();
avl_create(&fstypes, fstypes_compar,
sizeof (sa_fstype_t), offsetof(sa_fstype_t, node));
avl_add(&fstypes, &libshare_nfs_type);
avl_add(&fstypes, &libshare_smb_type);
}
int
sa_enable_share(const char *zfsname, const char *mountpoint,
const char *shareopts, const char *protocol)
{
int rc, ret = SA_OK;
boolean_t found_protocol = B_FALSE;
sa_fstype_t *fstype;
sa_fstype_t *fstype = find_proto(protocol);
if (!fstype)
return (SA_INVALID_PROTOCOL);
sa_share_impl_t impl_share = alloc_share(zfsname, mountpoint);
if (impl_share == NULL)
return (SA_NO_MEMORY);
fstype = fstypes;
while (fstype != NULL) {
if (strcmp(fstype->name, protocol) == 0) {
rc = fstype->ops->update_shareopts(impl_share,
shareopts);
if (rc != SA_OK)
break;
rc = fstype->ops->enable_share(impl_share);
if (rc != SA_OK)
ret = rc;
found_protocol = B_TRUE;
}
fstype = fstype->next;
}
free_share(impl_share);
return (found_protocol ? ret : SA_INVALID_PROTOCOL);
const struct sa_share_impl args =
init_share(zfsname, mountpoint, shareopts);
return (fstype->enable_share(&args));
}
int
sa_disable_share(const char *mountpoint, const char *protocol)
{
int rc, ret = SA_OK;
boolean_t found_protocol = B_FALSE;
sa_fstype_t *fstype;
sa_fstype_t *fstype = find_proto(protocol);
if (!fstype)
return (SA_INVALID_PROTOCOL);
sa_share_impl_t impl_share = alloc_share(NULL, mountpoint);
if (impl_share == NULL)
return (SA_NO_MEMORY);
fstype = fstypes;
while (fstype != NULL) {
if (strcmp(fstype->name, protocol) == 0) {
rc = fstype->ops->disable_share(impl_share);
if (rc != SA_OK)
ret = rc;
found_protocol = B_TRUE;
}
fstype = fstype->next;
}
free_share(impl_share);
return (found_protocol ? ret : SA_INVALID_PROTOCOL);
const struct sa_share_impl args = init_share(NULL, mountpoint, NULL);
return (fstype->disable_share(&args));
}
boolean_t
sa_is_shared(const char *mountpoint, const char *protocol)
{
sa_fstype_t *fstype;
boolean_t ret = B_FALSE;
/* guid value is not used */
sa_share_impl_t impl_share = alloc_share(NULL, mountpoint);
if (impl_share == NULL)
sa_fstype_t *fstype = find_proto(protocol);
if (!fstype)
return (B_FALSE);
fstype = fstypes;
while (fstype != NULL) {
if (strcmp(fstype->name, protocol) == 0) {
ret = fstype->ops->is_shared(impl_share);
}
fstype = fstype->next;
}
free_share(impl_share);
return (ret);
const struct sa_share_impl args = init_share(NULL, mountpoint, NULL);
return (fstype->is_shared(&args));
}
void
sa_commit_shares(const char *protocol)
{
sa_fstype_t *fstype = fstypes;
while (fstype != NULL) {
if (strcmp(fstype->name, protocol) == 0)
fstype->ops->commit_shares();
fstype = fstype->next;
}
sa_fstype_t *fstype = find_proto(protocol);
if (!fstype)
return;
fstype->commit_shares();
}
/*
@@ -181,185 +127,91 @@ const char *
sa_errorstr(int err)
{
static char errstr[32];
char *ret = NULL;
switch (err) {
case SA_OK:
ret = dgettext(TEXT_DOMAIN, "ok");
break;
return (dgettext(TEXT_DOMAIN, "ok"));
case SA_NO_SUCH_PATH:
ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
break;
return (dgettext(TEXT_DOMAIN, "path doesn't exist"));
case SA_NO_MEMORY:
ret = dgettext(TEXT_DOMAIN, "no memory");
break;
return (dgettext(TEXT_DOMAIN, "no memory"));
case SA_DUPLICATE_NAME:
ret = dgettext(TEXT_DOMAIN, "name in use");
break;
return (dgettext(TEXT_DOMAIN, "name in use"));
case SA_BAD_PATH:
ret = dgettext(TEXT_DOMAIN, "bad path");
break;
return (dgettext(TEXT_DOMAIN, "bad path"));
case SA_NO_SUCH_GROUP:
ret = dgettext(TEXT_DOMAIN, "no such group");
break;
return (dgettext(TEXT_DOMAIN, "no such group"));
case SA_CONFIG_ERR:
ret = dgettext(TEXT_DOMAIN, "configuration error");
break;
return (dgettext(TEXT_DOMAIN, "configuration error"));
case SA_SYSTEM_ERR:
ret = dgettext(TEXT_DOMAIN, "system error");
break;
return (dgettext(TEXT_DOMAIN, "system error"));
case SA_SYNTAX_ERR:
ret = dgettext(TEXT_DOMAIN, "syntax error");
break;
return (dgettext(TEXT_DOMAIN, "syntax error"));
case SA_NO_PERMISSION:
ret = dgettext(TEXT_DOMAIN, "no permission");
break;
return (dgettext(TEXT_DOMAIN, "no permission"));
case SA_BUSY:
ret = dgettext(TEXT_DOMAIN, "busy");
break;
return (dgettext(TEXT_DOMAIN, "busy"));
case SA_NO_SUCH_PROP:
ret = dgettext(TEXT_DOMAIN, "no such property");
break;
return (dgettext(TEXT_DOMAIN, "no such property"));
case SA_INVALID_NAME:
ret = dgettext(TEXT_DOMAIN, "invalid name");
break;
return (dgettext(TEXT_DOMAIN, "invalid name"));
case SA_INVALID_PROTOCOL:
ret = dgettext(TEXT_DOMAIN, "invalid protocol");
break;
return (dgettext(TEXT_DOMAIN, "invalid protocol"));
case SA_NOT_ALLOWED:
ret = dgettext(TEXT_DOMAIN, "operation not allowed");
break;
return (dgettext(TEXT_DOMAIN, "operation not allowed"));
case SA_BAD_VALUE:
ret = dgettext(TEXT_DOMAIN, "bad property value");
break;
return (dgettext(TEXT_DOMAIN, "bad property value"));
case SA_INVALID_SECURITY:
ret = dgettext(TEXT_DOMAIN, "invalid security type");
break;
return (dgettext(TEXT_DOMAIN, "invalid security type"));
case SA_NO_SUCH_SECURITY:
ret = dgettext(TEXT_DOMAIN, "security type not found");
break;
return (dgettext(TEXT_DOMAIN, "security type not found"));
case SA_VALUE_CONFLICT:
ret = dgettext(TEXT_DOMAIN, "property value conflict");
break;
return (dgettext(TEXT_DOMAIN, "property value conflict"));
case SA_NOT_IMPLEMENTED:
ret = dgettext(TEXT_DOMAIN, "not implemented");
break;
return (dgettext(TEXT_DOMAIN, "not implemented"));
case SA_INVALID_PATH:
ret = dgettext(TEXT_DOMAIN, "invalid path");
break;
return (dgettext(TEXT_DOMAIN, "invalid path"));
case SA_NOT_SUPPORTED:
ret = dgettext(TEXT_DOMAIN, "operation not supported");
break;
return (dgettext(TEXT_DOMAIN, "operation not supported"));
case SA_PROP_SHARE_ONLY:
ret = dgettext(TEXT_DOMAIN, "property not valid for group");
break;
return (dgettext(TEXT_DOMAIN, "property not valid for group"));
case SA_NOT_SHARED:
ret = dgettext(TEXT_DOMAIN, "not shared");
break;
return (dgettext(TEXT_DOMAIN, "not shared"));
case SA_NO_SUCH_RESOURCE:
ret = dgettext(TEXT_DOMAIN, "no such resource");
break;
return (dgettext(TEXT_DOMAIN, "no such resource"));
case SA_RESOURCE_REQUIRED:
ret = dgettext(TEXT_DOMAIN, "resource name required");
break;
return (dgettext(TEXT_DOMAIN, "resource name required"));
case SA_MULTIPLE_ERROR:
ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols");
break;
return (dgettext(TEXT_DOMAIN,
"errors from multiple protocols"));
case SA_PATH_IS_SUBDIR:
ret = dgettext(TEXT_DOMAIN, "path is a subpath of share");
break;
return (dgettext(TEXT_DOMAIN, "path is a subpath of share"));
case SA_PATH_IS_PARENTDIR:
ret = dgettext(TEXT_DOMAIN, "path is parent of a share");
break;
return (dgettext(TEXT_DOMAIN, "path is parent of a share"));
case SA_NO_SECTION:
ret = dgettext(TEXT_DOMAIN, "protocol requires a section");
break;
return (dgettext(TEXT_DOMAIN, "protocol requires a section"));
case SA_NO_PROPERTIES:
ret = dgettext(TEXT_DOMAIN, "properties not found");
break;
return (dgettext(TEXT_DOMAIN, "properties not found"));
case SA_NO_SUCH_SECTION:
ret = dgettext(TEXT_DOMAIN, "section not found");
break;
return (dgettext(TEXT_DOMAIN, "section not found"));
case SA_PASSWORD_ENC:
ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted");
break;
return (dgettext(TEXT_DOMAIN, "passwords must be encrypted"));
case SA_SHARE_EXISTS:
ret = dgettext(TEXT_DOMAIN, "path or file is already shared");
break;
return (dgettext(TEXT_DOMAIN,
"path or file is already shared"));
default:
(void) snprintf(errstr, sizeof (errstr),
dgettext(TEXT_DOMAIN, "unknown %d"), err);
ret = errstr;
return (errstr);
}
return (ret);
}
int
sa_validate_shareopts(const char *options, const char *proto)
sa_validate_shareopts(const char *options, const char *protocol)
{
sa_fstype_t *fstype;
sa_fstype_t *fstype = find_proto(protocol);
if (!fstype)
return (SA_INVALID_PROTOCOL);
fstype = fstypes;
while (fstype != NULL) {
if (strcmp(fstype->name, proto) != 0) {
fstype = fstype->next;
continue;
}
return (fstype->ops->validate_shareopts(options));
}
return (SA_INVALID_PROTOCOL);
}
static sa_share_impl_t
alloc_share(const char *zfsname, const char *mountpoint)
{
sa_share_impl_t impl_share;
impl_share = calloc(1, sizeof (struct sa_share_impl));
if (impl_share == NULL)
return (NULL);
if (mountpoint != NULL &&
((impl_share->sa_mountpoint = strdup(mountpoint)) == NULL)) {
free(impl_share);
return (NULL);
}
if (zfsname != NULL &&
((impl_share->sa_zfsname = strdup(zfsname)) == NULL)) {
free(impl_share->sa_mountpoint);
free(impl_share);
return (NULL);
}
impl_share->sa_fsinfo = calloc(fstypes_count,
sizeof (sa_share_fsinfo_t));
if (impl_share->sa_fsinfo == NULL) {
free(impl_share->sa_mountpoint);
free(impl_share->sa_zfsname);
free(impl_share);
return (NULL);
}
return (impl_share);
}
static void
free_share(sa_share_impl_t impl_share)
{
sa_fstype_t *fstype;
fstype = fstypes;
while (fstype != NULL) {
fstype->ops->clear_shareopts(impl_share);
fstype = fstype->next;
}
free(impl_share->sa_mountpoint);
free(impl_share->sa_zfsname);
free(impl_share->sa_fsinfo);
free(impl_share);
return (fstype->validate_shareopts(options));
}