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