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