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