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