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