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