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