| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2011 Gunnar Beutner |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <strings.h> |
| #include <libintl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <libzfs.h> |
| #include <libshare.h> |
| #include "libshare_impl.h" |
| #include "nfs.h" |
| #include "smb.h" |
| |
| static sa_share_impl_t find_share(sa_handle_impl_t handle, |
| const char *sharepath); |
| static sa_share_impl_t alloc_share(const char *sharepath); |
| static void free_share(sa_share_impl_t share); |
| |
| static void parse_sharetab(sa_handle_impl_t impl_handle); |
| static int process_share(sa_handle_impl_t impl_handle, |
| sa_share_impl_t impl_share, char *pathname, char *resource, |
| char *fstype, char *options, char *description, |
| char *dataset, boolean_t from_sharetab); |
| static void update_sharetab(sa_handle_impl_t impl_handle); |
| |
| static int update_zfs_share(sa_share_impl_t impl_handle, const char *proto); |
| static int update_zfs_shares(sa_handle_impl_t impl_handle, const char *proto); |
| |
| static int fstypes_count; |
| static sa_fstype_t *fstypes; |
| |
| sa_fstype_t * |
| register_fstype(const char *name, const sa_share_ops_t *ops) |
| { |
| 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); |
| } |
| |
| sa_handle_t |
| sa_init(int init_service) |
| { |
| sa_handle_impl_t impl_handle; |
| |
| impl_handle = calloc(1, sizeof (struct sa_handle_impl)); |
| |
| if (impl_handle == NULL) |
| return (NULL); |
| |
| impl_handle->zfs_libhandle = libzfs_init(); |
| |
| if (impl_handle->zfs_libhandle != NULL) { |
| libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE); |
| } |
| |
| parse_sharetab(impl_handle); |
| update_zfs_shares(impl_handle, NULL); |
| |
| return ((sa_handle_t)impl_handle); |
| } |
| |
| __attribute__((constructor)) static void |
| libshare_init(void) |
| { |
| libshare_nfs_init(); |
| libshare_smb_init(); |
| } |
| |
| static void |
| parse_sharetab(sa_handle_impl_t impl_handle) |
| { |
| FILE *fp; |
| char line[512]; |
| char *eol, *pathname, *resource, *fstype, *options, *description; |
| |
| fp = fopen(ZFS_SHARETAB, "r"); |
| |
| if (fp == NULL) |
| return; |
| |
| while (fgets(line, sizeof (line), fp) != NULL) { |
| eol = line + strlen(line) - 1; |
| |
| while (eol >= line) { |
| if (*eol != '\r' && *eol != '\n') |
| break; |
| |
| *eol = '\0'; |
| eol--; |
| } |
| |
| pathname = line; |
| |
| if ((resource = strchr(pathname, '\t')) == NULL) |
| continue; |
| |
| *resource = '\0'; |
| resource++; |
| |
| if ((fstype = strchr(resource, '\t')) == NULL) |
| continue; |
| |
| *fstype = '\0'; |
| fstype++; |
| |
| if ((options = strchr(fstype, '\t')) == NULL) |
| continue; |
| |
| *options = '\0'; |
| options++; |
| |
| if ((description = strchr(fstype, '\t')) != NULL) { |
| *description = '\0'; |
| description++; |
| } |
| |
| if (strcmp(resource, "-") == 0) |
| resource = NULL; |
| |
| (void) process_share(impl_handle, NULL, pathname, resource, |
| fstype, options, description, NULL, B_TRUE); |
| } |
| |
| fclose(fp); |
| } |
| |
| static void |
| update_sharetab(sa_handle_impl_t impl_handle) |
| { |
| sa_share_impl_t impl_share; |
| int temp_fd; |
| FILE *temp_fp; |
| char tempfile[] = ZFS_SHARETAB".XXXXXX"; |
| sa_fstype_t *fstype; |
| const char *resource; |
| |
| if (mkdir("/etc/dfs", 0755) < 0 && errno != EEXIST) { |
| return; |
| } |
| |
| temp_fd = mkstemp(tempfile); |
| |
| if (temp_fd < 0) |
| return; |
| |
| temp_fp = fdopen(temp_fd, "w"); |
| |
| if (temp_fp == NULL) |
| return; |
| |
| impl_share = impl_handle->shares; |
| while (impl_share != NULL) { |
| fstype = fstypes; |
| while (fstype != NULL) { |
| if (FSINFO(impl_share, fstype)->active && |
| FSINFO(impl_share, fstype)->shareopts != NULL) { |
| resource = FSINFO(impl_share, fstype)->resource; |
| |
| if (resource == NULL) |
| resource = "-"; |
| |
| fprintf(temp_fp, "%s\t%s\t%s\t%s\n", |
| impl_share->sharepath, resource, |
| fstype->name, |
| FSINFO(impl_share, fstype)->shareopts); |
| } |
| |
| fstype = fstype->next; |
| } |
| |
| impl_share = impl_share->next; |
| } |
| |
| fflush(temp_fp); |
| fsync(temp_fd); |
| fclose(temp_fp); |
| |
| (void) rename(tempfile, ZFS_SHARETAB); |
| } |
| |
| typedef struct update_cookie_s { |
| sa_handle_impl_t handle; |
| const char *proto; |
| } update_cookie_t; |
| |
| static int |
| update_zfs_shares_cb(zfs_handle_t *zhp, void *pcookie) |
| { |
| update_cookie_t *udata = (update_cookie_t *)pcookie; |
| char mountpoint[ZFS_MAXPROPLEN]; |
| char shareopts[ZFS_MAXPROPLEN]; |
| char *dataset; |
| zfs_type_t type = zfs_get_type(zhp); |
| |
| if (type == ZFS_TYPE_FILESYSTEM && |
| zfs_iter_filesystems(zhp, update_zfs_shares_cb, pcookie) != 0) { |
| zfs_close(zhp); |
| return (1); |
| } |
| |
| if (type != ZFS_TYPE_FILESYSTEM) { |
| zfs_close(zhp); |
| return (0); |
| } |
| |
| if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, |
| sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { |
| zfs_close(zhp); |
| return (0); |
| } |
| |
| dataset = (char *)zfs_get_name(zhp); |
| |
| if (dataset == NULL) { |
| zfs_close(zhp); |
| return (0); |
| } |
| |
| if (!zfs_is_mounted(zhp, NULL)) { |
| zfs_close(zhp); |
| return (0); |
| } |
| |
| if ((udata->proto == NULL || strcmp(udata->proto, "nfs") == 0) && |
| zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, |
| sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0 && |
| strcmp(shareopts, "off") != 0) { |
| (void) process_share(udata->handle, NULL, mountpoint, NULL, |
| "nfs", shareopts, NULL, dataset, B_FALSE); |
| } |
| |
| if ((udata->proto == NULL || strcmp(udata->proto, "smb") == 0) && |
| zfs_prop_get(zhp, ZFS_PROP_SHARESMB, shareopts, |
| sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0 && |
| strcmp(shareopts, "off") != 0) { |
| (void) process_share(udata->handle, NULL, mountpoint, NULL, |
| "smb", shareopts, NULL, dataset, B_FALSE); |
| } |
| |
| zfs_close(zhp); |
| |
| return (0); |
| } |
| |
| static int |
| update_zfs_share(sa_share_impl_t impl_share, const char *proto) |
| { |
| sa_handle_impl_t impl_handle = impl_share->handle; |
| zfs_handle_t *zhp; |
| update_cookie_t udata; |
| |
| if (impl_handle->zfs_libhandle == NULL) |
| return (SA_SYSTEM_ERR); |
| |
| assert(impl_share->dataset != NULL); |
| |
| zhp = zfs_open(impl_share->handle->zfs_libhandle, impl_share->dataset, |
| ZFS_TYPE_FILESYSTEM); |
| |
| if (zhp == NULL) |
| return (SA_SYSTEM_ERR); |
| |
| udata.handle = impl_handle; |
| udata.proto = proto; |
| (void) update_zfs_shares_cb(zhp, &udata); |
| |
| return (SA_OK); |
| } |
| |
| static int |
| update_zfs_shares(sa_handle_impl_t impl_handle, const char *proto) |
| { |
| update_cookie_t udata; |
| |
| if (impl_handle->zfs_libhandle == NULL) |
| return (SA_SYSTEM_ERR); |
| |
| udata.handle = impl_handle; |
| udata.proto = proto; |
| (void) zfs_iter_root(impl_handle->zfs_libhandle, update_zfs_shares_cb, |
| &udata); |
| |
| return (SA_OK); |
| } |
| |
| static int |
| process_share(sa_handle_impl_t impl_handle, sa_share_impl_t impl_share, |
| char *pathname, char *resource, char *proto, |
| char *options, char *description, char *dataset, |
| boolean_t from_sharetab) |
| { |
| struct stat statbuf; |
| int rc; |
| char *resource_dup = NULL, *dataset_dup = NULL; |
| boolean_t new_share; |
| sa_fstype_t *fstype; |
| |
| new_share = B_FALSE; |
| |
| if (impl_share == NULL) |
| impl_share = find_share(impl_handle, pathname); |
| |
| if (impl_share == NULL) { |
| if (lstat(pathname, &statbuf) != 0 || |
| !S_ISDIR(statbuf.st_mode)) |
| return (SA_BAD_PATH); |
| |
| impl_share = alloc_share(pathname); |
| |
| if (impl_share == NULL) { |
| rc = SA_NO_MEMORY; |
| goto err; |
| } |
| |
| new_share = B_TRUE; |
| } |
| |
| if (dataset != NULL) { |
| dataset_dup = strdup(dataset); |
| |
| if (dataset_dup == NULL) { |
| rc = SA_NO_MEMORY; |
| goto err; |
| } |
| } |
| |
| free(impl_share->dataset); |
| impl_share->dataset = dataset_dup; |
| |
| rc = SA_INVALID_PROTOCOL; |
| |
| fstype = fstypes; |
| while (fstype != NULL) { |
| if (strcmp(fstype->name, proto) == 0) { |
| if (resource != NULL) { |
| resource_dup = strdup(resource); |
| |
| if (resource_dup == NULL) { |
| rc = SA_NO_MEMORY; |
| goto err; |
| } |
| } |
| |
| free(FSINFO(impl_share, fstype)->resource); |
| FSINFO(impl_share, fstype)->resource = resource_dup; |
| |
| rc = fstype->ops->update_shareopts(impl_share, |
| resource, options); |
| |
| if (rc == SA_OK && from_sharetab) |
| FSINFO(impl_share, fstype)->active = B_TRUE; |
| |
| break; |
| } |
| |
| fstype = fstype->next; |
| } |
| |
| if (rc != SA_OK) |
| goto err; |
| |
| if (new_share) { |
| impl_share->handle = impl_handle; |
| |
| impl_share->next = impl_handle->shares; |
| impl_handle->shares = impl_share; |
| |
| } |
| |
| err: |
| if (rc != SA_OK) { |
| if (new_share) |
| free_share(impl_share); |
| } |
| |
| return (rc); |
| } |
| |
| void |
| sa_fini(sa_handle_t handle) |
| { |
| sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; |
| sa_share_impl_t impl_share, next; |
| sa_share_impl_t *pcurr; |
| |
| if (impl_handle == NULL) |
| return; |
| |
| /* |
| * clean up shares which don't have a non-NULL dataset property, |
| * which means they're in sharetab but we couldn't find their |
| * ZFS dataset. |
| */ |
| pcurr = &(impl_handle->shares); |
| impl_share = *pcurr; |
| while (impl_share != NULL) { |
| next = impl_share->next; |
| |
| if (impl_share->dataset == NULL) { |
| /* remove item from the linked list */ |
| *pcurr = next; |
| |
| sa_disable_share(impl_share, NULL); |
| |
| free_share(impl_share); |
| } else { |
| pcurr = &(impl_share->next); |
| } |
| |
| impl_share = next; |
| } |
| |
| update_sharetab(impl_handle); |
| |
| if (impl_handle->zfs_libhandle != NULL) |
| libzfs_fini(impl_handle->zfs_libhandle); |
| |
| impl_share = impl_handle->shares; |
| while (impl_share != NULL) { |
| next = impl_share->next; |
| free_share(impl_share); |
| impl_share = next; |
| } |
| |
| free(impl_handle); |
| } |
| |
| static sa_share_impl_t |
| find_share(sa_handle_impl_t impl_handle, const char *sharepath) |
| { |
| sa_share_impl_t impl_share; |
| |
| impl_share = impl_handle->shares; |
| while (impl_share != NULL) { |
| if (strcmp(impl_share->sharepath, sharepath) == 0) { |
| break; |
| } |
| |
| impl_share = impl_share->next; |
| } |
| |
| return (impl_share); |
| } |
| |
| sa_share_t |
| sa_find_share(sa_handle_t handle, char *sharepath) |
| { |
| return ((sa_share_t)find_share((sa_handle_impl_t)handle, sharepath)); |
| } |
| |
| int |
| sa_enable_share(sa_share_t share, char *protocol) |
| { |
| sa_share_impl_t impl_share = (sa_share_impl_t)share; |
| int rc, ret = SA_OK; |
| boolean_t found_protocol = B_FALSE; |
| sa_fstype_t *fstype; |
| |
| fstype = fstypes; |
| while (fstype != NULL) { |
| if (protocol == NULL || strcmp(fstype->name, protocol) == 0) { |
| update_zfs_share(impl_share, fstype->name); |
| |
| rc = fstype->ops->enable_share(impl_share); |
| |
| if (rc != SA_OK) |
| ret = rc; |
| else |
| FSINFO(impl_share, fstype)->active = B_TRUE; |
| |
| found_protocol = B_TRUE; |
| } |
| |
| fstype = fstype->next; |
| } |
| |
| update_sharetab(impl_share->handle); |
| |
| return (found_protocol ? ret : SA_INVALID_PROTOCOL); |
| } |
| |
| int |
| sa_disable_share(sa_share_t share, char *protocol) |
| { |
| sa_share_impl_t impl_share = (sa_share_impl_t)share; |
| int rc, ret = SA_OK; |
| boolean_t found_protocol = B_FALSE; |
| sa_fstype_t *fstype; |
| |
| fstype = fstypes; |
| while (fstype != NULL) { |
| if (protocol == NULL || strcmp(fstype->name, protocol) == 0) { |
| rc = fstype->ops->disable_share(impl_share); |
| |
| if (rc == SA_OK) { |
| fstype->ops->clear_shareopts(impl_share); |
| |
| FSINFO(impl_share, fstype)->active = B_FALSE; |
| } else |
| ret = rc; |
| |
| found_protocol = B_TRUE; |
| } |
| |
| fstype = fstype->next; |
| } |
| |
| update_sharetab(impl_share->handle); |
| |
| return (found_protocol ? ret : SA_INVALID_PROTOCOL); |
| } |
| |
| /* |
| * sa_errorstr(err) |
| * |
| * convert an error value to an error string |
| */ |
| char * |
| sa_errorstr(int err) |
| { |
| static char errstr[32]; |
| char *ret = NULL; |
| |
| switch (err) { |
| case SA_OK: |
| ret = dgettext(TEXT_DOMAIN, "ok"); |
| break; |
| case SA_NO_SUCH_PATH: |
| ret = dgettext(TEXT_DOMAIN, "path doesn't exist"); |
| break; |
| case SA_NO_MEMORY: |
| ret = dgettext(TEXT_DOMAIN, "no memory"); |
| break; |
| case SA_DUPLICATE_NAME: |
| ret = dgettext(TEXT_DOMAIN, "name in use"); |
| break; |
| case SA_BAD_PATH: |
| ret = dgettext(TEXT_DOMAIN, "bad path"); |
| break; |
| case SA_NO_SUCH_GROUP: |
| ret = dgettext(TEXT_DOMAIN, "no such group"); |
| break; |
| case SA_CONFIG_ERR: |
| ret = dgettext(TEXT_DOMAIN, "configuration error"); |
| break; |
| case SA_SYSTEM_ERR: |
| ret = dgettext(TEXT_DOMAIN, "system error"); |
| break; |
| case SA_SYNTAX_ERR: |
| ret = dgettext(TEXT_DOMAIN, "syntax error"); |
| break; |
| case SA_NO_PERMISSION: |
| ret = dgettext(TEXT_DOMAIN, "no permission"); |
| break; |
| case SA_BUSY: |
| ret = dgettext(TEXT_DOMAIN, "busy"); |
| break; |
| case SA_NO_SUCH_PROP: |
| ret = dgettext(TEXT_DOMAIN, "no such property"); |
| break; |
| case SA_INVALID_NAME: |
| ret = dgettext(TEXT_DOMAIN, "invalid name"); |
| break; |
| case SA_INVALID_PROTOCOL: |
| ret = dgettext(TEXT_DOMAIN, "invalid protocol"); |
| break; |
| case SA_NOT_ALLOWED: |
| ret = dgettext(TEXT_DOMAIN, "operation not allowed"); |
| break; |
| case SA_BAD_VALUE: |
| ret = dgettext(TEXT_DOMAIN, "bad property value"); |
| break; |
| case SA_INVALID_SECURITY: |
| ret = dgettext(TEXT_DOMAIN, "invalid security type"); |
| break; |
| case SA_NO_SUCH_SECURITY: |
| ret = dgettext(TEXT_DOMAIN, "security type not found"); |
| break; |
| case SA_VALUE_CONFLICT: |
| ret = dgettext(TEXT_DOMAIN, "property value conflict"); |
| break; |
| case SA_NOT_IMPLEMENTED: |
| ret = dgettext(TEXT_DOMAIN, "not implemented"); |
| break; |
| case SA_INVALID_PATH: |
| ret = dgettext(TEXT_DOMAIN, "invalid path"); |
| break; |
| case SA_NOT_SUPPORTED: |
| ret = dgettext(TEXT_DOMAIN, "operation not supported"); |
| break; |
| case SA_PROP_SHARE_ONLY: |
| ret = dgettext(TEXT_DOMAIN, "property not valid for group"); |
| break; |
| case SA_NOT_SHARED: |
| ret = dgettext(TEXT_DOMAIN, "not shared"); |
| break; |
| case SA_NO_SUCH_RESOURCE: |
| ret = dgettext(TEXT_DOMAIN, "no such resource"); |
| break; |
| case SA_RESOURCE_REQUIRED: |
| ret = dgettext(TEXT_DOMAIN, "resource name required"); |
| break; |
| case SA_MULTIPLE_ERROR: |
| ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols"); |
| break; |
| case SA_PATH_IS_SUBDIR: |
| ret = dgettext(TEXT_DOMAIN, "path is a subpath of share"); |
| break; |
| case SA_PATH_IS_PARENTDIR: |
| ret = dgettext(TEXT_DOMAIN, "path is parent of a share"); |
| break; |
| case SA_NO_SECTION: |
| ret = dgettext(TEXT_DOMAIN, "protocol requires a section"); |
| break; |
| case SA_NO_PROPERTIES: |
| ret = dgettext(TEXT_DOMAIN, "properties not found"); |
| break; |
| case SA_NO_SUCH_SECTION: |
| ret = dgettext(TEXT_DOMAIN, "section not found"); |
| break; |
| case SA_PASSWORD_ENC: |
| ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted"); |
| break; |
| case SA_SHARE_EXISTS: |
| ret = dgettext(TEXT_DOMAIN, "path or file is already shared"); |
| break; |
| default: |
| (void) snprintf(errstr, sizeof (errstr), |
| dgettext(TEXT_DOMAIN, "unknown %d"), err); |
| ret = errstr; |
| } |
| return (ret); |
| } |
| |
| int |
| sa_parse_legacy_options(sa_group_t group, char *options, char *proto) |
| { |
| sa_fstype_t *fstype; |
| |
| 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); |
| } |
| |
| boolean_t |
| sa_needs_refresh(sa_handle_t handle) |
| { |
| return (B_TRUE); |
| } |
| |
| libzfs_handle_t * |
| sa_get_zfs_handle(sa_handle_t handle) |
| { |
| sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; |
| |
| if (impl_handle == NULL) |
| return (NULL); |
| |
| return (impl_handle->zfs_libhandle); |
| } |
| |
| static sa_share_impl_t |
| alloc_share(const char *sharepath) |
| { |
| sa_share_impl_t impl_share; |
| |
| impl_share = calloc(1, sizeof (struct sa_share_impl)); |
| |
| if (impl_share == NULL) |
| return (NULL); |
| |
| impl_share->sharepath = strdup(sharepath); |
| |
| if (impl_share->sharepath == NULL) { |
| free(impl_share); |
| return (NULL); |
| } |
| |
| impl_share->fsinfo = calloc(fstypes_count, sizeof (sa_share_fsinfo_t)); |
| |
| if (impl_share->fsinfo == NULL) { |
| free(impl_share->sharepath); |
| 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); |
| |
| free(FSINFO(impl_share, fstype)->resource); |
| |
| fstype = fstype->next; |
| } |
| |
| free(impl_share->sharepath); |
| free(impl_share->dataset); |
| free(impl_share->fsinfo); |
| free(impl_share); |
| } |
| |
| int |
| sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share, |
| char *mountpoint, char *proto, zprop_source_t source, char *shareopts, |
| char *sourcestr, char *dataset) |
| { |
| sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; |
| sa_share_impl_t impl_share = (sa_share_impl_t)share; |
| |
| return (process_share(impl_handle, impl_share, mountpoint, NULL, |
| proto, shareopts, NULL, dataset, B_FALSE)); |
| } |
| |
| void |
| sa_update_sharetab_ts(sa_handle_t handle) |
| { |
| sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; |
| |
| update_sharetab(impl_handle); |
| } |