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