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