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