xref: /illumos-gate/usr/src/lib/libshare/smb/libshare_smb.c (revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0)
1*da6c28aaSamw /*
2*da6c28aaSamw  * CDDL HEADER START
3*da6c28aaSamw  *
4*da6c28aaSamw  * The contents of this file are subject to the terms of the
5*da6c28aaSamw  * Common Development and Distribution License (the "License").
6*da6c28aaSamw  * You may not use this file except in compliance with the License.
7*da6c28aaSamw  *
8*da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10*da6c28aaSamw  * See the License for the specific language governing permissions
11*da6c28aaSamw  * and limitations under the License.
12*da6c28aaSamw  *
13*da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14*da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16*da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17*da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18*da6c28aaSamw  *
19*da6c28aaSamw  * CDDL HEADER END
20*da6c28aaSamw  */
21*da6c28aaSamw 
22*da6c28aaSamw /*
23*da6c28aaSamw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24*da6c28aaSamw  * Use is subject to license terms.
25*da6c28aaSamw  */
26*da6c28aaSamw 
27*da6c28aaSamw #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*da6c28aaSamw 
29*da6c28aaSamw /*
30*da6c28aaSamw  * SMB specific functions
31*da6c28aaSamw  */
32*da6c28aaSamw #include <stdio.h>
33*da6c28aaSamw #include <string.h>
34*da6c28aaSamw #include <ctype.h>
35*da6c28aaSamw #include <stdlib.h>
36*da6c28aaSamw #include <unistd.h>
37*da6c28aaSamw #include <zone.h>
38*da6c28aaSamw #include <errno.h>
39*da6c28aaSamw #include <locale.h>
40*da6c28aaSamw #include <fcntl.h>
41*da6c28aaSamw #include <sys/types.h>
42*da6c28aaSamw #include <sys/stat.h>
43*da6c28aaSamw #include <syslog.h>
44*da6c28aaSamw #include "libshare.h"
45*da6c28aaSamw #include "libshare_impl.h"
46*da6c28aaSamw #include <pwd.h>
47*da6c28aaSamw #include <limits.h>
48*da6c28aaSamw #include <libscf.h>
49*da6c28aaSamw #include <strings.h>
50*da6c28aaSamw #include "libshare_smb.h"
51*da6c28aaSamw #include <rpcsvc/daemon_utils.h>
52*da6c28aaSamw #include <smbsrv/lmshare.h>
53*da6c28aaSamw #include <smbsrv/lmshare_door.h>
54*da6c28aaSamw #include <smbsrv/smbinfo.h>
55*da6c28aaSamw #include <smbsrv/libsmb.h>
56*da6c28aaSamw 
57*da6c28aaSamw /* internal functions */
58*da6c28aaSamw static int smb_share_init(void);
59*da6c28aaSamw static void smb_share_fini(void);
60*da6c28aaSamw static int smb_enable_share(sa_share_t);
61*da6c28aaSamw static int smb_share_changed(sa_share_t);
62*da6c28aaSamw static int smb_resource_changed(sa_resource_t);
63*da6c28aaSamw static int smb_rename_resource(sa_handle_t, sa_resource_t, char *);
64*da6c28aaSamw static int smb_disable_share(sa_share_t share, char *);
65*da6c28aaSamw static int smb_validate_property(sa_property_t, sa_optionset_t);
66*da6c28aaSamw static int smb_set_proto_prop(sa_property_t);
67*da6c28aaSamw static sa_protocol_properties_t smb_get_proto_set(void);
68*da6c28aaSamw static char *smb_get_status(void);
69*da6c28aaSamw static int smb_parse_optstring(sa_group_t, char *);
70*da6c28aaSamw static char *smb_format_options(sa_group_t, int);
71*da6c28aaSamw 
72*da6c28aaSamw static int smb_enable_service(void);
73*da6c28aaSamw 
74*da6c28aaSamw static int range_check_validator(int, char *);
75*da6c28aaSamw static int range_check_validator_zero_ok(int, char *);
76*da6c28aaSamw static int string_length_check_validator(int, char *);
77*da6c28aaSamw static int true_false_validator(int, char *);
78*da6c28aaSamw static int ip_address_validator_empty_ok(int, char *);
79*da6c28aaSamw static int ip_address_csv_list_validator_empty_ok(int, char *);
80*da6c28aaSamw static int ipc_mode_validator(int, char *);
81*da6c28aaSamw static int path_validator(int, char *);
82*da6c28aaSamw 
83*da6c28aaSamw static int smb_enable_resource(sa_resource_t);
84*da6c28aaSamw static int smb_disable_resource(sa_resource_t);
85*da6c28aaSamw static uint64_t smb_share_features(void);
86*da6c28aaSamw static int smb_list_transient(sa_handle_t);
87*da6c28aaSamw 
88*da6c28aaSamw /* size of basic format allocation */
89*da6c28aaSamw #define	OPT_CHUNK	1024
90*da6c28aaSamw 
91*da6c28aaSamw /*
92*da6c28aaSamw  * Indexes of entries in smb_proto_options table.
93*da6c28aaSamw  * Changes to smb_proto_options table may require
94*da6c28aaSamw  * an update to these values.
95*da6c28aaSamw  */
96*da6c28aaSamw #define	PROTO_OPT_WINS1			6
97*da6c28aaSamw #define	PROTO_OPT_WINS_EXCLUDE		8
98*da6c28aaSamw 
99*da6c28aaSamw 
100*da6c28aaSamw /*
101*da6c28aaSamw  * ops vector that provides the protocol specific info and operations
102*da6c28aaSamw  * for share management.
103*da6c28aaSamw  */
104*da6c28aaSamw 
105*da6c28aaSamw struct sa_plugin_ops sa_plugin_ops = {
106*da6c28aaSamw 	SA_PLUGIN_VERSION,
107*da6c28aaSamw 	SMB_PROTOCOL_NAME,
108*da6c28aaSamw 	smb_share_init,
109*da6c28aaSamw 	smb_share_fini,
110*da6c28aaSamw 	smb_enable_share,
111*da6c28aaSamw 	smb_disable_share,
112*da6c28aaSamw 	smb_validate_property,
113*da6c28aaSamw 	NULL,
114*da6c28aaSamw 	NULL,
115*da6c28aaSamw 	smb_parse_optstring,
116*da6c28aaSamw 	smb_format_options,
117*da6c28aaSamw 	smb_set_proto_prop,
118*da6c28aaSamw 	smb_get_proto_set,
119*da6c28aaSamw 	smb_get_status,
120*da6c28aaSamw 	NULL,
121*da6c28aaSamw 	NULL,
122*da6c28aaSamw 	NULL,
123*da6c28aaSamw 	smb_share_changed,
124*da6c28aaSamw 	smb_enable_resource,
125*da6c28aaSamw 	smb_disable_resource,
126*da6c28aaSamw 	smb_share_features,
127*da6c28aaSamw 	smb_list_transient,
128*da6c28aaSamw 	smb_resource_changed,
129*da6c28aaSamw 	smb_rename_resource,
130*da6c28aaSamw 	NULL,
131*da6c28aaSamw 	NULL
132*da6c28aaSamw };
133*da6c28aaSamw 
134*da6c28aaSamw /*
135*da6c28aaSamw  * option definitions.  Make sure to keep the #define for the option
136*da6c28aaSamw  * index just before the entry it is the index for. Changing the order
137*da6c28aaSamw  * can cause breakage.
138*da6c28aaSamw  */
139*da6c28aaSamw 
140*da6c28aaSamw struct option_defs optdefs[] = {
141*da6c28aaSamw 	{SHOPT_AD_CONTAINER, OPT_TYPE_STRING},
142*da6c28aaSamw 	{SHOPT_NAME, OPT_TYPE_NAME},
143*da6c28aaSamw 	{NULL, NULL},
144*da6c28aaSamw };
145*da6c28aaSamw 
146*da6c28aaSamw /*
147*da6c28aaSamw  * findopt(name)
148*da6c28aaSamw  *
149*da6c28aaSamw  * Lookup option "name" in the option table and return the table
150*da6c28aaSamw  * index.
151*da6c28aaSamw  */
152*da6c28aaSamw 
153*da6c28aaSamw static int
154*da6c28aaSamw findopt(char *name)
155*da6c28aaSamw {
156*da6c28aaSamw 	int i;
157*da6c28aaSamw 	if (name != NULL) {
158*da6c28aaSamw 		for (i = 0; optdefs[i].tag != NULL; i++) {
159*da6c28aaSamw 			if (strcmp(optdefs[i].tag, name) == 0)
160*da6c28aaSamw 				return (i);
161*da6c28aaSamw 		}
162*da6c28aaSamw 	}
163*da6c28aaSamw 	return (-1);
164*da6c28aaSamw }
165*da6c28aaSamw 
166*da6c28aaSamw /*
167*da6c28aaSamw  * is_a_number(number)
168*da6c28aaSamw  *
169*da6c28aaSamw  * is the string a number in one of the forms we want to use?
170*da6c28aaSamw  */
171*da6c28aaSamw 
172*da6c28aaSamw static int
173*da6c28aaSamw is_a_number(char *number)
174*da6c28aaSamw {
175*da6c28aaSamw 	int ret = 1;
176*da6c28aaSamw 	int hex = 0;
177*da6c28aaSamw 
178*da6c28aaSamw 	if (strncmp(number, "0x", 2) == 0) {
179*da6c28aaSamw 		number += 2;
180*da6c28aaSamw 		hex = 1;
181*da6c28aaSamw 	} else if (*number == '-') {
182*da6c28aaSamw 		number++; /* skip the minus */
183*da6c28aaSamw 	}
184*da6c28aaSamw 
185*da6c28aaSamw 	while (ret == 1 && *number != '\0') {
186*da6c28aaSamw 		if (hex) {
187*da6c28aaSamw 			ret = isxdigit(*number++);
188*da6c28aaSamw 		} else {
189*da6c28aaSamw 			ret = isdigit(*number++);
190*da6c28aaSamw 		}
191*da6c28aaSamw 	}
192*da6c28aaSamw 	return (ret);
193*da6c28aaSamw }
194*da6c28aaSamw 
195*da6c28aaSamw /*
196*da6c28aaSamw  * validresource(name)
197*da6c28aaSamw  *
198*da6c28aaSamw  * Check that name only has valid characters in it. The current valid
199*da6c28aaSamw  * set are the printable characters but not including:
200*da6c28aaSamw  *	" / \ [ ] : | < > + ; , ? * = \t
201*da6c28aaSamw  * Note that space is included and there is a maximum length.
202*da6c28aaSamw  */
203*da6c28aaSamw static int
204*da6c28aaSamw validresource(const char *name)
205*da6c28aaSamw {
206*da6c28aaSamw 	const char *cp;
207*da6c28aaSamw 	size_t len;
208*da6c28aaSamw 
209*da6c28aaSamw 	if (name == NULL)
210*da6c28aaSamw 		return (B_FALSE);
211*da6c28aaSamw 
212*da6c28aaSamw 	len = strlen(name);
213*da6c28aaSamw 	if (len == 0 || len > SA_MAX_RESOURCE_NAME)
214*da6c28aaSamw 		return (B_FALSE);
215*da6c28aaSamw 
216*da6c28aaSamw 	if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
217*da6c28aaSamw 		return (B_FALSE);
218*da6c28aaSamw 	}
219*da6c28aaSamw 
220*da6c28aaSamw 	for (cp = name; *cp != '\0'; cp++)
221*da6c28aaSamw 		if (iscntrl(*cp))
222*da6c28aaSamw 			return (B_FALSE);
223*da6c28aaSamw 
224*da6c28aaSamw 	return (B_TRUE);
225*da6c28aaSamw }
226*da6c28aaSamw 
227*da6c28aaSamw /*
228*da6c28aaSamw  * smb_isonline()
229*da6c28aaSamw  *
230*da6c28aaSamw  * Determine if the SMF service instance is in the online state or
231*da6c28aaSamw  * not. A number of operations depend on this state.
232*da6c28aaSamw  */
233*da6c28aaSamw static boolean_t
234*da6c28aaSamw smb_isonline(void)
235*da6c28aaSamw {
236*da6c28aaSamw 	char *str;
237*da6c28aaSamw 	boolean_t ret = B_FALSE;
238*da6c28aaSamw 
239*da6c28aaSamw 	if ((str = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI)) != NULL) {
240*da6c28aaSamw 		ret = (strcmp(str, SCF_STATE_STRING_ONLINE) == 0);
241*da6c28aaSamw 		free(str);
242*da6c28aaSamw 	}
243*da6c28aaSamw 	return (ret);
244*da6c28aaSamw }
245*da6c28aaSamw 
246*da6c28aaSamw /*
247*da6c28aaSamw  * smb_enable_share tells the implementation that it is to enable the share.
248*da6c28aaSamw  * This entails converting the path and options into the appropriate ioctl
249*da6c28aaSamw  * calls. It is assumed that all error checking of paths, etc. were
250*da6c28aaSamw  * done earlier.
251*da6c28aaSamw  */
252*da6c28aaSamw static int
253*da6c28aaSamw smb_enable_share(sa_share_t share)
254*da6c28aaSamw {
255*da6c28aaSamw 	char *path;
256*da6c28aaSamw 	char *rname;
257*da6c28aaSamw 	lmshare_info_t si;
258*da6c28aaSamw 	sa_resource_t resource;
259*da6c28aaSamw 	boolean_t iszfs;
260*da6c28aaSamw 	boolean_t privileged;
261*da6c28aaSamw 	int err = SA_OK;
262*da6c28aaSamw 	priv_set_t *priv_effective;
263*da6c28aaSamw 	boolean_t online;
264*da6c28aaSamw 
265*da6c28aaSamw 	priv_effective = priv_allocset();
266*da6c28aaSamw 	(void) getppriv(PRIV_EFFECTIVE, priv_effective);
267*da6c28aaSamw 	privileged = (priv_isfullset(priv_effective) == B_TRUE);
268*da6c28aaSamw 	priv_freeset(priv_effective);
269*da6c28aaSamw 
270*da6c28aaSamw 	/* get the path since it is important in several places */
271*da6c28aaSamw 	path = sa_get_share_attr(share, "path");
272*da6c28aaSamw 	if (path == NULL)
273*da6c28aaSamw 		return (SA_NO_SUCH_PATH);
274*da6c28aaSamw 
275*da6c28aaSamw 	online = smb_isonline();
276*da6c28aaSamw 
277*da6c28aaSamw 	iszfs = sa_path_is_zfs(path);
278*da6c28aaSamw 
279*da6c28aaSamw 	if (iszfs) {
280*da6c28aaSamw 
281*da6c28aaSamw 		if (privileged == B_FALSE && !online) {
282*da6c28aaSamw 
283*da6c28aaSamw 			if (!online) {
284*da6c28aaSamw 				(void) printf(dgettext(TEXT_DOMAIN,
285*da6c28aaSamw 				    "SMB: Cannot share remove "
286*da6c28aaSamw 				    "file system: %s\n"), path);
287*da6c28aaSamw 				(void) printf(dgettext(TEXT_DOMAIN,
288*da6c28aaSamw 				    "SMB: Service needs to be enabled "
289*da6c28aaSamw 				    "by a privileged user\n"));
290*da6c28aaSamw 				err = SA_NO_PERMISSION;
291*da6c28aaSamw 				errno = EPERM;
292*da6c28aaSamw 			}
293*da6c28aaSamw 			if (err) {
294*da6c28aaSamw 				sa_free_attr_string(path);
295*da6c28aaSamw 				return (err);
296*da6c28aaSamw 			}
297*da6c28aaSamw 
298*da6c28aaSamw 		}
299*da6c28aaSamw 	}
300*da6c28aaSamw 
301*da6c28aaSamw 	if (privileged == B_TRUE && !online) {
302*da6c28aaSamw 		err = smb_enable_service();
303*da6c28aaSamw 		if (err != SA_OK) {
304*da6c28aaSamw 			(void) printf(dgettext(TEXT_DOMAIN,
305*da6c28aaSamw 			    "SMB: Unable to enable service\n"));
306*da6c28aaSamw 			/*
307*da6c28aaSamw 			 * For now, it is OK to not be able to enable
308*da6c28aaSamw 			 * the service.
309*da6c28aaSamw 			 */
310*da6c28aaSamw 			if (err == SA_BUSY)
311*da6c28aaSamw 				err = SA_OK;
312*da6c28aaSamw 		} else {
313*da6c28aaSamw 			online = B_TRUE;
314*da6c28aaSamw 		}
315*da6c28aaSamw 	}
316*da6c28aaSamw 
317*da6c28aaSamw 	/*
318*da6c28aaSamw 	 * Don't bother trying to start shares if the service isn't
319*da6c28aaSamw 	 * running.
320*da6c28aaSamw 	 */
321*da6c28aaSamw 	if (!online)
322*da6c28aaSamw 		goto done;
323*da6c28aaSamw 
324*da6c28aaSamw 	/* Each share can have multiple resources */
325*da6c28aaSamw 	for (resource = sa_get_share_resource(share, NULL);
326*da6c28aaSamw 	    resource != NULL;
327*da6c28aaSamw 	    resource = sa_get_next_resource(resource)) {
328*da6c28aaSamw 		sa_optionset_t opts;
329*da6c28aaSamw 		bzero(&si, sizeof (lmshare_info_t));
330*da6c28aaSamw 		rname = sa_get_resource_attr(resource, "name");
331*da6c28aaSamw 		if (rname == NULL) {
332*da6c28aaSamw 			sa_free_attr_string(path);
333*da6c28aaSamw 			return (SA_NO_SUCH_RESOURCE);
334*da6c28aaSamw 		}
335*da6c28aaSamw 
336*da6c28aaSamw 		opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
337*da6c28aaSamw 		smb_build_lmshare_info(rname, path, opts, &si);
338*da6c28aaSamw 		sa_free_attr_string(rname);
339*da6c28aaSamw 
340*da6c28aaSamw 		sa_free_derived_optionset(opts);
341*da6c28aaSamw 		if (!iszfs) {
342*da6c28aaSamw 			err = lmshrd_add(&si);
343*da6c28aaSamw 		} else {
344*da6c28aaSamw 			share_t sh;
345*da6c28aaSamw 
346*da6c28aaSamw 			sa_sharetab_fill_zfs(share, &sh, "smb");
347*da6c28aaSamw 			err = sa_share_zfs(share, (char *)path, &sh,
348*da6c28aaSamw 			    &si, ZFS_SHARE_SMB);
349*da6c28aaSamw 
350*da6c28aaSamw 			sa_emptyshare(&sh);
351*da6c28aaSamw 		}
352*da6c28aaSamw 	}
353*da6c28aaSamw 	if (!iszfs)
354*da6c28aaSamw 		(void) sa_update_sharetab(share, "smb");
355*da6c28aaSamw done:
356*da6c28aaSamw 	sa_free_attr_string(path);
357*da6c28aaSamw 
358*da6c28aaSamw 	return (err == NERR_DuplicateShare ? 0 : err);
359*da6c28aaSamw }
360*da6c28aaSamw 
361*da6c28aaSamw /*
362*da6c28aaSamw  * This is the share for CIFS all shares have resource names.
363*da6c28aaSamw  * Enable tells the smb server to update its hash. If it fails
364*da6c28aaSamw  * because smb server is down, we just ignore as smb server loads
365*da6c28aaSamw  * the resources from sharemanager at startup.
366*da6c28aaSamw  */
367*da6c28aaSamw 
368*da6c28aaSamw static int
369*da6c28aaSamw smb_enable_resource(sa_resource_t resource)
370*da6c28aaSamw {
371*da6c28aaSamw 	char *path;
372*da6c28aaSamw 	char *rname;
373*da6c28aaSamw 	sa_optionset_t opts;
374*da6c28aaSamw 	sa_share_t share;
375*da6c28aaSamw 	lmshare_info_t si;
376*da6c28aaSamw 	int ret;
377*da6c28aaSamw 
378*da6c28aaSamw 	share = sa_get_resource_parent(resource);
379*da6c28aaSamw 	if (share == NULL)
380*da6c28aaSamw 		return (SA_NO_SUCH_PATH);
381*da6c28aaSamw 	path = sa_get_share_attr(share, "path");
382*da6c28aaSamw 	if (path == NULL)
383*da6c28aaSamw 		return (SA_SYSTEM_ERR);
384*da6c28aaSamw 	rname = sa_get_resource_attr(resource, "name");
385*da6c28aaSamw 	if (rname == NULL) {
386*da6c28aaSamw 		sa_free_attr_string(path);
387*da6c28aaSamw 		return (SA_NO_SUCH_RESOURCE);
388*da6c28aaSamw 	}
389*da6c28aaSamw 
390*da6c28aaSamw 	ret = smb_enable_service();
391*da6c28aaSamw 
392*da6c28aaSamw 	if (!smb_isonline()) {
393*da6c28aaSamw 		ret = SA_OK;
394*da6c28aaSamw 		goto done;
395*da6c28aaSamw 	}
396*da6c28aaSamw 
397*da6c28aaSamw 	opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
398*da6c28aaSamw 	smb_build_lmshare_info(rname, path, opts, &si);
399*da6c28aaSamw 	sa_free_attr_string(path);
400*da6c28aaSamw 	sa_free_attr_string(rname);
401*da6c28aaSamw 	sa_free_derived_optionset(opts);
402*da6c28aaSamw 	if (lmshrd_add(&si) != NERR_Success)
403*da6c28aaSamw 		return (SA_NOT_SHARED);
404*da6c28aaSamw 	(void) sa_update_sharetab(share, "smb");
405*da6c28aaSamw 
406*da6c28aaSamw done:
407*da6c28aaSamw 	return (ret);
408*da6c28aaSamw }
409*da6c28aaSamw 
410*da6c28aaSamw /*
411*da6c28aaSamw  * Remove it from smb server hash.
412*da6c28aaSamw  */
413*da6c28aaSamw static int
414*da6c28aaSamw smb_disable_resource(sa_resource_t resource)
415*da6c28aaSamw {
416*da6c28aaSamw 	char *rname;
417*da6c28aaSamw 	DWORD res;
418*da6c28aaSamw 	sa_share_t share;
419*da6c28aaSamw 
420*da6c28aaSamw 	rname = sa_get_resource_attr(resource, "name");
421*da6c28aaSamw 	if (rname == NULL)
422*da6c28aaSamw 		return (SA_NO_SUCH_RESOURCE);
423*da6c28aaSamw 
424*da6c28aaSamw 	if (smb_isonline()) {
425*da6c28aaSamw 		res = lmshrd_delete(rname);
426*da6c28aaSamw 		if (res != NERR_Success) {
427*da6c28aaSamw 			sa_free_attr_string(rname);
428*da6c28aaSamw 			return (SA_CONFIG_ERR);
429*da6c28aaSamw 		}
430*da6c28aaSamw 		sa_free_attr_string(rname);
431*da6c28aaSamw 		rname = NULL;
432*da6c28aaSamw 	}
433*da6c28aaSamw 	share = sa_get_resource_parent(resource);
434*da6c28aaSamw 	if (share != NULL) {
435*da6c28aaSamw 		rname = sa_get_share_attr(share, "path");
436*da6c28aaSamw 		if (rname != NULL) {
437*da6c28aaSamw 			(void) sa_delete_sharetab(rname, "smb");
438*da6c28aaSamw 			sa_free_attr_string(rname);
439*da6c28aaSamw 			rname = NULL;
440*da6c28aaSamw 		}
441*da6c28aaSamw 	}
442*da6c28aaSamw 	if (rname != NULL)
443*da6c28aaSamw 		sa_free_attr_string(rname);
444*da6c28aaSamw 	/*
445*da6c28aaSamw 	 * Always return OK as smb/server may be down and
446*da6c28aaSamw 	 * Shares will be picked up when loaded.
447*da6c28aaSamw 	 */
448*da6c28aaSamw 	return (SA_OK);
449*da6c28aaSamw }
450*da6c28aaSamw 
451*da6c28aaSamw /*
452*da6c28aaSamw  * smb_share_changed(sa_share_t share)
453*da6c28aaSamw  *
454*da6c28aaSamw  * The specified share has changed.
455*da6c28aaSamw  */
456*da6c28aaSamw static int
457*da6c28aaSamw smb_share_changed(sa_share_t share)
458*da6c28aaSamw {
459*da6c28aaSamw 	char *path;
460*da6c28aaSamw 	sa_resource_t resource;
461*da6c28aaSamw 
462*da6c28aaSamw 	/* get the path since it is important in several places */
463*da6c28aaSamw 	path = sa_get_share_attr(share, "path");
464*da6c28aaSamw 	if (path == NULL)
465*da6c28aaSamw 		return (SA_NO_SUCH_PATH);
466*da6c28aaSamw 	for (resource = sa_get_share_resource(share, NULL);
467*da6c28aaSamw 	    resource != NULL;
468*da6c28aaSamw 	    resource = sa_get_next_resource(resource))
469*da6c28aaSamw 		(void) smb_resource_changed(resource);
470*da6c28aaSamw 
471*da6c28aaSamw 	sa_free_attr_string(path);
472*da6c28aaSamw 
473*da6c28aaSamw 	return (SA_OK);
474*da6c28aaSamw }
475*da6c28aaSamw 
476*da6c28aaSamw /*
477*da6c28aaSamw  * smb_resource_changed(sa_resource_t resource)
478*da6c28aaSamw  *
479*da6c28aaSamw  * The specified resource has changed.
480*da6c28aaSamw  */
481*da6c28aaSamw static int
482*da6c28aaSamw smb_resource_changed(sa_resource_t resource)
483*da6c28aaSamw {
484*da6c28aaSamw 	DWORD res;
485*da6c28aaSamw 	lmshare_info_t si;
486*da6c28aaSamw 	lmshare_info_t new_si;
487*da6c28aaSamw 	char *rname, *path;
488*da6c28aaSamw 	sa_optionset_t opts;
489*da6c28aaSamw 	sa_share_t share;
490*da6c28aaSamw 
491*da6c28aaSamw 	rname = sa_get_resource_attr(resource, "name");
492*da6c28aaSamw 	if (rname == NULL)
493*da6c28aaSamw 		return (SA_NO_SUCH_RESOURCE);
494*da6c28aaSamw 
495*da6c28aaSamw 	share = sa_get_resource_parent(resource);
496*da6c28aaSamw 	if (share == NULL) {
497*da6c28aaSamw 		sa_free_attr_string(rname);
498*da6c28aaSamw 		return (SA_CONFIG_ERR);
499*da6c28aaSamw 	}
500*da6c28aaSamw 
501*da6c28aaSamw 	path = sa_get_share_attr(share, "path");
502*da6c28aaSamw 	if (path == NULL) {
503*da6c28aaSamw 		sa_free_attr_string(rname);
504*da6c28aaSamw 		return (SA_NO_SUCH_PATH);
505*da6c28aaSamw 	}
506*da6c28aaSamw 
507*da6c28aaSamw 	if (!smb_isonline()) {
508*da6c28aaSamw 		sa_free_attr_string(rname);
509*da6c28aaSamw 		return (SA_OK);
510*da6c28aaSamw 	}
511*da6c28aaSamw 
512*da6c28aaSamw 	/* Update the share cache in smb/server */
513*da6c28aaSamw 	res = lmshrd_getinfo(rname, &si);
514*da6c28aaSamw 	if (res != NERR_Success) {
515*da6c28aaSamw 		sa_free_attr_string(path);
516*da6c28aaSamw 		sa_free_attr_string(rname);
517*da6c28aaSamw 		return (SA_CONFIG_ERR);
518*da6c28aaSamw 	}
519*da6c28aaSamw 
520*da6c28aaSamw 	opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
521*da6c28aaSamw 	smb_build_lmshare_info(rname, path, opts, &new_si);
522*da6c28aaSamw 	sa_free_derived_optionset(opts);
523*da6c28aaSamw 	sa_free_attr_string(path);
524*da6c28aaSamw 	sa_free_attr_string(rname);
525*da6c28aaSamw 
526*da6c28aaSamw 	/*
527*da6c28aaSamw 	 * Update all fields from sa_share_t
528*da6c28aaSamw 	 * Get derived values.
529*da6c28aaSamw 	 */
530*da6c28aaSamw 	if (lmshrd_setinfo(&new_si) != LMSHR_DOOR_SRV_SUCCESS)
531*da6c28aaSamw 		return (SA_CONFIG_ERR);
532*da6c28aaSamw 	return (smb_enable_service());
533*da6c28aaSamw }
534*da6c28aaSamw 
535*da6c28aaSamw /*
536*da6c28aaSamw  * smb_disable_share(sa_share_t share)
537*da6c28aaSamw  *
538*da6c28aaSamw  * Unshare the specified share.
539*da6c28aaSamw  */
540*da6c28aaSamw static int
541*da6c28aaSamw smb_disable_share(sa_share_t share, char *path)
542*da6c28aaSamw {
543*da6c28aaSamw 	char *rname;
544*da6c28aaSamw 	sa_resource_t resource;
545*da6c28aaSamw 	boolean_t iszfs;
546*da6c28aaSamw 	int err = SA_OK;
547*da6c28aaSamw 
548*da6c28aaSamw 	iszfs = sa_path_is_zfs(path);
549*da6c28aaSamw 	if (!smb_isonline())
550*da6c28aaSamw 		goto done;
551*da6c28aaSamw 
552*da6c28aaSamw 	for (resource = sa_get_share_resource(share, NULL);
553*da6c28aaSamw 	    resource != NULL;
554*da6c28aaSamw 	    resource = sa_get_next_resource(resource)) {
555*da6c28aaSamw 		rname = sa_get_resource_attr(resource, "name");
556*da6c28aaSamw 		if (rname == NULL) {
557*da6c28aaSamw 			continue;
558*da6c28aaSamw 		}
559*da6c28aaSamw 		if (!iszfs) {
560*da6c28aaSamw 			err = lmshrd_delete(rname);
561*da6c28aaSamw 			switch (err) {
562*da6c28aaSamw 			case NERR_NetNameNotFound:
563*da6c28aaSamw 			case NERR_Success:
564*da6c28aaSamw 				err = SA_OK;
565*da6c28aaSamw 				break;
566*da6c28aaSamw 			default:
567*da6c28aaSamw 				err = SA_CONFIG_ERR;
568*da6c28aaSamw 				break;
569*da6c28aaSamw 			}
570*da6c28aaSamw 		} else {
571*da6c28aaSamw 			share_t sh;
572*da6c28aaSamw 
573*da6c28aaSamw 			sa_sharetab_fill_zfs(share, &sh, "smb");
574*da6c28aaSamw 			err = sa_share_zfs(share, (char *)path, &sh,
575*da6c28aaSamw 			    rname, ZFS_UNSHARE_SMB);
576*da6c28aaSamw 			sa_emptyshare(&sh);
577*da6c28aaSamw 		}
578*da6c28aaSamw 		sa_free_attr_string(rname);
579*da6c28aaSamw 	}
580*da6c28aaSamw done:
581*da6c28aaSamw 	if (!iszfs)
582*da6c28aaSamw 		(void) sa_delete_sharetab(path, "smb");
583*da6c28aaSamw 	return (err);
584*da6c28aaSamw }
585*da6c28aaSamw 
586*da6c28aaSamw /*
587*da6c28aaSamw  * smb_validate_property(property, parent)
588*da6c28aaSamw  *
589*da6c28aaSamw  * Check that the property has a legitimate value for its type.
590*da6c28aaSamw  */
591*da6c28aaSamw 
592*da6c28aaSamw static int
593*da6c28aaSamw smb_validate_property(sa_property_t property, sa_optionset_t parent)
594*da6c28aaSamw {
595*da6c28aaSamw 	int ret = SA_OK;
596*da6c28aaSamw 	char *propname;
597*da6c28aaSamw 	int optindex;
598*da6c28aaSamw 	sa_group_t parent_group;
599*da6c28aaSamw 	char *value;
600*da6c28aaSamw 
601*da6c28aaSamw 	propname = sa_get_property_attr(property, "type");
602*da6c28aaSamw 
603*da6c28aaSamw 	if ((optindex = findopt(propname)) < 0)
604*da6c28aaSamw 		ret = SA_NO_SUCH_PROP;
605*da6c28aaSamw 
606*da6c28aaSamw 	/* need to validate value range here as well */
607*da6c28aaSamw 	if (ret == SA_OK) {
608*da6c28aaSamw 		parent_group = sa_get_parent_group((sa_share_t)parent);
609*da6c28aaSamw 		if (optdefs[optindex].share && !sa_is_share(parent_group))
610*da6c28aaSamw 			ret = SA_PROP_SHARE_ONLY;
611*da6c28aaSamw 	}
612*da6c28aaSamw 	if (ret != SA_OK) {
613*da6c28aaSamw 		if (propname != NULL)
614*da6c28aaSamw 			sa_free_attr_string(propname);
615*da6c28aaSamw 		return (ret);
616*da6c28aaSamw 	}
617*da6c28aaSamw 
618*da6c28aaSamw 	value = sa_get_property_attr(property, "value");
619*da6c28aaSamw 	if (value != NULL) {
620*da6c28aaSamw 		/* first basic type checking */
621*da6c28aaSamw 		switch (optdefs[optindex].type) {
622*da6c28aaSamw 		case OPT_TYPE_NUMBER:
623*da6c28aaSamw 			/* check that the value is all digits */
624*da6c28aaSamw 			if (!is_a_number(value))
625*da6c28aaSamw 				ret = SA_BAD_VALUE;
626*da6c28aaSamw 			break;
627*da6c28aaSamw 		case OPT_TYPE_BOOLEAN:
628*da6c28aaSamw 			if (strlen(value) == 0 ||
629*da6c28aaSamw 			    strcasecmp(value, "true") == 0 ||
630*da6c28aaSamw 			    strcmp(value, "1") == 0 ||
631*da6c28aaSamw 			    strcasecmp(value, "false") == 0 ||
632*da6c28aaSamw 			    strcmp(value, "0") == 0) {
633*da6c28aaSamw 				ret = SA_OK;
634*da6c28aaSamw 			} else {
635*da6c28aaSamw 				ret = SA_BAD_VALUE;
636*da6c28aaSamw 			}
637*da6c28aaSamw 			break;
638*da6c28aaSamw 		case OPT_TYPE_NAME:
639*da6c28aaSamw 			/*
640*da6c28aaSamw 			 * Make sure no invalid characters
641*da6c28aaSamw 			 */
642*da6c28aaSamw 			if (validresource(value) == B_FALSE)
643*da6c28aaSamw 				ret = SA_BAD_VALUE;
644*da6c28aaSamw 			break;
645*da6c28aaSamw 		case OPT_TYPE_STRING:
646*da6c28aaSamw 			/* whatever is here should be ok */
647*da6c28aaSamw 			break;
648*da6c28aaSamw 		default:
649*da6c28aaSamw 			break;
650*da6c28aaSamw 		}
651*da6c28aaSamw 	}
652*da6c28aaSamw 
653*da6c28aaSamw 	if (value != NULL)
654*da6c28aaSamw 		sa_free_attr_string(value);
655*da6c28aaSamw 	if (ret == SA_OK && optdefs[optindex].check != NULL)
656*da6c28aaSamw 		/* do the property specific check */
657*da6c28aaSamw 		ret = optdefs[optindex].check(property);
658*da6c28aaSamw 
659*da6c28aaSamw 	if (propname != NULL)
660*da6c28aaSamw 		sa_free_attr_string(propname);
661*da6c28aaSamw 	return (ret);
662*da6c28aaSamw }
663*da6c28aaSamw 
664*da6c28aaSamw /*
665*da6c28aaSamw  * Protocol management functions
666*da6c28aaSamw  *
667*da6c28aaSamw  * properties defined in the default files are defined in
668*da6c28aaSamw  * proto_option_defs for parsing and validation.
669*da6c28aaSamw  */
670*da6c28aaSamw 
671*da6c28aaSamw struct smb_proto_option_defs {
672*da6c28aaSamw 	char *name;	/* display name -- remove protocol identifier */
673*da6c28aaSamw 	int smb_index;
674*da6c28aaSamw 	int32_t minval;
675*da6c28aaSamw 	int32_t maxval; /* In case of length of string this should be max */
676*da6c28aaSamw 	int (*validator)(int, char *);
677*da6c28aaSamw 	int32_t	refresh;
678*da6c28aaSamw } smb_proto_options[] = {
679*da6c28aaSamw 	{ SMB_CD_SYS_CMNT,
680*da6c28aaSamw 	    SMB_CI_SYS_CMNT, 0, MAX_VALUE_BUFLEN,
681*da6c28aaSamw 	    string_length_check_validator, SMB_REFRESH_REFRESH},
682*da6c28aaSamw 	{ SMB_CD_MAX_WORKERS,
683*da6c28aaSamw 	    SMB_CI_MAX_WORKERS, 64, 1024, range_check_validator,
684*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
685*da6c28aaSamw 	{ SMB_CD_NBSCOPE,
686*da6c28aaSamw 	    SMB_CI_NBSCOPE, 0, MAX_VALUE_BUFLEN,
687*da6c28aaSamw 	    string_length_check_validator, SMB_REFRESH_REFRESH},
688*da6c28aaSamw 	{ SMB_CD_RDR_IPCMODE,
689*da6c28aaSamw 	    SMB_CI_RDR_IPCMODE, 0, 0, ipc_mode_validator, SMB_REFRESH_REFRESH},
690*da6c28aaSamw 	{ SMB_CD_LM_LEVEL,
691*da6c28aaSamw 	    SMB_CI_LM_LEVEL, 2, 5, range_check_validator, SMB_REFRESH_REFRESH},
692*da6c28aaSamw 	{ SMB_CD_KEEPALIVE,
693*da6c28aaSamw 	    SMB_CI_KEEPALIVE, 20, 5400, range_check_validator_zero_ok,
694*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
695*da6c28aaSamw 	{ SMB_CD_WINS_SRV1,
696*da6c28aaSamw 	    SMB_CI_WINS_SRV1, 0, MAX_VALUE_BUFLEN,
697*da6c28aaSamw 	    ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
698*da6c28aaSamw 	{ SMB_CD_WINS_SRV2,
699*da6c28aaSamw 	    SMB_CI_WINS_SRV2, 0, MAX_VALUE_BUFLEN,
700*da6c28aaSamw 	    ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
701*da6c28aaSamw 	{ SMB_CD_WINS_EXCL,
702*da6c28aaSamw 	    SMB_CI_WINS_EXCL, 0, MAX_VALUE_BUFLEN,
703*da6c28aaSamw 	    ip_address_csv_list_validator_empty_ok, SMB_REFRESH_REFRESH},
704*da6c28aaSamw 	{ SMB_CD_SIGNING_ENABLE,
705*da6c28aaSamw 	    SMB_CI_SIGNING_ENABLE, 0, 0, true_false_validator,
706*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
707*da6c28aaSamw 	{ SMB_CD_SIGNING_REQD,
708*da6c28aaSamw 	    SMB_CI_SIGNING_REQD, 0, 0, true_false_validator,
709*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
710*da6c28aaSamw 	{ SMB_CD_RESTRICT_ANON,
711*da6c28aaSamw 	    SMB_CI_RESTRICT_ANON, 0, 0, true_false_validator,
712*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
713*da6c28aaSamw 	{ SMB_CD_DOMAIN_SRV,
714*da6c28aaSamw 	    SMB_CI_DOMAIN_SRV, 0, MAX_VALUE_BUFLEN,
715*da6c28aaSamw 	    ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
716*da6c28aaSamw 	{ SMB_CD_ADS_ENABLE,
717*da6c28aaSamw 	    SMB_CI_ADS_ENABLE, 0, 0, true_false_validator, SMB_REFRESH_REFRESH},
718*da6c28aaSamw 	{ SMB_CD_ADS_USER,
719*da6c28aaSamw 	    SMB_CI_ADS_USER, 0, MAX_VALUE_BUFLEN,
720*da6c28aaSamw 	    string_length_check_validator, SMB_REFRESH_REFRESH},
721*da6c28aaSamw 	{ SMB_CD_ADS_USER_CONTAINER,
722*da6c28aaSamw 	    SMB_CI_ADS_USER_CONTAINER, 0, MAX_VALUE_BUFLEN,
723*da6c28aaSamw 	    string_length_check_validator, SMB_REFRESH_REFRESH},
724*da6c28aaSamw 	{ SMB_CD_ADS_DOMAIN,
725*da6c28aaSamw 	    SMB_CI_ADS_DOMAIN, 0, MAX_VALUE_BUFLEN,
726*da6c28aaSamw 	    string_length_check_validator, SMB_REFRESH_REFRESH},
727*da6c28aaSamw 	{ SMB_CD_ADS_PASSWD,
728*da6c28aaSamw 	    SMB_CI_ADS_PASSWD, 0, MAX_VALUE_BUFLEN,
729*da6c28aaSamw 	    string_length_check_validator, SMB_REFRESH_REFRESH},
730*da6c28aaSamw 	{ SMB_CD_ADS_IPLOOKUP,
731*da6c28aaSamw 	    SMB_CI_ADS_IPLOOKUP, 0, 0, true_false_validator,
732*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
733*da6c28aaSamw 	{ SMB_CD_ADS_SITE,
734*da6c28aaSamw 	    SMB_CI_ADS_SITE, 0, MAX_VALUE_BUFLEN,
735*da6c28aaSamw 	    string_length_check_validator, SMB_REFRESH_REFRESH},
736*da6c28aaSamw 	{ SMB_CD_DYNDNS_ENABLE,
737*da6c28aaSamw 	    SMB_CI_DYNDNS_ENABLE, 0, 0, true_false_validator,
738*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
739*da6c28aaSamw 	{ SMB_CD_DYNDNS_RETRY_SEC,
740*da6c28aaSamw 	    SMB_CI_DYNDNS_RETRY_SEC, 0, 20, range_check_validator,
741*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
742*da6c28aaSamw 	{ SMB_CD_DYNDNS_RETRY_COUNT,
743*da6c28aaSamw 	    SMB_CI_DYNDNS_RETRY_COUNT, 3, 5, range_check_validator,
744*da6c28aaSamw 	    SMB_REFRESH_REFRESH},
745*da6c28aaSamw 	{ SMB_CD_AUTOHOME_MAP,
746*da6c28aaSamw 	    SMB_CI_AUTOHOME_MAP, 0, MAX_VALUE_BUFLEN,
747*da6c28aaSamw 	    path_validator},
748*da6c28aaSamw 	{NULL, -1, 0, 0, NULL}
749*da6c28aaSamw };
750*da6c28aaSamw 
751*da6c28aaSamw /*
752*da6c28aaSamw  * Check the range of value as int range.
753*da6c28aaSamw  */
754*da6c28aaSamw static int
755*da6c28aaSamw range_check_validator(int index, char *value)
756*da6c28aaSamw {
757*da6c28aaSamw 	int ret = SA_OK;
758*da6c28aaSamw 
759*da6c28aaSamw 	if (!is_a_number(value)) {
760*da6c28aaSamw 		ret = SA_BAD_VALUE;
761*da6c28aaSamw 	} else {
762*da6c28aaSamw 		int val;
763*da6c28aaSamw 		val = strtoul(value, NULL, 0);
764*da6c28aaSamw 		if (val < smb_proto_options[index].minval ||
765*da6c28aaSamw 		    val > smb_proto_options[index].maxval)
766*da6c28aaSamw 			ret = SA_BAD_VALUE;
767*da6c28aaSamw 	}
768*da6c28aaSamw 	return (ret);
769*da6c28aaSamw }
770*da6c28aaSamw 
771*da6c28aaSamw /*
772*da6c28aaSamw  * Check the range of value as int range.
773*da6c28aaSamw  */
774*da6c28aaSamw static int
775*da6c28aaSamw range_check_validator_zero_ok(int index, char *value)
776*da6c28aaSamw {
777*da6c28aaSamw 	int ret = SA_OK;
778*da6c28aaSamw 
779*da6c28aaSamw 	if (!is_a_number(value)) {
780*da6c28aaSamw 		ret = SA_BAD_VALUE;
781*da6c28aaSamw 	} else {
782*da6c28aaSamw 		int val;
783*da6c28aaSamw 		val = strtoul(value, NULL, 0);
784*da6c28aaSamw 		if (val == 0)
785*da6c28aaSamw 			ret = SA_OK;
786*da6c28aaSamw 		else {
787*da6c28aaSamw 			if (val < smb_proto_options[index].minval ||
788*da6c28aaSamw 			    val > smb_proto_options[index].maxval)
789*da6c28aaSamw 			ret = SA_BAD_VALUE;
790*da6c28aaSamw 		}
791*da6c28aaSamw 	}
792*da6c28aaSamw 	return (ret);
793*da6c28aaSamw }
794*da6c28aaSamw 
795*da6c28aaSamw /*
796*da6c28aaSamw  * Check the length of the string
797*da6c28aaSamw  */
798*da6c28aaSamw static int
799*da6c28aaSamw string_length_check_validator(int index, char *value)
800*da6c28aaSamw {
801*da6c28aaSamw 	int ret = SA_OK;
802*da6c28aaSamw 
803*da6c28aaSamw 	if (value == NULL)
804*da6c28aaSamw 		return (SA_BAD_VALUE);
805*da6c28aaSamw 	if (strlen(value) > smb_proto_options[index].maxval)
806*da6c28aaSamw 		ret = SA_BAD_VALUE;
807*da6c28aaSamw 	return (ret);
808*da6c28aaSamw }
809*da6c28aaSamw 
810*da6c28aaSamw /*
811*da6c28aaSamw  * Check yes/no
812*da6c28aaSamw  */
813*da6c28aaSamw /*ARGSUSED*/
814*da6c28aaSamw static int
815*da6c28aaSamw true_false_validator(int index, char *value)
816*da6c28aaSamw {
817*da6c28aaSamw 	if (value == NULL)
818*da6c28aaSamw 		return (SA_BAD_VALUE);
819*da6c28aaSamw 	if ((strcasecmp(value, "true") == 0) ||
820*da6c28aaSamw 	    (strcasecmp(value, "false") == 0))
821*da6c28aaSamw 		return (SA_OK);
822*da6c28aaSamw 	return (SA_BAD_VALUE);
823*da6c28aaSamw }
824*da6c28aaSamw 
825*da6c28aaSamw /*
826*da6c28aaSamw  * Check IP address.
827*da6c28aaSamw  */
828*da6c28aaSamw /*ARGSUSED*/
829*da6c28aaSamw static int
830*da6c28aaSamw ip_address_validator_empty_ok(int index, char *value)
831*da6c28aaSamw {
832*da6c28aaSamw 	char sbytes[16];
833*da6c28aaSamw 	int len;
834*da6c28aaSamw 
835*da6c28aaSamw 	if (value == NULL)
836*da6c28aaSamw 		return (SA_OK);
837*da6c28aaSamw 	len = strlen(value);
838*da6c28aaSamw 	if (len == 0)
839*da6c28aaSamw 		return (SA_OK);
840*da6c28aaSamw 	if (inet_pton(AF_INET, value, (void *)sbytes) != 1)
841*da6c28aaSamw 		return (SA_BAD_VALUE);
842*da6c28aaSamw 
843*da6c28aaSamw 	return (SA_OK);
844*da6c28aaSamw }
845*da6c28aaSamw 
846*da6c28aaSamw /*
847*da6c28aaSamw  * Check IP address list
848*da6c28aaSamw  */
849*da6c28aaSamw /*ARGSUSED*/
850*da6c28aaSamw static int
851*da6c28aaSamw ip_address_csv_list_validator_empty_ok(int index, char *value)
852*da6c28aaSamw {
853*da6c28aaSamw 	char sbytes[16];
854*da6c28aaSamw 	char *ip, *tmp, *ctx;
855*da6c28aaSamw 
856*da6c28aaSamw 	if (value == NULL || *value == '\0')
857*da6c28aaSamw 		return (SA_OK);
858*da6c28aaSamw 
859*da6c28aaSamw 	if (strlen(value) > MAX_VALUE_BUFLEN)
860*da6c28aaSamw 		return (SA_BAD_VALUE);
861*da6c28aaSamw 
862*da6c28aaSamw 	if ((tmp = strdup(value)) == NULL)
863*da6c28aaSamw 		return (SA_NO_MEMORY);
864*da6c28aaSamw 
865*da6c28aaSamw 	ip = strtok_r(tmp, ",", &ctx);
866*da6c28aaSamw 	while (ip) {
867*da6c28aaSamw 		if (strlen(ip) == 0) {
868*da6c28aaSamw 			free(tmp);
869*da6c28aaSamw 			return (SA_BAD_VALUE);
870*da6c28aaSamw 		}
871*da6c28aaSamw 		if (*ip != 0) {
872*da6c28aaSamw 			if (inet_pton(AF_INET, ip,
873*da6c28aaSamw 			    (void *)sbytes) != 1) {
874*da6c28aaSamw 				free(tmp);
875*da6c28aaSamw 				return (SA_BAD_VALUE);
876*da6c28aaSamw 			}
877*da6c28aaSamw 		}
878*da6c28aaSamw 		ip = strtok_r(0, ",", &ctx);
879*da6c28aaSamw 	}
880*da6c28aaSamw 
881*da6c28aaSamw 	free(tmp);
882*da6c28aaSamw 	return (SA_OK);
883*da6c28aaSamw }
884*da6c28aaSamw 
885*da6c28aaSamw /*
886*da6c28aaSamw  * Check IPC mode
887*da6c28aaSamw  */
888*da6c28aaSamw /*ARGSUSED*/
889*da6c28aaSamw static int
890*da6c28aaSamw ipc_mode_validator(int index, char *value)
891*da6c28aaSamw {
892*da6c28aaSamw 	if (value == NULL)
893*da6c28aaSamw 		return (SA_BAD_VALUE);
894*da6c28aaSamw 	if (strcasecmp(value, "anon") == 0)
895*da6c28aaSamw 		return (SA_OK);
896*da6c28aaSamw 	if (strcasecmp(value, "auth") == 0)
897*da6c28aaSamw 		return (SA_OK);
898*da6c28aaSamw 	return (SA_BAD_VALUE);
899*da6c28aaSamw }
900*da6c28aaSamw 
901*da6c28aaSamw /*
902*da6c28aaSamw  * Check path
903*da6c28aaSamw  */
904*da6c28aaSamw /*ARGSUSED*/
905*da6c28aaSamw static int
906*da6c28aaSamw path_validator(int index, char *value)
907*da6c28aaSamw {
908*da6c28aaSamw 	struct stat buffer;
909*da6c28aaSamw 	int fd, status;
910*da6c28aaSamw 
911*da6c28aaSamw 	if (value == NULL)
912*da6c28aaSamw 		return (SA_BAD_VALUE);
913*da6c28aaSamw 
914*da6c28aaSamw 	fd = open(value, O_RDONLY);
915*da6c28aaSamw 	if (fd < 0)
916*da6c28aaSamw 		return (SA_BAD_VALUE);
917*da6c28aaSamw 
918*da6c28aaSamw 	status = fstat(fd, &buffer);
919*da6c28aaSamw 	(void) close(fd);
920*da6c28aaSamw 
921*da6c28aaSamw 	if (status < 0)
922*da6c28aaSamw 		return (SA_BAD_VALUE);
923*da6c28aaSamw 
924*da6c28aaSamw 	if (buffer.st_mode & S_IFDIR)
925*da6c28aaSamw 		return (SA_OK);
926*da6c28aaSamw 	return (SA_BAD_VALUE);
927*da6c28aaSamw }
928*da6c28aaSamw 
929*da6c28aaSamw /*
930*da6c28aaSamw  * the protoset holds the defined options so we don't have to read
931*da6c28aaSamw  * them multiple times
932*da6c28aaSamw  */
933*da6c28aaSamw static sa_protocol_properties_t protoset;
934*da6c28aaSamw 
935*da6c28aaSamw static int
936*da6c28aaSamw findprotoopt(char *name)
937*da6c28aaSamw {
938*da6c28aaSamw 	int i;
939*da6c28aaSamw 	for (i = 0; smb_proto_options[i].name != NULL; i++) {
940*da6c28aaSamw 		if (strcasecmp(smb_proto_options[i].name, name) == 0)
941*da6c28aaSamw 			return (i);
942*da6c28aaSamw 	}
943*da6c28aaSamw 	return (-1);
944*da6c28aaSamw }
945*da6c28aaSamw 
946*da6c28aaSamw /*
947*da6c28aaSamw  * smb_load_proto_properties()
948*da6c28aaSamw  *
949*da6c28aaSamw  * read the smb config values from SMF.
950*da6c28aaSamw  */
951*da6c28aaSamw 
952*da6c28aaSamw static int
953*da6c28aaSamw smb_load_proto_properties()
954*da6c28aaSamw {
955*da6c28aaSamw 	sa_property_t prop;
956*da6c28aaSamw 	int index;
957*da6c28aaSamw 	char *value;
958*da6c28aaSamw 
959*da6c28aaSamw 	protoset = sa_create_protocol_properties(SMB_PROTOCOL_NAME);
960*da6c28aaSamw 	if (protoset == NULL)
961*da6c28aaSamw 		return (SA_NO_MEMORY);
962*da6c28aaSamw 
963*da6c28aaSamw 	if (smb_config_load() != 0)
964*da6c28aaSamw 		return (SA_CONFIG_ERR);
965*da6c28aaSamw 	for (index = 0; smb_proto_options[index].name != NULL; index++) {
966*da6c28aaSamw 		value = smb_config_getenv(smb_proto_options[index].smb_index);
967*da6c28aaSamw 		prop = sa_create_property(
968*da6c28aaSamw 		    smb_proto_options[index].name, value);
969*da6c28aaSamw 		(void) sa_add_protocol_property(protoset, prop);
970*da6c28aaSamw 	}
971*da6c28aaSamw 	return (SA_OK);
972*da6c28aaSamw }
973*da6c28aaSamw 
974*da6c28aaSamw /*
975*da6c28aaSamw  * smb_share_init()
976*da6c28aaSamw  *
977*da6c28aaSamw  * Initialize the smb plugin.
978*da6c28aaSamw  */
979*da6c28aaSamw 
980*da6c28aaSamw static int
981*da6c28aaSamw smb_share_init(void)
982*da6c28aaSamw {
983*da6c28aaSamw 	int ret = SA_OK;
984*da6c28aaSamw 
985*da6c28aaSamw 	if (sa_plugin_ops.sa_init != smb_share_init)
986*da6c28aaSamw 		return (SA_SYSTEM_ERR);
987*da6c28aaSamw 
988*da6c28aaSamw 	if (smb_load_proto_properties() != SA_OK)
989*da6c28aaSamw 		return (SA_SYSTEM_ERR);
990*da6c28aaSamw 
991*da6c28aaSamw 	return (ret);
992*da6c28aaSamw }
993*da6c28aaSamw 
994*da6c28aaSamw /*
995*da6c28aaSamw  * smb_share_fini()
996*da6c28aaSamw  *
997*da6c28aaSamw  */
998*da6c28aaSamw static void
999*da6c28aaSamw smb_share_fini(void)
1000*da6c28aaSamw {
1001*da6c28aaSamw 	xmlFreeNode(protoset);
1002*da6c28aaSamw 	protoset = NULL;
1003*da6c28aaSamw }
1004*da6c28aaSamw 
1005*da6c28aaSamw /*
1006*da6c28aaSamw  * smb_get_proto_set()
1007*da6c28aaSamw  *
1008*da6c28aaSamw  * Return an optionset with all the protocol specific properties in
1009*da6c28aaSamw  * it.
1010*da6c28aaSamw  */
1011*da6c28aaSamw static sa_protocol_properties_t
1012*da6c28aaSamw smb_get_proto_set(void)
1013*da6c28aaSamw {
1014*da6c28aaSamw 	return (protoset);
1015*da6c28aaSamw }
1016*da6c28aaSamw 
1017*da6c28aaSamw /*
1018*da6c28aaSamw  * How long to wait for service to come online
1019*da6c28aaSamw  */
1020*da6c28aaSamw #define	WAIT_FOR_SERVICE	15
1021*da6c28aaSamw 
1022*da6c28aaSamw /*
1023*da6c28aaSamw  * smb_enable_service()
1024*da6c28aaSamw  *
1025*da6c28aaSamw  */
1026*da6c28aaSamw static int
1027*da6c28aaSamw smb_enable_service(void)
1028*da6c28aaSamw {
1029*da6c28aaSamw 	int i;
1030*da6c28aaSamw 	int ret = SA_OK;
1031*da6c28aaSamw 
1032*da6c28aaSamw 	if (!smb_isonline()) {
1033*da6c28aaSamw 		if (smf_enable_instance(SMBD_DEFAULT_INSTANCE_FMRI, 0) != 0) {
1034*da6c28aaSamw 			(void) fprintf(stderr,
1035*da6c28aaSamw 			    dgettext(TEXT_DOMAIN,
1036*da6c28aaSamw 			    "%s failed to restart: %s\n"),
1037*da6c28aaSamw 			    scf_strerror(scf_error()));
1038*da6c28aaSamw 			return (SA_CONFIG_ERR);
1039*da6c28aaSamw 		}
1040*da6c28aaSamw 
1041*da6c28aaSamw 		/* Wait for service to come online */
1042*da6c28aaSamw 		for (i = 0; i < WAIT_FOR_SERVICE; i++) {
1043*da6c28aaSamw 			if (smb_isonline()) {
1044*da6c28aaSamw 				ret = SA_OK;
1045*da6c28aaSamw 				break;
1046*da6c28aaSamw 			} else {
1047*da6c28aaSamw 				ret = SA_BUSY;
1048*da6c28aaSamw 				(void) sleep(1);
1049*da6c28aaSamw 			}
1050*da6c28aaSamw 		}
1051*da6c28aaSamw 	}
1052*da6c28aaSamw 	return (ret);
1053*da6c28aaSamw }
1054*da6c28aaSamw 
1055*da6c28aaSamw /*
1056*da6c28aaSamw  * smb_validate_proto_prop(index, name, value)
1057*da6c28aaSamw  *
1058*da6c28aaSamw  * Verify that the property specified by name can take the new
1059*da6c28aaSamw  * value. This is a sanity check to prevent bad values getting into
1060*da6c28aaSamw  * the default files.
1061*da6c28aaSamw  */
1062*da6c28aaSamw static int
1063*da6c28aaSamw smb_validate_proto_prop(int index, char *name, char *value)
1064*da6c28aaSamw {
1065*da6c28aaSamw 	if ((name == NULL) || (index < 0))
1066*da6c28aaSamw 		return (SA_BAD_VALUE);
1067*da6c28aaSamw 
1068*da6c28aaSamw 	if (smb_proto_options[index].validator == NULL)
1069*da6c28aaSamw 		return (SA_OK);
1070*da6c28aaSamw 
1071*da6c28aaSamw 	if (smb_proto_options[index].validator(index, value) == SA_OK)
1072*da6c28aaSamw 		return (SA_OK);
1073*da6c28aaSamw 	return (SA_BAD_VALUE);
1074*da6c28aaSamw }
1075*da6c28aaSamw 
1076*da6c28aaSamw /*
1077*da6c28aaSamw  * smb_set_proto_prop(prop)
1078*da6c28aaSamw  *
1079*da6c28aaSamw  * check that prop is valid.
1080*da6c28aaSamw  */
1081*da6c28aaSamw /*ARGSUSED*/
1082*da6c28aaSamw static int
1083*da6c28aaSamw smb_set_proto_prop(sa_property_t prop)
1084*da6c28aaSamw {
1085*da6c28aaSamw 	int ret = SA_OK;
1086*da6c28aaSamw 	char *name;
1087*da6c28aaSamw 	char *value;
1088*da6c28aaSamw 	int index = -1;
1089*da6c28aaSamw 
1090*da6c28aaSamw 	name = sa_get_property_attr(prop, "type");
1091*da6c28aaSamw 	value = sa_get_property_attr(prop, "value");
1092*da6c28aaSamw 	if (name != NULL && value != NULL) {
1093*da6c28aaSamw 		index = findprotoopt(name);
1094*da6c28aaSamw 		if (index >= 0) {
1095*da6c28aaSamw 			/* should test for valid value */
1096*da6c28aaSamw 			ret = smb_validate_proto_prop(index, name, value);
1097*da6c28aaSamw 			if (ret == SA_OK) {
1098*da6c28aaSamw 				/* Save to SMF */
1099*da6c28aaSamw 				smb_config_setenv(
1100*da6c28aaSamw 				    smb_proto_options[index].smb_index, value);
1101*da6c28aaSamw 				/*
1102*da6c28aaSamw 				 * Specialized refresh mechanisms can
1103*da6c28aaSamw 				 * be flagged in the proto_options and
1104*da6c28aaSamw 				 * processed here.
1105*da6c28aaSamw 				 */
1106*da6c28aaSamw 				if (smb_proto_options[index].refresh &
1107*da6c28aaSamw 				    SMB_REFRESH_REFRESH)
1108*da6c28aaSamw 					(void) smf_refresh_instance(
1109*da6c28aaSamw 					    SMBD_DEFAULT_INSTANCE_FMRI);
1110*da6c28aaSamw 				else if (smb_proto_options[index].refresh &
1111*da6c28aaSamw 				    SMB_REFRESH_RESTART)
1112*da6c28aaSamw 					(void) smf_restart_instance(
1113*da6c28aaSamw 					    SMBD_DEFAULT_INSTANCE_FMRI);
1114*da6c28aaSamw 			}
1115*da6c28aaSamw 		}
1116*da6c28aaSamw 	}
1117*da6c28aaSamw 	if (name != NULL)
1118*da6c28aaSamw 		sa_free_attr_string(name);
1119*da6c28aaSamw 	if (value != NULL)
1120*da6c28aaSamw 		sa_free_attr_string(value);
1121*da6c28aaSamw 
1122*da6c28aaSamw 	return (ret);
1123*da6c28aaSamw }
1124*da6c28aaSamw 
1125*da6c28aaSamw /*
1126*da6c28aaSamw  * smb_get_status()
1127*da6c28aaSamw  *
1128*da6c28aaSamw  * What is the current status of the smbd? We use the SMF state here.
1129*da6c28aaSamw  * Caller must free the returned value.
1130*da6c28aaSamw  */
1131*da6c28aaSamw 
1132*da6c28aaSamw static char *
1133*da6c28aaSamw smb_get_status(void)
1134*da6c28aaSamw {
1135*da6c28aaSamw 	char *state = NULL;
1136*da6c28aaSamw 	state = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI);
1137*da6c28aaSamw 	return (state != NULL ? state : "-");
1138*da6c28aaSamw }
1139*da6c28aaSamw 
1140*da6c28aaSamw /*
1141*da6c28aaSamw  * This protocol plugin require resource names
1142*da6c28aaSamw  */
1143*da6c28aaSamw static uint64_t
1144*da6c28aaSamw smb_share_features(void)
1145*da6c28aaSamw {
1146*da6c28aaSamw 	return (SA_FEATURE_RESOURCE | SA_FEATURE_ALLOWSUBDIRS |
1147*da6c28aaSamw 	    SA_FEATURE_ALLOWPARDIRS);
1148*da6c28aaSamw }
1149*da6c28aaSamw 
1150*da6c28aaSamw /*
1151*da6c28aaSamw  * This should be used to convert lmshare_info to sa_resource_t
1152*da6c28aaSamw  * Should only be needed to build temp shares/resources to be
1153*da6c28aaSamw  * supplied to sharemanager to display temp shares.
1154*da6c28aaSamw  */
1155*da6c28aaSamw static int
1156*da6c28aaSamw smb_build_tmp_sa_resource(sa_handle_t handle, lmshare_info_t *si)
1157*da6c28aaSamw {
1158*da6c28aaSamw 	int err;
1159*da6c28aaSamw 	sa_share_t share;
1160*da6c28aaSamw 	sa_group_t group;
1161*da6c28aaSamw 	sa_resource_t resource;
1162*da6c28aaSamw 
1163*da6c28aaSamw 	if (si == NULL)
1164*da6c28aaSamw 		return (SA_INVALID_NAME);
1165*da6c28aaSamw 
1166*da6c28aaSamw 	/*
1167*da6c28aaSamw 	 * First determine if the "share path" is already shared
1168*da6c28aaSamw 	 * somewhere. If it is, we have to use it as the authority on
1169*da6c28aaSamw 	 * where the transient share lives so will use it's parent
1170*da6c28aaSamw 	 * group. If it doesn't exist, it needs to land in "smb".
1171*da6c28aaSamw 	 */
1172*da6c28aaSamw 
1173*da6c28aaSamw 	share = sa_find_share(handle, si->directory);
1174*da6c28aaSamw 	if (share != NULL) {
1175*da6c28aaSamw 		group = sa_get_parent_group(share);
1176*da6c28aaSamw 	} else {
1177*da6c28aaSamw 		group = smb_get_smb_share_group(handle);
1178*da6c28aaSamw 		if (group == NULL)
1179*da6c28aaSamw 			return (SA_NO_SUCH_GROUP);
1180*da6c28aaSamw 		share = sa_get_share(group, si->directory);
1181*da6c28aaSamw 		if (share == NULL) {
1182*da6c28aaSamw 			share = sa_add_share(group, si->directory,
1183*da6c28aaSamw 			    SA_SHARE_TRANSIENT, &err);
1184*da6c28aaSamw 			if (share == NULL)
1185*da6c28aaSamw 				return (SA_NO_SUCH_PATH);
1186*da6c28aaSamw 		}
1187*da6c28aaSamw 	}
1188*da6c28aaSamw 
1189*da6c28aaSamw 	/*
1190*da6c28aaSamw 	 * Now handle the resource. Make sure that the resource is
1191*da6c28aaSamw 	 * transient and added to the share.
1192*da6c28aaSamw 	 */
1193*da6c28aaSamw 	resource = sa_get_share_resource(share, si->share_name);
1194*da6c28aaSamw 	if (resource == NULL) {
1195*da6c28aaSamw 		resource = sa_add_resource(share,
1196*da6c28aaSamw 		    si->share_name, SA_SHARE_TRANSIENT, &err);
1197*da6c28aaSamw 		if (resource == NULL)
1198*da6c28aaSamw 			return (SA_NO_SUCH_RESOURCE);
1199*da6c28aaSamw 	}
1200*da6c28aaSamw 
1201*da6c28aaSamw 	/* set resource attributes now */
1202*da6c28aaSamw 	(void) sa_set_resource_attr(resource, "description", si->comment);
1203*da6c28aaSamw 	(void) sa_set_resource_attr(resource, SHOPT_AD_CONTAINER,
1204*da6c28aaSamw 	    si->container);
1205*da6c28aaSamw 
1206*da6c28aaSamw 	return (SA_OK);
1207*da6c28aaSamw }
1208*da6c28aaSamw 
1209*da6c28aaSamw /*
1210*da6c28aaSamw  * Return smb transient shares.  Note that we really want to look at
1211*da6c28aaSamw  * all current shares from SMB in order to determine this. Transient
1212*da6c28aaSamw  * shares should be those that don't appear in either the SMF or ZFS
1213*da6c28aaSamw  * configurations.  Those that are in the repositories will be
1214*da6c28aaSamw  * filtered out by smb_build_tmp_sa_resource.
1215*da6c28aaSamw  */
1216*da6c28aaSamw static int
1217*da6c28aaSamw smb_list_transient(sa_handle_t handle)
1218*da6c28aaSamw {
1219*da6c28aaSamw 	int i, offset, num;
1220*da6c28aaSamw 	lmshare_list_t list;
1221*da6c28aaSamw 	int res;
1222*da6c28aaSamw 
1223*da6c28aaSamw 	num = lmshrd_num_shares();
1224*da6c28aaSamw 	if (num <= 0)
1225*da6c28aaSamw 		return (SA_OK);
1226*da6c28aaSamw 	offset = 0;
1227*da6c28aaSamw 	while (lmshrd_list(offset, &list) != NERR_InternalError) {
1228*da6c28aaSamw 		if (list.no == 0)
1229*da6c28aaSamw 			break;
1230*da6c28aaSamw 		for (i = 0; i < list.no; i++) {
1231*da6c28aaSamw 			res = smb_build_tmp_sa_resource(handle,
1232*da6c28aaSamw 			    &(list.smbshr[i]));
1233*da6c28aaSamw 			if (res != SA_OK)
1234*da6c28aaSamw 				return (res);
1235*da6c28aaSamw 		}
1236*da6c28aaSamw 		offset += list.no;
1237*da6c28aaSamw 	}
1238*da6c28aaSamw 
1239*da6c28aaSamw 	return (SA_OK);
1240*da6c28aaSamw }
1241*da6c28aaSamw 
1242*da6c28aaSamw /*
1243*da6c28aaSamw  * fix_resource_name(share, name,  prefix)
1244*da6c28aaSamw  *
1245*da6c28aaSamw  * Construct a name where the ZFS dataset has the prefix replaced with "name".
1246*da6c28aaSamw  */
1247*da6c28aaSamw static char *
1248*da6c28aaSamw fix_resource_name(sa_share_t share, char *name, char *prefix)
1249*da6c28aaSamw {
1250*da6c28aaSamw 	char *dataset = NULL;
1251*da6c28aaSamw 	char *newname = NULL;
1252*da6c28aaSamw 	size_t psize;
1253*da6c28aaSamw 	size_t nsize;
1254*da6c28aaSamw 
1255*da6c28aaSamw 	dataset = sa_get_share_attr(share, "dataset");
1256*da6c28aaSamw 
1257*da6c28aaSamw 	if (dataset != NULL && strcmp(dataset, prefix) != 0) {
1258*da6c28aaSamw 		psize = strlen(prefix);
1259*da6c28aaSamw 		if (strncmp(dataset, prefix, psize) == 0) {
1260*da6c28aaSamw 			/* need string plus ',' and NULL */
1261*da6c28aaSamw 			nsize = (strlen(dataset) - psize) + strlen(name) + 2;
1262*da6c28aaSamw 			newname = calloc(nsize, 1);
1263*da6c28aaSamw 			if (newname != NULL) {
1264*da6c28aaSamw 				(void) snprintf(newname, nsize, "%s%s", name,
1265*da6c28aaSamw 				    dataset + psize);
1266*da6c28aaSamw 				sa_fix_resource_name(newname);
1267*da6c28aaSamw 			}
1268*da6c28aaSamw 			sa_free_attr_string(dataset);
1269*da6c28aaSamw 			return (newname);
1270*da6c28aaSamw 		}
1271*da6c28aaSamw 	}
1272*da6c28aaSamw 	if (dataset != NULL)
1273*da6c28aaSamw 		sa_free_attr_string(dataset);
1274*da6c28aaSamw 	return (strdup(name));
1275*da6c28aaSamw }
1276*da6c28aaSamw 
1277*da6c28aaSamw /*
1278*da6c28aaSamw  * smb_parse_optstring(group, options)
1279*da6c28aaSamw  *
1280*da6c28aaSamw  * parse a compact option string into individual options. This allows
1281*da6c28aaSamw  * ZFS sharesmb and sharemgr "share" command to work.  group can be a
1282*da6c28aaSamw  * group, a share or a resource.
1283*da6c28aaSamw  */
1284*da6c28aaSamw static int
1285*da6c28aaSamw smb_parse_optstring(sa_group_t group, char *options)
1286*da6c28aaSamw {
1287*da6c28aaSamw 	char *dup;
1288*da6c28aaSamw 	char *base;
1289*da6c28aaSamw 	char *lasts;
1290*da6c28aaSamw 	char *token;
1291*da6c28aaSamw 	sa_optionset_t optionset;
1292*da6c28aaSamw 	sa_group_t parent = NULL;
1293*da6c28aaSamw 	sa_resource_t resource = NULL;
1294*da6c28aaSamw 	int iszfs = 0;
1295*da6c28aaSamw 	int persist = 0;
1296*da6c28aaSamw 	int need_optionset = 0;
1297*da6c28aaSamw 	int ret = SA_OK;
1298*da6c28aaSamw 	sa_property_t prop;
1299*da6c28aaSamw 
1300*da6c28aaSamw 	/*
1301*da6c28aaSamw 	 * In order to not attempt to change ZFS properties unless
1302*da6c28aaSamw 	 * absolutely necessary, we never do it in the legacy parsing
1303*da6c28aaSamw 	 * so we need to keep track of this.
1304*da6c28aaSamw 	 */
1305*da6c28aaSamw 	if (sa_is_share(group)) {
1306*da6c28aaSamw 		char *zfs;
1307*da6c28aaSamw 
1308*da6c28aaSamw 		parent = sa_get_parent_group(group);
1309*da6c28aaSamw 		if (parent != NULL) {
1310*da6c28aaSamw 			zfs = sa_get_group_attr(parent, "zfs");
1311*da6c28aaSamw 			if (zfs != NULL) {
1312*da6c28aaSamw 				sa_free_attr_string(zfs);
1313*da6c28aaSamw 				iszfs = 1;
1314*da6c28aaSamw 			}
1315*da6c28aaSamw 		}
1316*da6c28aaSamw 	} else {
1317*da6c28aaSamw 		iszfs = sa_group_is_zfs(group);
1318*da6c28aaSamw 		/*
1319*da6c28aaSamw 		 * If a ZFS group, then we need to see if a resource
1320*da6c28aaSamw 		 * name is being set. If so, bail with
1321*da6c28aaSamw 		 * SA_PROP_SHARE_ONLY, so we come back in with a share
1322*da6c28aaSamw 		 * instead of a group.
1323*da6c28aaSamw 		 */
1324*da6c28aaSamw 		if (strncmp(options, "name=", sizeof ("name=") - 1) == 0 ||
1325*da6c28aaSamw 		    strstr(options, ",name=") != NULL) {
1326*da6c28aaSamw 			return (SA_PROP_SHARE_ONLY);
1327*da6c28aaSamw 		}
1328*da6c28aaSamw 	}
1329*da6c28aaSamw 
1330*da6c28aaSamw 	/* do we have an existing optionset? */
1331*da6c28aaSamw 	optionset = sa_get_optionset(group, "smb");
1332*da6c28aaSamw 	if (optionset == NULL) {
1333*da6c28aaSamw 		/* didn't find existing optionset so create one */
1334*da6c28aaSamw 		optionset = sa_create_optionset(group, "smb");
1335*da6c28aaSamw 		if (optionset == NULL)
1336*da6c28aaSamw 			return (SA_NO_MEMORY);
1337*da6c28aaSamw 	} else {
1338*da6c28aaSamw 		/*
1339*da6c28aaSamw 		 * If an optionset already exists, we've come through
1340*da6c28aaSamw 		 * twice so ignore the second time.
1341*da6c28aaSamw 		 */
1342*da6c28aaSamw 		return (ret);
1343*da6c28aaSamw 	}
1344*da6c28aaSamw 
1345*da6c28aaSamw 	/* We need a copy of options for the next part. */
1346*da6c28aaSamw 	dup = strdup(options);
1347*da6c28aaSamw 	if (dup == NULL)
1348*da6c28aaSamw 		return (SA_NO_MEMORY);
1349*da6c28aaSamw 
1350*da6c28aaSamw 	/*
1351*da6c28aaSamw 	 * SMB properties are straightforward and are strings,
1352*da6c28aaSamw 	 * integers or booleans.  Properties are separated by
1353*da6c28aaSamw 	 * commas. It will be necessary to parse quotes due to some
1354*da6c28aaSamw 	 * strings not having a restricted characters set.
1355*da6c28aaSamw 	 *
1356*da6c28aaSamw 	 * Note that names will create a resource. For now, if there
1357*da6c28aaSamw 	 * is a set of properties "before" the first name="", those
1358*da6c28aaSamw 	 * properties will be placed on the group.
1359*da6c28aaSamw 	 */
1360*da6c28aaSamw 	persist = sa_is_persistent(group);
1361*da6c28aaSamw 	base = dup;
1362*da6c28aaSamw 	token = dup;
1363*da6c28aaSamw 	lasts = NULL;
1364*da6c28aaSamw 	while (token != NULL && ret == SA_OK) {
1365*da6c28aaSamw 		ret = SA_OK;
1366*da6c28aaSamw 		token = strtok_r(base, ",", &lasts);
1367*da6c28aaSamw 		base = NULL;
1368*da6c28aaSamw 		if (token != NULL) {
1369*da6c28aaSamw 			char *value;
1370*da6c28aaSamw 			/*
1371*da6c28aaSamw 			 * All SMB properties have values so there
1372*da6c28aaSamw 			 * MUST be an '=' character.  If it doesn't,
1373*da6c28aaSamw 			 * it is a syntax error.
1374*da6c28aaSamw 			 */
1375*da6c28aaSamw 			value = strchr(token, '=');
1376*da6c28aaSamw 			if (value != NULL) {
1377*da6c28aaSamw 				*value++ = '\0';
1378*da6c28aaSamw 			} else {
1379*da6c28aaSamw 				ret = SA_SYNTAX_ERR;
1380*da6c28aaSamw 				break;
1381*da6c28aaSamw 			}
1382*da6c28aaSamw 			/*
1383*da6c28aaSamw 			 * We may need to handle a "name" property
1384*da6c28aaSamw 			 * that is a ZFS imposed resource name. Each
1385*da6c28aaSamw 			 * name would trigger getting a new "resource"
1386*da6c28aaSamw 			 * to put properties on. For now, assume no
1387*da6c28aaSamw 			 * "name" property for special handling.
1388*da6c28aaSamw 			 */
1389*da6c28aaSamw 
1390*da6c28aaSamw 			if (strcmp(token, "name") == 0) {
1391*da6c28aaSamw 				char *prefix;
1392*da6c28aaSamw 				char *name = NULL;
1393*da6c28aaSamw 				/*
1394*da6c28aaSamw 				 * We have a name, so now work on the
1395*da6c28aaSamw 				 * resource level. We have a "share"
1396*da6c28aaSamw 				 * in "group" due to the caller having
1397*da6c28aaSamw 				 * added it. If we are called with a
1398*da6c28aaSamw 				 * group, the check for group/share
1399*da6c28aaSamw 				 * at the beginning of this function
1400*da6c28aaSamw 				 * will bail out the parse if there is a
1401*da6c28aaSamw 				 * "name" but no share.
1402*da6c28aaSamw 				 */
1403*da6c28aaSamw 				if (!iszfs) {
1404*da6c28aaSamw 					ret = SA_SYNTAX_ERR;
1405*da6c28aaSamw 					break;
1406*da6c28aaSamw 				}
1407*da6c28aaSamw 				/*
1408*da6c28aaSamw 				 * Make sure the parent group has the
1409*da6c28aaSamw 				 * "prefix" property since we will
1410*da6c28aaSamw 				 * need to use this for constructing
1411*da6c28aaSamw 				 * inherited name= values.
1412*da6c28aaSamw 				 */
1413*da6c28aaSamw 				prefix = sa_get_group_attr(parent, "prefix");
1414*da6c28aaSamw 				if (prefix == NULL) {
1415*da6c28aaSamw 					prefix = sa_get_group_attr(parent,
1416*da6c28aaSamw 					    "name");
1417*da6c28aaSamw 					if (prefix != NULL) {
1418*da6c28aaSamw 						(void) sa_set_group_attr(parent,
1419*da6c28aaSamw 						    "prefix", prefix);
1420*da6c28aaSamw 					}
1421*da6c28aaSamw 				}
1422*da6c28aaSamw 				name = fix_resource_name((sa_share_t)group,
1423*da6c28aaSamw 				    value, prefix);
1424*da6c28aaSamw 				if (name != NULL) {
1425*da6c28aaSamw 					resource = sa_add_resource(
1426*da6c28aaSamw 					    (sa_share_t)group, name,
1427*da6c28aaSamw 					    SA_SHARE_TRANSIENT, &ret);
1428*da6c28aaSamw 					sa_free_attr_string(name);
1429*da6c28aaSamw 				} else {
1430*da6c28aaSamw 					ret = SA_NO_MEMORY;
1431*da6c28aaSamw 				}
1432*da6c28aaSamw 				if (prefix != NULL)
1433*da6c28aaSamw 					sa_free_attr_string(prefix);
1434*da6c28aaSamw 
1435*da6c28aaSamw 				/* A resource level optionset is needed */
1436*da6c28aaSamw 
1437*da6c28aaSamw 				need_optionset = 1;
1438*da6c28aaSamw 				if (resource == NULL) {
1439*da6c28aaSamw 					ret = SA_NO_MEMORY;
1440*da6c28aaSamw 					break;
1441*da6c28aaSamw 				}
1442*da6c28aaSamw 				continue;
1443*da6c28aaSamw 			}
1444*da6c28aaSamw 
1445*da6c28aaSamw 			if (need_optionset) {
1446*da6c28aaSamw 				optionset = sa_create_optionset(resource,
1447*da6c28aaSamw 				    "smb");
1448*da6c28aaSamw 				need_optionset = 0;
1449*da6c28aaSamw 			}
1450*da6c28aaSamw 
1451*da6c28aaSamw 			prop = sa_create_property(token, value);
1452*da6c28aaSamw 			if (prop == NULL)
1453*da6c28aaSamw 				ret = SA_NO_MEMORY;
1454*da6c28aaSamw 			else
1455*da6c28aaSamw 				ret = sa_add_property(optionset, prop);
1456*da6c28aaSamw 			if (ret != SA_OK)
1457*da6c28aaSamw 				break;
1458*da6c28aaSamw 			if (!iszfs)
1459*da6c28aaSamw 				ret = sa_commit_properties(optionset, !persist);
1460*da6c28aaSamw 		}
1461*da6c28aaSamw 	}
1462*da6c28aaSamw 	free(dup);
1463*da6c28aaSamw 	return (ret);
1464*da6c28aaSamw }
1465*da6c28aaSamw 
1466*da6c28aaSamw /*
1467*da6c28aaSamw  * smb_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1468*da6c28aaSamw  *
1469*da6c28aaSamw  * provides a mechanism to format SMB properties into legacy output
1470*da6c28aaSamw  * format. If the buffer would overflow, it is reallocated and grown
1471*da6c28aaSamw  * as appropriate. Special cases of converting internal form of values
1472*da6c28aaSamw  * to those used by "share" are done. this function does one property
1473*da6c28aaSamw  * at a time.
1474*da6c28aaSamw  */
1475*da6c28aaSamw 
1476*da6c28aaSamw static void
1477*da6c28aaSamw smb_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1478*da6c28aaSamw 			sa_property_t prop, int sep)
1479*da6c28aaSamw {
1480*da6c28aaSamw 	char *name;
1481*da6c28aaSamw 	char *value;
1482*da6c28aaSamw 	int curlen;
1483*da6c28aaSamw 	char *buff = *rbuff;
1484*da6c28aaSamw 	size_t buffsize = *rbuffsize;
1485*da6c28aaSamw 
1486*da6c28aaSamw 	name = sa_get_property_attr(prop, "type");
1487*da6c28aaSamw 	value = sa_get_property_attr(prop, "value");
1488*da6c28aaSamw 	if (buff != NULL)
1489*da6c28aaSamw 		curlen = strlen(buff);
1490*da6c28aaSamw 	else
1491*da6c28aaSamw 		curlen = 0;
1492*da6c28aaSamw 	if (name != NULL) {
1493*da6c28aaSamw 		int len;
1494*da6c28aaSamw 		len = strlen(name) + sep;
1495*da6c28aaSamw 
1496*da6c28aaSamw 		/*
1497*da6c28aaSamw 		 * A future RFE would be to replace this with more
1498*da6c28aaSamw 		 * generic code and to possibly handle more types.
1499*da6c28aaSamw 		 *
1500*da6c28aaSamw 		 * For now, everything else is treated as a string. If
1501*da6c28aaSamw 		 * we get any properties that aren't exactly
1502*da6c28aaSamw 		 * name/value pairs, we may need to
1503*da6c28aaSamw 		 * interpret/transform.
1504*da6c28aaSamw 		 */
1505*da6c28aaSamw 		if (value != NULL)
1506*da6c28aaSamw 			len += 1 + strlen(value);
1507*da6c28aaSamw 
1508*da6c28aaSamw 		while (buffsize <= (curlen + len)) {
1509*da6c28aaSamw 			/* need more room */
1510*da6c28aaSamw 			buffsize += incr;
1511*da6c28aaSamw 			buff = realloc(buff, buffsize);
1512*da6c28aaSamw 			*rbuff = buff;
1513*da6c28aaSamw 			*rbuffsize = buffsize;
1514*da6c28aaSamw 			if (buff == NULL) {
1515*da6c28aaSamw 				/* realloc failed so free everything */
1516*da6c28aaSamw 				if (*rbuff != NULL)
1517*da6c28aaSamw 					free(*rbuff);
1518*da6c28aaSamw 				goto err;
1519*da6c28aaSamw 			}
1520*da6c28aaSamw 		}
1521*da6c28aaSamw 		if (buff == NULL)
1522*da6c28aaSamw 			goto err;
1523*da6c28aaSamw 		(void) snprintf(buff + curlen, buffsize - curlen,
1524*da6c28aaSamw 		    "%s%s=%s", sep ? "," : "",
1525*da6c28aaSamw 		    name, value != NULL ? value : "\"\"");
1526*da6c28aaSamw 
1527*da6c28aaSamw 	}
1528*da6c28aaSamw err:
1529*da6c28aaSamw 	if (name != NULL)
1530*da6c28aaSamw 		sa_free_attr_string(name);
1531*da6c28aaSamw 	if (value != NULL)
1532*da6c28aaSamw 		sa_free_attr_string(value);
1533*da6c28aaSamw }
1534*da6c28aaSamw 
1535*da6c28aaSamw /*
1536*da6c28aaSamw  * smb_format_resource_options(resource, hier)
1537*da6c28aaSamw  *
1538*da6c28aaSamw  * format all the options on the group into a flattened option
1539*da6c28aaSamw  * string. If hier is non-zero, walk up the tree to get inherited
1540*da6c28aaSamw  * options.
1541*da6c28aaSamw  */
1542*da6c28aaSamw 
1543*da6c28aaSamw static char *
1544*da6c28aaSamw smb_format_options(sa_group_t group, int hier)
1545*da6c28aaSamw {
1546*da6c28aaSamw 	sa_optionset_t options = NULL;
1547*da6c28aaSamw 	sa_property_t prop;
1548*da6c28aaSamw 	int sep = 0;
1549*da6c28aaSamw 	char *buff;
1550*da6c28aaSamw 	size_t buffsize;
1551*da6c28aaSamw 
1552*da6c28aaSamw 
1553*da6c28aaSamw 	buff = malloc(OPT_CHUNK);
1554*da6c28aaSamw 	if (buff == NULL)
1555*da6c28aaSamw 		return (NULL);
1556*da6c28aaSamw 
1557*da6c28aaSamw 	buff[0] = '\0';
1558*da6c28aaSamw 	buffsize = OPT_CHUNK;
1559*da6c28aaSamw 
1560*da6c28aaSamw 	/*
1561*da6c28aaSamw 	 * We may have a an optionset relative to this item. format
1562*da6c28aaSamw 	 * these if we find them and then add any security definitions.
1563*da6c28aaSamw 	 */
1564*da6c28aaSamw 
1565*da6c28aaSamw 	options = sa_get_derived_optionset(group, "smb", hier);
1566*da6c28aaSamw 
1567*da6c28aaSamw 	/*
1568*da6c28aaSamw 	 * do the default set first but skip any option that is also
1569*da6c28aaSamw 	 * in the protocol specific optionset.
1570*da6c28aaSamw 	 */
1571*da6c28aaSamw 	if (options != NULL) {
1572*da6c28aaSamw 		for (prop = sa_get_property(options, NULL);
1573*da6c28aaSamw 		    prop != NULL; prop = sa_get_next_property(prop)) {
1574*da6c28aaSamw 			/*
1575*da6c28aaSamw 			 * use this one since we skipped any
1576*da6c28aaSamw 			 * of these that were also in
1577*da6c28aaSamw 			 * optdefault
1578*da6c28aaSamw 			 */
1579*da6c28aaSamw 			smb_sprint_option(&buff, &buffsize, OPT_CHUNK,
1580*da6c28aaSamw 			    prop, sep);
1581*da6c28aaSamw 			if (buff == NULL) {
1582*da6c28aaSamw 				/*
1583*da6c28aaSamw 				 * buff could become NULL if there
1584*da6c28aaSamw 				 * isn't enough memory for
1585*da6c28aaSamw 				 * smb_sprint_option to realloc()
1586*da6c28aaSamw 				 * as necessary. We can't really
1587*da6c28aaSamw 				 * do anything about it at this
1588*da6c28aaSamw 				 * point so we return NULL.  The
1589*da6c28aaSamw 				 * caller should handle the
1590*da6c28aaSamw 				 * failure.
1591*da6c28aaSamw 				 */
1592*da6c28aaSamw 				if (options != NULL)
1593*da6c28aaSamw 					sa_free_derived_optionset(
1594*da6c28aaSamw 					    options);
1595*da6c28aaSamw 				return (buff);
1596*da6c28aaSamw 			}
1597*da6c28aaSamw 			sep = 1;
1598*da6c28aaSamw 		}
1599*da6c28aaSamw 	}
1600*da6c28aaSamw 
1601*da6c28aaSamw 	if (options != NULL)
1602*da6c28aaSamw 		sa_free_derived_optionset(options);
1603*da6c28aaSamw 	return (buff);
1604*da6c28aaSamw }
1605*da6c28aaSamw 
1606*da6c28aaSamw /*
1607*da6c28aaSamw  * smb_rename_resource(resource, newname)
1608*da6c28aaSamw  *
1609*da6c28aaSamw  * Change the current exported name of the resource to newname.
1610*da6c28aaSamw  */
1611*da6c28aaSamw /*ARGSUSED*/
1612*da6c28aaSamw int
1613*da6c28aaSamw smb_rename_resource(sa_handle_t handle, sa_resource_t resource, char *newname)
1614*da6c28aaSamw {
1615*da6c28aaSamw 	int ret = SA_OK;
1616*da6c28aaSamw 	int err;
1617*da6c28aaSamw 	char *oldname;
1618*da6c28aaSamw 
1619*da6c28aaSamw 	oldname = sa_get_resource_attr(resource, "name");
1620*da6c28aaSamw 	if (oldname == NULL)
1621*da6c28aaSamw 		return (SA_NO_SUCH_RESOURCE);
1622*da6c28aaSamw 
1623*da6c28aaSamw 	err = lmshrd_rename(oldname, newname);
1624*da6c28aaSamw 
1625*da6c28aaSamw 	/* improve error values somewhat */
1626*da6c28aaSamw 	switch (err) {
1627*da6c28aaSamw 	case NERR_Success:
1628*da6c28aaSamw 		break;
1629*da6c28aaSamw 	case NERR_InternalError:
1630*da6c28aaSamw 		ret = SA_SYSTEM_ERR;
1631*da6c28aaSamw 		break;
1632*da6c28aaSamw 	case NERR_DuplicateShare:
1633*da6c28aaSamw 		ret = SA_DUPLICATE_NAME;
1634*da6c28aaSamw 		break;
1635*da6c28aaSamw 	default:
1636*da6c28aaSamw 		ret = SA_CONFIG_ERR;
1637*da6c28aaSamw 		break;
1638*da6c28aaSamw 	}
1639*da6c28aaSamw 
1640*da6c28aaSamw 	return (ret);
1641*da6c28aaSamw }
1642