4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * core library for common functions across all config store types
28 * and file systems to be exported. This includes legacy dfstab/sharetab
29 * parsing. Need to eliminate XML where possible.
30 */
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <unistd.h>
36 #include <limits.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <libxml/parser.h>
41 #include <libxml/tree.h>
42 #include "libshare.h"
43 #include "libshare_impl.h"
73 /* used internally only */
74 typedef
75 struct sharelist {
76 struct sharelist *next;
77 int persist;
78 char *path;
79 char *resource;
80 char *fstype;
81 char *options;
82 char *description;
83 char *group;
84 char *origline;
85 int lineno;
86 } xfs_sharelist_t;
87 static void parse_dfstab(sa_handle_t, char *, xmlNodePtr);
88 extern char *_sa_get_token(char *);
89 static void dfs_free_list(xfs_sharelist_t *);
90 /* prototypes */
91 void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
92 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
93 extern sa_group_t _sa_create_group(sa_handle_impl_t, char *);
94 static void outdfstab(FILE *, xfs_sharelist_t *);
95 extern int _sa_remove_optionset(sa_optionset_t);
96 extern int set_node_share(void *, char *, char *);
97 extern void set_node_attr(void *, char *, char *);
98
99 /*
100 * sablocksigs(*sigs)
101 *
102 * block important signals for a critical region. Arg is a pointer to
103 * a sigset_t that is used later for the unblock.
104 */
105 void
106 sablocksigs(sigset_t *sigs)
107 {
108 sigset_t new;
109
110 if (sigs != NULL) {
111 (void) sigprocmask(SIG_BLOCK, NULL, &new);
112 (void) sigaddset(&new, SIGHUP);
113 (void) sigaddset(&new, SIGINT);
724 if (errno == EACCES || errno == EPERM)
725 ret = SA_NO_PERMISSION;
726 else
727 ret = SA_CONFIG_ERR;
728 }
729 sa_free_attr_string(path);
730 }
731 out:
732 if (persist != NULL)
733 sa_free_attr_string(persist);
734 return (ret);
735 }
736
737 /*
738 * sa_is_security(optname, proto)
739 *
740 * Check to see if optname is a security (named optionset) specific
741 * property for the specified protocol.
742 */
743
744 int
745 sa_is_security(char *optname, char *proto)
746 {
747 int ret = 0;
748 if (proto != NULL)
749 ret = sa_proto_security_prop(proto, optname);
750 return (ret);
751 }
752
753 /*
754 * add_syntax_comment(root, line, err, todfstab)
755 *
756 * Add a comment to the document indicating a syntax error. If
757 * todfstab is set, write it back to the dfstab file as well.
758 */
759
760 static void
761 add_syntax_comment(xmlNodePtr root, char *line, char *err, int todfstab)
762 {
763 xmlNodePtr node;
764
765 node = xmlNewChild(root, NULL, (xmlChar *)"error", (xmlChar *)line);
766 if (node != NULL)
767 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)err);
768 if (todfstab)
769 sa_comment_line(line, err);
770 }
771
772 /*
773 * sa_is_share(object)
774 *
775 * returns true of the object is of type "share".
776 */
777
778 int
779 sa_is_share(void *object)
780 {
781 if (object != NULL) {
782 if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0)
783 return (1);
784 }
785 return (0);
786 }
787 /*
788 * sa_is_resource(object)
789 *
790 * returns true of the object is of type "share".
791 */
792
793 int
794 sa_is_resource(void *object)
795 {
796 if (object != NULL) {
797 if (strcmp((char *)((xmlNodePtr)object)->name, "resource") == 0)
798 return (1);
799 }
800 return (0);
801 }
802
803 /*
804 * _sa_remove_property(property)
805 *
806 * remove a property only from the document.
807 */
808
809 static void
810 _sa_remove_property(sa_property_t property)
811 {
812 xmlUnlinkNode((xmlNodePtr)property);
813 xmlFreeNode((xmlNodePtr)property);
814 }
815
816 /*
817 * _sa_create_dummy_share()
818 *
819 * Create a share entry suitable for parsing but not tied to any real
820 * config tree. Need to have a parent as well as the node to parse
1501 * command to abort. Since we
1502 * add it to the default list,
1503 * everything works properly
1504 * anyway and the library
1505 * doesn't need to give a
1506 * warning.
1507 */
1508 share = _sa_add_share(lgroup,
1509 tmplist->path, SA_SHARE_TRANSIENT,
1510 &err, (uint64_t)SA_FEATURE_NONE);
1511 }
1512 } else {
1513 if (sa_zfs_is_shared(handle, tmplist->path)) {
1514 group = sa_get_group(handle, "zfs");
1515 if (group == NULL) {
1516 group = sa_create_group(handle,
1517 "zfs", &err);
1518 if (group == NULL &&
1519 err == SA_NO_PERMISSION) {
1520 group = _sa_create_group(
1521 (sa_handle_impl_t)
1522 handle,
1523 "zfs");
1524 }
1525 if (group != NULL) {
1526 (void) sa_create_optionset(
1527 group, tmplist->fstype);
1528 (void) sa_set_group_attr(group,
1529 "zfs", "true");
1530 }
1531 }
1532 if (group != NULL) {
1533 share = _sa_add_share(group,
1534 tmplist->path, SA_SHARE_TRANSIENT,
1535 &err, (uint64_t)SA_FEATURE_NONE);
1536 }
1537 } else {
1538 share = _sa_add_share(lgroup, tmplist->path,
1539 SA_SHARE_TRANSIENT, &err,
1540 (uint64_t)SA_FEATURE_NONE);
1541 }
1542 }
1543 if (share == NULL)
1558 if (tmplist->description != NULL) {
1559 xmlNodePtr node;
1560 node = xmlNewChild((xmlNodePtr)share, NULL,
1561 (xmlChar *)"description", NULL);
1562 xmlNodeSetContent(node,
1563 (xmlChar *)tmplist->description);
1564 }
1565 legacy = 1;
1566 }
1567 }
1568 dfs_free_list(list);
1569 return (legacy);
1570 }
1571
1572 /*
1573 * Get the transient shares from the sharetab (or other) file. since
1574 * these are transient, they only appear in the working file and not
1575 * in a repository.
1576 */
1577 int
1578 gettransients(sa_handle_impl_t ihandle, xmlNodePtr *root)
1579 {
1580 int legacy = 0;
1581 int numproto;
1582 char **protocols = NULL;
1583 int i;
1584
1585 if (root != NULL) {
1586 if (*root == NULL)
1587 *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
1588 if (*root != NULL) {
1589 legacy = parse_sharetab(ihandle);
1590 numproto = sa_get_protocols(&protocols);
1591 for (i = 0; i < numproto; i++)
1592 legacy |= sa_proto_get_transients(
1593 (sa_handle_t)ihandle, protocols[i]);
1594 if (protocols != NULL)
1595 free(protocols);
1596 }
1597 }
1598 return (legacy);
1599 }
1600
1601 /*
1602 * sa_has_prop(optionset, prop)
1603 *
1604 * Is the specified property a member of the optionset?
1605 */
1606
1607 int
1608 sa_has_prop(sa_optionset_t optionset, sa_property_t prop)
1609 {
1610 char *name;
1611 sa_property_t otherprop;
1612 int result = 0;
1613
2072 sh->sh_fstype = NULL;
2073 if (sh->sh_opts != NULL)
2074 free(sh->sh_opts);
2075 sh->sh_opts = NULL;
2076 if (sh->sh_descr != NULL)
2077 free(sh->sh_descr);
2078 sh->sh_descr = NULL;
2079 }
2080
2081 /*
2082 * sa_update_sharetab_ts(handle)
2083 *
2084 * Update the internal timestamp of when sharetab was last
2085 * changed. This needs to be public for ZFS to get at it.
2086 */
2087
2088 void
2089 sa_update_sharetab_ts(sa_handle_t handle)
2090 {
2091 struct stat st;
2092 sa_handle_impl_t implhandle = (sa_handle_impl_t)handle;
2093
2094 if (implhandle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0)
2095 implhandle->tssharetab = TSTAMP(st.st_mtim);
2096 }
2097
2098 /*
2099 * sa_update_sharetab(share, proto)
2100 *
2101 * Update the sharetab file with info from the specified share.
2102 * This could be an update or add.
2103 */
2104
2105 int
2106 sa_update_sharetab(sa_share_t share, char *proto)
2107 {
2108 int ret = SA_OK;
2109 share_t sh;
2110 char *path;
2111 sa_handle_t handle;
2112
2113 path = sa_get_share_attr(share, "path");
2114 if (path != NULL) {
2115 (void) memset(&sh, '\0', sizeof (sh));
2158 (void) memset(&sh, '\0', sizeof (sh));
2159 sh.sh_path = path;
2160 sh.sh_fstype = proto;
2161
2162 ret = _sharefs(SHAREFS_REMOVE, &sh);
2163 if (handle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0)
2164 sa_update_sharetab_ts(handle);
2165 }
2166 return (ret);
2167 }
2168
2169 /*
2170 * sa_needs_refresh(handle)
2171 *
2172 * Returns B_TRUE if the internal cache needs to be refreshed do to a
2173 * change by another process. B_FALSE returned otherwise.
2174 */
2175 boolean_t
2176 sa_needs_refresh(sa_handle_t handle)
2177 {
2178 sa_handle_impl_t implhandle = (sa_handle_impl_t)handle;
2179 struct stat st;
2180 char *str;
2181 uint64_t tstamp;
2182 scf_simple_prop_t *prop;
2183
2184 if (handle == NULL)
2185 return (B_TRUE);
2186
2187 /*
2188 * If sharetab has changed, then there was an external
2189 * change. Check sharetab first since it is updated by ZFS as
2190 * well as sharemgr. This is where external ZFS changes are
2191 * caught.
2192 */
2193 if (stat(SA_LEGACY_SHARETAB, &st) == 0 &&
2194 TSTAMP(st.st_mtim) != implhandle->tssharetab)
2195 return (B_TRUE);
2196
2197 /*
2198 * If sharetab wasn't changed, check whether there were any
2199 * SMF transactions that modified the config but didn't
2200 * initiate a share. This is less common but does happen.
2201 */
2202 prop = scf_simple_prop_get(implhandle->scfhandle->handle,
2203 (const char *)SA_SVC_FMRI_BASE ":default", "state",
2204 "lastupdate");
2205 if (prop != NULL) {
2206 str = scf_simple_prop_next_astring(prop);
2207 if (str != NULL)
2208 tstamp = strtoull(str, NULL, 0);
2209 else
2210 tstamp = 0;
2211 scf_simple_prop_free(prop);
2212 if (tstamp != implhandle->tstrans)
2213 return (B_TRUE);
2214 }
2215
2216 return (B_FALSE);
2217 }
2218
2219 /*
2220 * sa_fix_resource_name(path)
2221 *
2222 * Convert invalid characters in a resource name (SMB share name)
2223 * to underscores ('_'). The list of invalid characters includes
2224 * control characters and the following:
2225 *
2226 * " / \ [ ] : | < > + ; , ? * =
2227 *
2228 * The caller must pass a valid path. Leading and trailing slashes
2229 * are stripped from the path before converting invalid characters.
2230 * Resource names are restricted to SA_MAX_RESOURCE_NAME characters.
2231 */
2232 void
|
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2013 RackTop Systems.
25 */
26
27 /*
28 * core library for common functions across all config store types
29 * and file systems to be exported. This includes legacy dfstab/sharetab
30 * parsing. Need to eliminate XML where possible.
31 */
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <limits.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <libxml/parser.h>
42 #include <libxml/tree.h>
43 #include "libshare.h"
44 #include "libshare_impl.h"
74 /* used internally only */
75 typedef
76 struct sharelist {
77 struct sharelist *next;
78 int persist;
79 char *path;
80 char *resource;
81 char *fstype;
82 char *options;
83 char *description;
84 char *group;
85 char *origline;
86 int lineno;
87 } xfs_sharelist_t;
88 static void parse_dfstab(sa_handle_t, char *, xmlNodePtr);
89 extern char *_sa_get_token(char *);
90 static void dfs_free_list(xfs_sharelist_t *);
91 /* prototypes */
92 void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
93 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
94 extern sa_group_t _sa_create_group(sa_handle_t, char *);
95 static void outdfstab(FILE *, xfs_sharelist_t *);
96 extern int _sa_remove_optionset(sa_optionset_t);
97 extern int set_node_share(void *, char *, char *);
98 extern void set_node_attr(void *, char *, char *);
99
100 /*
101 * sablocksigs(*sigs)
102 *
103 * block important signals for a critical region. Arg is a pointer to
104 * a sigset_t that is used later for the unblock.
105 */
106 void
107 sablocksigs(sigset_t *sigs)
108 {
109 sigset_t new;
110
111 if (sigs != NULL) {
112 (void) sigprocmask(SIG_BLOCK, NULL, &new);
113 (void) sigaddset(&new, SIGHUP);
114 (void) sigaddset(&new, SIGINT);
725 if (errno == EACCES || errno == EPERM)
726 ret = SA_NO_PERMISSION;
727 else
728 ret = SA_CONFIG_ERR;
729 }
730 sa_free_attr_string(path);
731 }
732 out:
733 if (persist != NULL)
734 sa_free_attr_string(persist);
735 return (ret);
736 }
737
738 /*
739 * sa_is_security(optname, proto)
740 *
741 * Check to see if optname is a security (named optionset) specific
742 * property for the specified protocol.
743 */
744
745 boolean_t
746 sa_is_security(char *optname, char *proto)
747 {
748 int ret = B_FALSE;
749 if (proto != NULL)
750 ret = sa_proto_security_prop(proto, optname);
751 return (ret);
752 }
753
754 /*
755 * add_syntax_comment(root, line, err, todfstab)
756 *
757 * Add a comment to the document indicating a syntax error. If
758 * todfstab is set, write it back to the dfstab file as well.
759 */
760
761 static void
762 add_syntax_comment(xmlNodePtr root, char *line, char *err, int todfstab)
763 {
764 xmlNodePtr node;
765
766 node = xmlNewChild(root, NULL, (xmlChar *)"error", (xmlChar *)line);
767 if (node != NULL)
768 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)err);
769 if (todfstab)
770 sa_comment_line(line, err);
771 }
772
773 /*
774 * sa_is_share(object)
775 *
776 * returns true of the object is of type "share".
777 */
778
779 boolean_t
780 sa_is_share(void *object)
781 {
782 if (object != NULL) {
783 if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0)
784 return (B_TRUE);
785 }
786 return (B_FALSE);
787 }
788 /*
789 * sa_is_resource(object)
790 *
791 * returns true of the object is of type "share".
792 */
793
794 boolean_t
795 sa_is_resource(void *object)
796 {
797 if (object != NULL) {
798 if (strcmp((char *)((xmlNodePtr)object)->name, "resource") == 0)
799 return (B_TRUE);
800 }
801 return (B_FALSE);
802 }
803
804 /*
805 * _sa_remove_property(property)
806 *
807 * remove a property only from the document.
808 */
809
810 static void
811 _sa_remove_property(sa_property_t property)
812 {
813 xmlUnlinkNode((xmlNodePtr)property);
814 xmlFreeNode((xmlNodePtr)property);
815 }
816
817 /*
818 * _sa_create_dummy_share()
819 *
820 * Create a share entry suitable for parsing but not tied to any real
821 * config tree. Need to have a parent as well as the node to parse
1502 * command to abort. Since we
1503 * add it to the default list,
1504 * everything works properly
1505 * anyway and the library
1506 * doesn't need to give a
1507 * warning.
1508 */
1509 share = _sa_add_share(lgroup,
1510 tmplist->path, SA_SHARE_TRANSIENT,
1511 &err, (uint64_t)SA_FEATURE_NONE);
1512 }
1513 } else {
1514 if (sa_zfs_is_shared(handle, tmplist->path)) {
1515 group = sa_get_group(handle, "zfs");
1516 if (group == NULL) {
1517 group = sa_create_group(handle,
1518 "zfs", &err);
1519 if (group == NULL &&
1520 err == SA_NO_PERMISSION) {
1521 group = _sa_create_group(
1522 handle, "zfs");
1523 }
1524 if (group != NULL) {
1525 (void) sa_create_optionset(
1526 group, tmplist->fstype);
1527 (void) sa_set_group_attr(group,
1528 "zfs", "true");
1529 }
1530 }
1531 if (group != NULL) {
1532 share = _sa_add_share(group,
1533 tmplist->path, SA_SHARE_TRANSIENT,
1534 &err, (uint64_t)SA_FEATURE_NONE);
1535 }
1536 } else {
1537 share = _sa_add_share(lgroup, tmplist->path,
1538 SA_SHARE_TRANSIENT, &err,
1539 (uint64_t)SA_FEATURE_NONE);
1540 }
1541 }
1542 if (share == NULL)
1557 if (tmplist->description != NULL) {
1558 xmlNodePtr node;
1559 node = xmlNewChild((xmlNodePtr)share, NULL,
1560 (xmlChar *)"description", NULL);
1561 xmlNodeSetContent(node,
1562 (xmlChar *)tmplist->description);
1563 }
1564 legacy = 1;
1565 }
1566 }
1567 dfs_free_list(list);
1568 return (legacy);
1569 }
1570
1571 /*
1572 * Get the transient shares from the sharetab (or other) file. since
1573 * these are transient, they only appear in the working file and not
1574 * in a repository.
1575 */
1576 int
1577 gettransients(sa_handle_t handle, xmlNodePtr *root)
1578 {
1579 int legacy = 0;
1580 int numproto;
1581 char **protocols = NULL;
1582 int i;
1583
1584 if (root != NULL) {
1585 if (*root == NULL)
1586 *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
1587 if (*root != NULL) {
1588 legacy = parse_sharetab(handle);
1589 numproto = sa_get_protocols(&protocols);
1590 for (i = 0; i < numproto; i++)
1591 legacy |= sa_proto_get_transients(
1592 handle, protocols[i]);
1593 if (protocols != NULL)
1594 free(protocols);
1595 }
1596 }
1597 return (legacy);
1598 }
1599
1600 /*
1601 * sa_has_prop(optionset, prop)
1602 *
1603 * Is the specified property a member of the optionset?
1604 */
1605
1606 int
1607 sa_has_prop(sa_optionset_t optionset, sa_property_t prop)
1608 {
1609 char *name;
1610 sa_property_t otherprop;
1611 int result = 0;
1612
2071 sh->sh_fstype = NULL;
2072 if (sh->sh_opts != NULL)
2073 free(sh->sh_opts);
2074 sh->sh_opts = NULL;
2075 if (sh->sh_descr != NULL)
2076 free(sh->sh_descr);
2077 sh->sh_descr = NULL;
2078 }
2079
2080 /*
2081 * sa_update_sharetab_ts(handle)
2082 *
2083 * Update the internal timestamp of when sharetab was last
2084 * changed. This needs to be public for ZFS to get at it.
2085 */
2086
2087 void
2088 sa_update_sharetab_ts(sa_handle_t handle)
2089 {
2090 struct stat st;
2091
2092 if (handle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0)
2093 handle->tssharetab = TSTAMP(st.st_mtim);
2094 }
2095
2096 /*
2097 * sa_update_sharetab(share, proto)
2098 *
2099 * Update the sharetab file with info from the specified share.
2100 * This could be an update or add.
2101 */
2102
2103 int
2104 sa_update_sharetab(sa_share_t share, char *proto)
2105 {
2106 int ret = SA_OK;
2107 share_t sh;
2108 char *path;
2109 sa_handle_t handle;
2110
2111 path = sa_get_share_attr(share, "path");
2112 if (path != NULL) {
2113 (void) memset(&sh, '\0', sizeof (sh));
2156 (void) memset(&sh, '\0', sizeof (sh));
2157 sh.sh_path = path;
2158 sh.sh_fstype = proto;
2159
2160 ret = _sharefs(SHAREFS_REMOVE, &sh);
2161 if (handle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0)
2162 sa_update_sharetab_ts(handle);
2163 }
2164 return (ret);
2165 }
2166
2167 /*
2168 * sa_needs_refresh(handle)
2169 *
2170 * Returns B_TRUE if the internal cache needs to be refreshed do to a
2171 * change by another process. B_FALSE returned otherwise.
2172 */
2173 boolean_t
2174 sa_needs_refresh(sa_handle_t handle)
2175 {
2176 struct stat st;
2177 char *str;
2178 uint64_t tstamp;
2179 scf_simple_prop_t *prop;
2180
2181 if (handle == NULL)
2182 return (B_TRUE);
2183
2184 /*
2185 * If sharetab has changed, then there was an external
2186 * change. Check sharetab first since it is updated by ZFS as
2187 * well as sharemgr. This is where external ZFS changes are
2188 * caught.
2189 */
2190 if (stat(SA_LEGACY_SHARETAB, &st) == 0 &&
2191 TSTAMP(st.st_mtim) != handle->tssharetab)
2192 return (B_TRUE);
2193
2194 /*
2195 * If sharetab wasn't changed, check whether there were any
2196 * SMF transactions that modified the config but didn't
2197 * initiate a share. This is less common but does happen.
2198 */
2199 prop = scf_simple_prop_get(handle->scfhandle->handle,
2200 (const char *)SA_SVC_FMRI_BASE ":default", "state",
2201 "lastupdate");
2202 if (prop != NULL) {
2203 str = scf_simple_prop_next_astring(prop);
2204 if (str != NULL)
2205 tstamp = strtoull(str, NULL, 0);
2206 else
2207 tstamp = 0;
2208 scf_simple_prop_free(prop);
2209 if (tstamp != handle->tstrans)
2210 return (B_TRUE);
2211 }
2212
2213 return (B_FALSE);
2214 }
2215
2216 /*
2217 * sa_fix_resource_name(path)
2218 *
2219 * Convert invalid characters in a resource name (SMB share name)
2220 * to underscores ('_'). The list of invalid characters includes
2221 * control characters and the following:
2222 *
2223 * " / \ [ ] : | < > + ; , ? * =
2224 *
2225 * The caller must pass a valid path. Leading and trailing slashes
2226 * are stripped from the path before converting invalid characters.
2227 * Resource names are restricted to SA_MAX_RESOURCE_NAME characters.
2228 */
2229 void
|