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