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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2013 RackTop Systems.
25 */
26
27 /*
28 * Share control API
29 */
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <libxml/parser.h>
38 #include <libxml/tree.h>
39 #include "libshare.h"
40 #include "libshare_impl.h"
41 #include <libscf.h>
42 #include "scfutil.h"
43 #include <ctype.h>
44 #include <libintl.h>
45 #include <thread.h>
46 #include <synch.h>
47
48 #define DFS_LOCK_FILE "/etc/dfs/fstypes"
49 #define SA_STRSIZE 256 /* max string size for names */
50
51 /*
52 * internal object type values returned by sa_get_object_type()
53 */
54 #define SA_TYPE_UNKNOWN 0
55 #define SA_TYPE_GROUP 1
56 #define SA_TYPE_SHARE 2
57 #define SA_TYPE_RESOURCE 3
58 #define SA_TYPE_OPTIONSET 4
59 #define SA_TYPE_ALTSPACE 5
60
61 /*
62 * internal data structures
63 */
64
65 extern struct sa_proto_plugin *sap_proto_list;
66
67 /* current SMF/SVC repository handle */
68 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
69 extern int gettransients(sa_handle_t, xmlNodePtr *);
70 extern char *sa_fstype(char *);
71 extern boolean_t sa_is_share(void *);
72 extern boolean_t sa_is_resource(void *);
73 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
74 extern boolean_t sa_group_is_zfs(sa_group_t);
75 extern boolean_t sa_path_is_zfs(char *);
76 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
77 extern int sa_zfs_set_sharesmb(sa_group_t, char *, int);
78 extern void update_legacy_config(sa_handle_t);
79 extern int issubdir(char *, char *);
80 extern int sa_zfs_init(sa_handle_t);
81 extern void sa_zfs_fini(sa_handle_t);
82 extern void sablocksigs(sigset_t *);
83 extern void saunblocksigs(sigset_t *);
84 static sa_group_t sa_get_optionset_parent(sa_optionset_t);
85 static char *get_node_attr(void *, char *);
86 extern void sa_update_sharetab_ts(sa_handle_t);
87
88 /*
89 * Data structures for finding/managing the document root to access
90 * handle mapping. The list isn't expected to grow very large so a
91 * simple list is acceptable. The purpose is to provide a way to start
92 * with a group or share and find the library handle needed for
93 * various operations.
94 */
95 mutex_t sa_global_lock;
96 struct doc2handle {
97 struct doc2handle *next;
98 xmlNodePtr root;
99 sa_handle_t handle;
100 };
101
102 mutex_t sa_dfstab_lock;
103
104 /* definitions used in a couple of property functions */
105 #define SA_PROP_OP_REMOVE 1
106 #define SA_PROP_OP_ADD 2
107 #define SA_PROP_OP_UPDATE 3
108
109 static struct doc2handle *sa_global_handles = NULL;
110
111 /* helper functions */
112
113 /*
114 * sa_errorstr(err)
115 *
116 * convert an error value to an error string
117 */
118
119 char *
120 sa_errorstr(int err)
121 {
122 static char errstr[32];
123 char *ret = NULL;
124
125 switch (err) {
126 case SA_OK:
127 ret = dgettext(TEXT_DOMAIN, "ok");
128 break;
129 case SA_NO_SUCH_PATH:
130 ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
131 break;
132 case SA_NO_MEMORY:
133 ret = dgettext(TEXT_DOMAIN, "no memory");
134 break;
135 case SA_DUPLICATE_NAME:
136 ret = dgettext(TEXT_DOMAIN, "name in use");
137 break;
138 case SA_BAD_PATH:
139 ret = dgettext(TEXT_DOMAIN, "bad path");
140 break;
141 case SA_NO_SUCH_GROUP:
142 ret = dgettext(TEXT_DOMAIN, "no such group");
143 break;
144 case SA_CONFIG_ERR:
145 ret = dgettext(TEXT_DOMAIN, "configuration error");
146 break;
147 case SA_SYSTEM_ERR:
148 ret = dgettext(TEXT_DOMAIN, "system error");
149 break;
150 case SA_SYNTAX_ERR:
151 ret = dgettext(TEXT_DOMAIN, "syntax error");
152 break;
153 case SA_NO_PERMISSION:
154 ret = dgettext(TEXT_DOMAIN, "no permission");
155 break;
156 case SA_BUSY:
157 ret = dgettext(TEXT_DOMAIN, "busy");
158 break;
159 case SA_NO_SUCH_PROP:
160 ret = dgettext(TEXT_DOMAIN, "no such property");
161 break;
162 case SA_INVALID_NAME:
163 ret = dgettext(TEXT_DOMAIN, "invalid name");
164 break;
165 case SA_INVALID_PROTOCOL:
166 ret = dgettext(TEXT_DOMAIN, "invalid protocol");
167 break;
168 case SA_NOT_ALLOWED:
169 ret = dgettext(TEXT_DOMAIN, "operation not allowed");
170 break;
171 case SA_BAD_VALUE:
172 ret = dgettext(TEXT_DOMAIN, "bad property value");
173 break;
174 case SA_INVALID_SECURITY:
175 ret = dgettext(TEXT_DOMAIN, "invalid security type");
176 break;
177 case SA_NO_SUCH_SECURITY:
178 ret = dgettext(TEXT_DOMAIN, "security type not found");
179 break;
180 case SA_VALUE_CONFLICT:
181 ret = dgettext(TEXT_DOMAIN, "property value conflict");
182 break;
183 case SA_NOT_IMPLEMENTED:
184 ret = dgettext(TEXT_DOMAIN, "not implemented");
185 break;
186 case SA_INVALID_PATH:
187 ret = dgettext(TEXT_DOMAIN, "invalid path");
188 break;
189 case SA_NOT_SUPPORTED:
190 ret = dgettext(TEXT_DOMAIN, "operation not supported");
191 break;
192 case SA_PROP_SHARE_ONLY:
193 ret = dgettext(TEXT_DOMAIN, "property not valid for group");
194 break;
195 case SA_NOT_SHARED:
196 ret = dgettext(TEXT_DOMAIN, "not shared");
197 break;
198 case SA_NO_SUCH_RESOURCE:
199 ret = dgettext(TEXT_DOMAIN, "no such resource");
200 break;
201 case SA_RESOURCE_REQUIRED:
202 ret = dgettext(TEXT_DOMAIN, "resource name required");
203 break;
204 case SA_MULTIPLE_ERROR:
205 ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols");
206 break;
207 case SA_PATH_IS_SUBDIR:
208 ret = dgettext(TEXT_DOMAIN, "path is a subpath of share");
209 break;
210 case SA_PATH_IS_PARENTDIR:
211 ret = dgettext(TEXT_DOMAIN, "path is parent of a share");
212 break;
213 case SA_NO_SECTION:
214 ret = dgettext(TEXT_DOMAIN, "protocol requires a section");
215 break;
216 case SA_NO_PROPERTIES:
217 ret = dgettext(TEXT_DOMAIN, "properties not found");
218 break;
219 case SA_NO_SUCH_SECTION:
220 ret = dgettext(TEXT_DOMAIN, "section not found");
221 break;
222 case SA_PASSWORD_ENC:
223 ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted");
224 break;
225 case SA_SHARE_EXISTS:
226 ret = dgettext(TEXT_DOMAIN, "path or file is already shared");
227 break;
228 default:
229 (void) snprintf(errstr, sizeof (errstr),
230 dgettext(TEXT_DOMAIN, "unknown %d"), err);
231 ret = errstr;
232 }
233 return (ret);
234 }
235
236 /*
237 * Document root to active handle mapping functions. These are only
238 * used internally. A mutex is used to prevent access while the list
239 * is changing. In general, the list will be relatively short - one
240 * item per thread that has called sa_init().
241 */
242
243 sa_handle_t
244 get_handle_for_root(xmlNodePtr root)
245 {
246 struct doc2handle *item;
247
248 (void) mutex_lock(&sa_global_lock);
249 for (item = sa_global_handles; item != NULL; item = item->next) {
250 if (item->root == root)
251 break;
252 }
253 (void) mutex_unlock(&sa_global_lock);
254 if (item != NULL)
255 return (item->handle);
256 return (NULL);
257 }
258
259 static int
260 add_handle_for_root(xmlNodePtr root, sa_handle_t handle)
261 {
262 struct doc2handle *item;
263 int ret = SA_NO_MEMORY;
264
265 item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1);
266 if (item != NULL) {
267 item->root = root;
268 item->handle = handle;
269 (void) mutex_lock(&sa_global_lock);
270 item->next = sa_global_handles;
271 sa_global_handles = item;
272 (void) mutex_unlock(&sa_global_lock);
273 ret = SA_OK;
274 }
275 return (ret);
276 }
277
278 /*
279 * remove_handle_for_root(root)
280 *
281 * Walks the list of handles and removes the one for this "root" from
282 * the list. It is up to the caller to free the data.
283 */
284
285 static void
286 remove_handle_for_root(xmlNodePtr root)
287 {
288 struct doc2handle *item, *prev;
289
290 (void) mutex_lock(&sa_global_lock);
291 for (prev = NULL, item = sa_global_handles; item != NULL;
292 item = item->next) {
293 if (item->root == root) {
294 /* first in the list */
295 if (prev == NULL)
296 sa_global_handles = sa_global_handles->next;
297 else
298 prev->next = item->next;
299 /* Item is out of the list so free the list structure */
300 free(item);
301 break;
302 }
303 prev = item;
304 }
305 (void) mutex_unlock(&sa_global_lock);
306 }
307
308 /*
309 * sa_find_group_handle(sa_group_t group)
310 *
311 * Find the sa_handle_t for the configuration associated with this
312 * group.
313 */
314 sa_handle_t
315 sa_find_group_handle(sa_group_t group)
316 {
317 xmlNodePtr node = (xmlNodePtr)group;
318 sa_handle_t handle;
319
320 while (node != NULL) {
321 if (strcmp((char *)(node->name), "sharecfg") == 0) {
322 /* have the root so get the handle */
323 handle = get_handle_for_root(node);
324 return (handle);
325 }
326 node = node->parent;
327 }
328 return (NULL);
329 }
330
331 /*
332 * set_legacy_timestamp(root, path, timevalue)
333 *
334 * add the current timestamp value to the configuration for use in
335 * determining when to update the legacy files. For SMF, this
336 * property is kept in default/operation/legacy_timestamp
337 */
338
339 static void
340 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
341 {
342 xmlNodePtr node;
343 xmlChar *lpath = NULL;
344 sa_handle_t handle;
345
346 /* Have to have a handle or else we weren't initialized. */
347 handle = get_handle_for_root(root);
348 if (handle == NULL)
349 return;
350
351 for (node = root->xmlChildrenNode; node != NULL;
352 node = node->next) {
353 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
354 /* a possible legacy node for this path */
355 lpath = xmlGetProp(node, (xmlChar *)"path");
356 if (lpath != NULL &&
357 xmlStrcmp(lpath, (xmlChar *)path) == 0) {
358 xmlFree(lpath);
359 break;
360 }
361 if (lpath != NULL)
362 xmlFree(lpath);
363 }
364 }
365 if (node == NULL) {
366 /* need to create the first legacy timestamp node */
367 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
368 }
369 if (node != NULL) {
370 char tstring[32];
371 int ret;
372
373 (void) snprintf(tstring, sizeof (tstring), "%lld", tval);
374 (void) xmlSetProp(node, (xmlChar *)"timestamp",
375 (xmlChar *)tstring);
376 (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
377 /* now commit to SMF */
378 ret = sa_get_instance(handle->scfhandle, "default");
379 if (ret == SA_OK) {
380 ret = sa_start_transaction(handle->scfhandle,
381 "operation");
382 if (ret == SA_OK) {
383 ret = sa_set_property(handle->scfhandle,
384 "legacy-timestamp", tstring);
385 if (ret == SA_OK) {
386 (void) sa_end_transaction(
387 handle->scfhandle, handle);
388 } else {
389 sa_abort_transaction(handle->scfhandle);
390 }
391 }
392 }
393 }
394 }
395
396 /*
397 * is_shared(share)
398 *
399 * determine if the specified share is currently shared or not.
400 */
401 static int
402 is_shared(sa_share_t share)
403 {
404 char *shared;
405 int result = 0; /* assume not */
406
407 shared = sa_get_share_attr(share, "shared");
408 if (shared != NULL) {
409 if (strcmp(shared, "true") == 0)
410 result = 1;
411 sa_free_attr_string(shared);
412 }
413 return (result);
414 }
415
416 /*
417 * excluded_protocol(share, proto)
418 *
419 * Returns B_TRUE if the specified protocol appears in the "exclude"
420 * property. This is used to prevent sharing special case shares
421 * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is
422 * returned if the protocol isn't in the list.
423 */
424 static boolean_t
425 excluded_protocol(sa_share_t share, char *proto)
426 {
427 char *protolist;
428 char *str;
429 char *token;
430
431 protolist = sa_get_share_attr(share, "exclude");
432 if (protolist != NULL) {
433 str = protolist;
434 while ((token = strtok(str, ",")) != NULL) {
435 if (strcmp(token, proto) == 0) {
436 sa_free_attr_string(protolist);
437 return (B_TRUE);
438 }
439 str = NULL;
440 }
441 sa_free_attr_string(protolist);
442 }
443 return (B_FALSE);
444 }
445
446 /*
447 * checksubdirgroup(group, newpath, strictness)
448 *
449 * check all the specified newpath against all the paths in the
450 * group. This is a helper function for checksubdir to make it easier
451 * to also check ZFS subgroups.
452 * The strictness values mean:
453 * SA_CHECK_NORMAL == only check newpath against shares that are active
454 * SA_CHECK_STRICT == check newpath against both active shares and those
455 * stored in the repository
456 */
457 static int
458 checksubdirgroup(sa_group_t group, char *newpath, int strictness)
459 {
460 sa_share_t share;
461 char *path;
462 int issub = SA_OK;
463 int subdir;
464 int parent;
465
466 if (newpath == NULL)
467 return (SA_INVALID_PATH);
468
469 for (share = sa_get_share(group, NULL); share != NULL;
470 share = sa_get_next_share(share)) {
471 /*
472 * The original behavior of share never checked
473 * against the permanent configuration
474 * (/etc/dfs/dfstab). PIT has a number of cases where
475 * it depends on this older behavior even though it
476 * could be considered incorrect. We may tighten this
477 * up in the future.
478 */
479 if (strictness == SA_CHECK_NORMAL && !is_shared(share))
480 continue;
481
482 path = sa_get_share_attr(share, "path");
483 /*
484 * If path is NULL, then a share is in the process of
485 * construction or someone has modified the property
486 * group inappropriately. It should be
487 * ignored. issubdir() comes from the original share
488 * implementation and does the difficult part of
489 * checking subdirectories.
490 */
491 if (path == NULL)
492 continue;
493
494 if (strcmp(path, newpath) == 0) {
495 issub = SA_INVALID_PATH;
496 } else {
497 subdir = issubdir(newpath, path);
498 parent = issubdir(path, newpath);
499 if (subdir || parent) {
500 sa_free_attr_string(path);
501 path = NULL;
502 return (subdir ?
503 SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR);
504 }
505 }
506 sa_free_attr_string(path);
507 path = NULL;
508 }
509 return (issub);
510 }
511
512 /*
513 * checksubdir(newpath, strictness)
514 *
515 * checksubdir determines if the specified path (newpath) is a
516 * subdirectory of another share. It calls checksubdirgroup() to do
517 * the complicated work. The strictness parameter determines how
518 * strict a check to make against the path. The strictness values
519 * mean: SA_CHECK_NORMAL == only check newpath against shares that are
520 * active SA_CHECK_STRICT == check newpath against both active shares
521 * and those * stored in the repository
522 */
523 static int
524 checksubdir(sa_handle_t handle, char *newpath, int strictness)
525 {
526 sa_group_t group;
527 int issub = SA_OK;
528 char *path = NULL;
529
530 for (group = sa_get_group(handle, NULL);
531 group != NULL && issub == SA_OK;
532 group = sa_get_next_group(group)) {
533 if (sa_group_is_zfs(group)) {
534 sa_group_t subgroup;
535 for (subgroup = sa_get_sub_group(group);
536 subgroup != NULL && issub == SA_OK;
537 subgroup = sa_get_next_group(subgroup))
538 issub = checksubdirgroup(subgroup, newpath,
539 strictness);
540 } else {
541 issub = checksubdirgroup(group, newpath, strictness);
542 }
543 }
544 if (path != NULL)
545 sa_free_attr_string(path);
546 return (issub);
547 }
548
549 /*
550 * validpath(path, strictness)
551 * determine if the provided path is valid for a share. It shouldn't
552 * be a sub-dir of an already shared path or the parent directory of a
553 * share path.
554 */
555 static int
556 validpath(sa_handle_t handle, char *path, int strictness)
557 {
558 int error = SA_OK;
559 struct stat st;
560 sa_share_t share;
561 char *fstype;
562
563 if (*path != '/')
564 return (SA_BAD_PATH);
565
566 if (stat(path, &st) < 0) {
567 error = SA_NO_SUCH_PATH;
568 } else {
569 share = sa_find_share(handle, path);
570 if (share != NULL)
571 error = SA_DUPLICATE_NAME;
572
573 if (error == SA_OK) {
574 /*
575 * check for special case with file system
576 * that might have restrictions. For now, ZFS
577 * is the only case since it has its own idea
578 * of how to configure shares. We do this
579 * before subdir checking since things like
580 * ZFS will do that for us. This should also
581 * be done via plugin interface.
582 */
583 fstype = sa_fstype(path);
584 if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
585 if (sa_zfs_is_shared(handle, path))
586 error = SA_INVALID_NAME;
587 }
588 if (fstype != NULL)
589 sa_free_fstype(fstype);
590 }
591 if (error == SA_OK)
592 error = checksubdir(handle, path, strictness);
593 }
594 return (error);
595 }
596
597 /*
598 * check to see if group/share is persistent.
599 *
600 * "group" can be either an sa_group_t or an sa_share_t. (void *)
601 * works since both thse types are also void *.
602 * If the share is a ZFS share, mark it as persistent.
603 */
604 boolean_t
605 sa_is_persistent(void *group)
606 {
607 char *type;
608 boolean_t persist = B_TRUE;
609 sa_group_t grp;
610
611 type = sa_get_group_attr((sa_group_t)group, "type");
612 if (type != NULL) {
613 if (strcmp(type, "transient") == 0)
614 persist = B_FALSE;
615 sa_free_attr_string(type);
616 }
617
618 grp = (sa_is_share(group)) ? sa_get_parent_group(group) : group;
619 if (sa_group_is_zfs(grp))
620 persist = B_TRUE;
621
622 return (persist);
623 }
624
625 /*
626 * sa_valid_group_name(name)
627 *
628 * check that the "name" contains only valid characters and otherwise
629 * fits the required naming conventions. Valid names must start with
630 * an alphabetic and the remainder may consist of only alphanumeric
631 * plus the '-' and '_' characters. This name limitation comes from
632 * inherent limitations in SMF.
633 */
634
635 int
636 sa_valid_group_name(char *name)
637 {
638 int ret = 1;
639 ssize_t len;
640
641 if (name != NULL && isalpha(*name)) {
642 char c;
643 len = strlen(name);
644 if (len < (scf_max_name_len - sizeof ("group:"))) {
645 for (c = *name++; c != '\0' && ret != 0; c = *name++) {
646 if (!isalnum(c) && c != '-' && c != '_')
647 ret = 0;
648 }
649 } else {
650 ret = 0;
651 }
652 } else {
653 ret = 0;
654 }
655 return (ret);
656 }
657
658
659 /*
660 * is_zfs_group(group)
661 * Determine if the specified group is a ZFS sharenfs group
662 */
663 static int
664 is_zfs_group(sa_group_t group)
665 {
666 int ret = 0;
667 xmlNodePtr parent;
668 xmlChar *zfs;
669
670 if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0)
671 parent = (xmlNodePtr)sa_get_parent_group(group);
672 else
673 parent = (xmlNodePtr)group;
674 zfs = xmlGetProp(parent, (xmlChar *)"zfs");
675 if (zfs != NULL) {
676 xmlFree(zfs);
677 ret = 1;
678 }
679 return (ret);
680 }
681
682 /*
683 * sa_get_object_type(object)
684 *
685 * This function returns a numeric value representing the object
686 * type. This allows using simpler checks when doing type specific
687 * operations.
688 */
689
690 static int
691 sa_get_object_type(void *object)
692 {
693 xmlNodePtr node = (xmlNodePtr)object;
694 int type;
695
696 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0)
697 type = SA_TYPE_GROUP;
698 else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0)
699 type = SA_TYPE_SHARE;
700 else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
701 type = SA_TYPE_RESOURCE;
702 else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0)
703 type = SA_TYPE_OPTIONSET;
704 else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0)
705 type = SA_TYPE_ALTSPACE;
706 else
707 assert(0);
708 return (type);
709 }
710
711 /*
712 * sa_optionset_name(optionset, oname, len, id)
713 * return the SMF name for the optionset. If id is not NULL, it
714 * will have the GUID value for a share and should be used
715 * instead of the keyword "optionset" which is used for
716 * groups. If the optionset doesn't have a protocol type
717 * associated with it, "default" is used. This shouldn't happen
718 * at this point but may be desirable in the future if there are
719 * protocol independent properties added. The name is returned in
720 * oname.
721 */
722
723 static int
724 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
725 {
726 char *proto;
727 void *parent;
728 int ptype;
729
730 if (id == NULL)
731 id = "optionset";
732
733 parent = sa_get_optionset_parent(optionset);
734 if (parent != NULL) {
735 ptype = sa_get_object_type(parent);
736 proto = sa_get_optionset_attr(optionset, "type");
737 if (ptype != SA_TYPE_RESOURCE) {
738 len = snprintf(oname, len, "%s_%s", id,
739 proto ? proto : "default");
740 } else {
741 char *index;
742 index = get_node_attr((void *)parent, "id");
743 if (index != NULL) {
744 len = snprintf(oname, len, "%s_%s_%s", id,
745 proto ? proto : "default", index);
746 sa_free_attr_string(index);
747 } else {
748 len = 0;
749 }
750 }
751
752 if (proto != NULL)
753 sa_free_attr_string(proto);
754 } else {
755 len = 0;
756 }
757 return (len);
758 }
759
760 /*
761 * sa_security_name(optionset, oname, len, id)
762 *
763 * return the SMF name for the security. If id is not NULL, it will
764 * have the GUID value for a share and should be used instead of the
765 * keyword "optionset" which is used for groups. If the optionset
766 * doesn't have a protocol type associated with it, "default" is
767 * used. This shouldn't happen at this point but may be desirable in
768 * the future if there are protocol independent properties added. The
769 * name is returned in oname. The security type is also encoded into
770 * the name. In the future, this wil *be handled a bit differently.
771 */
772
773 static int
774 sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
775 {
776 char *proto;
777 char *sectype;
778
779 if (id == NULL)
780 id = "optionset";
781
782 proto = sa_get_security_attr(security, "type");
783 sectype = sa_get_security_attr(security, "sectype");
784 len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default",
785 sectype ? sectype : "default");
786 if (proto != NULL)
787 sa_free_attr_string(proto);
788 if (sectype != NULL)
789 sa_free_attr_string(sectype);
790 return (len);
791 }
792
793 /*
794 * verifydefgroupopts(handle)
795 *
796 * Make sure a "default" group exists and has default protocols enabled.
797 */
798 static void
799 verifydefgroupopts(sa_handle_t handle)
800 {
801 sa_group_t defgrp;
802 sa_optionset_t opt;
803
804 defgrp = sa_get_group(handle, "default");
805 if (defgrp != NULL) {
806 opt = sa_get_optionset(defgrp, NULL);
807 /*
808 * NFS is the default for default group
809 */
810 if (opt == NULL)
811 opt = sa_create_optionset(defgrp, "nfs");
812 }
813 }
814
815 /*
816 * sa_init(init_service)
817 * Initialize the API
818 * find all the shared objects
819 * init the tables with all objects
820 * read in the current configuration
821 */
822
823 #define GETPROP(prop) scf_simple_prop_next_astring(prop)
824 #define CHECKTSTAMP(st, tval) stat(SA_LEGACY_DFSTAB, &st) >= 0 && \
825 tval != TSTAMP(st.st_ctim)
826
827 sa_handle_t
828 sa_init(int init_service)
829 {
830 struct stat st;
831 int legacy = 0;
832 uint64_t tval = 0;
833 int lockfd;
834 sigset_t old;
835 int updatelegacy = B_FALSE;
836 scf_simple_prop_t *prop;
837 sa_handle_t handle;
838 int err;
839
840 handle = calloc(sizeof (struct sa_handle), 1);
841
842 if (handle != NULL) {
843 /*
844 * Get protocol specific structures, but only if this
845 * is the only handle.
846 */
847 (void) mutex_lock(&sa_global_lock);
848 if (sa_global_handles == NULL)
849 (void) proto_plugin_init();
850 (void) mutex_unlock(&sa_global_lock);
851 if (init_service & SA_INIT_SHARE_API) {
852 /*
853 * initialize access into libzfs. We use this
854 * when collecting info about ZFS datasets and
855 * shares.
856 */
857 if (sa_zfs_init(handle) == B_FALSE) {
858 free(handle);
859 (void) mutex_lock(&sa_global_lock);
860 (void) proto_plugin_fini();
861 (void) mutex_unlock(&sa_global_lock);
862 return (NULL);
863 }
864 /*
865 * since we want to use SMF, initialize an svc handle
866 * and find out what is there.
867 */
868 handle->scfhandle = sa_scf_init(handle);
869 if (handle->scfhandle != NULL) {
870 /*
871 * Need to lock the extraction of the
872 * configuration if the dfstab file has
873 * changed. Lock everything now and release if
874 * not needed. Use a file that isn't being
875 * manipulated by other parts of the system in
876 * order to not interfere with locking. Using
877 * dfstab doesn't work.
878 */
879 sablocksigs(&old);
880 lockfd = open(DFS_LOCK_FILE, O_RDWR);
881 if (lockfd >= 0) {
882 extern int errno;
883 errno = 0;
884 (void) lockf(lockfd, F_LOCK, 0);
885 (void) mutex_lock(&sa_dfstab_lock);
886 /*
887 * Check whether we are going to need
888 * to merge any dfstab changes. This
889 * is done by comparing the value of
890 * legacy-timestamp with the current
891 * st_ctim of the file. If they are
892 * different, an update is needed and
893 * the file must remain locked until
894 * the merge is done in order to
895 * prevent multiple startups from
896 * changing the SMF repository at the
897 * same time. The first to get the
898 * lock will make any changes before
899 * the others can read the repository.
900 */
901 prop = scf_simple_prop_get
902 (handle->scfhandle->handle,
903 (const char *)SA_SVC_FMRI_BASE
904 ":default", "operation",
905 "legacy-timestamp");
906 if (prop != NULL) {
907 char *i64;
908 i64 = GETPROP(prop);
909 if (i64 != NULL)
910 tval = strtoull(i64,
911 NULL, 0);
912 if (CHECKTSTAMP(st, tval))
913 updatelegacy = B_TRUE;
914 scf_simple_prop_free(prop);
915 } else {
916 /*
917 * We haven't set the
918 * timestamp before so do it.
919 */
920 updatelegacy = B_TRUE;
921 }
922 if (updatelegacy == B_FALSE) {
923 (void) mutex_unlock(
924 &sa_dfstab_lock);
925 (void) lockf(lockfd, F_ULOCK,
926 0);
927 (void) close(lockfd);
928 }
929
930 }
931 /*
932 * It is essential that the document tree and
933 * the internal list of roots to handles be
934 * setup before anything that might try to
935 * create a new object is called. The document
936 * tree is the combination of handle->doc and
937 * handle->tree. This allows searches,
938 * etc. when all you have is an object in the
939 * tree.
940 */
941 handle->doc = xmlNewDoc((xmlChar *)"1.0");
942 handle->tree = xmlNewNode(NULL,
943 (xmlChar *)"sharecfg");
944 if (handle->doc != NULL &&
945 handle->tree != NULL) {
946 (void) xmlDocSetRootElement(handle->doc,
947 handle->tree);
948 err = add_handle_for_root(handle->tree,
949 handle);
950 if (err == SA_OK)
951 err = sa_get_config(
952 handle->scfhandle,
953 handle->tree, handle);
954 } else {
955 if (handle->doc != NULL)
956 xmlFreeDoc(handle->doc);
957 if (handle->tree != NULL)
958 xmlFreeNode(handle->tree);
959 err = SA_NO_MEMORY;
960 }
961
962 saunblocksigs(&old);
963
964 if (err != SA_OK) {
965 /*
966 * If we couldn't add the tree handle
967 * to the list, then things are going
968 * to fail badly. Might as well undo
969 * everything now and fail the
970 * sa_init().
971 */
972 sa_fini(handle);
973 if (updatelegacy == B_TRUE) {
974 (void) mutex_unlock(
975 &sa_dfstab_lock);
976 (void) lockf(lockfd,
977 F_ULOCK, 0);
978 (void) close(lockfd);
979 }
980 return (NULL);
981 }
982
983 if (tval == 0) {
984 /*
985 * first time so make sure
986 * default is setup
987 */
988 verifydefgroupopts(handle);
989 }
990
991 if (updatelegacy == B_TRUE) {
992 sablocksigs(&old);
993 getlegacyconfig(handle,
994 SA_LEGACY_DFSTAB, &handle->tree);
995 if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
996 set_legacy_timestamp(
997 handle->tree,
998 SA_LEGACY_DFSTAB,
999 TSTAMP(st.st_ctim));
1000 saunblocksigs(&old);
1001 /*
1002 * Safe to unlock now to allow
1003 * others to run
1004 */
1005 (void) mutex_unlock(&sa_dfstab_lock);
1006 (void) lockf(lockfd, F_ULOCK, 0);
1007 (void) close(lockfd);
1008 }
1009 /* Get sharetab timestamp */
1010 sa_update_sharetab_ts(handle);
1011
1012 /* Get lastupdate (transaction) timestamp */
1013 prop = scf_simple_prop_get(
1014 handle->scfhandle->handle,
1015 (const char *)SA_SVC_FMRI_BASE ":default",
1016 "state", "lastupdate");
1017 if (prop != NULL) {
1018 char *str;
1019 str =
1020 scf_simple_prop_next_astring(prop);
1021 if (str != NULL)
1022 handle->tstrans =
1023 strtoull(str, NULL, 0);
1024 else
1025 handle->tstrans = 0;
1026 scf_simple_prop_free(prop);
1027 }
1028 legacy |= sa_get_zfs_shares(handle, "zfs");
1029 legacy |= gettransients(handle, &handle->tree);
1030 }
1031 }
1032 }
1033 return (handle);
1034 }
1035
1036 /*
1037 * sa_fini(handle)
1038 * Uninitialize the API structures including the configuration
1039 * data structures and ZFS related data.
1040 */
1041
1042 void
1043 sa_fini(sa_handle_t handle)
1044 {
1045 if (handle != NULL) {
1046 /*
1047 * Free the config trees and any other data structures
1048 * used in the handle.
1049 */
1050 if (handle->doc != NULL)
1051 xmlFreeDoc(handle->doc);
1052
1053 /* Remove and free the entry in the global list. */
1054 remove_handle_for_root(handle->tree);
1055
1056 /*
1057 * If this was the last handle to release, unload the
1058 * plugins that were loaded. Use a mutex in case
1059 * another thread is reinitializing.
1060 */
1061 (void) mutex_lock(&sa_global_lock);
1062 if (sa_global_handles == NULL)
1063 (void) proto_plugin_fini();
1064 (void) mutex_unlock(&sa_global_lock);
1065
1066 sa_scf_fini(handle->scfhandle);
1067 sa_zfs_fini(handle);
1068
1069 /* Make sure we free the handle */
1070 free(handle);
1071
1072 }
1073 }
1074
1075 /*
1076 * sa_get_protocols(char **protocol)
1077 * Get array of protocols that are supported
1078 * Returns pointer to an allocated and NULL terminated
1079 * array of strings. Caller must free.
1080 * This really should be determined dynamically.
1081 * If there aren't any defined, return -1.
1082 * Use free() to return memory.
1083 */
1084
1085 int
1086 sa_get_protocols(char ***protocols)
1087 {
1088 int numproto = -1;
1089
1090 if (protocols != NULL) {
1091 struct sa_proto_plugin *plug;
1092 for (numproto = 0, plug = sap_proto_list; plug != NULL;
1093 plug = plug->plugin_next) {
1094 numproto++;
1095 }
1096
1097 *protocols = calloc(numproto + 1, sizeof (char *));
1098 if (*protocols != NULL) {
1099 int ret = 0;
1100 for (plug = sap_proto_list; plug != NULL;
1101 plug = plug->plugin_next) {
1102 /* faking for now */
1103 (*protocols)[ret++] =
1104 plug->plugin_ops->sa_protocol;
1105 }
1106 } else {
1107 numproto = -1;
1108 }
1109 }
1110 return (numproto);
1111 }
1112
1113 /*
1114 * find_group_by_name(node, group)
1115 *
1116 * search the XML document subtree specified by node to find the group
1117 * specified by group. Searching subtree allows subgroups to be
1118 * searched for.
1119 */
1120
1121 static xmlNodePtr
1122 find_group_by_name(xmlNodePtr node, xmlChar *group)
1123 {
1124 xmlChar *name = NULL;
1125
1126 for (node = node->xmlChildrenNode; node != NULL;
1127 node = node->next) {
1128 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
1129 /* if no groupname, return the first found */
1130 if (group == NULL)
1131 break;
1132 name = xmlGetProp(node, (xmlChar *)"name");
1133 if (name != NULL && xmlStrcmp(name, group) == 0)
1134 break;
1135 if (name != NULL) {
1136 xmlFree(name);
1137 name = NULL;
1138 }
1139 }
1140 }
1141 if (name != NULL)
1142 xmlFree(name);
1143 return (node);
1144 }
1145
1146 /*
1147 * sa_get_group(groupname)
1148 * Return the "group" specified. If groupname is NULL,
1149 * return the first group of the list of groups.
1150 */
1151 sa_group_t
1152 sa_get_group(sa_handle_t handle, char *groupname)
1153 {
1154 xmlNodePtr node = NULL;
1155 char *subgroup = NULL;
1156 char *group = NULL;
1157
1158 if (handle != NULL && handle->tree != NULL) {
1159 if (groupname != NULL) {
1160 group = strdup(groupname);
1161 if (group != NULL) {
1162 subgroup = strchr(group, '/');
1163 if (subgroup != NULL)
1164 *subgroup++ = '\0';
1165 }
1166 }
1167 /*
1168 * We want to find the, possibly, named group. If
1169 * group is not NULL, then lookup the name. If it is
1170 * NULL, we only do the find if groupname is also
1171 * NULL. This allows lookup of the "first" group in
1172 * the internal list.
1173 */
1174 if (group != NULL || groupname == NULL)
1175 node = find_group_by_name(handle->tree,
1176 (xmlChar *)group);
1177
1178 /* if a subgroup, find it before returning */
1179 if (subgroup != NULL && node != NULL)
1180 node = find_group_by_name(node, (xmlChar *)subgroup);
1181 }
1182 if (node != NULL && (char *)group != NULL)
1183 (void) sa_get_instance(handle->scfhandle, (char *)group);
1184 if (group != NULL)
1185 free(group);
1186 return ((sa_group_t)(node));
1187 }
1188
1189 /*
1190 * sa_get_next_group(group)
1191 * Return the "next" group after the specified group from
1192 * the internal group list. NULL if there are no more.
1193 */
1194 sa_group_t
1195 sa_get_next_group(sa_group_t group)
1196 {
1197 xmlNodePtr ngroup = NULL;
1198 if (group != NULL) {
1199 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1200 ngroup = ngroup->next) {
1201 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1202 break;
1203 }
1204 }
1205 return ((sa_group_t)ngroup);
1206 }
1207
1208 /*
1209 * sa_get_share(group, sharepath)
1210 * Return the share object for the share specified. The share
1211 * must be in the specified group. Return NULL if not found.
1212 */
1213 sa_share_t
1214 sa_get_share(sa_group_t group, char *sharepath)
1215 {
1216 xmlNodePtr node = NULL;
1217 xmlChar *path;
1218
1219 /*
1220 * For future scalability, this should end up building a cache
1221 * since it will get called regularly by the mountd and info
1222 * services.
1223 */
1224 if (group != NULL) {
1225 for (node = ((xmlNodePtr)group)->children; node != NULL;
1226 node = node->next) {
1227 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1228 if (sharepath == NULL) {
1229 break;
1230 } else {
1231 /* is it the correct share? */
1232 path = xmlGetProp(node,
1233 (xmlChar *)"path");
1234 if (path != NULL &&
1235 xmlStrcmp(path,
1236 (xmlChar *)sharepath) == 0) {
1237 xmlFree(path);
1238 break;
1239 }
1240 xmlFree(path);
1241 }
1242 }
1243 }
1244 }
1245 return ((sa_share_t)node);
1246 }
1247
1248 /*
1249 * sa_get_next_share(share)
1250 * Return the next share following the specified share
1251 * from the internal list of shares. Returns NULL if there
1252 * are no more shares. The list is relative to the same
1253 * group.
1254 */
1255 sa_share_t
1256 sa_get_next_share(sa_share_t share)
1257 {
1258 xmlNodePtr node = NULL;
1259
1260 if (share != NULL) {
1261 for (node = ((xmlNodePtr)share)->next; node != NULL;
1262 node = node->next) {
1263 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1264 break;
1265 }
1266 }
1267 }
1268 return ((sa_share_t)node);
1269 }
1270
1271 /*
1272 * _sa_get_child_node(node, type)
1273 *
1274 * find the child node of the specified node that has "type". This is
1275 * used to implement several internal functions.
1276 */
1277
1278 static xmlNodePtr
1279 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1280 {
1281 xmlNodePtr child;
1282 for (child = node->xmlChildrenNode; child != NULL;
1283 child = child->next)
1284 if (xmlStrcmp(child->name, type) == 0)
1285 return (child);
1286 return ((xmlNodePtr)NULL);
1287 }
1288
1289 /*
1290 * find_share(group, path)
1291 *
1292 * Search all the shares in the specified group for one that has the
1293 * specified path.
1294 */
1295
1296 static sa_share_t
1297 find_share(sa_group_t group, char *sharepath)
1298 {
1299 sa_share_t share;
1300 char *path;
1301
1302 for (share = sa_get_share(group, NULL); share != NULL;
1303 share = sa_get_next_share(share)) {
1304 path = sa_get_share_attr(share, "path");
1305 if (path != NULL && strcmp(path, sharepath) == 0) {
1306 sa_free_attr_string(path);
1307 break;
1308 }
1309 if (path != NULL)
1310 sa_free_attr_string(path);
1311 }
1312 return (share);
1313 }
1314
1315 /*
1316 * sa_get_sub_group(group)
1317 *
1318 * Get the first sub-group of group. The sa_get_next_group() function
1319 * can be used to get the rest. This is currently only used for ZFS
1320 * sub-groups but could be used to implement a more general mechanism.
1321 */
1322
1323 sa_group_t
1324 sa_get_sub_group(sa_group_t group)
1325 {
1326 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1327 (xmlChar *)"group"));
1328 }
1329
1330 /*
1331 * sa_find_share(sharepath)
1332 * Finds a share regardless of group. In the future, this
1333 * function should utilize a cache and hash table of some kind.
1334 * The current assumption is that a path will only be shared
1335 * once. In the future, this may change as implementation of
1336 * resource names comes into being.
1337 */
1338 sa_share_t
1339 sa_find_share(sa_handle_t handle, char *sharepath)
1340 {
1341 sa_group_t group;
1342 sa_group_t zgroup;
1343 sa_share_t share = NULL;
1344 int done = 0;
1345
1346 for (group = sa_get_group(handle, NULL); group != NULL && !done;
1347 group = sa_get_next_group(group)) {
1348 if (is_zfs_group(group)) {
1349 for (zgroup =
1350 (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1351 (xmlChar *)"group");
1352 zgroup != NULL;
1353 zgroup = sa_get_next_group(zgroup)) {
1354 share = find_share(zgroup, sharepath);
1355 if (share != NULL)
1356 break;
1357 }
1358 } else {
1359 share = find_share(group, sharepath);
1360 }
1361 if (share != NULL)
1362 break;
1363 }
1364 return (share);
1365 }
1366
1367 /*
1368 * sa_check_path(group, path, strictness)
1369 *
1370 * Check that path is a valid path relative to the group. Currently,
1371 * we are ignoring the group and checking only the NFS rules. Later,
1372 * we may want to use the group to then check against the protocols
1373 * enabled on the group. The strictness values mean:
1374 * SA_CHECK_NORMAL == only check newpath against shares that are active
1375 * SA_CHECK_STRICT == check newpath against both active shares and those
1376 * stored in the repository
1377 */
1378
1379 int
1380 sa_check_path(sa_group_t group, char *path, int strictness)
1381 {
1382 sa_handle_t handle;
1383
1384 handle = sa_find_group_handle(group);
1385 if (handle == NULL)
1386 return (SA_BAD_PATH);
1387
1388 return (validpath(handle, path, strictness));
1389 }
1390
1391 /*
1392 * mark_excluded_protos(group, share, flags)
1393 *
1394 * Walk through all the protocols enabled for the group and check to
1395 * see if the share has any of them should be in the exclude list
1396 * based on the featureset of the protocol. If there are any, add the
1397 * "exclude" property to the share.
1398 */
1399 static void
1400 mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
1401 {
1402 sa_optionset_t optionset;
1403 char exclude_list[SA_STRSIZE];
1404 char *sep = "";
1405
1406 exclude_list[0] = '\0';
1407 for (optionset = sa_get_optionset(group, NULL);
1408 optionset != NULL;
1409 optionset = sa_get_next_optionset(optionset)) {
1410 char *value;
1411 uint64_t features;
1412 value = sa_get_optionset_attr(optionset, "type");
1413 if (value == NULL)
1414 continue;
1415 features = sa_proto_get_featureset(value);
1416 if (!(features & flags)) {
1417 (void) strlcat(exclude_list, sep,
1418 sizeof (exclude_list));
1419 (void) strlcat(exclude_list, value,
1420 sizeof (exclude_list));
1421 sep = ",";
1422 }
1423 sa_free_attr_string(value);
1424 }
1425 if (exclude_list[0] != '\0')
1426 (void) xmlSetProp(share, (xmlChar *)"exclude",
1427 (xmlChar *)exclude_list);
1428 }
1429
1430 /*
1431 * get_all_features(group)
1432 *
1433 * Walk through all the protocols on the group and collect all
1434 * possible enabled features. This is the OR of all the featuresets.
1435 */
1436 static uint64_t
1437 get_all_features(sa_group_t group)
1438 {
1439 sa_optionset_t optionset;
1440 uint64_t features = 0;
1441
1442 for (optionset = sa_get_optionset(group, NULL);
1443 optionset != NULL;
1444 optionset = sa_get_next_optionset(optionset)) {
1445 char *value;
1446 value = sa_get_optionset_attr(optionset, "type");
1447 if (value == NULL)
1448 continue;
1449 features |= sa_proto_get_featureset(value);
1450 sa_free_attr_string(value);
1451 }
1452 return (features);
1453 }
1454
1455
1456 /*
1457 * _sa_add_share(group, sharepath, persist, *error, flags)
1458 *
1459 * Common code for all types of add_share. sa_add_share() is the
1460 * public API, we also need to be able to do this when parsing legacy
1461 * files and construction of the internal configuration while
1462 * extracting config info from SMF. "flags" indicates if some
1463 * protocols need relaxed rules while other don't. These values are
1464 * the featureset values defined in libshare.h.
1465 */
1466
1467 sa_share_t
1468 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
1469 uint64_t flags)
1470 {
1471 xmlNodePtr node = NULL;
1472 int err;
1473
1474 err = SA_OK; /* assume success */
1475
1476 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1477 if (node == NULL) {
1478 if (error != NULL)
1479 *error = SA_NO_MEMORY;
1480 return (node);
1481 }
1482
1483 (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1484 (void) xmlSetProp(node, (xmlChar *)"type",
1485 persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1486 if (flags != 0)
1487 mark_excluded_protos(group, node, flags);
1488 if (persist != SA_SHARE_TRANSIENT) {
1489 /*
1490 * persistent shares come in two flavors: SMF and
1491 * ZFS. Sort this one out based on target group and
1492 * path type. Both NFS and SMB are supported. First,
1493 * check to see if the protocol is enabled on the
1494 * subgroup and then setup the share appropriately.
1495 */
1496 if (sa_group_is_zfs(group) &&
1497 sa_path_is_zfs(sharepath)) {
1498 if (sa_get_optionset(group, "nfs") != NULL)
1499 err = sa_zfs_set_sharenfs(group, sharepath, 1);
1500 else if (sa_get_optionset(group, "smb") != NULL)
1501 err = sa_zfs_set_sharesmb(group, sharepath, 1);
1502 } else {
1503 sa_handle_t handle = sa_find_group_handle(group);
1504 if (handle != NULL) {
1505 err = sa_commit_share(handle->scfhandle,
1506 group, (sa_share_t)node);
1507 } else {
1508 err = SA_SYSTEM_ERR;
1509 }
1510 }
1511 }
1512 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
1513 /* called by the dfstab parser so could be a show */
1514 err = SA_OK;
1515
1516 if (err != SA_OK) {
1517 /*
1518 * we couldn't commit to the repository so undo
1519 * our internal state to reflect reality.
1520 */
1521 xmlUnlinkNode(node);
1522 xmlFreeNode(node);
1523 node = NULL;
1524 }
1525
1526 if (error != NULL)
1527 *error = err;
1528
1529 return (node);
1530 }
1531
1532 /*
1533 * sa_add_share(group, sharepath, persist, *error)
1534 *
1535 * Add a new share object to the specified group. The share will
1536 * have the specified sharepath and will only be constructed if
1537 * it is a valid path to be shared. NULL is returned on error
1538 * and a detailed error value will be returned via the error
1539 * pointer.
1540 */
1541 sa_share_t
1542 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1543 {
1544 xmlNodePtr node = NULL;
1545 int strictness = SA_CHECK_NORMAL;
1546 sa_handle_t handle;
1547 uint64_t special = 0;
1548 uint64_t features;
1549
1550 /*
1551 * If the share is to be permanent, use strict checking so a
1552 * bad config doesn't get created. Transient shares only need
1553 * to check against the currently active
1554 * shares. SA_SHARE_PARSER is a modifier used internally to
1555 * indicate that we are being called by the dfstab parser and
1556 * that we need strict checking in all cases. Normally persist
1557 * is in integer value but SA_SHARE_PARSER may be or'd into
1558 * it as an override.
1559 */
1560 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1561 strictness = SA_CHECK_STRICT;
1562
1563 handle = sa_find_group_handle(group);
1564
1565 /*
1566 * need to determine if the share is valid. The rules are:
1567 * - The path must not already exist
1568 * - The path must not be a subdir or parent dir of an
1569 * existing path unless at least one protocol allows it.
1570 * The sub/parent check is done in sa_check_path().
1571 */
1572
1573 if (sa_find_share(handle, sharepath) == NULL) {
1574 *error = sa_check_path(group, sharepath, strictness);
1575 features = get_all_features(group);
1576 switch (*error) {
1577 case SA_PATH_IS_SUBDIR:
1578 if (features & SA_FEATURE_ALLOWSUBDIRS)
1579 special |= SA_FEATURE_ALLOWSUBDIRS;
1580 break;
1581 case SA_PATH_IS_PARENTDIR:
1582 if (features & SA_FEATURE_ALLOWPARDIRS)
1583 special |= SA_FEATURE_ALLOWPARDIRS;
1584 break;
1585 }
1586 if (*error == SA_OK || special != SA_FEATURE_NONE)
1587 node = _sa_add_share(group, sharepath, persist,
1588 error, special);
1589 } else {
1590 *error = SA_DUPLICATE_NAME;
1591 }
1592
1593 return ((sa_share_t)node);
1594 }
1595
1596 /*
1597 * sa_enable_share(share, protocol)
1598 * Enable the specified share to the specified protocol.
1599 * If protocol is NULL, then all protocols.
1600 */
1601 int
1602 sa_enable_share(sa_share_t share, char *protocol)
1603 {
1604 char *sharepath;
1605 struct stat st;
1606 int err = SA_OK;
1607 int ret;
1608
1609 sharepath = sa_get_share_attr(share, "path");
1610 if (sharepath == NULL)
1611 return (SA_NO_MEMORY);
1612 if (stat(sharepath, &st) < 0) {
1613 err = SA_NO_SUCH_PATH;
1614 } else {
1615 /* tell the server about the share */
1616 if (protocol != NULL) {
1617 if (excluded_protocol(share, protocol))
1618 goto done;
1619
1620 /* lookup protocol specific handler */
1621 err = sa_proto_share(protocol, share);
1622 if (err == SA_OK)
1623 (void) sa_set_share_attr(share,
1624 "shared", "true");
1625 } else {
1626 /* Tell all protocols about the share */
1627 sa_group_t group;
1628 sa_optionset_t optionset;
1629
1630 group = sa_get_parent_group(share);
1631
1632 for (optionset = sa_get_optionset(group, NULL);
1633 optionset != NULL;
1634 optionset = sa_get_next_optionset(optionset)) {
1635 char *proto;
1636 proto = sa_get_optionset_attr(optionset,
1637 "type");
1638 if (proto != NULL) {
1639 if (!excluded_protocol(share, proto)) {
1640 ret = sa_proto_share(proto,
1641 share);
1642 if (ret != SA_OK)
1643 err = ret;
1644 }
1645 sa_free_attr_string(proto);
1646 }
1647 }
1648 (void) sa_set_share_attr(share, "shared", "true");
1649 }
1650 }
1651 done:
1652 if (sharepath != NULL)
1653 sa_free_attr_string(sharepath);
1654 return (err);
1655 }
1656
1657 /*
1658 * sa_disable_share(share, protocol)
1659 * Disable the specified share to the specified protocol. If
1660 * protocol is NULL, then all protocols that are enabled for the
1661 * share should be disabled.
1662 */
1663 int
1664 sa_disable_share(sa_share_t share, char *protocol)
1665 {
1666 char *path;
1667 int err = SA_OK;
1668 int ret = SA_OK;
1669
1670 path = sa_get_share_attr(share, "path");
1671
1672 if (protocol != NULL) {
1673 ret = sa_proto_unshare(share, protocol, path);
1674 } else {
1675 /* need to do all protocols */
1676 sa_group_t group;
1677 sa_optionset_t optionset;
1678
1679 group = sa_get_parent_group(share);
1680
1681 /* Tell all protocols about the share */
1682 for (optionset = sa_get_optionset(group, NULL);
1683 optionset != NULL;
1684 optionset = sa_get_next_optionset(optionset)) {
1685 char *proto;
1686
1687 proto = sa_get_optionset_attr(optionset, "type");
1688 if (proto != NULL) {
1689 err = sa_proto_unshare(share, proto, path);
1690 if (err != SA_OK)
1691 ret = err;
1692 sa_free_attr_string(proto);
1693 }
1694 }
1695 }
1696 if (ret == SA_OK)
1697 (void) sa_set_share_attr(share, "shared", NULL);
1698 if (path != NULL)
1699 sa_free_attr_string(path);
1700 return (ret);
1701 }
1702
1703 /*
1704 * sa_remove_share(share)
1705 *
1706 * remove the specified share from its containing group.
1707 * Remove from the SMF or ZFS configuration space.
1708 */
1709
1710 int
1711 sa_remove_share(sa_share_t share)
1712 {
1713 sa_group_t group;
1714 int ret = SA_OK;
1715 char *type;
1716 int transient = 0;
1717 char *groupname;
1718 char *zfs;
1719
1720 type = sa_get_share_attr(share, "type");
1721 group = sa_get_parent_group(share);
1722 zfs = sa_get_group_attr(group, "zfs");
1723 groupname = sa_get_group_attr(group, "name");
1724 if (type != NULL && strcmp(type, "persist") != 0)
1725 transient = 1;
1726 if (type != NULL)
1727 sa_free_attr_string(type);
1728
1729 /* remove the node from its group then free the memory */
1730
1731 /*
1732 * need to test if "busy"
1733 */
1734 /* only do SMF action if permanent */
1735 if (!transient || zfs != NULL) {
1736 /* remove from legacy dfstab as well as possible SMF */
1737 ret = sa_delete_legacy(share, NULL);
1738 if (ret == SA_OK) {
1739 if (!sa_group_is_zfs(group)) {
1740 sa_handle_t handle =
1741 sa_find_group_handle(group);
1742 if (handle != NULL) {
1743 ret = sa_delete_share(
1744 handle->scfhandle, group,
1745 share);
1746 } else {
1747 ret = SA_SYSTEM_ERR;
1748 }
1749 } else {
1750 char *sharepath = sa_get_share_attr(share,
1751 "path");
1752 if (sharepath != NULL) {
1753 ret = sa_zfs_set_sharenfs(group,
1754 sharepath, 0);
1755 sa_free_attr_string(sharepath);
1756 }
1757 }
1758 }
1759 }
1760 if (groupname != NULL)
1761 sa_free_attr_string(groupname);
1762 if (zfs != NULL)
1763 sa_free_attr_string(zfs);
1764
1765 xmlUnlinkNode((xmlNodePtr)share);
1766 xmlFreeNode((xmlNodePtr)share);
1767 return (ret);
1768 }
1769
1770 /*
1771 * sa_move_share(group, share)
1772 *
1773 * move the specified share to the specified group. Update SMF
1774 * appropriately.
1775 */
1776
1777 int
1778 sa_move_share(sa_group_t group, sa_share_t share)
1779 {
1780 sa_group_t oldgroup;
1781 int ret = SA_OK;
1782
1783 /* remove the node from its group then free the memory */
1784
1785 oldgroup = sa_get_parent_group(share);
1786 if (oldgroup != group) {
1787 sa_handle_t handle;
1788 xmlUnlinkNode((xmlNodePtr)share);
1789 /*
1790 * now that the share isn't in its old group, add to
1791 * the new one
1792 */
1793 (void) xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1794 /* need to deal with SMF */
1795 handle = sa_find_group_handle(group);
1796 if (handle != NULL) {
1797 /*
1798 * need to remove from old group first and then add to
1799 * new group. Ideally, we would do the other order but
1800 * need to avoid having the share in two groups at the
1801 * same time.
1802 */
1803 ret = sa_delete_share(handle->scfhandle, oldgroup,
1804 share);
1805 if (ret == SA_OK)
1806 ret = sa_commit_share(handle->scfhandle,
1807 group, share);
1808 } else {
1809 ret = SA_SYSTEM_ERR;
1810 }
1811 }
1812 return (ret);
1813 }
1814
1815 /*
1816 * sa_get_parent_group(share)
1817 *
1818 * Return the containing group for the share. If a group was actually
1819 * passed in, we don't want a parent so return NULL.
1820 */
1821
1822 sa_group_t
1823 sa_get_parent_group(sa_share_t share)
1824 {
1825 xmlNodePtr node = NULL;
1826 if (share != NULL) {
1827 node = ((xmlNodePtr)share)->parent;
1828 /*
1829 * make sure parent is a group and not sharecfg since
1830 * we may be cheating and passing in a group.
1831 * Eventually, groups of groups might come into being.
1832 */
1833 if (node == NULL ||
1834 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1835 node = NULL;
1836 }
1837 return ((sa_group_t)node);
1838 }
1839
1840 /*
1841 * _sa_create_group(handle, groupname)
1842 *
1843 * Create a group in the document. The caller will need to deal with
1844 * configuration store and activation.
1845 */
1846
1847 sa_group_t
1848 _sa_create_group(sa_handle_t handle, char *groupname)
1849 {
1850 xmlNodePtr node = NULL;
1851
1852 if (sa_valid_group_name(groupname)) {
1853 node = xmlNewChild(handle->tree, NULL, (xmlChar *)"group",
1854 NULL);
1855 if (node != NULL) {
1856 (void) xmlSetProp(node, (xmlChar *)"name",
1857 (xmlChar *)groupname);
1858 (void) xmlSetProp(node, (xmlChar *)"state",
1859 (xmlChar *)"enabled");
1860 }
1861 }
1862 return ((sa_group_t)node);
1863 }
1864
1865 /*
1866 * _sa_create_zfs_group(group, groupname)
1867 *
1868 * Create a ZFS subgroup under the specified group. This may
1869 * eventually form the basis of general sub-groups, but is currently
1870 * restricted to ZFS.
1871 */
1872 sa_group_t
1873 _sa_create_zfs_group(sa_group_t group, char *groupname)
1874 {
1875 xmlNodePtr node = NULL;
1876
1877 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1878 if (node != NULL) {
1879 (void) xmlSetProp(node, (xmlChar *)"name",
1880 (xmlChar *)groupname);
1881 (void) xmlSetProp(node, (xmlChar *)"state",
1882 (xmlChar *)"enabled");
1883 }
1884
1885 return ((sa_group_t)node);
1886 }
1887
1888 /*
1889 * sa_create_group(groupname, *error)
1890 *
1891 * Create a new group with groupname. Need to validate that it is a
1892 * legal name for SMF and the construct the SMF service instance of
1893 * svc:/network/shares/group to implement the group. All necessary
1894 * operational properties must be added to the group at this point
1895 * (via the SMF transaction model).
1896 */
1897 sa_group_t
1898 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1899 {
1900 xmlNodePtr node = NULL;
1901 sa_group_t group;
1902 int ret;
1903 char rbacstr[SA_STRSIZE];
1904
1905 ret = SA_OK;
1906
1907 if (handle == NULL || handle->scfhandle == NULL) {
1908 ret = SA_SYSTEM_ERR;
1909 goto err;
1910 }
1911
1912 group = sa_get_group(handle, groupname);
1913 if (group != NULL) {
1914 ret = SA_DUPLICATE_NAME;
1915 } else {
1916 if (sa_valid_group_name(groupname)) {
1917 node = xmlNewChild(handle->tree, NULL,
1918 (xmlChar *)"group", NULL);
1919 if (node != NULL) {
1920 (void) xmlSetProp(node, (xmlChar *)"name",
1921 (xmlChar *)groupname);
1922 /* default to the group being enabled */
1923 (void) xmlSetProp(node, (xmlChar *)"state",
1924 (xmlChar *)"enabled");
1925 ret = sa_create_instance(handle->scfhandle,
1926 groupname);
1927 if (ret == SA_OK) {
1928 ret = sa_start_transaction(
1929 handle->scfhandle,
1930 "operation");
1931 }
1932 if (ret == SA_OK) {
1933 ret = sa_set_property(
1934 handle->scfhandle,
1935 "state", "enabled");
1936 if (ret == SA_OK) {
1937 ret = sa_end_transaction(
1938 handle->scfhandle,
1939 handle);
1940 } else {
1941 sa_abort_transaction(
1942 handle->scfhandle);
1943 }
1944 }
1945 if (ret == SA_OK) {
1946 /* initialize the RBAC strings */
1947 ret = sa_start_transaction(
1948 handle->scfhandle,
1949 "general");
1950 if (ret == SA_OK) {
1951 (void) snprintf(rbacstr,
1952 sizeof (rbacstr), "%s.%s",
1953 SA_RBAC_MANAGE, groupname);
1954 ret = sa_set_property(
1955 handle->scfhandle,
1956 "action_authorization",
1957 rbacstr);
1958 }
1959 if (ret == SA_OK) {
1960 (void) snprintf(rbacstr,
1961 sizeof (rbacstr), "%s.%s",
1962 SA_RBAC_VALUE, groupname);
1963 ret = sa_set_property(
1964 handle->scfhandle,
1965 "value_authorization",
1966 rbacstr);
1967 }
1968 if (ret == SA_OK) {
1969 ret = sa_end_transaction(
1970 handle->scfhandle,
1971 handle);
1972 } else {
1973 sa_abort_transaction(
1974 handle->scfhandle);
1975 }
1976 }
1977 if (ret != SA_OK) {
1978 /*
1979 * Couldn't commit the group
1980 * so we need to undo
1981 * internally.
1982 */
1983 xmlUnlinkNode(node);
1984 xmlFreeNode(node);
1985 node = NULL;
1986 }
1987 } else {
1988 ret = SA_NO_MEMORY;
1989 }
1990 } else {
1991 ret = SA_INVALID_NAME;
1992 }
1993 }
1994 err:
1995 if (error != NULL)
1996 *error = ret;
1997 return ((sa_group_t)node);
1998 }
1999
2000 /*
2001 * sa_remove_group(group)
2002 *
2003 * Remove the specified group. This deletes from the SMF repository.
2004 * All property groups and properties are removed.
2005 */
2006
2007 int
2008 sa_remove_group(sa_group_t group)
2009 {
2010 char *name;
2011 int ret = SA_OK;
2012 sa_handle_t handle;
2013
2014 handle = sa_find_group_handle(group);
2015 if (handle != NULL) {
2016 name = sa_get_group_attr(group, "name");
2017 if (name != NULL) {
2018 ret = sa_delete_instance(handle->scfhandle, name);
2019 sa_free_attr_string(name);
2020 }
2021 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
2022 xmlFreeNode((xmlNodePtr)group); /* now it is gone */
2023 } else {
2024 ret = SA_SYSTEM_ERR;
2025 }
2026 return (ret);
2027 }
2028
2029 /*
2030 * sa_update_config()
2031 *
2032 * Used to update legacy files that need to be updated in bulk
2033 * Currently, this is a placeholder and will go away in a future
2034 * release.
2035 */
2036
2037 int
2038 sa_update_config(sa_handle_t handle)
2039 {
2040 /*
2041 * do legacy files first so we can tell when they change.
2042 * This will go away when we start updating individual records
2043 * rather than the whole file.
2044 */
2045 update_legacy_config(handle);
2046 return (SA_OK);
2047 }
2048
2049 /*
2050 * get_node_attr(node, tag)
2051 *
2052 * Get the specified tag(attribute) if it exists on the node. This is
2053 * used internally by a number of attribute oriented functions.
2054 */
2055
2056 static char *
2057 get_node_attr(void *nodehdl, char *tag)
2058 {
2059 xmlNodePtr node = (xmlNodePtr)nodehdl;
2060 xmlChar *name = NULL;
2061
2062 if (node != NULL)
2063 name = xmlGetProp(node, (xmlChar *)tag);
2064 return ((char *)name);
2065 }
2066
2067 /*
2068 * set_node_attr(node, tag)
2069 *
2070 * Set the specified tag(attribute) to the specified value This is
2071 * used internally by a number of attribute oriented functions. It
2072 * doesn't update the repository, only the internal document state.
2073 */
2074
2075 void
2076 set_node_attr(void *nodehdl, char *tag, char *value)
2077 {
2078 xmlNodePtr node = (xmlNodePtr)nodehdl;
2079 if (node != NULL && tag != NULL) {
2080 if (value != NULL)
2081 (void) xmlSetProp(node, (xmlChar *)tag,
2082 (xmlChar *)value);
2083 else
2084 (void) xmlUnsetProp(node, (xmlChar *)tag);
2085 }
2086 }
2087
2088 /*
2089 * sa_get_group_attr(group, tag)
2090 *
2091 * Get the specied attribute, if defined, for the group.
2092 */
2093
2094 char *
2095 sa_get_group_attr(sa_group_t group, char *tag)
2096 {
2097 return (get_node_attr((void *)group, tag));
2098 }
2099
2100 /*
2101 * sa_set_group_attr(group, tag, value)
2102 *
2103 * set the specified tag/attribute on the group using value as its
2104 * value.
2105 *
2106 * This will result in setting the property in the SMF repository as
2107 * well as in the internal document.
2108 */
2109
2110 int
2111 sa_set_group_attr(sa_group_t group, char *tag, char *value)
2112 {
2113 int ret;
2114 char *groupname;
2115 sa_handle_t handle;
2116
2117 /*
2118 * ZFS group/subgroup doesn't need the handle so shortcut.
2119 */
2120 if (sa_group_is_zfs(group)) {
2121 set_node_attr((void *)group, tag, value);
2122 return (SA_OK);
2123 }
2124
2125 handle = sa_find_group_handle(group);
2126 if (handle != NULL) {
2127 groupname = sa_get_group_attr(group, "name");
2128 ret = sa_get_instance(handle->scfhandle, groupname);
2129 if (ret == SA_OK) {
2130 set_node_attr((void *)group, tag, value);
2131 ret = sa_start_transaction(handle->scfhandle,
2132 "operation");
2133 if (ret == SA_OK) {
2134 ret = sa_set_property(handle->scfhandle,
2135 tag, value);
2136 if (ret == SA_OK)
2137 ret = sa_end_transaction(
2138 handle->scfhandle,
2139 handle);
2140 else
2141 sa_abort_transaction(
2142 handle->scfhandle);
2143 }
2144 if (ret == SA_SYSTEM_ERR)
2145 ret = SA_NO_PERMISSION;
2146 }
2147 if (groupname != NULL)
2148 sa_free_attr_string(groupname);
2149 } else {
2150 ret = SA_SYSTEM_ERR;
2151 }
2152 return (ret);
2153 }
2154
2155 /*
2156 * sa_get_share_attr(share, tag)
2157 *
2158 * Return the value of the tag/attribute set on the specified
2159 * share. Returns NULL if the tag doesn't exist.
2160 */
2161
2162 char *
2163 sa_get_share_attr(sa_share_t share, char *tag)
2164 {
2165 return (get_node_attr((void *)share, tag));
2166 }
2167
2168 /*
2169 * _sa_set_share_description(share, description)
2170 *
2171 * Add a description tag with text contents to the specified share. A
2172 * separate XML tag is used rather than a property. This can also be
2173 * used with resources.
2174 */
2175
2176 xmlNodePtr
2177 _sa_set_share_description(void *share, char *content)
2178 {
2179 xmlNodePtr node;
2180 node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
2181 NULL);
2182 xmlNodeSetContent(node, (xmlChar *)content);
2183 return (node);
2184 }
2185
2186 /*
2187 * sa_set_share_attr(share, tag, value)
2188 *
2189 * Set the share attribute specified by tag to the specified value. In
2190 * the case of "resource", enforce a no duplicates in a group rule. If
2191 * the share is not transient, commit the changes to the repository
2192 * else just update the share internally.
2193 */
2194
2195 int
2196 sa_set_share_attr(sa_share_t share, char *tag, char *value)
2197 {
2198 sa_group_t group;
2199 sa_share_t resource;
2200 int ret = SA_OK;
2201
2202 group = sa_get_parent_group(share);
2203
2204 /*
2205 * There are some attributes that may have specific
2206 * restrictions on them. Initially, only "resource" has
2207 * special meaning that needs to be checked. Only one instance
2208 * of a resource name may exist within a group.
2209 */
2210
2211 if (strcmp(tag, "resource") == 0) {
2212 resource = sa_get_resource(group, value);
2213 if (resource != share && resource != NULL)
2214 ret = SA_DUPLICATE_NAME;
2215 }
2216 if (ret == SA_OK) {
2217 set_node_attr((void *)share, tag, value);
2218 if (group != NULL) {
2219 char *type;
2220 /* we can probably optimize this some */
2221 type = sa_get_share_attr(share, "type");
2222 if (type == NULL || strcmp(type, "transient") != 0) {
2223 sa_handle_t handle = sa_find_group_handle(
2224 group);
2225 if (handle != NULL) {
2226 ret = sa_commit_share(
2227 handle->scfhandle, group,
2228 share);
2229 } else {
2230 ret = SA_SYSTEM_ERR;
2231 }
2232 }
2233 if (type != NULL)
2234 sa_free_attr_string(type);
2235 }
2236 }
2237 return (ret);
2238 }
2239
2240 /*
2241 * sa_get_property_attr(prop, tag)
2242 *
2243 * Get the value of the specified property attribute. Standard
2244 * attributes are "type" and "value".
2245 */
2246
2247 char *
2248 sa_get_property_attr(sa_property_t prop, char *tag)
2249 {
2250 return (get_node_attr((void *)prop, tag));
2251 }
2252
2253 /*
2254 * sa_get_optionset_attr(prop, tag)
2255 *
2256 * Get the value of the specified property attribute. Standard
2257 * attribute is "type".
2258 */
2259
2260 char *
2261 sa_get_optionset_attr(sa_property_t optionset, char *tag)
2262 {
2263 return (get_node_attr((void *)optionset, tag));
2264
2265 }
2266
2267 /*
2268 * sa_set_optionset_attr(optionset, tag, value)
2269 *
2270 * Set the specified attribute(tag) to the specified value on the
2271 * optionset.
2272 */
2273
2274 void
2275 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
2276 {
2277 set_node_attr((void *)optionset, tag, value);
2278 }
2279
2280 /*
2281 * sa_free_attr_string(string)
2282 *
2283 * Free the string that was returned in one of the sa_get_*_attr()
2284 * functions.
2285 */
2286
2287 void
2288 sa_free_attr_string(char *string)
2289 {
2290 xmlFree((xmlChar *)string);
2291 }
2292
2293 /*
2294 * sa_get_optionset(group, proto)
2295 *
2296 * Return the optionset, if it exists, that is associated with the
2297 * specified protocol.
2298 */
2299
2300 sa_optionset_t
2301 sa_get_optionset(void *group, char *proto)
2302 {
2303 xmlNodePtr node;
2304 xmlChar *value = NULL;
2305
2306 for (node = ((xmlNodePtr)group)->children; node != NULL;
2307 node = node->next) {
2308 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2309 value = xmlGetProp(node, (xmlChar *)"type");
2310 if (proto != NULL) {
2311 if (value != NULL &&
2312 xmlStrcmp(value, (xmlChar *)proto) == 0) {
2313 break;
2314 }
2315 if (value != NULL) {
2316 xmlFree(value);
2317 value = NULL;
2318 }
2319 } else {
2320 break;
2321 }
2322 }
2323 }
2324 if (value != NULL)
2325 xmlFree(value);
2326 return ((sa_optionset_t)node);
2327 }
2328
2329 /*
2330 * sa_get_next_optionset(optionset)
2331 *
2332 * Return the next optionset in the group. NULL if this was the last.
2333 */
2334
2335 sa_optionset_t
2336 sa_get_next_optionset(sa_optionset_t optionset)
2337 {
2338 xmlNodePtr node;
2339
2340 for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2341 node = node->next) {
2342 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2343 break;
2344 }
2345 }
2346 return ((sa_optionset_t)node);
2347 }
2348
2349 /*
2350 * sa_get_security(group, sectype, proto)
2351 *
2352 * Return the security optionset. The internal name is a hold over
2353 * from the implementation and will be changed before the API is
2354 * finalized. This is really a named optionset that can be negotiated
2355 * as a group of properties (like NFS security options).
2356 */
2357
2358 sa_security_t
2359 sa_get_security(sa_group_t group, char *sectype, char *proto)
2360 {
2361 xmlNodePtr node;
2362 xmlChar *value = NULL;
2363
2364 for (node = ((xmlNodePtr)group)->children; node != NULL;
2365 node = node->next) {
2366 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2367 if (proto != NULL) {
2368 value = xmlGetProp(node, (xmlChar *)"type");
2369 if (value == NULL ||
2370 (value != NULL &&
2371 xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2372 /* it doesn't match so continue */
2373 xmlFree(value);
2374 value = NULL;
2375 continue;
2376 }
2377 }
2378 if (value != NULL) {
2379 xmlFree(value);
2380 value = NULL;
2381 }
2382 /* potential match */
2383 if (sectype != NULL) {
2384 value = xmlGetProp(node, (xmlChar *)"sectype");
2385 if (value != NULL &&
2386 xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2387 break;
2388 }
2389 } else {
2390 break;
2391 }
2392 }
2393 if (value != NULL) {
2394 xmlFree(value);
2395 value = NULL;
2396 }
2397 }
2398 if (value != NULL)
2399 xmlFree(value);
2400 return ((sa_security_t)node);
2401 }
2402
2403 /*
2404 * sa_get_next_security(security)
2405 *
2406 * Get the next security optionset if one exists.
2407 */
2408
2409 sa_security_t
2410 sa_get_next_security(sa_security_t security)
2411 {
2412 xmlNodePtr node;
2413
2414 for (node = ((xmlNodePtr)security)->next; node != NULL;
2415 node = node->next) {
2416 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2417 break;
2418 }
2419 }
2420 return ((sa_security_t)node);
2421 }
2422
2423 /*
2424 * sa_get_property(optionset, prop)
2425 *
2426 * Get the property object with the name specified in prop from the
2427 * optionset.
2428 */
2429
2430 sa_property_t
2431 sa_get_property(sa_optionset_t optionset, char *prop)
2432 {
2433 xmlNodePtr node = (xmlNodePtr)optionset;
2434 xmlChar *value = NULL;
2435
2436 if (optionset == NULL)
2437 return (NULL);
2438
2439 for (node = node->children; node != NULL;
2440 node = node->next) {
2441 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2442 if (prop == NULL)
2443 break;
2444 value = xmlGetProp(node, (xmlChar *)"type");
2445 if (value != NULL &&
2446 xmlStrcmp(value, (xmlChar *)prop) == 0) {
2447 break;
2448 }
2449 if (value != NULL) {
2450 xmlFree(value);
2451 value = NULL;
2452 }
2453 }
2454 }
2455 if (value != NULL)
2456 xmlFree(value);
2457 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2458 /*
2459 * avoid a non option node -- it is possible to be a
2460 * text node
2461 */
2462 node = NULL;
2463 }
2464 return ((sa_property_t)node);
2465 }
2466
2467 /*
2468 * sa_get_next_property(property)
2469 *
2470 * Get the next property following the specified property. NULL if
2471 * this was the last.
2472 */
2473
2474 sa_property_t
2475 sa_get_next_property(sa_property_t property)
2476 {
2477 xmlNodePtr node;
2478
2479 for (node = ((xmlNodePtr)property)->next; node != NULL;
2480 node = node->next) {
2481 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2482 break;
2483 }
2484 }
2485 return ((sa_property_t)node);
2486 }
2487
2488 /*
2489 * sa_set_share_description(share, content)
2490 *
2491 * Set the description of share to content.
2492 */
2493
2494 int
2495 sa_set_share_description(sa_share_t share, char *content)
2496 {
2497 xmlNodePtr node;
2498 sa_group_t group;
2499 int ret = SA_OK;
2500
2501 for (node = ((xmlNodePtr)share)->children; node != NULL;
2502 node = node->next) {
2503 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2504 break;
2505 }
2506 }
2507 /* no existing description but want to add */
2508 if (node == NULL && content != NULL) {
2509 /* add a description */
2510 node = _sa_set_share_description(share, content);
2511 } else if (node != NULL && content != NULL) {
2512 /* update a description */
2513 xmlNodeSetContent(node, (xmlChar *)content);
2514 } else if (node != NULL && content == NULL) {
2515 /* remove an existing description */
2516 xmlUnlinkNode(node);
2517 xmlFreeNode(node);
2518 }
2519 group = sa_get_parent_group(share);
2520 if (group != NULL &&
2521 sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
2522 sa_handle_t handle = sa_find_group_handle(group);
2523 if (handle != NULL) {
2524 ret = sa_commit_share(handle->scfhandle, group,
2525 share);
2526 } else {
2527 ret = SA_SYSTEM_ERR;
2528 }
2529 }
2530 return (ret);
2531 }
2532
2533 /*
2534 * fixproblemchars(string)
2535 *
2536 * don't want any newline or tab characters in the text since these
2537 * could break display of data and legacy file formats.
2538 */
2539 static void
2540 fixproblemchars(char *str)
2541 {
2542 int c;
2543 for (c = *str; c != '\0'; c = *++str) {
2544 if (c == '\t' || c == '\n')
2545 *str = ' ';
2546 else if (c == '"')
2547 *str = '\'';
2548 }
2549 }
2550
2551 /*
2552 * sa_get_share_description(share)
2553 *
2554 * Return the description text for the specified share if it
2555 * exists. NULL if no description exists.
2556 */
2557
2558 char *
2559 sa_get_share_description(sa_share_t share)
2560 {
2561 xmlChar *description = NULL;
2562 xmlNodePtr node;
2563
2564 for (node = ((xmlNodePtr)share)->children; node != NULL;
2565 node = node->next) {
2566 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2567 break;
2568 }
2569 }
2570 if (node != NULL) {
2571 description = xmlNodeGetContent(node);
2572 fixproblemchars((char *)description);
2573 }
2574 return ((char *)description);
2575 }
2576
2577 /*
2578 * sa_free(share_description(description)
2579 *
2580 * Free the description string.
2581 */
2582
2583 void
2584 sa_free_share_description(char *description)
2585 {
2586 xmlFree((xmlChar *)description);
2587 }
2588
2589 /*
2590 * sa_create_optionset(group, proto)
2591 *
2592 * Create an optionset for the specified protocol in the specied
2593 * group. This is manifested as a property group within SMF.
2594 */
2595
2596 sa_optionset_t
2597 sa_create_optionset(sa_group_t group, char *proto)
2598 {
2599 sa_optionset_t optionset;
2600 sa_group_t parent = group;
2601 sa_share_t share = NULL;
2602 int err = SA_OK;
2603 char *id = NULL;
2604
2605 optionset = sa_get_optionset(group, proto);
2606 if (optionset != NULL) {
2607 /* can't have a duplicate protocol */
2608 optionset = NULL;
2609 } else {
2610 /*
2611 * Account for resource names being slightly
2612 * different.
2613 */
2614 if (sa_is_share(group)) {
2615 /*
2616 * Transient shares do not have an "id" so not an
2617 * error to not find one.
2618 */
2619 id = sa_get_share_attr((sa_share_t)group, "id");
2620 } else if (sa_is_resource(group)) {
2621 share = sa_get_resource_parent(
2622 (sa_resource_t)group);
2623 id = sa_get_resource_attr(share, "id");
2624
2625 /* id can be NULL if the group is transient (ZFS) */
2626 if (id == NULL && sa_is_persistent(group))
2627 err = SA_NO_MEMORY;
2628 }
2629 if (err == SA_NO_MEMORY) {
2630 /*
2631 * Couldn't get the id for the share or
2632 * resource. While this could be a
2633 * configuration issue, it is most likely an
2634 * out of memory. In any case, fail the create.
2635 */
2636 return (NULL);
2637 }
2638
2639 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2640 NULL, (xmlChar *)"optionset", NULL);
2641 /*
2642 * only put to repository if on a group and we were
2643 * able to create an optionset.
2644 */
2645 if (optionset != NULL) {
2646 char oname[SA_STRSIZE];
2647 char *groupname;
2648
2649 /*
2650 * Need to get parent group in all cases, but also get
2651 * the share if this is a resource.
2652 */
2653 if (sa_is_share(group)) {
2654 parent = sa_get_parent_group((sa_share_t)group);
2655 } else if (sa_is_resource(group)) {
2656 share = sa_get_resource_parent(
2657 (sa_resource_t)group);
2658 parent = sa_get_parent_group(share);
2659 }
2660
2661 sa_set_optionset_attr(optionset, "type", proto);
2662
2663 (void) sa_optionset_name(optionset, oname,
2664 sizeof (oname), id);
2665 groupname = sa_get_group_attr(parent, "name");
2666 if (groupname != NULL && sa_is_persistent(group)) {
2667 sa_handle_t handle = sa_find_group_handle(
2668 group);
2669 assert(handle != NULL);
2670 if (handle != NULL) {
2671 (void) sa_get_instance(
2672 handle->scfhandle, groupname);
2673 (void) sa_create_pgroup(
2674 handle->scfhandle, oname);
2675 }
2676 }
2677 if (groupname != NULL)
2678 sa_free_attr_string(groupname);
2679 }
2680 }
2681
2682 if (id != NULL)
2683 sa_free_attr_string(id);
2684 return (optionset);
2685 }
2686
2687 /*
2688 * sa_get_property_parent(property)
2689 *
2690 * Given a property, return the object it is a property of. This will
2691 * be an optionset of some type.
2692 */
2693
2694 static sa_optionset_t
2695 sa_get_property_parent(sa_property_t property)
2696 {
2697 xmlNodePtr node = NULL;
2698
2699 if (property != NULL)
2700 node = ((xmlNodePtr)property)->parent;
2701 return ((sa_optionset_t)node);
2702 }
2703
2704 /*
2705 * sa_get_optionset_parent(optionset)
2706 *
2707 * Return the parent of the specified optionset. This could be a group
2708 * or a share.
2709 */
2710
2711 static sa_group_t
2712 sa_get_optionset_parent(sa_optionset_t optionset)
2713 {
2714 xmlNodePtr node = NULL;
2715
2716 if (optionset != NULL)
2717 node = ((xmlNodePtr)optionset)->parent;
2718 return ((sa_group_t)node);
2719 }
2720
2721 /*
2722 * zfs_needs_update(share)
2723 *
2724 * In order to avoid making multiple updates to a ZFS share when
2725 * setting properties, the share attribute "changed" will be set to
2726 * true when a property is added or modified. When done adding
2727 * properties, we can then detect that an update is needed. We then
2728 * clear the state here to detect additional changes.
2729 */
2730
2731 static int
2732 zfs_needs_update(sa_share_t share)
2733 {
2734 char *attr;
2735 int result = 0;
2736
2737 attr = sa_get_share_attr(share, "changed");
2738 if (attr != NULL) {
2739 sa_free_attr_string(attr);
2740 result = 1;
2741 }
2742 set_node_attr((void *)share, "changed", NULL);
2743 return (result);
2744 }
2745
2746 /*
2747 * zfs_set_update(share)
2748 *
2749 * Set the changed attribute of the share to true.
2750 */
2751
2752 static void
2753 zfs_set_update(sa_share_t share)
2754 {
2755 set_node_attr((void *)share, "changed", "true");
2756 }
2757
2758 /*
2759 * sa_commit_properties(optionset, clear)
2760 *
2761 * Check if SMF or ZFS config and either update or abort the pending
2762 * changes.
2763 */
2764
2765 int
2766 sa_commit_properties(sa_optionset_t optionset, int clear)
2767 {
2768 sa_group_t group;
2769 sa_group_t parent;
2770 int zfs = 0;
2771 int needsupdate = 0;
2772 int ret = SA_OK;
2773 sa_handle_t handle;
2774
2775 group = sa_get_optionset_parent(optionset);
2776 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2777 /* only update ZFS if on a share */
2778 parent = sa_get_parent_group(group);
2779 zfs++;
2780 if (parent != NULL && is_zfs_group(parent))
2781 needsupdate = zfs_needs_update(group);
2782 else
2783 zfs = 0;
2784 }
2785 if (zfs) {
2786 if (!clear && needsupdate)
2787 ret = sa_zfs_update((sa_share_t)group);
2788 } else {
2789 handle = sa_find_group_handle(group);
2790 if (handle != NULL) {
2791 if (clear) {
2792 (void) sa_abort_transaction(
2793 handle->scfhandle);
2794 } else {
2795 ret = sa_end_transaction(
2796 handle->scfhandle, handle);
2797 }
2798 } else {
2799 ret = SA_SYSTEM_ERR;
2800 }
2801 }
2802 return (ret);
2803 }
2804
2805 /*
2806 * sa_destroy_optionset(optionset)
2807 *
2808 * Remove the optionset from its group. Update the repository to
2809 * reflect this change.
2810 */
2811
2812 int
2813 sa_destroy_optionset(sa_optionset_t optionset)
2814 {
2815 char name[SA_STRSIZE];
2816 int len;
2817 int ret;
2818 char *id = NULL;
2819 sa_group_t group;
2820 int ispersist = 1;
2821
2822 /* now delete the prop group */
2823 group = sa_get_optionset_parent(optionset);
2824 if (group != NULL) {
2825 if (sa_is_resource(group)) {
2826 sa_resource_t resource = group;
2827 sa_share_t share = sa_get_resource_parent(resource);
2828 group = sa_get_parent_group(share);
2829 id = sa_get_share_attr(share, "id");
2830 } else if (sa_is_share(group)) {
2831 id = sa_get_share_attr((sa_share_t)group, "id");
2832 }
2833 ispersist = sa_is_persistent(group);
2834 }
2835 if (ispersist) {
2836 sa_handle_t handle = sa_find_group_handle(group);
2837 len = sa_optionset_name(optionset, name, sizeof (name), id);
2838 if (handle != NULL) {
2839 if (len > 0) {
2840 ret = sa_delete_pgroup(handle->scfhandle,
2841 name);
2842 }
2843 } else {
2844 ret = SA_SYSTEM_ERR;
2845 }
2846 }
2847 xmlUnlinkNode((xmlNodePtr)optionset);
2848 xmlFreeNode((xmlNodePtr)optionset);
2849 if (id != NULL)
2850 sa_free_attr_string(id);
2851 return (ret);
2852 }
2853
2854 /* private to the implementation */
2855 int
2856 _sa_remove_optionset(sa_optionset_t optionset)
2857 {
2858 int ret = SA_OK;
2859
2860 xmlUnlinkNode((xmlNodePtr)optionset);
2861 xmlFreeNode((xmlNodePtr)optionset);
2862 return (ret);
2863 }
2864
2865 /*
2866 * sa_create_security(group, sectype, proto)
2867 *
2868 * Create a security optionset (one that has a type name and a
2869 * proto). Security is left over from a pure NFS implementation. The
2870 * naming will change in the future when the API is released.
2871 */
2872 sa_security_t
2873 sa_create_security(sa_group_t group, char *sectype, char *proto)
2874 {
2875 sa_security_t security;
2876 char *id = NULL;
2877 sa_group_t parent;
2878 char *groupname = NULL;
2879
2880 if (group != NULL && sa_is_share(group)) {
2881 id = sa_get_share_attr((sa_share_t)group, "id");
2882 parent = sa_get_parent_group(group);
2883 if (parent != NULL)
2884 groupname = sa_get_group_attr(parent, "name");
2885 } else if (group != NULL) {
2886 groupname = sa_get_group_attr(group, "name");
2887 }
2888
2889 security = sa_get_security(group, sectype, proto);
2890 if (security != NULL) {
2891 /* can't have a duplicate security option */
2892 security = NULL;
2893 } else {
2894 security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2895 NULL, (xmlChar *)"security", NULL);
2896 if (security != NULL) {
2897 char oname[SA_STRSIZE];
2898 sa_set_security_attr(security, "type", proto);
2899
2900 sa_set_security_attr(security, "sectype", sectype);
2901 (void) sa_security_name(security, oname,
2902 sizeof (oname), id);
2903 if (groupname != NULL && sa_is_persistent(group)) {
2904 sa_handle_t handle = sa_find_group_handle(
2905 group);
2906 if (handle != NULL) {
2907 (void) sa_get_instance(
2908 handle->scfhandle, groupname);
2909 (void) sa_create_pgroup(
2910 handle->scfhandle, oname);
2911 }
2912 }
2913 }
2914 }
2915 if (id != NULL)
2916 sa_free_attr_string(id);
2917 if (groupname != NULL)
2918 sa_free_attr_string(groupname);
2919 return (security);
2920 }
2921
2922 /*
2923 * sa_destroy_security(security)
2924 *
2925 * Remove the specified optionset from the document and the
2926 * configuration.
2927 */
2928
2929 int
2930 sa_destroy_security(sa_security_t security)
2931 {
2932 char name[SA_STRSIZE];
2933 int len;
2934 int ret = SA_OK;
2935 char *id = NULL;
2936 sa_group_t group;
2937 int iszfs = 0;
2938 int ispersist = 1;
2939
2940 group = sa_get_optionset_parent(security);
2941
2942 if (group != NULL)
2943 iszfs = sa_group_is_zfs(group);
2944
2945 if (group != NULL && !iszfs) {
2946 if (sa_is_share(group))
2947 ispersist = sa_is_persistent(group);
2948 id = sa_get_share_attr((sa_share_t)group, "id");
2949 }
2950 if (ispersist) {
2951 len = sa_security_name(security, name, sizeof (name), id);
2952 if (!iszfs && len > 0) {
2953 sa_handle_t handle = sa_find_group_handle(group);
2954 if (handle != NULL) {
2955 ret = sa_delete_pgroup(handle->scfhandle,
2956 name);
2957 } else {
2958 ret = SA_SYSTEM_ERR;
2959 }
2960 }
2961 }
2962 xmlUnlinkNode((xmlNodePtr)security);
2963 xmlFreeNode((xmlNodePtr)security);
2964 if (iszfs)
2965 ret = sa_zfs_update(group);
2966 if (id != NULL)
2967 sa_free_attr_string(id);
2968 return (ret);
2969 }
2970
2971 /*
2972 * sa_get_security_attr(optionset, tag)
2973 *
2974 * Return the specified attribute value from the optionset.
2975 */
2976
2977 char *
2978 sa_get_security_attr(sa_property_t optionset, char *tag)
2979 {
2980 return (get_node_attr((void *)optionset, tag));
2981
2982 }
2983
2984 /*
2985 * sa_set_security_attr(optionset, tag, value)
2986 *
2987 * Set the optioset attribute specied by tag to the specified value.
2988 */
2989
2990 void
2991 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
2992 {
2993 set_node_attr((void *)optionset, tag, value);
2994 }
2995
2996 /*
2997 * is_nodetype(node, type)
2998 *
2999 * Check to see if node is of the type specified.
3000 */
3001
3002 static int
3003 is_nodetype(void *node, char *type)
3004 {
3005 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
3006 }
3007
3008 /*
3009 * add_or_update()
3010 *
3011 * Add or update a property. Pulled out of sa_set_prop_by_prop for
3012 * readability.
3013 */
3014 static int
3015 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
3016 scf_transaction_entry_t *entry, char *name, char *valstr)
3017 {
3018 int ret = SA_SYSTEM_ERR;
3019
3020 if (value != NULL) {
3021 if (type == SA_PROP_OP_ADD)
3022 ret = scf_transaction_property_new(scf_handle->trans,
3023 entry, name, SCF_TYPE_ASTRING);
3024 else
3025 ret = scf_transaction_property_change(scf_handle->trans,
3026 entry, name, SCF_TYPE_ASTRING);
3027 if (ret == 0) {
3028 ret = scf_value_set_astring(value, valstr);
3029 if (ret == 0)
3030 ret = scf_entry_add_value(entry, value);
3031 if (ret == 0)
3032 return (ret);
3033 scf_value_destroy(value);
3034 } else {
3035 scf_entry_destroy(entry);
3036 }
3037 }
3038 return (SA_SYSTEM_ERR);
3039 }
3040
3041 /*
3042 * sa_set_prop_by_prop(optionset, group, prop, type)
3043 *
3044 * Add/remove/update the specified property prop into the optionset or
3045 * share. If a share, sort out which property group based on GUID. In
3046 * all cases, the appropriate transaction is set (or ZFS share is
3047 * marked as needing an update)
3048 */
3049
3050 static int
3051 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
3052 sa_property_t prop, int type)
3053 {
3054 char *name;
3055 char *valstr;
3056 int ret = SA_OK;
3057 scf_transaction_entry_t *entry;
3058 scf_value_t *value;
3059 int opttype; /* 1 == optionset, 0 == security */
3060 char *id = NULL;
3061 int iszfs = 0;
3062 sa_group_t parent = NULL;
3063 sa_share_t share = NULL;
3064 sa_handle_t handle;
3065 scfutilhandle_t *scf_handle;
3066
3067 if (!sa_is_persistent(group)) {
3068 /*
3069 * if the group/share is not persistent we don't need
3070 * to do anything here
3071 */
3072 return (SA_OK);
3073 }
3074 handle = sa_find_group_handle(group);
3075 if (handle == NULL || handle->scfhandle == NULL)
3076 return (SA_SYSTEM_ERR);
3077 scf_handle = handle->scfhandle;
3078 name = sa_get_property_attr(prop, "type");
3079 valstr = sa_get_property_attr(prop, "value");
3080 entry = scf_entry_create(scf_handle->handle);
3081 opttype = is_nodetype((void *)optionset, "optionset");
3082
3083 /*
3084 * Check for share vs. resource since they need slightly
3085 * different treatment given the hierarchy.
3086 */
3087 if (valstr != NULL && entry != NULL) {
3088 if (sa_is_share(group)) {
3089 parent = sa_get_parent_group(group);
3090 share = (sa_share_t)group;
3091 if (parent != NULL)
3092 iszfs = is_zfs_group(parent);
3093 } else if (sa_is_resource(group)) {
3094 share = sa_get_parent_group(group);
3095 if (share != NULL)
3096 parent = sa_get_parent_group(share);
3097 } else {
3098 iszfs = is_zfs_group(group);
3099 }
3100 if (!iszfs) {
3101 if (scf_handle->trans == NULL) {
3102 char oname[SA_STRSIZE];
3103 char *groupname = NULL;
3104 if (share != NULL) {
3105 if (parent != NULL)
3106 groupname =
3107 sa_get_group_attr(parent,
3108 "name");
3109 id = sa_get_share_attr(
3110 (sa_share_t)share, "id");
3111 } else {
3112 groupname = sa_get_group_attr(group,
3113 "name");
3114 }
3115 if (groupname != NULL) {
3116 ret = sa_get_instance(scf_handle,
3117 groupname);
3118 sa_free_attr_string(groupname);
3119 }
3120 if (opttype)
3121 (void) sa_optionset_name(optionset,
3122 oname, sizeof (oname), id);
3123 else
3124 (void) sa_security_name(optionset,
3125 oname, sizeof (oname), id);
3126 ret = sa_start_transaction(scf_handle, oname);
3127 if (id != NULL)
3128 sa_free_attr_string(id);
3129 }
3130 if (ret == SA_OK) {
3131 switch (type) {
3132 case SA_PROP_OP_REMOVE:
3133 ret = scf_transaction_property_delete(
3134 scf_handle->trans, entry, name);
3135 break;
3136 case SA_PROP_OP_ADD:
3137 case SA_PROP_OP_UPDATE:
3138 value = scf_value_create(
3139 scf_handle->handle);
3140 ret = add_or_update(scf_handle, type,
3141 value, entry, name, valstr);
3142 break;
3143 }
3144 }
3145 } else {
3146 /*
3147 * ZFS update. The calling function would have updated
3148 * the internal XML structure. Just need to flag it as
3149 * changed for ZFS.
3150 */
3151 zfs_set_update((sa_share_t)group);
3152 }
3153 }
3154
3155 if (name != NULL)
3156 sa_free_attr_string(name);
3157 if (valstr != NULL)
3158 sa_free_attr_string(valstr);
3159 else if (entry != NULL)
3160 scf_entry_destroy(entry);
3161
3162 if (ret == -1)
3163 ret = SA_SYSTEM_ERR;
3164
3165 return (ret);
3166 }
3167
3168 /*
3169 * sa_create_section(name, value)
3170 *
3171 * Create a new section with the specified name and extra data.
3172 */
3173
3174 sa_property_t
3175 sa_create_section(char *name, char *extra)
3176 {
3177 xmlNodePtr node;
3178
3179 node = xmlNewNode(NULL, (xmlChar *)"section");
3180 if (node != NULL) {
3181 if (name != NULL)
3182 (void) xmlSetProp(node, (xmlChar *)"name",
3183 (xmlChar *)name);
3184 if (extra != NULL)
3185 (void) xmlSetProp(node, (xmlChar *)"extra",
3186 (xmlChar *)extra);
3187 }
3188 return ((sa_property_t)node);
3189 }
3190
3191 void
3192 sa_set_section_attr(sa_property_t sect, char *name, char *value)
3193 {
3194 (void) xmlSetProp(sect, (xmlChar *)name, (xmlChar *)value);
3195 }
3196
3197 /*
3198 * sa_create_property(section, name, value)
3199 *
3200 * Create a new property with the specified name and value.
3201 */
3202
3203 sa_property_t
3204 sa_create_property(char *name, char *value)
3205 {
3206 xmlNodePtr node;
3207
3208 node = xmlNewNode(NULL, (xmlChar *)"option");
3209 if (node != NULL) {
3210 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
3211 (void) xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
3212 }
3213 return ((sa_property_t)node);
3214 }
3215
3216 /*
3217 * sa_add_property(object, property)
3218 *
3219 * Add the specified property to the object. Issue the appropriate
3220 * transaction or mark a ZFS object as needing an update.
3221 */
3222
3223 int
3224 sa_add_property(void *object, sa_property_t property)
3225 {
3226 int ret = SA_OK;
3227 sa_group_t parent;
3228 sa_group_t group;
3229 char *proto;
3230
3231 if (property != NULL) {
3232 sa_handle_t handle;
3233 handle = sa_find_group_handle((sa_group_t)object);
3234 /* It is legitimate to not find a handle */
3235 proto = sa_get_optionset_attr(object, "type");
3236 if ((ret = sa_valid_property(handle, object, proto,
3237 property)) == SA_OK) {
3238 property = (sa_property_t)xmlAddChild(
3239 (xmlNodePtr)object, (xmlNodePtr)property);
3240 } else {
3241 if (proto != NULL)
3242 sa_free_attr_string(proto);
3243 return (ret);
3244 }
3245 if (proto != NULL)
3246 sa_free_attr_string(proto);
3247 }
3248
3249
3250 parent = sa_get_parent_group(object);
3251 if (!sa_is_persistent(parent))
3252 return (ret);
3253
3254 if (sa_is_resource(parent)) {
3255 /*
3256 * Resources are children of share. Need to go up two
3257 * levels to find the group but the parent needs to be
3258 * the share at this point in order to get the "id".
3259 */
3260 parent = sa_get_parent_group(parent);
3261 group = sa_get_parent_group(parent);
3262 } else if (sa_is_share(parent)) {
3263 group = sa_get_parent_group(parent);
3264 } else {
3265 group = parent;
3266 }
3267
3268 if (property == NULL) {
3269 ret = SA_NO_MEMORY;
3270 } else {
3271 char oname[SA_STRSIZE];
3272
3273 if (!is_zfs_group(group)) {
3274 char *id = NULL;
3275 sa_handle_t handle;
3276 scfutilhandle_t *scf_handle;
3277
3278 handle = sa_find_group_handle(group);
3279 if (handle == NULL ||
3280 handle->scfhandle == NULL)
3281 ret = SA_SYSTEM_ERR;
3282 if (ret == SA_OK) {
3283 scf_handle = handle->scfhandle;
3284 if (sa_is_share((sa_group_t)parent)) {
3285 id = sa_get_share_attr(
3286 (sa_share_t)parent, "id");
3287 }
3288 if (scf_handle->trans == NULL) {
3289 if (is_nodetype(object, "optionset")) {
3290 (void) sa_optionset_name(
3291 (sa_optionset_t)object,
3292 oname, sizeof (oname), id);
3293 } else {
3294 (void) sa_security_name(
3295 (sa_optionset_t)object,
3296 oname, sizeof (oname), id);
3297 }
3298 ret = sa_start_transaction(scf_handle,
3299 oname);
3300 }
3301 if (ret == SA_OK) {
3302 char *name;
3303 char *value;
3304 name = sa_get_property_attr(property,
3305 "type");
3306 value = sa_get_property_attr(property,
3307 "value");
3308 if (name != NULL && value != NULL) {
3309 if (scf_handle->scf_state ==
3310 SCH_STATE_INIT) {
3311 ret = sa_set_property(
3312 scf_handle, name,
3313 value);
3314 }
3315 } else {
3316 ret = SA_CONFIG_ERR;
3317 }
3318 if (name != NULL)
3319 sa_free_attr_string(
3320 name);
3321 if (value != NULL)
3322 sa_free_attr_string(value);
3323 }
3324 if (id != NULL)
3325 sa_free_attr_string(id);
3326 }
3327 } else {
3328 /*
3329 * ZFS is a special case. We do want
3330 * to allow editing property/security
3331 * lists since we can have a better
3332 * syntax and we also want to keep
3333 * things consistent when possible.
3334 *
3335 * Right now, we defer until the
3336 * sa_commit_properties so we can get
3337 * them all at once. We do need to
3338 * mark the share as "changed"
3339 */
3340 zfs_set_update((sa_share_t)parent);
3341 }
3342 }
3343 return (ret);
3344 }
3345
3346 /*
3347 * sa_remove_property(property)
3348 *
3349 * Remove the specied property from its containing object. Update the
3350 * repository as appropriate.
3351 */
3352
3353 int
3354 sa_remove_property(sa_property_t property)
3355 {
3356 int ret = SA_OK;
3357
3358 if (property != NULL) {
3359 sa_optionset_t optionset;
3360 sa_group_t group;
3361 optionset = sa_get_property_parent(property);
3362 if (optionset != NULL) {
3363 group = sa_get_optionset_parent(optionset);
3364 if (group != NULL) {
3365 ret = sa_set_prop_by_prop(optionset, group,
3366 property, SA_PROP_OP_REMOVE);
3367 }
3368 }
3369 xmlUnlinkNode((xmlNodePtr)property);
3370 xmlFreeNode((xmlNodePtr)property);
3371 } else {
3372 ret = SA_NO_SUCH_PROP;
3373 }
3374 return (ret);
3375 }
3376
3377 /*
3378 * sa_update_property(property, value)
3379 *
3380 * Update the specified property to the new value. If value is NULL,
3381 * we currently treat this as a remove.
3382 */
3383
3384 int
3385 sa_update_property(sa_property_t property, char *value)
3386 {
3387 int ret = SA_OK;
3388 if (value == NULL) {
3389 return (sa_remove_property(property));
3390 } else {
3391 sa_optionset_t optionset;
3392 sa_group_t group;
3393 set_node_attr((void *)property, "value", value);
3394 optionset = sa_get_property_parent(property);
3395 if (optionset != NULL) {
3396 group = sa_get_optionset_parent(optionset);
3397 if (group != NULL) {
3398 ret = sa_set_prop_by_prop(optionset, group,
3399 property, SA_PROP_OP_UPDATE);
3400 }
3401 } else {
3402 ret = SA_NO_SUCH_PROP;
3403 }
3404 }
3405 return (ret);
3406 }
3407
3408 /*
3409 * sa_get_protocol_section(propset, prop)
3410 *
3411 * Get the specified protocol specific section. These are global to
3412 * the protocol and not specific to a group or share.
3413 */
3414
3415 sa_protocol_properties_t
3416 sa_get_protocol_section(sa_protocol_properties_t propset, char *section)
3417 {
3418 xmlNodePtr node = (xmlNodePtr)propset;
3419 xmlChar *value = NULL;
3420 char *proto;
3421
3422 proto = sa_get_optionset_attr(propset, "type");
3423 if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3424 if (proto != NULL)
3425 sa_free_attr_string(proto);
3426 return (propset);
3427 }
3428
3429 for (node = node->children; node != NULL;
3430 node = node->next) {
3431 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3432 if (section == NULL)
3433 break;
3434 value = xmlGetProp(node, (xmlChar *)"name");
3435 if (value != NULL &&
3436 xmlStrcasecmp(value, (xmlChar *)section) == 0) {
3437 break;
3438 }
3439 if (value != NULL) {
3440 xmlFree(value);
3441 value = NULL;
3442 }
3443 }
3444 }
3445 if (value != NULL)
3446 xmlFree(value);
3447 if (proto != NULL)
3448 sa_free_attr_string(proto);
3449 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) {
3450 /*
3451 * avoid a non option node -- it is possible to be a
3452 * text node
3453 */
3454 node = NULL;
3455 }
3456 return ((sa_protocol_properties_t)node);
3457 }
3458
3459 /*
3460 * sa_get_next_protocol_section(prop, find)
3461 *
3462 * Get the next protocol specific section in the list.
3463 */
3464
3465 sa_property_t
3466 sa_get_next_protocol_section(sa_property_t prop, char *find)
3467 {
3468 xmlNodePtr node;
3469 xmlChar *value = NULL;
3470 char *proto;
3471
3472 proto = sa_get_optionset_attr(prop, "type");
3473 if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3474 if (proto != NULL)
3475 sa_free_attr_string(proto);
3476 return ((sa_property_t)NULL);
3477 }
3478
3479 for (node = ((xmlNodePtr)prop)->next; node != NULL;
3480 node = node->next) {
3481 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3482 if (find == NULL)
3483 break;
3484 value = xmlGetProp(node, (xmlChar *)"name");
3485 if (value != NULL &&
3486 xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3487 break;
3488 }
3489 if (value != NULL) {
3490 xmlFree(value);
3491 value = NULL;
3492 }
3493
3494 }
3495 }
3496 if (value != NULL)
3497 xmlFree(value);
3498 if (proto != NULL)
3499 sa_free_attr_string(proto);
3500 return ((sa_property_t)node);
3501 }
3502
3503 /*
3504 * sa_get_protocol_property(propset, prop)
3505 *
3506 * Get the specified protocol specific property. These are global to
3507 * the protocol and not specific to a group or share.
3508 */
3509
3510 sa_property_t
3511 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3512 {
3513 xmlNodePtr node = (xmlNodePtr)propset;
3514 xmlChar *value = NULL;
3515
3516 if (propset == NULL)
3517 return (NULL);
3518
3519 for (node = node->children; node != NULL;
3520 node = node->next) {
3521 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3522 if (prop == NULL)
3523 break;
3524 value = xmlGetProp(node, (xmlChar *)"type");
3525 if (value != NULL &&
3526 xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3527 break;
3528 }
3529 if (value != NULL) {
3530 xmlFree(value);
3531 value = NULL;
3532 }
3533 }
3534 }
3535 if (value != NULL)
3536 xmlFree(value);
3537 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3538 /*
3539 * avoid a non option node -- it is possible to be a
3540 * text node
3541 */
3542 node = NULL;
3543 }
3544 return ((sa_property_t)node);
3545 }
3546
3547 /*
3548 * sa_get_next_protocol_property(prop)
3549 *
3550 * Get the next protocol specific property in the list.
3551 */
3552
3553 sa_property_t
3554 sa_get_next_protocol_property(sa_property_t prop, char *find)
3555 {
3556 xmlNodePtr node;
3557 xmlChar *value = NULL;
3558
3559 for (node = ((xmlNodePtr)prop)->next; node != NULL;
3560 node = node->next) {
3561 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3562 if (find == NULL)
3563 break;
3564 value = xmlGetProp(node, (xmlChar *)"type");
3565 if (value != NULL &&
3566 xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3567 break;
3568 }
3569 if (value != NULL) {
3570 xmlFree(value);
3571 value = NULL;
3572 }
3573
3574 }
3575 }
3576 if (value != NULL)
3577 xmlFree(value);
3578 return ((sa_property_t)node);
3579 }
3580
3581 /*
3582 * sa_set_protocol_property(prop, value)
3583 *
3584 * Set the specified property to have the new value. The protocol
3585 * specific plugin will then be called to update the property.
3586 */
3587
3588 int
3589 sa_set_protocol_property(sa_property_t prop, char *section, char *value)
3590 {
3591 sa_protocol_properties_t propset;
3592 char *proto;
3593 int ret = SA_INVALID_PROTOCOL;
3594
3595 propset = ((xmlNodePtr)prop)->parent;
3596 if (propset != NULL) {
3597 proto = sa_get_optionset_attr(propset, "type");
3598 if (proto != NULL) {
3599 if (section != NULL)
3600 set_node_attr((xmlNodePtr)prop, "section",
3601 section);
3602 set_node_attr((xmlNodePtr)prop, "value", value);
3603 ret = sa_proto_set_property(proto, prop);
3604 sa_free_attr_string(proto);
3605 }
3606 }
3607 return (ret);
3608 }
3609
3610 /*
3611 * sa_add_protocol_property(propset, prop)
3612 *
3613 * Add a new property to the protocol specific property set.
3614 */
3615
3616 int
3617 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3618 {
3619 xmlNodePtr node;
3620
3621 /* should check for legitimacy */
3622 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3623 if (node != NULL)
3624 return (SA_OK);
3625 return (SA_NO_MEMORY);
3626 }
3627
3628 /*
3629 * sa_create_protocol_properties(proto)
3630 *
3631 * Create a protocol specific property set.
3632 */
3633
3634 sa_protocol_properties_t
3635 sa_create_protocol_properties(char *proto)
3636 {
3637 xmlNodePtr node;
3638
3639 node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3640 if (node != NULL)
3641 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3642 return (node);
3643 }
3644
3645 /*
3646 * sa_get_share_resource(share, resource)
3647 *
3648 * Get the named resource from the share, if it exists. If resource is
3649 * NULL, get the first resource.
3650 */
3651
3652 sa_resource_t
3653 sa_get_share_resource(sa_share_t share, char *resource)
3654 {
3655 xmlNodePtr node = NULL;
3656 xmlChar *name;
3657
3658 if (share != NULL) {
3659 for (node = ((xmlNodePtr)share)->children; node != NULL;
3660 node = node->next) {
3661 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
3662 if (resource == NULL) {
3663 /*
3664 * We are looking for the first
3665 * resource node and not a names
3666 * resource.
3667 */
3668 break;
3669 } else {
3670 /* is it the correct share? */
3671 name = xmlGetProp(node,
3672 (xmlChar *)"name");
3673 if (name != NULL &&
3674 xmlStrcasecmp(name,
3675 (xmlChar *)resource) == 0) {
3676 xmlFree(name);
3677 break;
3678 }
3679 xmlFree(name);
3680 }
3681 }
3682 }
3683 }
3684 return ((sa_resource_t)node);
3685 }
3686
3687 /*
3688 * sa_get_next_resource(resource)
3689 * Return the next share following the specified share
3690 * from the internal list of shares. Returns NULL if there
3691 * are no more shares. The list is relative to the same
3692 * group.
3693 */
3694 sa_share_t
3695 sa_get_next_resource(sa_resource_t resource)
3696 {
3697 xmlNodePtr node = NULL;
3698
3699 if (resource != NULL) {
3700 for (node = ((xmlNodePtr)resource)->next; node != NULL;
3701 node = node->next) {
3702 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
3703 break;
3704 }
3705 }
3706 return ((sa_share_t)node);
3707 }
3708
3709 /*
3710 * _sa_get_next_resource_index(share)
3711 *
3712 * get the next resource index number (one greater then current largest)
3713 */
3714
3715 static int
3716 _sa_get_next_resource_index(sa_share_t share)
3717 {
3718 sa_resource_t resource;
3719 int index = 0;
3720 char *id;
3721
3722 for (resource = sa_get_share_resource(share, NULL);
3723 resource != NULL;
3724 resource = sa_get_next_resource(resource)) {
3725 id = get_node_attr((void *)resource, "id");
3726 if (id != NULL) {
3727 int val;
3728 val = atoi(id);
3729 if (val > index)
3730 index = val;
3731 sa_free_attr_string(id);
3732 }
3733 }
3734 return (index + 1);
3735 }
3736
3737
3738 /*
3739 * sa_add_resource(share, resource, persist, &err)
3740 *
3741 * Adds a new resource name associated with share. The resource name
3742 * must be unique in the system and will be case insensitive (eventually).
3743 */
3744
3745 sa_resource_t
3746 sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
3747 {
3748 xmlNodePtr node;
3749 int err = SA_OK;
3750 sa_resource_t res;
3751 sa_group_t group;
3752 sa_handle_t handle;
3753 char istring[8]; /* just big enough for an integer value */
3754 int index;
3755
3756 group = sa_get_parent_group(share);
3757 handle = sa_find_group_handle(group);
3758 res = sa_find_resource(handle, resource);
3759 if (res != NULL) {
3760 err = SA_DUPLICATE_NAME;
3761 res = NULL;
3762 } else {
3763 node = xmlNewChild((xmlNodePtr)share, NULL,
3764 (xmlChar *)"resource", NULL);
3765 if (node != NULL) {
3766 (void) xmlSetProp(node, (xmlChar *)"name",
3767 (xmlChar *)resource);
3768 (void) xmlSetProp(node, (xmlChar *)"type", persist ?
3769 (xmlChar *)"persist" : (xmlChar *)"transient");
3770 if (persist != SA_SHARE_TRANSIENT) {
3771 index = _sa_get_next_resource_index(share);
3772 (void) snprintf(istring, sizeof (istring), "%d",
3773 index);
3774 (void) xmlSetProp(node, (xmlChar *)"id",
3775 (xmlChar *)istring);
3776
3777 if (!sa_is_persistent((sa_group_t)share))
3778 goto done;
3779
3780 if (!sa_group_is_zfs(group)) {
3781 /* ZFS doesn't use resource names */
3782 sa_handle_t handle;
3783
3784 handle = sa_find_group_handle(
3785 group);
3786 if (handle != NULL)
3787 err = sa_commit_share(
3788 handle->scfhandle, group,
3789 share);
3790 else
3791 err = SA_SYSTEM_ERR;
3792 } else {
3793 err = sa_zfs_update((sa_share_t)group);
3794 }
3795 }
3796 }
3797 }
3798 done:
3799 if (error != NULL)
3800 *error = err;
3801 return ((sa_resource_t)node);
3802 }
3803
3804 /*
3805 * sa_remove_resource(resource)
3806 *
3807 * Remove the resource name from the share (and the system)
3808 */
3809
3810 int
3811 sa_remove_resource(sa_resource_t resource)
3812 {
3813 sa_share_t share;
3814 sa_group_t group;
3815 char *type;
3816 int ret = SA_OK;
3817 boolean_t transient = B_FALSE;
3818 sa_optionset_t opt;
3819
3820 share = sa_get_resource_parent(resource);
3821 type = sa_get_share_attr(share, "type");
3822 group = sa_get_parent_group(share);
3823
3824
3825 if (type != NULL) {
3826 if (strcmp(type, "persist") != 0)
3827 transient = B_TRUE;
3828 sa_free_attr_string(type);
3829 }
3830
3831 /* Disable the resource for all protocols. */
3832 (void) sa_disable_resource(resource, NULL);
3833
3834 /* Remove any optionsets from the resource. */
3835 for (opt = sa_get_optionset(resource, NULL);
3836 opt != NULL;
3837 opt = sa_get_next_optionset(opt))
3838 (void) sa_destroy_optionset(opt);
3839
3840 /* Remove from the share */
3841 xmlUnlinkNode((xmlNode *)resource);
3842 xmlFreeNode((xmlNode *)resource);
3843
3844 /* only do SMF action if permanent and not ZFS */
3845 if (transient)
3846 return (ret);
3847
3848 if (!sa_group_is_zfs(group)) {
3849 sa_handle_t handle = sa_find_group_handle(group);
3850 if (handle != NULL)
3851 ret = sa_commit_share(handle->scfhandle, group, share);
3852 else
3853 ret = SA_SYSTEM_ERR;
3854 } else {
3855 ret = sa_zfs_update((sa_share_t)group);
3856 }
3857
3858 return (ret);
3859 }
3860
3861 /*
3862 * proto_rename_resource(handle, group, resource, newname)
3863 *
3864 * Helper function for sa_rename_resource that notifies the protocol
3865 * of a resource name change prior to a config repository update.
3866 */
3867 static int
3868 proto_rename_resource(sa_handle_t handle, sa_group_t group,
3869 sa_resource_t resource, char *newname)
3870 {
3871 sa_optionset_t optionset;
3872 int ret = SA_OK;
3873 int err;
3874
3875 for (optionset = sa_get_optionset(group, NULL);
3876 optionset != NULL;
3877 optionset = sa_get_next_optionset(optionset)) {
3878 char *type;
3879 type = sa_get_optionset_attr(optionset, "type");
3880 if (type != NULL) {
3881 err = sa_proto_rename_resource(handle, type, resource,
3882 newname);
3883 if (err != SA_OK)
3884 ret = err;
3885 sa_free_attr_string(type);
3886 }
3887 }
3888 return (ret);
3889 }
3890
3891 /*
3892 * sa_rename_resource(resource, newname)
3893 *
3894 * Rename the resource to the new name, if it is unique.
3895 */
3896
3897 int
3898 sa_rename_resource(sa_resource_t resource, char *newname)
3899 {
3900 sa_share_t share;
3901 sa_group_t group = NULL;
3902 sa_resource_t target;
3903 int ret = SA_CONFIG_ERR;
3904 sa_handle_t handle = NULL;
3905
3906 share = sa_get_resource_parent(resource);
3907 if (share == NULL)
3908 return (ret);
3909
3910 group = sa_get_parent_group(share);
3911 if (group == NULL)
3912 return (ret);
3913
3914 handle = sa_find_group_handle(group);
3915 if (handle == NULL)
3916 return (ret);
3917
3918 target = sa_find_resource(handle, newname);
3919 if (target != NULL) {
3920 ret = SA_DUPLICATE_NAME;
3921 } else {
3922 /*
3923 * Everything appears to be valid at this
3924 * point. Change the name of the active share and then
3925 * update the share in the appropriate repository.
3926 */
3927 ret = proto_rename_resource(handle, group, resource, newname);
3928 set_node_attr(resource, "name", newname);
3929
3930 if (!sa_is_persistent((sa_group_t)share))
3931 return (ret);
3932
3933 if (!sa_group_is_zfs(group)) {
3934 ret = sa_commit_share(handle->scfhandle, group,
3935 share);
3936 } else {
3937 ret = sa_zfs_update((sa_share_t)group);
3938 }
3939 }
3940 return (ret);
3941 }
3942
3943 /*
3944 * sa_get_resource_attr(resource, tag)
3945 *
3946 * Get the named attribute of the resource. "name" and "id" are
3947 * currently defined. NULL if tag not defined.
3948 */
3949
3950 char *
3951 sa_get_resource_attr(sa_resource_t resource, char *tag)
3952 {
3953 return (get_node_attr((void *)resource, tag));
3954 }
3955
3956 /*
3957 * sa_set_resource_attr(resource, tag, value)
3958 *
3959 * Get the named attribute of the resource. "name" and "id" are
3960 * currently defined. NULL if tag not defined. Currently we don't do
3961 * much, but additional checking may be needed in the future.
3962 */
3963
3964 int
3965 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
3966 {
3967 set_node_attr((void *)resource, tag, value);
3968 return (SA_OK);
3969 }
3970
3971 /*
3972 * sa_get_resource_parent(resource_t)
3973 *
3974 * Returns the share associated with the resource.
3975 */
3976
3977 sa_share_t
3978 sa_get_resource_parent(sa_resource_t resource)
3979 {
3980 sa_share_t share = NULL;
3981
3982 if (resource != NULL)
3983 share = (sa_share_t)((xmlNodePtr)resource)->parent;
3984 return (share);
3985 }
3986
3987 /*
3988 * find_resource(group, name)
3989 *
3990 * Find the resource within the group.
3991 */
3992
3993 static sa_resource_t
3994 find_resource(sa_group_t group, char *resname)
3995 {
3996 sa_share_t share;
3997 sa_resource_t resource = NULL;
3998 char *name;
3999
4000 /* Iterate over all the shares and resources in the group. */
4001 for (share = sa_get_share(group, NULL);
4002 share != NULL && resource == NULL;
4003 share = sa_get_next_share(share)) {
4004 for (resource = sa_get_share_resource(share, NULL);
4005 resource != NULL;
4006 resource = sa_get_next_resource(resource)) {
4007 name = sa_get_resource_attr(resource, "name");
4008 if (name != NULL && xmlStrcasecmp((xmlChar*)name,
4009 (xmlChar*)resname) == 0) {
4010 sa_free_attr_string(name);
4011 break;
4012 }
4013 if (name != NULL) {
4014 sa_free_attr_string(name);
4015 }
4016 }
4017 }
4018 return (resource);
4019 }
4020
4021 /*
4022 * sa_find_resource(name)
4023 *
4024 * Find the named resource in the system.
4025 */
4026
4027 sa_resource_t
4028 sa_find_resource(sa_handle_t handle, char *name)
4029 {
4030 sa_group_t group;
4031 sa_group_t zgroup;
4032 sa_resource_t resource = NULL;
4033
4034 /*
4035 * Iterate over all groups and zfs subgroups and check for
4036 * resource name in them.
4037 */
4038 for (group = sa_get_group(handle, NULL); group != NULL;
4039 group = sa_get_next_group(group)) {
4040
4041 if (is_zfs_group(group)) {
4042 for (zgroup =
4043 (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
4044 (xmlChar *)"group");
4045 zgroup != NULL && resource == NULL;
4046 zgroup = sa_get_next_group(zgroup)) {
4047 resource = find_resource(zgroup, name);
4048 }
4049 } else {
4050 resource = find_resource(group, name);
4051 }
4052 if (resource != NULL)
4053 break;
4054 }
4055 return (resource);
4056 }
4057
4058 /*
4059 * sa_get_resource(group, resource)
4060 *
4061 * Search all the shares in the specified group for a share with a
4062 * resource name matching the one specified.
4063 *
4064 * In the future, it may be advantageous to allow group to be NULL and
4065 * search all groups but that isn't needed at present.
4066 */
4067
4068 sa_resource_t
4069 sa_get_resource(sa_group_t group, char *resource)
4070 {
4071 sa_share_t share = NULL;
4072 sa_resource_t res = NULL;
4073
4074 if (resource != NULL) {
4075 for (share = sa_get_share(group, NULL);
4076 share != NULL && res == NULL;
4077 share = sa_get_next_share(share)) {
4078 res = sa_get_share_resource(share, resource);
4079 }
4080 }
4081 return (res);
4082 }
4083
4084 /*
4085 * get_protocol_list(optionset, object)
4086 *
4087 * Get the protocol optionset list for the object and add them as
4088 * properties to optionset.
4089 */
4090 static int
4091 get_protocol_list(sa_optionset_t optionset, void *object)
4092 {
4093 sa_property_t prop;
4094 sa_optionset_t opts;
4095 int ret = SA_OK;
4096
4097 for (opts = sa_get_optionset(object, NULL);
4098 opts != NULL;
4099 opts = sa_get_next_optionset(opts)) {
4100 char *type;
4101 type = sa_get_optionset_attr(opts, "type");
4102 /*
4103 * It is possible to have a non-protocol optionset. We
4104 * skip any of those found.
4105 */
4106 if (type == NULL)
4107 continue;
4108 prop = sa_create_property(type, "true");
4109 sa_free_attr_string(type);
4110 if (prop != NULL)
4111 prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset,
4112 (xmlNodePtr)prop);
4113 /* If prop is NULL, don't bother continuing */
4114 if (prop == NULL) {
4115 ret = SA_NO_MEMORY;
4116 break;
4117 }
4118 }
4119 return (ret);
4120 }
4121
4122 /*
4123 * sa_free_protoset(optionset)
4124 *
4125 * Free the protocol property optionset.
4126 */
4127 static void
4128 sa_free_protoset(sa_optionset_t optionset)
4129 {
4130 if (optionset != NULL) {
4131 xmlUnlinkNode((xmlNodePtr) optionset);
4132 xmlFreeNode((xmlNodePtr) optionset);
4133 }
4134 }
4135
4136 /*
4137 * sa_optionset_t sa_get_active_protocols(object)
4138 *
4139 * Return a list of the protocols that are active for the object.
4140 * This is currently an internal helper function, but could be
4141 * made visible if there is enough demand for it.
4142 *
4143 * The function finds the parent group and extracts the protocol
4144 * optionsets creating a new optionset with the protocols as properties.
4145 *
4146 * The caller must free the returned optionset.
4147 */
4148
4149 static sa_optionset_t
4150 sa_get_active_protocols(void *object)
4151 {
4152 sa_optionset_t options;
4153 sa_share_t share = NULL;
4154 sa_group_t group = NULL;
4155 sa_resource_t resource = NULL;
4156 int ret = SA_OK;
4157
4158 if (object == NULL)
4159 return (NULL);
4160 options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset");
4161 if (options == NULL)
4162 return (NULL);
4163
4164 /*
4165 * Find the objects up the tree that might have protocols
4166 * enabled on them.
4167 */
4168 if (sa_is_resource(object)) {
4169 resource = (sa_resource_t)object;
4170 share = sa_get_resource_parent(resource);
4171 group = sa_get_parent_group(share);
4172 } else if (sa_is_share(object)) {
4173 share = (sa_share_t)object;
4174 group = sa_get_parent_group(share);
4175 } else {
4176 group = (sa_group_t)group;
4177 }
4178 if (resource != NULL)
4179 ret = get_protocol_list(options, resource);
4180 if (ret == SA_OK && share != NULL)
4181 ret = get_protocol_list(options, share);
4182 if (ret == SA_OK && group != NULL)
4183 ret = get_protocol_list(options, group);
4184
4185 /*
4186 * If there was an error, we won't have a complete list so
4187 * abandon everything. The caller will have to deal with the
4188 * issue.
4189 */
4190 if (ret != SA_OK) {
4191 sa_free_protoset(options);
4192 options = NULL;
4193 }
4194 return (options);
4195 }
4196
4197 /*
4198 * sa_enable_resource, protocol)
4199 * Disable the specified share to the specified protocol.
4200 * If protocol is NULL, then all protocols.
4201 */
4202 int
4203 sa_enable_resource(sa_resource_t resource, char *protocol)
4204 {
4205 int ret = SA_OK;
4206
4207 if (protocol != NULL) {
4208 ret = sa_proto_share_resource(protocol, resource);
4209 } else {
4210 sa_optionset_t protoset;
4211 sa_property_t prop;
4212 char *proto;
4213 int err;
4214
4215 /* need to do all protocols */
4216 protoset = sa_get_active_protocols(resource);
4217 if (protoset == NULL)
4218 return (SA_NO_MEMORY);
4219 for (prop = sa_get_property(protoset, NULL);
4220 prop != NULL;
4221 prop = sa_get_next_property(prop)) {
4222 proto = sa_get_property_attr(prop, "type");
4223 if (proto == NULL) {
4224 ret = SA_NO_MEMORY;
4225 continue;
4226 }
4227 err = sa_proto_share_resource(proto, resource);
4228 if (err != SA_OK)
4229 ret = err;
4230 sa_free_attr_string(proto);
4231 }
4232 sa_free_protoset(protoset);
4233 }
4234 if (ret == SA_OK)
4235 (void) sa_set_resource_attr(resource, "shared", NULL);
4236
4237 return (ret);
4238 }
4239
4240 /*
4241 * sa_disable_resource(resource, protocol)
4242 *
4243 * Disable the specified share for the specified protocol. If
4244 * protocol is NULL, then all protocols. If the underlying
4245 * protocol doesn't implement disable at the resource level, we
4246 * disable at the share level.
4247 */
4248 int
4249 sa_disable_resource(sa_resource_t resource, char *protocol)
4250 {
4251 int ret = SA_OK;
4252
4253 if (protocol != NULL) {
4254 ret = sa_proto_unshare_resource(protocol, resource);
4255 if (ret == SA_NOT_IMPLEMENTED) {
4256 sa_share_t parent;
4257 /*
4258 * The protocol doesn't implement unshare
4259 * resource. That implies that resource names are
4260 * simple aliases for this protocol so we need to
4261 * unshare the share.
4262 */
4263 parent = sa_get_resource_parent(resource);
4264 if (parent != NULL)
4265 ret = sa_disable_share(parent, protocol);
4266 else
4267 ret = SA_CONFIG_ERR;
4268 }
4269 } else {
4270 sa_optionset_t protoset;
4271 sa_property_t prop;
4272 char *proto;
4273 int err;
4274
4275 /* need to do all protocols */
4276 protoset = sa_get_active_protocols(resource);
4277 if (protoset == NULL)
4278 return (SA_NO_MEMORY);
4279 for (prop = sa_get_property(protoset, NULL);
4280 prop != NULL;
4281 prop = sa_get_next_property(prop)) {
4282 proto = sa_get_property_attr(prop, "type");
4283 if (proto == NULL) {
4284 ret = SA_NO_MEMORY;
4285 continue;
4286 }
4287 err = sa_proto_unshare_resource(proto, resource);
4288 if (err == SA_NOT_SUPPORTED) {
4289 sa_share_t parent;
4290 parent = sa_get_resource_parent(resource);
4291 if (parent != NULL)
4292 err = sa_disable_share(parent, proto);
4293 else
4294 err = SA_CONFIG_ERR;
4295 }
4296 if (err != SA_OK)
4297 ret = err;
4298 sa_free_attr_string(proto);
4299 }
4300 sa_free_protoset(protoset);
4301 }
4302 if (ret == SA_OK)
4303 (void) sa_set_resource_attr(resource, "shared", NULL);
4304
4305 return (ret);
4306 }
4307
4308 /*
4309 * sa_set_resource_description(resource, content)
4310 *
4311 * Set the description of share to content.
4312 */
4313
4314 int
4315 sa_set_resource_description(sa_resource_t resource, char *content)
4316 {
4317 xmlNodePtr node;
4318 sa_group_t group;
4319 sa_share_t share;
4320 int ret = SA_OK;
4321
4322 for (node = ((xmlNodePtr)resource)->children;
4323 node != NULL;
4324 node = node->next) {
4325 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
4326 break;
4327 }
4328 }
4329
4330 /* no existing description but want to add */
4331 if (node == NULL && content != NULL) {
4332 /* add a description */
4333 node = _sa_set_share_description(resource, content);
4334 } else if (node != NULL && content != NULL) {
4335 /* update a description */
4336 xmlNodeSetContent(node, (xmlChar *)content);
4337 } else if (node != NULL && content == NULL) {
4338 /* remove an existing description */
4339 xmlUnlinkNode(node);
4340 xmlFreeNode(node);
4341 }
4342
4343 share = sa_get_resource_parent(resource);
4344 group = sa_get_parent_group(share);
4345 if (group != NULL &&
4346 sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
4347 sa_handle_t handle = sa_find_group_handle(group);
4348 if (handle != NULL)
4349 ret = sa_commit_share(handle->scfhandle,
4350 group, share);
4351 else
4352 ret = SA_SYSTEM_ERR;
4353 }
4354 return (ret);
4355 }
4356
4357 /*
4358 * sa_get_resource_description(share)
4359 *
4360 * Return the description text for the specified share if it
4361 * exists. NULL if no description exists.
4362 */
4363
4364 char *
4365 sa_get_resource_description(sa_resource_t resource)
4366 {
4367 xmlChar *description = NULL;
4368 xmlNodePtr node;
4369
4370 for (node = ((xmlNodePtr)resource)->children; node != NULL;
4371 node = node->next) {
4372 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
4373 break;
4374 }
4375 if (node != NULL) {
4376 description = xmlNodeGetContent(node);
4377 fixproblemchars((char *)description);
4378 }
4379 return ((char *)description);
4380 }