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