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