diff options
Diffstat (limited to 'usr/src/lib/libshare/common/libshare.c')
-rw-r--r-- | usr/src/lib/libshare/common/libshare.c | 1194 |
1 files changed, 1034 insertions, 160 deletions
diff --git a/usr/src/lib/libshare/common/libshare.c b/usr/src/lib/libshare/common/libshare.c index 25213e97a4..49104b2abe 100644 --- a/usr/src/lib/libshare/common/libshare.c +++ b/usr/src/lib/libshare/common/libshare.c @@ -58,6 +58,16 @@ #define SA_STRSIZE 256 /* max string size for names */ /* + * internal object type values returned by sa_get_object_type() + */ +#define SA_TYPE_UNKNOWN 0 +#define SA_TYPE_GROUP 1 +#define SA_TYPE_SHARE 2 +#define SA_TYPE_RESOURCE 3 +#define SA_TYPE_OPTIONSET 4 +#define SA_TYPE_ALTSPACE 5 + +/* * internal data structures */ @@ -69,16 +79,20 @@ extern int gettransients(sa_handle_impl_t, xmlNodePtr *); extern int sa_valid_property(void *, char *, sa_property_t); extern char *sa_fstype(char *); extern int sa_is_share(void *); +extern int sa_is_resource(void *); extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ extern int sa_group_is_zfs(sa_group_t); extern int sa_path_is_zfs(char *); extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); +extern int sa_zfs_set_sharesmb(sa_group_t, char *, int); extern void update_legacy_config(sa_handle_t); extern int issubdir(char *, char *); extern int sa_zfs_init(sa_handle_impl_t); extern void sa_zfs_fini(sa_handle_impl_t); extern void sablocksigs(sigset_t *); extern void saunblocksigs(sigset_t *); +static sa_group_t sa_get_optionset_parent(sa_optionset_t); +static char *get_node_attr(void *, char *); /* * Data structures for finding/managing the document root to access @@ -188,6 +202,21 @@ sa_errorstr(int err) 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; default: (void) snprintf(errstr, sizeof (errstr), dgettext(TEXT_DOMAIN, "unknown %d"), err); @@ -376,6 +405,36 @@ is_shared(sa_share_t share) } /* + * excluded_protocol(share, proto) + * + * Returns B_TRUE if the specified protocol appears in the "exclude" + * property. This is used to prevent sharing special case shares + * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is + * returned if the protocol isn't in the list. + */ +static boolean_t +excluded_protocol(sa_share_t share, char *proto) +{ + char *protolist; + char *str; + char *token; + + protolist = sa_get_share_attr(share, "exclude"); + if (protolist != NULL) { + str = protolist; + while ((token = strtok(str, ",")) != NULL) { + if (strcmp(token, proto) == 0) { + sa_free_attr_string(protolist); + return (B_TRUE); + } + str = NULL; + } + sa_free_attr_string(protolist); + } + return (B_FALSE); +} + +/* * checksubdirgroup(group, newpath, strictness) * * check all the specified newpath against all the paths in the @@ -392,6 +451,11 @@ checksubdirgroup(sa_group_t group, char *newpath, int strictness) sa_share_t share; char *path; int issub = SA_OK; + int subdir; + int parent; + + if (newpath == NULL) + return (SA_INVALID_PATH); for (share = sa_get_share(group, NULL); share != NULL; share = sa_get_next_share(share)) { @@ -417,13 +481,18 @@ checksubdirgroup(sa_group_t group, char *newpath, int strictness) */ if (path == NULL) continue; - if (newpath != NULL && - (strcmp(path, newpath) == 0 || issubdir(newpath, path) || - issubdir(path, newpath))) { - sa_free_attr_string(path); - path = NULL; + + if (strcmp(path, newpath) == 0) { issub = SA_INVALID_PATH; - break; + } else { + subdir = issubdir(newpath, path); + parent = issubdir(path, newpath); + if (subdir || parent) { + sa_free_attr_string(path); + path = NULL; + return (subdir ? + SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR); + } } sa_free_attr_string(path); path = NULL; @@ -446,15 +515,16 @@ static int checksubdir(sa_handle_t handle, char *newpath, int strictness) { sa_group_t group; - int issub; + int issub = SA_OK; char *path = NULL; - for (issub = 0, group = sa_get_group(handle, NULL); - group != NULL && !issub; group = sa_get_next_group(group)) { + for (group = sa_get_group(handle, NULL); + group != NULL && issub == SA_OK; + group = sa_get_next_group(group)) { if (sa_group_is_zfs(group)) { sa_group_t subgroup; for (subgroup = sa_get_sub_group(group); - subgroup != NULL && !issub; + subgroup != NULL && issub == SA_OK; subgroup = sa_get_next_group(subgroup)) issub = checksubdirgroup(subgroup, newpath, strictness); @@ -517,14 +587,17 @@ validpath(sa_handle_t handle, char *path, int strictness) /* * check to see if group/share is persistent. + * + * "group" can be either an sa_group_t or an sa_share_t. (void *) + * works since both thse types are also void *. */ -static int -is_persistent(sa_group_t group) +int +sa_is_persistent(void *group) { char *type; int persist = 1; - type = sa_get_group_attr(group, "type"); + type = sa_get_group_attr((sa_group_t)group, "type"); if (type != NULL && strcmp(type, "transient") == 0) persist = 0; if (type != NULL) @@ -590,6 +663,35 @@ is_zfs_group(sa_group_t group) } /* + * sa_get_object_type(object) + * + * This function returns a numeric value representing the object + * type. This allows using simpler checks when doing type specific + * operations. + */ + +static int +sa_get_object_type(void *object) +{ + xmlNodePtr node = (xmlNodePtr)object; + int type; + + if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) + type = SA_TYPE_GROUP; + else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) + type = SA_TYPE_SHARE; + else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) + type = SA_TYPE_RESOURCE; + else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) + type = SA_TYPE_OPTIONSET; + else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) + type = SA_TYPE_ALTSPACE; + else + assert(0); + return (type); +} + +/* * sa_optionset_name(optionset, oname, len, id) * return the SMF name for the optionset. If id is not NULL, it * will have the GUID value for a share and should be used @@ -605,15 +707,34 @@ static int sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) { char *proto; + void *parent; + int ptype; if (id == NULL) id = "optionset"; - proto = sa_get_optionset_attr(optionset, "type"); - len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default"); + parent = sa_get_optionset_parent(optionset); + if (parent != NULL) { + ptype = sa_get_object_type(parent); + proto = sa_get_optionset_attr(optionset, "type"); + if (ptype != SA_TYPE_RESOURCE) { + len = snprintf(oname, len, "%s_%s", id, + proto ? proto : "default"); + } else { + char *index; + index = get_node_attr((void *)parent, "id"); + if (index != NULL) + len = snprintf(oname, len, "%s_%s_%s", id, + proto ? proto : "default", index); + else + len = 0; + } - if (proto != NULL) - sa_free_attr_string(proto); + if (proto != NULL) + sa_free_attr_string(proto); + } else { + len = 0; + } return (len); } @@ -660,6 +781,7 @@ verifydefgroupopts(sa_handle_t handle) { sa_group_t defgrp; sa_optionset_t opt; + defgrp = sa_get_group(handle, "default"); if (defgrp != NULL) { opt = sa_get_optionset(defgrp, NULL); @@ -1187,7 +1309,7 @@ sa_find_share(sa_handle_t handle, char *sharepath) /* * sa_check_path(group, path, strictness) * - * check that path is a valid path relative to the group. Currently, + * Check that path is a valid path relative to the group. Currently, * we are ignoring the group and checking only the NFS rules. Later, * we may want to use the group to then check against the protocols * enabled on the group. The strictness values mean: @@ -1206,16 +1328,84 @@ sa_check_path(sa_group_t group, char *path, int strictness) } /* - * _sa_add_share(group, sharepath, persist, *error) + * mark_excluded_protos(group, share, flags) + * + * Walk through all the protocols enabled for the group and check to + * see if the share has any of them should be in the exclude list + * based on the featureset of the protocol. If there are any, add the + * "exclude" property to the share. + */ +static void +mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags) +{ + sa_optionset_t optionset; + char exclude_list[SA_STRSIZE]; + char *sep = ""; + + exclude_list[0] = '\0'; + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *value; + uint64_t features; + value = sa_get_optionset_attr(optionset, "type"); + if (value == NULL) + continue; + features = sa_proto_get_featureset(value); + sa_free_attr_string(value); + if (!(features & flags)) { + (void) strlcat(exclude_list, sep, + sizeof (exclude_list)); + (void) strlcat(exclude_list, value, + sizeof (exclude_list)); + sep = ","; + } + } + if (exclude_list[0] != '\0') + xmlSetProp(share, (xmlChar *)"exclude", + (xmlChar *)exclude_list); +} + +/* + * get_all_features(group) * - * common code for all types of add_share. sa_add_share() is the + * Walk through all the protocols on the group and collect all + * possible enabled features. This is the OR of all the featuresets. + */ +static uint64_t +get_all_features(sa_group_t group) +{ + sa_optionset_t optionset; + uint64_t features = 0; + + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *value; + value = sa_get_optionset_attr(optionset, "type"); + if (value == NULL) + continue; + features |= sa_proto_get_featureset(value); + sa_free_attr_string(value); + } + return (features); +} + + +/* + * _sa_add_share(group, sharepath, persist, *error, flags) + * + * Common code for all types of add_share. sa_add_share() is the * public API, we also need to be able to do this when parsing legacy * files and construction of the internal configuration while - * extracting config info from SMF. + * extracting config info from SMF. "flags" indicates if some + * protocols need relaxed rules while other don't. These values are + * the featureset values defined in libshare.h. */ sa_share_t -_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) +_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error, + uint64_t flags) { xmlNodePtr node = NULL; int err; @@ -1223,52 +1413,60 @@ _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) err = SA_OK; /* assume success */ node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL); - if (node != NULL) { - xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); - xmlSetProp(node, (xmlChar *)"type", - persist ? (xmlChar *)"persist" : (xmlChar *)"transient"); - if (persist != SA_SHARE_TRANSIENT) { - /* - * persistent shares come in two flavors: SMF and - * ZFS. Sort this one out based on target group and - * path type. Currently, only NFS is supported in the - * ZFS group and it is always on. - */ - if (sa_group_is_zfs(group) && - sa_path_is_zfs(sharepath)) { + if (node == NULL) { + if (error != NULL) + *error = SA_NO_MEMORY; + return (node); + } + + xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); + xmlSetProp(node, (xmlChar *)"type", + persist ? (xmlChar *)"persist" : (xmlChar *)"transient"); + if (flags != 0) + mark_excluded_protos(group, node, flags); + if (persist != SA_SHARE_TRANSIENT) { + /* + * persistent shares come in two flavors: SMF and + * ZFS. Sort this one out based on target group and + * path type. Both NFS and SMB are supported. First, + * check to see if the protocol is enabled on the + * subgroup and then setup the share appropriately. + */ + if (sa_group_is_zfs(group) && + sa_path_is_zfs(sharepath)) { + if (sa_get_optionset(group, "nfs") != NULL) err = sa_zfs_set_sharenfs(group, sharepath, 1); + else if (sa_get_optionset(group, "smb") != NULL) + err = sa_zfs_set_sharesmb(group, sharepath, 1); + } else { + sa_handle_impl_t impl_handle; + impl_handle = + (sa_handle_impl_t)sa_find_group_handle(group); + if (impl_handle != NULL) { + err = sa_commit_share(impl_handle->scfhandle, + group, (sa_share_t)node); } else { - sa_handle_impl_t impl_handle; - impl_handle = - (sa_handle_impl_t)sa_find_group_handle( - group); - if (impl_handle != NULL) { - err = sa_commit_share( - impl_handle->scfhandle, group, - (sa_share_t)node); - } else { - err = SA_SYSTEM_ERR; - } + err = SA_SYSTEM_ERR; } } - if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { - /* called by the dfstab parser so could be a show */ - err = SA_OK; - } - if (err != SA_OK) { - /* - * we couldn't commit to the repository so undo - * our internal state to reflect reality. - */ - xmlUnlinkNode(node); - xmlFreeNode(node); - node = NULL; - } - } else { - err = SA_NO_MEMORY; } + if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) + /* called by the dfstab parser so could be a show */ + err = SA_OK; + + if (err != SA_OK) { + /* + * we couldn't commit to the repository so undo + * our internal state to reflect reality. + */ + xmlUnlinkNode(node); + xmlFreeNode(node); + node = NULL; + } + if (error != NULL) *error = err; + return (node); } @@ -1285,9 +1483,10 @@ sa_share_t sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) { xmlNodePtr node = NULL; - sa_share_t dup; int strictness = SA_CHECK_NORMAL; sa_handle_t handle; + uint64_t special = 0; + uint64_t features; /* * If the share is to be permanent, use strict checking so a @@ -1304,12 +1503,33 @@ sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) handle = sa_find_group_handle(group); - if ((dup = sa_find_share(handle, sharepath)) == NULL && - (*error = sa_check_path(group, sharepath, strictness)) == SA_OK) { - node = _sa_add_share(group, sharepath, persist, error); - } - if (dup != NULL) + /* + * need to determine if the share is valid. The rules are: + * - The path must not already exist + * - The path must not be a subdir or parent dir of an + * existing path unless at least one protocol allows it. + * The sub/parent check is done in sa_check_path(). + */ + + if (sa_find_share(handle, sharepath) == NULL) { + *error = sa_check_path(group, sharepath, strictness); + features = get_all_features(group); + switch (*error) { + case SA_PATH_IS_SUBDIR: + if (features & SA_FEATURE_ALLOWSUBDIRS) + special |= SA_FEATURE_ALLOWSUBDIRS; + break; + case SA_PATH_IS_PARENTDIR: + if (features & SA_FEATURE_ALLOWPARDIRS) + special |= SA_FEATURE_ALLOWPARDIRS; + break; + } + if (*error == SA_OK || special != SA_FEATURE_NONE) + node = _sa_add_share(group, sharepath, persist, + error, special); + } else { *error = SA_DUPLICATE_NAME; + } return ((sa_share_t)node); } @@ -1324,28 +1544,52 @@ sa_enable_share(sa_share_t share, char *protocol) { char *sharepath; struct stat st; - int err = 0; + int err = SA_OK; + int ret; sharepath = sa_get_share_attr(share, "path"); + if (sharepath == NULL) + return (SA_NO_MEMORY); if (stat(sharepath, &st) < 0) { err = SA_NO_SUCH_PATH; } else { /* tell the server about the share */ if (protocol != NULL) { + if (excluded_protocol(share, protocol)) + goto done; + /* lookup protocol specific handler */ err = sa_proto_share(protocol, share); if (err == SA_OK) - (void) sa_set_share_attr(share, "shared", - "true"); + (void) sa_set_share_attr(share, + "shared", "true"); } else { - /* - * Tell all protocols. Only NFS for now but - * SMB is coming. - */ - err = sa_proto_share("nfs", share); + /* Tell all protocols about the share */ + sa_group_t group; + sa_optionset_t optionset; + + group = sa_get_parent_group(share); + + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *proto; + proto = sa_get_optionset_attr(optionset, + "type"); + if (proto != NULL) { + if (!excluded_protocol(share, proto)) { + ret = sa_proto_share(proto, + share); + if (ret != SA_OK) + err = ret; + } + sa_free_attr_string(proto); + } + } (void) sa_set_share_attr(share, "shared", "true"); } } +done: if (sharepath != NULL) sa_free_attr_string(sharepath); return (err); @@ -1353,31 +1597,47 @@ sa_enable_share(sa_share_t share, char *protocol) /* * sa_disable_share(share, protocol) - * Disable the specified share to the specified protocol. - * If protocol is NULL, then all protocols. + * Disable the specified share to the specified protocol. If + * protocol is NULL, then all protocols that are enabled for the + * share should be disabled. */ int sa_disable_share(sa_share_t share, char *protocol) { char *path; - char *shared; + int err = SA_OK; int ret = SA_OK; path = sa_get_share_attr(share, "path"); - shared = sa_get_share_attr(share, "shared"); if (protocol != NULL) { ret = sa_proto_unshare(share, protocol, path); } else { /* need to do all protocols */ - ret = sa_proto_unshare(share, "nfs", path); + sa_group_t group; + sa_optionset_t optionset; + + group = sa_get_parent_group(share); + + /* Tell all protocols about the share */ + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *proto; + + proto = sa_get_optionset_attr(optionset, "type"); + if (proto != NULL) { + err = sa_proto_unshare(share, proto, path); + if (err != SA_OK) + ret = err; + sa_free_attr_string(proto); + } + } } if (ret == SA_OK) (void) sa_set_share_attr(share, "shared", NULL); if (path != NULL) sa_free_attr_string(path); - if (shared != NULL) - sa_free_attr_string(shared); return (ret); } @@ -1415,7 +1675,7 @@ sa_remove_share(sa_share_t share) /* only do SMF action if permanent */ if (!transient || zfs != NULL) { /* remove from legacy dfstab as well as possible SMF */ - ret = sa_delete_legacy(share); + ret = sa_delete_legacy(share, NULL); if (ret == SA_OK) { if (!sa_group_is_zfs(group)) { sa_handle_impl_t impl_handle; @@ -1497,7 +1757,7 @@ sa_move_share(sa_group_t group, sa_share_t share) /* * sa_get_parent_group(share) * - * Return the containg group for the share. If a group was actually + * Return the containing group for the share. If a group was actually * passed in, we don't want a parent so return NULL. */ @@ -1728,7 +1988,7 @@ sa_update_config(sa_handle_t handle) /* * get_node_attr(node, tag) * - * Get the speficied tag(attribute) if it exists on the node. This is + * Get the specified tag(attribute) if it exists on the node. This is * used internally by a number of attribute oriented functions. */ @@ -1746,7 +2006,7 @@ get_node_attr(void *nodehdl, char *tag) /* * get_node_attr(node, tag) * - * Set the speficied tag(attribute) to the specified value This is + * Set the specified tag(attribute) to the specified value This is * used internally by a number of attribute oriented functions. It * doesn't update the repository, only the internal document state. */ @@ -1792,6 +2052,14 @@ sa_set_group_attr(sa_group_t group, char *tag, char *value) char *groupname; sa_handle_impl_t impl_handle; + /* + * ZFS group/subgroup doesn't need the handle so shortcut. + */ + if (sa_group_is_zfs(group)) { + set_node_attr((void *)group, tag, value); + return (SA_OK); + } + impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); if (impl_handle != NULL) { groupname = sa_get_group_attr(group, "name"); @@ -1833,47 +2101,15 @@ sa_get_share_attr(sa_share_t share, char *tag) } /* - * sa_get_resource(group, resource) - * - * Search all the shares in the speified group for a share with a - * resource name matching the one specified. - * - * In the future, it may be advantageous to allow group to be NULL and - * search all groups but that isn't needed at present. - */ - -sa_share_t -sa_get_resource(sa_group_t group, char *resource) -{ - sa_share_t share = NULL; - char *name = NULL; - - if (resource != NULL) { - for (share = sa_get_share(group, NULL); share != NULL; - share = sa_get_next_share(share)) { - name = sa_get_share_attr(share, "resource"); - if (name != NULL) { - if (strcmp(name, resource) == 0) - break; - sa_free_attr_string(name); - name = NULL; - } - } - if (name != NULL) - sa_free_attr_string(name); - } - return ((sa_share_t)share); -} - -/* * _sa_set_share_description(share, description) * - * Add a description tag with text contents to the specified share. - * A separate XML tag is used rather than a property. + * Add a description tag with text contents to the specified share. A + * separate XML tag is used rather than a property. This can also be + * used with resources. */ xmlNodePtr -_sa_set_share_description(sa_share_t share, char *content) +_sa_set_share_description(void *share, char *content) { xmlNodePtr node; node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description", @@ -2205,7 +2441,6 @@ sa_set_share_description(sa_share_t share, char *content) break; } } - group = sa_get_parent_group(share); /* no existing description but want to add */ if (node == NULL && content != NULL) { /* add a description */ @@ -2218,7 +2453,8 @@ sa_set_share_description(sa_share_t share, char *content) xmlUnlinkNode(node); xmlFreeNode(node); } - if (group != NULL && is_persistent((sa_group_t)share)) { + group = sa_get_parent_group(share); + if (group != NULL && sa_is_persistent(share)) { sa_handle_impl_t impl_handle; impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); if (impl_handle != NULL) { @@ -2269,7 +2505,7 @@ sa_get_share_description(sa_share_t share) } } if (node != NULL) { - description = xmlNodeGetContent((xmlNodePtr)share); + description = xmlNodeGetContent(node); fixproblemchars((char *)description); } return ((char *)description); @@ -2299,12 +2535,44 @@ sa_create_optionset(sa_group_t group, char *proto) { sa_optionset_t optionset; sa_group_t parent = group; + sa_share_t share = NULL; + int err = SA_OK; + char *id = NULL; optionset = sa_get_optionset(group, proto); if (optionset != NULL) { /* can't have a duplicate protocol */ optionset = NULL; } else { + /* + * Account for resource names being slightly + * different. + */ + if (sa_is_share(group)) { + /* + * Transient shares do not have an "id" so not an + * error to not find one. + */ + id = sa_get_share_attr((sa_share_t)group, "id"); + } else if (sa_is_resource(group)) { + share = sa_get_resource_parent( + (sa_resource_t)group); + id = sa_get_resource_attr(share, "id"); + + /* id can be NULL if the group is transient (ZFS) */ + if (id == NULL && sa_is_persistent(group)) + err = SA_NO_MEMORY; + } + if (err == SA_NO_MEMORY) { + /* + * Couldn't get the id for the share or + * resource. While this could be a + * configuration issue, it is most likely an + * out of memory. In any case, fail the create. + */ + return (NULL); + } + optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"optionset", NULL); /* @@ -2314,38 +2582,44 @@ sa_create_optionset(sa_group_t group, char *proto) if (optionset != NULL) { char oname[SA_STRSIZE]; char *groupname; - char *id = NULL; - if (sa_is_share(group)) + /* + * Need to get parent group in all cases, but also get + * the share if this is a resource. + */ + if (sa_is_share(group)) { parent = sa_get_parent_group((sa_share_t)group); + } else if (sa_is_resource(group)) { + share = sa_get_resource_parent( + (sa_resource_t)group); + parent = sa_get_parent_group(share); + } sa_set_optionset_attr(optionset, "type", proto); - if (sa_is_share(group)) { - id = sa_get_share_attr((sa_share_t)group, "id"); - } (void) sa_optionset_name(optionset, oname, sizeof (oname), id); groupname = sa_get_group_attr(parent, "name"); - if (groupname != NULL && is_persistent(group)) { + if (groupname != NULL && sa_is_persistent(group)) { sa_handle_impl_t impl_handle; - impl_handle = (sa_handle_impl_t) - sa_find_group_handle(group); + impl_handle = + (sa_handle_impl_t)sa_find_group_handle( + group); assert(impl_handle != NULL); if (impl_handle != NULL) { (void) sa_get_instance( - impl_handle->scfhandle, - groupname); + impl_handle->scfhandle, groupname); (void) sa_create_pgroup( impl_handle->scfhandle, oname); } } if (groupname != NULL) sa_free_attr_string(groupname); - if (id != NULL) - sa_free_attr_string(id); } } + + if (id != NULL) + sa_free_attr_string(id); return (optionset); } @@ -2388,7 +2662,7 @@ sa_get_optionset_parent(sa_optionset_t optionset) * * In order to avoid making multiple updates to a ZFS share when * setting properties, the share attribute "changed" will be set to - * true when a property is added or modifed. When done adding + * true when a property is added or modified. When done adding * properties, we can then detect that an update is needed. We then * clear the state here to detect additional changes. */ @@ -2470,7 +2744,7 @@ sa_commit_properties(sa_optionset_t optionset, int clear) /* * sa_destroy_optionset(optionset) * - * Remove the optionset from its group. Update the repostory to + * Remove the optionset from its group. Update the repository to * reflect this change. */ @@ -2486,9 +2760,16 @@ sa_destroy_optionset(sa_optionset_t optionset) /* now delete the prop group */ group = sa_get_optionset_parent(optionset); - if (group != NULL && sa_is_share(group)) { - ispersist = is_persistent(group); - id = sa_get_share_attr((sa_share_t)group, "id"); + if (group != NULL) { + if (sa_is_resource(group)) { + sa_resource_t resource = group; + sa_share_t share = sa_get_resource_parent(resource); + group = sa_get_parent_group(share); + id = sa_get_share_attr(share, "id"); + } else if (sa_is_share(group)) { + id = sa_get_share_attr((sa_share_t)group, "id"); + } + ispersist = sa_is_persistent(group); } if (ispersist) { sa_handle_impl_t impl_handle; @@ -2559,7 +2840,7 @@ sa_create_security(sa_group_t group, char *sectype, char *proto) sa_set_security_attr(security, "sectype", sectype); (void) sa_security_name(security, oname, sizeof (oname), id); - if (groupname != NULL && is_persistent(group)) { + if (groupname != NULL && sa_is_persistent(group)) { sa_handle_impl_t impl_handle; impl_handle = (sa_handle_impl_t)sa_find_group_handle( @@ -2603,7 +2884,7 @@ sa_destroy_security(sa_security_t security) if (group != NULL && !iszfs) { if (sa_is_share(group)) - ispersist = is_persistent(group); + ispersist = sa_is_persistent(group); id = sa_get_share_attr((sa_share_t)group, "id"); } if (ispersist) { @@ -2666,7 +2947,6 @@ is_nodetype(void *node, char *type) return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); } - /* * add_or_update() * @@ -2721,12 +3001,12 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, int opttype; /* 1 == optionset, 0 == security */ char *id = NULL; int iszfs = 0; - int isshare = 0; sa_group_t parent = NULL; + sa_share_t share = NULL; sa_handle_impl_t impl_handle; scfutilhandle_t *scf_handle; - if (!is_persistent(group)) { + if (!sa_is_persistent(group)) { /* * if the group/share is not persistent we don't need * to do anything here @@ -2742,12 +3022,20 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, entry = scf_entry_create(scf_handle->handle); opttype = is_nodetype((void *)optionset, "optionset"); + /* + * Check for share vs. resource since they need slightly + * different treatment given the hierarchy. + */ if (valstr != NULL && entry != NULL) { if (sa_is_share(group)) { - isshare = 1; parent = sa_get_parent_group(group); + share = (sa_share_t)group; if (parent != NULL) iszfs = is_zfs_group(parent); + } else if (sa_is_resource(group)) { + share = sa_get_parent_group(group); + if (share != NULL) + parent = sa_get_parent_group(share); } else { iszfs = is_zfs_group(group); } @@ -2755,15 +3043,13 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, if (scf_handle->trans == NULL) { char oname[SA_STRSIZE]; char *groupname = NULL; - if (isshare) { - if (parent != NULL) { + if (share != NULL) { + if (parent != NULL) groupname = sa_get_group_attr(parent, "name"); - } - id = - sa_get_share_attr((sa_share_t)group, - "id"); + id = sa_get_share_attr( + (sa_share_t)share, "id"); } else { groupname = sa_get_group_attr(group, "name"); @@ -2870,14 +3156,22 @@ sa_add_property(void *object, sa_property_t property) sa_free_attr_string(proto); parent = sa_get_parent_group(object); - if (!is_persistent(parent)) { + if (!sa_is_persistent(parent)) return (ret); - } - if (sa_is_share(parent)) + if (sa_is_resource(parent)) { + /* + * Resources are children of share. Need to go up two + * levels to find the group but the parent needs to be + * the share at this point in order to get the "id". + */ + parent = sa_get_parent_group(parent); group = sa_get_parent_group(parent); - else + } else if (sa_is_share(parent)) { + group = sa_get_parent_group(parent); + } else { group = parent; + } if (property == NULL) { ret = SA_NO_MEMORY; @@ -3110,7 +3404,7 @@ sa_set_protocol_property(sa_property_t prop, char *value) /* * sa_add_protocol_property(propset, prop) * - * Add a new property to the protocol sepcific property set. + * Add a new property to the protocol specific property set. */ int @@ -3128,7 +3422,7 @@ sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) /* * sa_create_protocol_properties(proto) * - * Create a protocol specifity property set. + * Create a protocol specific property set. */ sa_protocol_properties_t @@ -3141,3 +3435,583 @@ sa_create_protocol_properties(char *proto) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); return (node); } + +/* + * sa_get_share_resource(share, resource) + * + * Get the named resource from the share, if it exists. If resource is + * NULL, get the first resource. + */ + +sa_resource_t +sa_get_share_resource(sa_share_t share, char *resource) +{ + xmlNodePtr node = NULL; + xmlChar *name; + + if (share != NULL) { + for (node = ((xmlNodePtr)share)->children; node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) { + if (resource == NULL) { + /* + * We are looking for the first + * resource node and not a names + * resource. + */ + break; + } else { + /* is it the correct share? */ + name = xmlGetProp(node, + (xmlChar *)"name"); + if (name != NULL && + xmlStrcasecmp(name, + (xmlChar *)resource) == 0) { + xmlFree(name); + break; + } + xmlFree(name); + } + } + } + } + return ((sa_resource_t)node); +} + +/* + * sa_get_next_resource(resource) + * Return the next share following the specified share + * from the internal list of shares. Returns NULL if there + * are no more shares. The list is relative to the same + * group. + */ +sa_share_t +sa_get_next_resource(sa_resource_t resource) +{ + xmlNodePtr node = NULL; + + if (resource != NULL) { + for (node = ((xmlNodePtr)resource)->next; node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) + break; + } + } + return ((sa_share_t)node); +} + +/* + * _sa_get_next_resource_index(share) + * + * get the next resource index number (one greater then current largest) + */ + +static int +_sa_get_next_resource_index(sa_share_t share) +{ + sa_resource_t resource; + int index = 0; + char *id; + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + id = get_node_attr((void *)resource, "id"); + if (id != NULL) { + int val; + val = atoi(id); + if (val > index) + index = val; + sa_free_attr_string(id); + } + } + return (index + 1); +} + + +/* + * sa_add_resource(share, resource, persist, &err) + * + * Adds a new resource name associated with share. The resource name + * must be unique in the system and will be case insensitive (eventually). + */ + +sa_resource_t +sa_add_resource(sa_share_t share, char *resource, int persist, int *error) +{ + xmlNodePtr node; + int err = SA_OK; + sa_resource_t res; + sa_group_t group; + sa_handle_t handle; + char istring[8]; /* just big enough for an integer value */ + int index; + + group = sa_get_parent_group(share); + handle = sa_find_group_handle(group); + res = sa_find_resource(handle, resource); + if (res != NULL) { + err = SA_DUPLICATE_NAME; + res = NULL; + } else { + node = xmlNewChild((xmlNodePtr)share, NULL, + (xmlChar *)"resource", NULL); + if (node != NULL) { + xmlSetProp(node, (xmlChar *)"name", + (xmlChar *)resource); + xmlSetProp(node, (xmlChar *)"type", persist ? + (xmlChar *)"persist" : (xmlChar *)"transient"); + if (persist != SA_SHARE_TRANSIENT) { + index = _sa_get_next_resource_index(share); + (void) snprintf(istring, sizeof (istring), "%d", + index); + xmlSetProp(node, (xmlChar *)"id", + (xmlChar *)istring); + if (!sa_group_is_zfs(group) && + sa_is_persistent((sa_group_t)share)) { + /* ZFS doesn't use resource names */ + sa_handle_impl_t ihandle; + ihandle = (sa_handle_impl_t) + sa_find_group_handle( + group); + if (ihandle != NULL) + err = sa_commit_share( + ihandle->scfhandle, group, + share); + else + err = SA_SYSTEM_ERR; + } + } + } + } + if (error != NULL) + *error = err; + return ((sa_resource_t)node); +} + +/* + * sa_remove_resource(resource) + * + * Remove the resource name from the share (and the system) + */ + +int +sa_remove_resource(sa_resource_t resource) +{ + sa_share_t share; + sa_group_t group; + char *type; + int ret = SA_OK; + int transient = 0; + + share = sa_get_resource_parent(resource); + type = sa_get_share_attr(share, "type"); + group = sa_get_parent_group(share); + + + if (type != NULL) { + if (strcmp(type, "persist") != 0) + transient = 1; + sa_free_attr_string(type); + } + + /* Remove from the share */ + xmlUnlinkNode((xmlNode *)resource); + xmlFreeNode((xmlNode *)resource); + + /* only do SMF action if permanent and not ZFS */ + if (!transient && !sa_group_is_zfs(group)) { + sa_handle_impl_t ihandle; + ihandle = (sa_handle_impl_t)sa_find_group_handle(group); + if (ihandle != NULL) + ret = sa_commit_share(ihandle->scfhandle, group, share); + else + ret = SA_SYSTEM_ERR; + } + return (ret); +} + +/* + * proto_resource_rename(handle, group, resource, newname) + * + * Helper function for sa_rename_resource that notifies the protocol + * of a resource name change prior to a config repository update. + */ +static int +proto_rename_resource(sa_handle_t handle, sa_group_t group, + sa_resource_t resource, char *newname) +{ + sa_optionset_t optionset; + int ret = SA_OK; + int err; + + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *type; + type = sa_get_optionset_attr(optionset, "type"); + if (type != NULL) { + err = sa_proto_rename_resource(handle, type, resource, + newname); + if (err != SA_OK) + ret = err; + sa_free_attr_string(type); + } + } + return (ret); +} + +/* + * sa_rename_resource(resource, newname) + * + * Rename the resource to the new name, if it is unique. + */ + +int +sa_rename_resource(sa_resource_t resource, char *newname) +{ + sa_share_t share; + sa_group_t group = NULL; + sa_resource_t target; + int ret = SA_CONFIG_ERR; + sa_handle_t handle = NULL; + + share = sa_get_resource_parent(resource); + if (share == NULL) + return (ret); + + group = sa_get_parent_group(share); + if (group == NULL) + return (ret); + + handle = (sa_handle_impl_t)sa_find_group_handle(group); + if (handle == NULL) + return (ret); + + target = sa_find_resource(handle, newname); + if (target != NULL) { + ret = SA_DUPLICATE_NAME; + } else { + /* + * Everything appears to be valid at this + * point. Change the name of the active share and then + * update the share in the appropriate repository. + */ + ret = proto_rename_resource(handle, group, resource, newname); + set_node_attr(resource, "name", newname); + if (!sa_group_is_zfs(group) && + sa_is_persistent((sa_group_t)share)) { + sa_handle_impl_t ihandle = (sa_handle_impl_t)handle; + ret = sa_commit_share(ihandle->scfhandle, group, + share); + } + } + return (ret); +} + +/* + * sa_get_resource_attr(resource, tag) + * + * Get the named attribute of the resource. "name" and "id" are + * currently defined. NULL if tag not defined. + */ + +char * +sa_get_resource_attr(sa_resource_t resource, char *tag) +{ + return (get_node_attr((void *)resource, tag)); +} + +/* + * sa_set_resource_attr(resource, tag, value) + * + * Get the named attribute of the resource. "name" and "id" are + * currently defined. NULL if tag not defined. Currently we don't do + * much, but additional checking may be needed in the future. + */ + +int +sa_set_resource_attr(sa_resource_t resource, char *tag, char *value) +{ + set_node_attr((void *)resource, tag, value); + return (SA_OK); +} + +/* + * sa_get_resource_parent(resource_t) + * + * Returns the share associated with the resource. + */ + +sa_share_t +sa_get_resource_parent(sa_resource_t resource) +{ + sa_share_t share = NULL; + + if (resource != NULL) + share = (sa_share_t)((xmlNodePtr)resource)->parent; + return (share); +} + +/* + * find_resource(group, name) + * + * Find the resource within the group. + */ + +static sa_resource_t +find_resource(sa_group_t group, char *resname) +{ + sa_share_t share; + sa_resource_t resource = NULL; + char *name; + + /* Iterate over all the shares and resources in the group. */ + for (share = sa_get_share(group, NULL); + share != NULL && resource == NULL; + share = sa_get_next_share(share)) { + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + name = sa_get_resource_attr(resource, "name"); + if (name != NULL && xmlStrcasecmp((xmlChar*)name, + (xmlChar*)resname) == 0) { + sa_free_attr_string(name); + break; + } + if (name != NULL) { + sa_free_attr_string(name); + } + } + } + return (resource); +} + +/* + * sa_find_resource(name) + * + * Find the named resource in the system. + */ + +sa_resource_t +sa_find_resource(sa_handle_t handle, char *name) +{ + sa_group_t group; + sa_group_t zgroup; + sa_resource_t resource = NULL; + + /* + * Iterate over all groups and zfs subgroups and check for + * resource name in them. + */ + for (group = sa_get_group(handle, NULL); group != NULL; + group = sa_get_next_group(group)) { + + if (is_zfs_group(group)) { + for (zgroup = + (sa_group_t)_sa_get_child_node((xmlNodePtr)group, + (xmlChar *)"group"); + zgroup != NULL && resource == NULL; + zgroup = sa_get_next_group(zgroup)) { + resource = find_resource(zgroup, name); + } + } else { + resource = find_resource(group, name); + } + if (resource != NULL) + break; + } + return (resource); +} + +/* + * sa_get_resource(group, resource) + * + * Search all the shares in the specified group for a share with a + * resource name matching the one specified. + * + * In the future, it may be advantageous to allow group to be NULL and + * search all groups but that isn't needed at present. + */ + +sa_resource_t +sa_get_resource(sa_group_t group, char *resource) +{ + sa_share_t share = NULL; + sa_resource_t res = NULL; + + if (resource != NULL) { + for (share = sa_get_share(group, NULL); + share != NULL && res == NULL; + share = sa_get_next_share(share)) { + res = sa_get_share_resource(share, resource); + } + } + return (res); +} + +/* + * sa_enable_resource, protocol) + * Disable the specified share to the specified protocol. + * If protocol is NULL, then all protocols. + */ +int +sa_enable_resource(sa_resource_t resource, char *protocol) +{ + int ret = SA_OK; + char **protocols; + int numproto; + + if (protocol != NULL) { + ret = sa_proto_share_resource(protocol, resource); + } else { + /* need to do all protocols */ + if ((numproto = sa_get_protocols(&protocols)) >= 0) { + int i, err; + for (i = 0; i < numproto; i++) { + err = sa_proto_share_resource( + protocols[i], resource); + if (err != SA_OK) + ret = err; + } + free(protocols); + } + } + if (ret == SA_OK) + (void) sa_set_resource_attr(resource, "shared", NULL); + + return (ret); +} + +/* + * sa_disable_resource(resource, protocol) + * + * Disable the specified share for the specified protocol. If + * protocol is NULL, then all protocols. If the underlying + * protocol doesn't implement disable at the resource level, we + * disable at the share level. + */ +int +sa_disable_resource(sa_resource_t resource, char *protocol) +{ + int ret = SA_OK; + char **protocols; + int numproto; + + if (protocol != NULL) { + ret = sa_proto_unshare_resource(protocol, resource); + if (ret == SA_NOT_IMPLEMENTED) { + sa_share_t parent; + /* + * The protocol doesn't implement unshare + * resource. That implies that resource names are + * simple aliases for this protocol so we need to + * unshare the share. + */ + parent = sa_get_resource_parent(resource); + if (parent != NULL) + ret = sa_disable_share(parent, protocol); + else + ret = SA_CONFIG_ERR; + } + } else { + /* need to do all protocols */ + if ((numproto = sa_get_protocols(&protocols)) >= 0) { + int i, err; + for (i = 0; i < numproto; i++) { + err = sa_proto_unshare_resource(protocols[i], + resource); + if (err == SA_NOT_SUPPORTED) { + sa_share_t parent; + parent = sa_get_resource_parent( + resource); + if (parent != NULL) + err = sa_disable_share(parent, + protocols[i]); + else + err = SA_CONFIG_ERR; + } + if (err != SA_OK) + ret = err; + } + free(protocols); + } + } + if (ret == SA_OK) + (void) sa_set_resource_attr(resource, "shared", NULL); + + return (ret); +} + +/* + * sa_set_resource_description(resource, content) + * + * Set the description of share to content. + */ + +int +sa_set_resource_description(sa_resource_t resource, char *content) +{ + xmlNodePtr node; + sa_group_t group; + sa_share_t share; + int ret = SA_OK; + + for (node = ((xmlNodePtr)resource)->children; + node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { + break; + } + } + + /* no existing description but want to add */ + if (node == NULL && content != NULL) { + /* add a description */ + node = _sa_set_share_description(resource, content); + } else if (node != NULL && content != NULL) { + /* update a description */ + xmlNodeSetContent(node, (xmlChar *)content); + } else if (node != NULL && content == NULL) { + /* remove an existing description */ + xmlUnlinkNode(node); + xmlFreeNode(node); + } + share = sa_get_resource_parent(resource); + group = sa_get_parent_group(share); + if (group != NULL && sa_is_persistent(share)) { + sa_handle_impl_t impl_handle; + impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); + if (impl_handle != NULL) + ret = sa_commit_share(impl_handle->scfhandle, + group, share); + else + ret = SA_SYSTEM_ERR; + } + return (ret); +} + +/* + * sa_get_resource_description(share) + * + * Return the description text for the specified share if it + * exists. NULL if no description exists. + */ + +char * +sa_get_resource_description(sa_resource_t resource) +{ + xmlChar *description = NULL; + xmlNodePtr node; + + for (node = ((xmlNodePtr)resource)->children; node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) + break; + } + if (node != NULL) { + description = xmlNodeGetContent(node); + fixproblemchars((char *)description); + } + return ((char *)description); +} |