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