1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Share control API 29 */ 30 #include <stdio.h> 31 #include <string.h> 32 #include <ctype.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <fcntl.h> 36 #include <unistd.h> 37 #include <libxml/parser.h> 38 #include <libxml/tree.h> 39 #include "libshare.h" 40 #include "libshare_impl.h" 41 #include <libscf.h> 42 #include "scfutil.h" 43 #include <ctype.h> 44 #include <libintl.h> 45 #include <thread.h> 46 #include <synch.h> 47 48 #define DFS_LOCK_FILE "/etc/dfs/fstypes" 49 #define SA_STRSIZE 256 /* max string size for names */ 50 51 /* 52 * internal object type values returned by sa_get_object_type() 53 */ 54 #define SA_TYPE_UNKNOWN 0 55 #define SA_TYPE_GROUP 1 56 #define SA_TYPE_SHARE 2 57 #define SA_TYPE_RESOURCE 3 58 #define SA_TYPE_OPTIONSET 4 59 #define SA_TYPE_ALTSPACE 5 60 61 /* 62 * internal data structures 63 */ 64 65 extern struct sa_proto_plugin *sap_proto_list; 66 67 /* current SMF/SVC repository handle */ 68 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *); 69 extern int gettransients(sa_handle_impl_t, xmlNodePtr *); 70 extern char *sa_fstype(char *); 71 extern int sa_is_share(void *); 72 extern int sa_is_resource(void *); 73 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ 74 extern int sa_group_is_zfs(sa_group_t); 75 extern int sa_path_is_zfs(char *); 76 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); 77 extern int sa_zfs_set_sharesmb(sa_group_t, char *, int); 78 extern void update_legacy_config(sa_handle_t); 79 extern int issubdir(char *, char *); 80 extern int sa_zfs_init(sa_handle_impl_t); 81 extern void sa_zfs_fini(sa_handle_impl_t); 82 extern void sablocksigs(sigset_t *); 83 extern void saunblocksigs(sigset_t *); 84 static sa_group_t sa_get_optionset_parent(sa_optionset_t); 85 static char *get_node_attr(void *, char *); 86 extern void sa_update_sharetab_ts(sa_handle_t); 87 88 /* 89 * Data structures for finding/managing the document root to access 90 * handle mapping. The list isn't expected to grow very large so a 91 * simple list is acceptable. The purpose is to provide a way to start 92 * with a group or share and find the library handle needed for 93 * various operations. 94 */ 95 mutex_t sa_global_lock; 96 struct doc2handle { 97 struct doc2handle *next; 98 xmlNodePtr root; 99 sa_handle_impl_t handle; 100 }; 101 102 /* definitions used in a couple of property functions */ 103 #define SA_PROP_OP_REMOVE 1 104 #define SA_PROP_OP_ADD 2 105 #define SA_PROP_OP_UPDATE 3 106 107 static struct doc2handle *sa_global_handles = NULL; 108 109 /* helper functions */ 110 111 /* 112 * sa_errorstr(err) 113 * 114 * convert an error value to an error string 115 */ 116 117 char * 118 sa_errorstr(int err) 119 { 120 static char errstr[32]; 121 char *ret = NULL; 122 123 switch (err) { 124 case SA_OK: 125 ret = dgettext(TEXT_DOMAIN, "ok"); 126 break; 127 case SA_NO_SUCH_PATH: 128 ret = dgettext(TEXT_DOMAIN, "path doesn't exist"); 129 break; 130 case SA_NO_MEMORY: 131 ret = dgettext(TEXT_DOMAIN, "no memory"); 132 break; 133 case SA_DUPLICATE_NAME: 134 ret = dgettext(TEXT_DOMAIN, "name in use"); 135 break; 136 case SA_BAD_PATH: 137 ret = dgettext(TEXT_DOMAIN, "bad path"); 138 break; 139 case SA_NO_SUCH_GROUP: 140 ret = dgettext(TEXT_DOMAIN, "no such group"); 141 break; 142 case SA_CONFIG_ERR: 143 ret = dgettext(TEXT_DOMAIN, "configuration error"); 144 break; 145 case SA_SYSTEM_ERR: 146 ret = dgettext(TEXT_DOMAIN, "system error"); 147 break; 148 case SA_SYNTAX_ERR: 149 ret = dgettext(TEXT_DOMAIN, "syntax error"); 150 break; 151 case SA_NO_PERMISSION: 152 ret = dgettext(TEXT_DOMAIN, "no permission"); 153 break; 154 case SA_BUSY: 155 ret = dgettext(TEXT_DOMAIN, "busy"); 156 break; 157 case SA_NO_SUCH_PROP: 158 ret = dgettext(TEXT_DOMAIN, "no such property"); 159 break; 160 case SA_INVALID_NAME: 161 ret = dgettext(TEXT_DOMAIN, "invalid name"); 162 break; 163 case SA_INVALID_PROTOCOL: 164 ret = dgettext(TEXT_DOMAIN, "invalid protocol"); 165 break; 166 case SA_NOT_ALLOWED: 167 ret = dgettext(TEXT_DOMAIN, "operation not allowed"); 168 break; 169 case SA_BAD_VALUE: 170 ret = dgettext(TEXT_DOMAIN, "bad property value"); 171 break; 172 case SA_INVALID_SECURITY: 173 ret = dgettext(TEXT_DOMAIN, "invalid security type"); 174 break; 175 case SA_NO_SUCH_SECURITY: 176 ret = dgettext(TEXT_DOMAIN, "security type not found"); 177 break; 178 case SA_VALUE_CONFLICT: 179 ret = dgettext(TEXT_DOMAIN, "property value conflict"); 180 break; 181 case SA_NOT_IMPLEMENTED: 182 ret = dgettext(TEXT_DOMAIN, "not implemented"); 183 break; 184 case SA_INVALID_PATH: 185 ret = dgettext(TEXT_DOMAIN, "invalid path"); 186 break; 187 case SA_NOT_SUPPORTED: 188 ret = dgettext(TEXT_DOMAIN, "operation not supported"); 189 break; 190 case SA_PROP_SHARE_ONLY: 191 ret = dgettext(TEXT_DOMAIN, "property not valid for group"); 192 break; 193 case SA_NOT_SHARED: 194 ret = dgettext(TEXT_DOMAIN, "not shared"); 195 break; 196 case SA_NO_SUCH_RESOURCE: 197 ret = dgettext(TEXT_DOMAIN, "no such resource"); 198 break; 199 case SA_RESOURCE_REQUIRED: 200 ret = dgettext(TEXT_DOMAIN, "resource name required"); 201 break; 202 case SA_MULTIPLE_ERROR: 203 ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols"); 204 break; 205 case SA_PATH_IS_SUBDIR: 206 ret = dgettext(TEXT_DOMAIN, "path is a subpath of share"); 207 break; 208 case SA_PATH_IS_PARENTDIR: 209 ret = dgettext(TEXT_DOMAIN, "path is parent of a share"); 210 break; 211 case SA_NO_SECTION: 212 ret = dgettext(TEXT_DOMAIN, "protocol requires a section"); 213 break; 214 case SA_NO_PROPERTIES: 215 ret = dgettext(TEXT_DOMAIN, "properties not found"); 216 break; 217 case SA_NO_SUCH_SECTION: 218 ret = dgettext(TEXT_DOMAIN, "section not found"); 219 break; 220 case SA_PASSWORD_ENC: 221 ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted"); 222 break; 223 default: 224 (void) snprintf(errstr, sizeof (errstr), 225 dgettext(TEXT_DOMAIN, "unknown %d"), err); 226 ret = errstr; 227 } 228 return (ret); 229 } 230 231 /* 232 * Document root to active handle mapping functions. These are only 233 * used internally. A mutex is used to prevent access while the list 234 * is changing. In general, the list will be relatively short - one 235 * item per thread that has called sa_init(). 236 */ 237 238 sa_handle_impl_t 239 get_handle_for_root(xmlNodePtr root) 240 { 241 struct doc2handle *item; 242 243 (void) mutex_lock(&sa_global_lock); 244 for (item = sa_global_handles; item != NULL; item = item->next) { 245 if (item->root == root) 246 break; 247 } 248 (void) mutex_unlock(&sa_global_lock); 249 if (item != NULL) 250 return (item->handle); 251 return (NULL); 252 } 253 254 static int 255 add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle) 256 { 257 struct doc2handle *item; 258 int ret = SA_NO_MEMORY; 259 260 item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1); 261 if (item != NULL) { 262 item->root = root; 263 item->handle = handle; 264 (void) mutex_lock(&sa_global_lock); 265 item->next = sa_global_handles; 266 sa_global_handles = item; 267 (void) mutex_unlock(&sa_global_lock); 268 ret = SA_OK; 269 } 270 return (ret); 271 } 272 273 /* 274 * remove_handle_for_root(root) 275 * 276 * Walks the list of handles and removes the one for this "root" from 277 * the list. It is up to the caller to free the data. 278 */ 279 280 static void 281 remove_handle_for_root(xmlNodePtr root) 282 { 283 struct doc2handle *item, *prev; 284 285 (void) mutex_lock(&sa_global_lock); 286 for (prev = NULL, item = sa_global_handles; item != NULL; 287 item = item->next) { 288 if (item->root == root) { 289 /* first in the list */ 290 if (prev == NULL) 291 sa_global_handles = sa_global_handles->next; 292 else 293 prev->next = item->next; 294 /* Item is out of the list so free the list structure */ 295 free(item); 296 break; 297 } 298 prev = item; 299 } 300 (void) mutex_unlock(&sa_global_lock); 301 } 302 303 /* 304 * sa_find_group_handle(sa_group_t group) 305 * 306 * Find the sa_handle_t for the configuration associated with this 307 * group. 308 */ 309 sa_handle_t 310 sa_find_group_handle(sa_group_t group) 311 { 312 xmlNodePtr node = (xmlNodePtr)group; 313 sa_handle_t handle; 314 315 while (node != NULL) { 316 if (strcmp((char *)(node->name), "sharecfg") == 0) { 317 /* have the root so get the handle */ 318 handle = (sa_handle_t)get_handle_for_root(node); 319 return (handle); 320 } 321 node = node->parent; 322 } 323 return (NULL); 324 } 325 326 /* 327 * set_legacy_timestamp(root, path, timevalue) 328 * 329 * add the current timestamp value to the configuration for use in 330 * determining when to update the legacy files. For SMF, this 331 * property is kept in default/operation/legacy_timestamp 332 */ 333 334 static void 335 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval) 336 { 337 xmlNodePtr node; 338 xmlChar *lpath = NULL; 339 sa_handle_impl_t handle; 340 341 /* Have to have a handle or else we weren't initialized. */ 342 handle = get_handle_for_root(root); 343 if (handle == NULL) 344 return; 345 346 for (node = root->xmlChildrenNode; node != NULL; 347 node = node->next) { 348 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) { 349 /* a possible legacy node for this path */ 350 lpath = xmlGetProp(node, (xmlChar *)"path"); 351 if (lpath != NULL && 352 xmlStrcmp(lpath, (xmlChar *)path) == 0) { 353 xmlFree(lpath); 354 break; 355 } 356 if (lpath != NULL) 357 xmlFree(lpath); 358 } 359 } 360 if (node == NULL) { 361 /* need to create the first legacy timestamp node */ 362 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL); 363 } 364 if (node != NULL) { 365 char tstring[32]; 366 int ret; 367 368 (void) snprintf(tstring, sizeof (tstring), "%lld", tval); 369 (void) xmlSetProp(node, (xmlChar *)"timestamp", 370 (xmlChar *)tstring); 371 (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path); 372 /* now commit to SMF */ 373 ret = sa_get_instance(handle->scfhandle, "default"); 374 if (ret == SA_OK) { 375 ret = sa_start_transaction(handle->scfhandle, 376 "operation"); 377 if (ret == SA_OK) { 378 ret = sa_set_property(handle->scfhandle, 379 "legacy-timestamp", tstring); 380 if (ret == SA_OK) { 381 (void) sa_end_transaction( 382 handle->scfhandle, handle); 383 } else { 384 sa_abort_transaction(handle->scfhandle); 385 } 386 } 387 } 388 } 389 } 390 391 /* 392 * is_shared(share) 393 * 394 * determine if the specified share is currently shared or not. 395 */ 396 static int 397 is_shared(sa_share_t share) 398 { 399 char *shared; 400 int result = 0; /* assume not */ 401 402 shared = sa_get_share_attr(share, "shared"); 403 if (shared != NULL) { 404 if (strcmp(shared, "true") == 0) 405 result = 1; 406 sa_free_attr_string(shared); 407 } 408 return (result); 409 } 410 411 /* 412 * excluded_protocol(share, proto) 413 * 414 * Returns B_TRUE if the specified protocol appears in the "exclude" 415 * property. This is used to prevent sharing special case shares 416 * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is 417 * returned if the protocol isn't in the list. 418 */ 419 static boolean_t 420 excluded_protocol(sa_share_t share, char *proto) 421 { 422 char *protolist; 423 char *str; 424 char *token; 425 426 protolist = sa_get_share_attr(share, "exclude"); 427 if (protolist != NULL) { 428 str = protolist; 429 while ((token = strtok(str, ",")) != NULL) { 430 if (strcmp(token, proto) == 0) { 431 sa_free_attr_string(protolist); 432 return (B_TRUE); 433 } 434 str = NULL; 435 } 436 sa_free_attr_string(protolist); 437 } 438 return (B_FALSE); 439 } 440 441 /* 442 * checksubdirgroup(group, newpath, strictness) 443 * 444 * check all the specified newpath against all the paths in the 445 * group. This is a helper function for checksubdir to make it easier 446 * to also check ZFS subgroups. 447 * The strictness values mean: 448 * SA_CHECK_NORMAL == only check newpath against shares that are active 449 * SA_CHECK_STRICT == check newpath against both active shares and those 450 * stored in the repository 451 */ 452 static int 453 checksubdirgroup(sa_group_t group, char *newpath, int strictness) 454 { 455 sa_share_t share; 456 char *path; 457 int issub = SA_OK; 458 int subdir; 459 int parent; 460 461 if (newpath == NULL) 462 return (SA_INVALID_PATH); 463 464 for (share = sa_get_share(group, NULL); share != NULL; 465 share = sa_get_next_share(share)) { 466 /* 467 * The original behavior of share never checked 468 * against the permanent configuration 469 * (/etc/dfs/dfstab). PIT has a number of cases where 470 * it depends on this older behavior even though it 471 * could be considered incorrect. We may tighten this 472 * up in the future. 473 */ 474 if (strictness == SA_CHECK_NORMAL && !is_shared(share)) 475 continue; 476 477 path = sa_get_share_attr(share, "path"); 478 /* 479 * If path is NULL, then a share is in the process of 480 * construction or someone has modified the property 481 * group inappropriately. It should be 482 * ignored. issubdir() comes from the original share 483 * implementation and does the difficult part of 484 * checking subdirectories. 485 */ 486 if (path == NULL) 487 continue; 488 489 if (strcmp(path, newpath) == 0) { 490 issub = SA_INVALID_PATH; 491 } else { 492 subdir = issubdir(newpath, path); 493 parent = issubdir(path, newpath); 494 if (subdir || parent) { 495 sa_free_attr_string(path); 496 path = NULL; 497 return (subdir ? 498 SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR); 499 } 500 } 501 sa_free_attr_string(path); 502 path = NULL; 503 } 504 return (issub); 505 } 506 507 /* 508 * checksubdir(newpath, strictness) 509 * 510 * checksubdir determines if the specified path (newpath) is a 511 * subdirectory of another share. It calls checksubdirgroup() to do 512 * the complicated work. The strictness parameter determines how 513 * strict a check to make against the path. The strictness values 514 * mean: SA_CHECK_NORMAL == only check newpath against shares that are 515 * active SA_CHECK_STRICT == check newpath against both active shares 516 * and those * stored in the repository 517 */ 518 static int 519 checksubdir(sa_handle_t handle, char *newpath, int strictness) 520 { 521 sa_group_t group; 522 int issub = SA_OK; 523 char *path = NULL; 524 525 for (group = sa_get_group(handle, NULL); 526 group != NULL && issub == SA_OK; 527 group = sa_get_next_group(group)) { 528 if (sa_group_is_zfs(group)) { 529 sa_group_t subgroup; 530 for (subgroup = sa_get_sub_group(group); 531 subgroup != NULL && issub == SA_OK; 532 subgroup = sa_get_next_group(subgroup)) 533 issub = checksubdirgroup(subgroup, newpath, 534 strictness); 535 } else { 536 issub = checksubdirgroup(group, newpath, strictness); 537 } 538 } 539 if (path != NULL) 540 sa_free_attr_string(path); 541 return (issub); 542 } 543 544 /* 545 * validpath(path, strictness) 546 * determine if the provided path is valid for a share. It shouldn't 547 * be a sub-dir of an already shared path or the parent directory of a 548 * share path. 549 */ 550 static int 551 validpath(sa_handle_t handle, char *path, int strictness) 552 { 553 int error = SA_OK; 554 struct stat st; 555 sa_share_t share; 556 char *fstype; 557 558 if (*path != '/') 559 return (SA_BAD_PATH); 560 561 if (stat(path, &st) < 0) { 562 error = SA_NO_SUCH_PATH; 563 } else { 564 share = sa_find_share(handle, path); 565 if (share != NULL) 566 error = SA_DUPLICATE_NAME; 567 568 if (error == SA_OK) { 569 /* 570 * check for special case with file system 571 * that might have restrictions. For now, ZFS 572 * is the only case since it has its own idea 573 * of how to configure shares. We do this 574 * before subdir checking since things like 575 * ZFS will do that for us. This should also 576 * be done via plugin interface. 577 */ 578 fstype = sa_fstype(path); 579 if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 580 if (sa_zfs_is_shared(handle, path)) 581 error = SA_INVALID_NAME; 582 } 583 if (fstype != NULL) 584 sa_free_fstype(fstype); 585 } 586 if (error == SA_OK) 587 error = checksubdir(handle, path, strictness); 588 } 589 return (error); 590 } 591 592 /* 593 * check to see if group/share is persistent. 594 * 595 * "group" can be either an sa_group_t or an sa_share_t. (void *) 596 * works since both thse types are also void *. 597 */ 598 int 599 sa_is_persistent(void *group) 600 { 601 char *type; 602 int persist = 1; 603 604 type = sa_get_group_attr((sa_group_t)group, "type"); 605 if (type != NULL && strcmp(type, "transient") == 0) 606 persist = 0; 607 if (type != NULL) 608 sa_free_attr_string(type); 609 return (persist); 610 } 611 612 /* 613 * sa_valid_group_name(name) 614 * 615 * check that the "name" contains only valid characters and otherwise 616 * fits the required naming conventions. Valid names must start with 617 * an alphabetic and the remainder may consist of only alphanumeric 618 * plus the '-' and '_' characters. This name limitation comes from 619 * inherent limitations in SMF. 620 */ 621 622 int 623 sa_valid_group_name(char *name) 624 { 625 int ret = 1; 626 ssize_t len; 627 628 if (name != NULL && isalpha(*name)) { 629 char c; 630 len = strlen(name); 631 if (len < (scf_max_name_len - sizeof ("group:"))) { 632 for (c = *name++; c != '\0' && ret != 0; c = *name++) { 633 if (!isalnum(c) && c != '-' && c != '_') 634 ret = 0; 635 } 636 } else { 637 ret = 0; 638 } 639 } else { 640 ret = 0; 641 } 642 return (ret); 643 } 644 645 646 /* 647 * is_zfs_group(group) 648 * Determine if the specified group is a ZFS sharenfs group 649 */ 650 static int 651 is_zfs_group(sa_group_t group) 652 { 653 int ret = 0; 654 xmlNodePtr parent; 655 xmlChar *zfs; 656 657 if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) 658 parent = (xmlNodePtr)sa_get_parent_group(group); 659 else 660 parent = (xmlNodePtr)group; 661 zfs = xmlGetProp(parent, (xmlChar *)"zfs"); 662 if (zfs != NULL) { 663 xmlFree(zfs); 664 ret = 1; 665 } 666 return (ret); 667 } 668 669 /* 670 * sa_get_object_type(object) 671 * 672 * This function returns a numeric value representing the object 673 * type. This allows using simpler checks when doing type specific 674 * operations. 675 */ 676 677 static int 678 sa_get_object_type(void *object) 679 { 680 xmlNodePtr node = (xmlNodePtr)object; 681 int type; 682 683 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) 684 type = SA_TYPE_GROUP; 685 else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) 686 type = SA_TYPE_SHARE; 687 else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) 688 type = SA_TYPE_RESOURCE; 689 else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) 690 type = SA_TYPE_OPTIONSET; 691 else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) 692 type = SA_TYPE_ALTSPACE; 693 else 694 assert(0); 695 return (type); 696 } 697 698 /* 699 * sa_optionset_name(optionset, oname, len, id) 700 * return the SMF name for the optionset. If id is not NULL, it 701 * will have the GUID value for a share and should be used 702 * instead of the keyword "optionset" which is used for 703 * groups. If the optionset doesn't have a protocol type 704 * associated with it, "default" is used. This shouldn't happen 705 * at this point but may be desirable in the future if there are 706 * protocol independent properties added. The name is returned in 707 * oname. 708 */ 709 710 static int 711 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) 712 { 713 char *proto; 714 void *parent; 715 int ptype; 716 717 if (id == NULL) 718 id = "optionset"; 719 720 parent = sa_get_optionset_parent(optionset); 721 if (parent != NULL) { 722 ptype = sa_get_object_type(parent); 723 proto = sa_get_optionset_attr(optionset, "type"); 724 if (ptype != SA_TYPE_RESOURCE) { 725 len = snprintf(oname, len, "%s_%s", id, 726 proto ? proto : "default"); 727 } else { 728 char *index; 729 index = get_node_attr((void *)parent, "id"); 730 if (index != NULL) 731 len = snprintf(oname, len, "%s_%s_%s", id, 732 proto ? proto : "default", index); 733 else 734 len = 0; 735 } 736 737 if (proto != NULL) 738 sa_free_attr_string(proto); 739 } else { 740 len = 0; 741 } 742 return (len); 743 } 744 745 /* 746 * sa_security_name(optionset, oname, len, id) 747 * 748 * return the SMF name for the security. If id is not NULL, it will 749 * have the GUID value for a share and should be used instead of the 750 * keyword "optionset" which is used for groups. If the optionset 751 * doesn't have a protocol type associated with it, "default" is 752 * used. This shouldn't happen at this point but may be desirable in 753 * the future if there are protocol independent properties added. The 754 * name is returned in oname. The security type is also encoded into 755 * the name. In the future, this wil *be handled a bit differently. 756 */ 757 758 static int 759 sa_security_name(sa_security_t security, char *oname, size_t len, char *id) 760 { 761 char *proto; 762 char *sectype; 763 764 if (id == NULL) 765 id = "optionset"; 766 767 proto = sa_get_security_attr(security, "type"); 768 sectype = sa_get_security_attr(security, "sectype"); 769 len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default", 770 sectype ? sectype : "default"); 771 if (proto != NULL) 772 sa_free_attr_string(proto); 773 if (sectype != NULL) 774 sa_free_attr_string(sectype); 775 return (len); 776 } 777 778 /* 779 * verifydefgroupopts(handle) 780 * 781 * Make sure a "default" group exists and has default protocols enabled. 782 */ 783 static void 784 verifydefgroupopts(sa_handle_t handle) 785 { 786 sa_group_t defgrp; 787 sa_optionset_t opt; 788 789 defgrp = sa_get_group(handle, "default"); 790 if (defgrp != NULL) { 791 opt = sa_get_optionset(defgrp, NULL); 792 /* 793 * NFS is the default for default group 794 */ 795 if (opt == NULL) 796 opt = sa_create_optionset(defgrp, "nfs"); 797 } 798 } 799 800 /* 801 * sa_init(init_service) 802 * Initialize the API 803 * find all the shared objects 804 * init the tables with all objects 805 * read in the current configuration 806 */ 807 808 #define GETPROP(prop) scf_simple_prop_next_astring(prop) 809 #define CHECKTSTAMP(st, tval) stat(SA_LEGACY_DFSTAB, &st) >= 0 && \ 810 tval != TSTAMP(st.st_ctim) 811 812 sa_handle_t 813 sa_init(int init_service) 814 { 815 struct stat st; 816 int legacy = 0; 817 uint64_t tval = 0; 818 int lockfd; 819 sigset_t old; 820 int updatelegacy = B_FALSE; 821 scf_simple_prop_t *prop; 822 sa_handle_impl_t handle; 823 int err; 824 825 handle = calloc(sizeof (struct sa_handle_impl), 1); 826 827 if (handle != NULL) { 828 /* 829 * Get protocol specific structures, but only if this 830 * is the only handle. 831 */ 832 (void) mutex_lock(&sa_global_lock); 833 if (sa_global_handles == NULL) 834 (void) proto_plugin_init(); 835 (void) mutex_unlock(&sa_global_lock); 836 if (init_service & SA_INIT_SHARE_API) { 837 /* 838 * initialize access into libzfs. We use this 839 * when collecting info about ZFS datasets and 840 * shares. 841 */ 842 if (sa_zfs_init(handle) == B_FALSE) { 843 free(handle); 844 (void) mutex_lock(&sa_global_lock); 845 (void) proto_plugin_fini(); 846 (void) mutex_unlock(&sa_global_lock); 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 if (proto != NULL) 3409 sa_free_attr_string(proto); 3410 return (propset); 3411 } 3412 3413 for (node = node->children; node != NULL; 3414 node = node->next) { 3415 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) { 3416 if (section == NULL) 3417 break; 3418 value = xmlGetProp(node, (xmlChar *)"name"); 3419 if (value != NULL && 3420 xmlStrcasecmp(value, (xmlChar *)section) == 0) { 3421 break; 3422 } 3423 if (value != NULL) { 3424 xmlFree(value); 3425 value = NULL; 3426 } 3427 } 3428 } 3429 if (value != NULL) 3430 xmlFree(value); 3431 if (proto != NULL) 3432 sa_free_attr_string(proto); 3433 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) { 3434 /* 3435 * avoid a non option node -- it is possible to be a 3436 * text node 3437 */ 3438 node = NULL; 3439 } 3440 return ((sa_protocol_properties_t)node); 3441 } 3442 3443 /* 3444 * sa_get_next_protocol_section(prop, find) 3445 * 3446 * Get the next protocol specific section in the list. 3447 */ 3448 3449 sa_property_t 3450 sa_get_next_protocol_section(sa_property_t prop, char *find) 3451 { 3452 xmlNodePtr node; 3453 xmlChar *value = NULL; 3454 char *proto; 3455 3456 proto = sa_get_optionset_attr(prop, "type"); 3457 if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) { 3458 if (proto != NULL) 3459 sa_free_attr_string(proto); 3460 return ((sa_property_t)NULL); 3461 } 3462 3463 for (node = ((xmlNodePtr)prop)->next; node != NULL; 3464 node = node->next) { 3465 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) { 3466 if (find == NULL) 3467 break; 3468 value = xmlGetProp(node, (xmlChar *)"name"); 3469 if (value != NULL && 3470 xmlStrcasecmp(value, (xmlChar *)find) == 0) { 3471 break; 3472 } 3473 if (value != NULL) { 3474 xmlFree(value); 3475 value = NULL; 3476 } 3477 3478 } 3479 } 3480 if (value != NULL) 3481 xmlFree(value); 3482 if (proto != NULL) 3483 sa_free_attr_string(proto); 3484 return ((sa_property_t)node); 3485 } 3486 3487 /* 3488 * sa_get_protocol_property(propset, prop) 3489 * 3490 * Get the specified protocol specific property. These are global to 3491 * the protocol and not specific to a group or share. 3492 */ 3493 3494 sa_property_t 3495 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 3496 { 3497 xmlNodePtr node = (xmlNodePtr)propset; 3498 xmlChar *value = NULL; 3499 3500 if (propset == NULL) 3501 return (NULL); 3502 3503 for (node = node->children; node != NULL; 3504 node = node->next) { 3505 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3506 if (prop == NULL) 3507 break; 3508 value = xmlGetProp(node, (xmlChar *)"type"); 3509 if (value != NULL && 3510 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 3511 break; 3512 } 3513 if (value != NULL) { 3514 xmlFree(value); 3515 value = NULL; 3516 } 3517 } 3518 } 3519 if (value != NULL) 3520 xmlFree(value); 3521 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 3522 /* 3523 * avoid a non option node -- it is possible to be a 3524 * text node 3525 */ 3526 node = NULL; 3527 } 3528 return ((sa_property_t)node); 3529 } 3530 3531 /* 3532 * sa_get_next_protocol_property(prop) 3533 * 3534 * Get the next protocol specific property in the list. 3535 */ 3536 3537 sa_property_t 3538 sa_get_next_protocol_property(sa_property_t prop, char *find) 3539 { 3540 xmlNodePtr node; 3541 xmlChar *value = NULL; 3542 3543 for (node = ((xmlNodePtr)prop)->next; node != NULL; 3544 node = node->next) { 3545 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3546 if (find == NULL) 3547 break; 3548 value = xmlGetProp(node, (xmlChar *)"type"); 3549 if (value != NULL && 3550 xmlStrcasecmp(value, (xmlChar *)find) == 0) { 3551 break; 3552 } 3553 if (value != NULL) { 3554 xmlFree(value); 3555 value = NULL; 3556 } 3557 3558 } 3559 } 3560 if (value != NULL) 3561 xmlFree(value); 3562 return ((sa_property_t)node); 3563 } 3564 3565 /* 3566 * sa_set_protocol_property(prop, value) 3567 * 3568 * Set the specified property to have the new value. The protocol 3569 * specific plugin will then be called to update the property. 3570 */ 3571 3572 int 3573 sa_set_protocol_property(sa_property_t prop, char *section, char *value) 3574 { 3575 sa_protocol_properties_t propset; 3576 char *proto; 3577 int ret = SA_INVALID_PROTOCOL; 3578 3579 propset = ((xmlNodePtr)prop)->parent; 3580 if (propset != NULL) { 3581 proto = sa_get_optionset_attr(propset, "type"); 3582 if (proto != NULL) { 3583 if (section != NULL) 3584 set_node_attr((xmlNodePtr)prop, "section", 3585 section); 3586 set_node_attr((xmlNodePtr)prop, "value", value); 3587 ret = sa_proto_set_property(proto, prop); 3588 sa_free_attr_string(proto); 3589 } 3590 } 3591 return (ret); 3592 } 3593 3594 /* 3595 * sa_add_protocol_property(propset, prop) 3596 * 3597 * Add a new property to the protocol specific property set. 3598 */ 3599 3600 int 3601 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 3602 { 3603 xmlNodePtr node; 3604 3605 /* should check for legitimacy */ 3606 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 3607 if (node != NULL) 3608 return (SA_OK); 3609 return (SA_NO_MEMORY); 3610 } 3611 3612 /* 3613 * sa_create_protocol_properties(proto) 3614 * 3615 * Create a protocol specific property set. 3616 */ 3617 3618 sa_protocol_properties_t 3619 sa_create_protocol_properties(char *proto) 3620 { 3621 xmlNodePtr node; 3622 3623 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 3624 if (node != NULL) 3625 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 3626 return (node); 3627 } 3628 3629 /* 3630 * sa_get_share_resource(share, resource) 3631 * 3632 * Get the named resource from the share, if it exists. If resource is 3633 * NULL, get the first resource. 3634 */ 3635 3636 sa_resource_t 3637 sa_get_share_resource(sa_share_t share, char *resource) 3638 { 3639 xmlNodePtr node = NULL; 3640 xmlChar *name; 3641 3642 if (share != NULL) { 3643 for (node = ((xmlNodePtr)share)->children; node != NULL; 3644 node = node->next) { 3645 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) { 3646 if (resource == NULL) { 3647 /* 3648 * We are looking for the first 3649 * resource node and not a names 3650 * resource. 3651 */ 3652 break; 3653 } else { 3654 /* is it the correct share? */ 3655 name = xmlGetProp(node, 3656 (xmlChar *)"name"); 3657 if (name != NULL && 3658 xmlStrcasecmp(name, 3659 (xmlChar *)resource) == 0) { 3660 xmlFree(name); 3661 break; 3662 } 3663 xmlFree(name); 3664 } 3665 } 3666 } 3667 } 3668 return ((sa_resource_t)node); 3669 } 3670 3671 /* 3672 * sa_get_next_resource(resource) 3673 * Return the next share following the specified share 3674 * from the internal list of shares. Returns NULL if there 3675 * are no more shares. The list is relative to the same 3676 * group. 3677 */ 3678 sa_share_t 3679 sa_get_next_resource(sa_resource_t resource) 3680 { 3681 xmlNodePtr node = NULL; 3682 3683 if (resource != NULL) { 3684 for (node = ((xmlNodePtr)resource)->next; node != NULL; 3685 node = node->next) { 3686 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) 3687 break; 3688 } 3689 } 3690 return ((sa_share_t)node); 3691 } 3692 3693 /* 3694 * _sa_get_next_resource_index(share) 3695 * 3696 * get the next resource index number (one greater then current largest) 3697 */ 3698 3699 static int 3700 _sa_get_next_resource_index(sa_share_t share) 3701 { 3702 sa_resource_t resource; 3703 int index = 0; 3704 char *id; 3705 3706 for (resource = sa_get_share_resource(share, NULL); 3707 resource != NULL; 3708 resource = sa_get_next_resource(resource)) { 3709 id = get_node_attr((void *)resource, "id"); 3710 if (id != NULL) { 3711 int val; 3712 val = atoi(id); 3713 if (val > index) 3714 index = val; 3715 sa_free_attr_string(id); 3716 } 3717 } 3718 return (index + 1); 3719 } 3720 3721 3722 /* 3723 * sa_add_resource(share, resource, persist, &err) 3724 * 3725 * Adds a new resource name associated with share. The resource name 3726 * must be unique in the system and will be case insensitive (eventually). 3727 */ 3728 3729 sa_resource_t 3730 sa_add_resource(sa_share_t share, char *resource, int persist, int *error) 3731 { 3732 xmlNodePtr node; 3733 int err = SA_OK; 3734 sa_resource_t res; 3735 sa_group_t group; 3736 sa_handle_t handle; 3737 char istring[8]; /* just big enough for an integer value */ 3738 int index; 3739 3740 group = sa_get_parent_group(share); 3741 handle = sa_find_group_handle(group); 3742 res = sa_find_resource(handle, resource); 3743 if (res != NULL) { 3744 err = SA_DUPLICATE_NAME; 3745 res = NULL; 3746 } else { 3747 node = xmlNewChild((xmlNodePtr)share, NULL, 3748 (xmlChar *)"resource", NULL); 3749 if (node != NULL) { 3750 (void) xmlSetProp(node, (xmlChar *)"name", 3751 (xmlChar *)resource); 3752 (void) xmlSetProp(node, (xmlChar *)"type", persist ? 3753 (xmlChar *)"persist" : (xmlChar *)"transient"); 3754 if (persist != SA_SHARE_TRANSIENT) { 3755 index = _sa_get_next_resource_index(share); 3756 (void) snprintf(istring, sizeof (istring), "%d", 3757 index); 3758 (void) xmlSetProp(node, (xmlChar *)"id", 3759 (xmlChar *)istring); 3760 3761 if (!sa_is_persistent((sa_group_t)share)) 3762 goto done; 3763 3764 if (!sa_group_is_zfs(group)) { 3765 /* ZFS doesn't use resource names */ 3766 sa_handle_impl_t ihandle; 3767 3768 ihandle = (sa_handle_impl_t) 3769 sa_find_group_handle( 3770 group); 3771 if (ihandle != NULL) 3772 err = sa_commit_share( 3773 ihandle->scfhandle, group, 3774 share); 3775 else 3776 err = SA_SYSTEM_ERR; 3777 } else { 3778 err = sa_zfs_update((sa_share_t)group); 3779 } 3780 } 3781 } 3782 } 3783 done: 3784 if (error != NULL) 3785 *error = err; 3786 return ((sa_resource_t)node); 3787 } 3788 3789 /* 3790 * sa_remove_resource(resource) 3791 * 3792 * Remove the resource name from the share (and the system) 3793 */ 3794 3795 int 3796 sa_remove_resource(sa_resource_t resource) 3797 { 3798 sa_share_t share; 3799 sa_group_t group; 3800 char *type; 3801 int ret = SA_OK; 3802 boolean_t transient = B_FALSE; 3803 sa_optionset_t opt; 3804 3805 share = sa_get_resource_parent(resource); 3806 type = sa_get_share_attr(share, "type"); 3807 group = sa_get_parent_group(share); 3808 3809 3810 if (type != NULL) { 3811 if (strcmp(type, "persist") != 0) 3812 transient = B_TRUE; 3813 sa_free_attr_string(type); 3814 } 3815 3816 /* Disable the resource for all protocols. */ 3817 (void) sa_disable_resource(resource, NULL); 3818 3819 /* Remove any optionsets from the resource. */ 3820 for (opt = sa_get_optionset(resource, NULL); 3821 opt != NULL; 3822 opt = sa_get_next_optionset(opt)) 3823 (void) sa_destroy_optionset(opt); 3824 3825 /* Remove from the share */ 3826 xmlUnlinkNode((xmlNode *)resource); 3827 xmlFreeNode((xmlNode *)resource); 3828 3829 /* only do SMF action if permanent and not ZFS */ 3830 if (transient) 3831 return (ret); 3832 3833 if (!sa_group_is_zfs(group)) { 3834 sa_handle_impl_t ihandle; 3835 ihandle = (sa_handle_impl_t)sa_find_group_handle(group); 3836 if (ihandle != NULL) 3837 ret = sa_commit_share(ihandle->scfhandle, group, share); 3838 else 3839 ret = SA_SYSTEM_ERR; 3840 } else { 3841 ret = sa_zfs_update((sa_share_t)group); 3842 } 3843 return (ret); 3844 } 3845 3846 /* 3847 * proto_resource_rename(handle, group, resource, newname) 3848 * 3849 * Helper function for sa_rename_resource that notifies the protocol 3850 * of a resource name change prior to a config repository update. 3851 */ 3852 static int 3853 proto_rename_resource(sa_handle_t handle, sa_group_t group, 3854 sa_resource_t resource, char *newname) 3855 { 3856 sa_optionset_t optionset; 3857 int ret = SA_OK; 3858 int err; 3859 3860 for (optionset = sa_get_optionset(group, NULL); 3861 optionset != NULL; 3862 optionset = sa_get_next_optionset(optionset)) { 3863 char *type; 3864 type = sa_get_optionset_attr(optionset, "type"); 3865 if (type != NULL) { 3866 err = sa_proto_rename_resource(handle, type, resource, 3867 newname); 3868 if (err != SA_OK) 3869 ret = err; 3870 sa_free_attr_string(type); 3871 } 3872 } 3873 return (ret); 3874 } 3875 3876 /* 3877 * sa_rename_resource(resource, newname) 3878 * 3879 * Rename the resource to the new name, if it is unique. 3880 */ 3881 3882 int 3883 sa_rename_resource(sa_resource_t resource, char *newname) 3884 { 3885 sa_share_t share; 3886 sa_group_t group = NULL; 3887 sa_resource_t target; 3888 int ret = SA_CONFIG_ERR; 3889 sa_handle_t handle = NULL; 3890 3891 share = sa_get_resource_parent(resource); 3892 if (share == NULL) 3893 return (ret); 3894 3895 group = sa_get_parent_group(share); 3896 if (group == NULL) 3897 return (ret); 3898 3899 handle = (sa_handle_impl_t)sa_find_group_handle(group); 3900 if (handle == NULL) 3901 return (ret); 3902 3903 target = sa_find_resource(handle, newname); 3904 if (target != NULL) { 3905 ret = SA_DUPLICATE_NAME; 3906 } else { 3907 /* 3908 * Everything appears to be valid at this 3909 * point. Change the name of the active share and then 3910 * update the share in the appropriate repository. 3911 */ 3912 ret = proto_rename_resource(handle, group, resource, newname); 3913 set_node_attr(resource, "name", newname); 3914 3915 if (!sa_is_persistent((sa_group_t)share)) 3916 return (ret); 3917 3918 if (!sa_group_is_zfs(group)) { 3919 sa_handle_impl_t ihandle = (sa_handle_impl_t)handle; 3920 ret = sa_commit_share(ihandle->scfhandle, group, 3921 share); 3922 } else { 3923 ret = sa_zfs_update((sa_share_t)group); 3924 } 3925 } 3926 return (ret); 3927 } 3928 3929 /* 3930 * sa_get_resource_attr(resource, tag) 3931 * 3932 * Get the named attribute of the resource. "name" and "id" are 3933 * currently defined. NULL if tag not defined. 3934 */ 3935 3936 char * 3937 sa_get_resource_attr(sa_resource_t resource, char *tag) 3938 { 3939 return (get_node_attr((void *)resource, tag)); 3940 } 3941 3942 /* 3943 * sa_set_resource_attr(resource, tag, value) 3944 * 3945 * Get the named attribute of the resource. "name" and "id" are 3946 * currently defined. NULL if tag not defined. Currently we don't do 3947 * much, but additional checking may be needed in the future. 3948 */ 3949 3950 int 3951 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value) 3952 { 3953 set_node_attr((void *)resource, tag, value); 3954 return (SA_OK); 3955 } 3956 3957 /* 3958 * sa_get_resource_parent(resource_t) 3959 * 3960 * Returns the share associated with the resource. 3961 */ 3962 3963 sa_share_t 3964 sa_get_resource_parent(sa_resource_t resource) 3965 { 3966 sa_share_t share = NULL; 3967 3968 if (resource != NULL) 3969 share = (sa_share_t)((xmlNodePtr)resource)->parent; 3970 return (share); 3971 } 3972 3973 /* 3974 * find_resource(group, name) 3975 * 3976 * Find the resource within the group. 3977 */ 3978 3979 static sa_resource_t 3980 find_resource(sa_group_t group, char *resname) 3981 { 3982 sa_share_t share; 3983 sa_resource_t resource = NULL; 3984 char *name; 3985 3986 /* Iterate over all the shares and resources in the group. */ 3987 for (share = sa_get_share(group, NULL); 3988 share != NULL && resource == NULL; 3989 share = sa_get_next_share(share)) { 3990 for (resource = sa_get_share_resource(share, NULL); 3991 resource != NULL; 3992 resource = sa_get_next_resource(resource)) { 3993 name = sa_get_resource_attr(resource, "name"); 3994 if (name != NULL && xmlStrcasecmp((xmlChar*)name, 3995 (xmlChar*)resname) == 0) { 3996 sa_free_attr_string(name); 3997 break; 3998 } 3999 if (name != NULL) { 4000 sa_free_attr_string(name); 4001 } 4002 } 4003 } 4004 return (resource); 4005 } 4006 4007 /* 4008 * sa_find_resource(name) 4009 * 4010 * Find the named resource in the system. 4011 */ 4012 4013 sa_resource_t 4014 sa_find_resource(sa_handle_t handle, char *name) 4015 { 4016 sa_group_t group; 4017 sa_group_t zgroup; 4018 sa_resource_t resource = NULL; 4019 4020 /* 4021 * Iterate over all groups and zfs subgroups and check for 4022 * resource name in them. 4023 */ 4024 for (group = sa_get_group(handle, NULL); group != NULL; 4025 group = sa_get_next_group(group)) { 4026 4027 if (is_zfs_group(group)) { 4028 for (zgroup = 4029 (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 4030 (xmlChar *)"group"); 4031 zgroup != NULL && resource == NULL; 4032 zgroup = sa_get_next_group(zgroup)) { 4033 resource = find_resource(zgroup, name); 4034 } 4035 } else { 4036 resource = find_resource(group, name); 4037 } 4038 if (resource != NULL) 4039 break; 4040 } 4041 return (resource); 4042 } 4043 4044 /* 4045 * sa_get_resource(group, resource) 4046 * 4047 * Search all the shares in the specified group for a share with a 4048 * resource name matching the one specified. 4049 * 4050 * In the future, it may be advantageous to allow group to be NULL and 4051 * search all groups but that isn't needed at present. 4052 */ 4053 4054 sa_resource_t 4055 sa_get_resource(sa_group_t group, char *resource) 4056 { 4057 sa_share_t share = NULL; 4058 sa_resource_t res = NULL; 4059 4060 if (resource != NULL) { 4061 for (share = sa_get_share(group, NULL); 4062 share != NULL && res == NULL; 4063 share = sa_get_next_share(share)) { 4064 res = sa_get_share_resource(share, resource); 4065 } 4066 } 4067 return (res); 4068 } 4069 4070 /* 4071 * get_protocol_list(optionset, object) 4072 * 4073 * Get the protocol optionset list for the object and add them as 4074 * properties to optionset. 4075 */ 4076 static int 4077 get_protocol_list(sa_optionset_t optionset, void *object) 4078 { 4079 sa_property_t prop; 4080 sa_optionset_t opts; 4081 int ret = SA_OK; 4082 4083 for (opts = sa_get_optionset(object, NULL); 4084 opts != NULL; 4085 opts = sa_get_next_optionset(opts)) { 4086 char *type; 4087 type = sa_get_optionset_attr(opts, "type"); 4088 /* 4089 * It is possible to have a non-protocol optionset. We 4090 * skip any of those found. 4091 */ 4092 if (type == NULL) 4093 continue; 4094 prop = sa_create_property(type, "true"); 4095 sa_free_attr_string(type); 4096 if (prop != NULL) 4097 prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset, 4098 (xmlNodePtr)prop); 4099 /* If prop is NULL, don't bother continuing */ 4100 if (prop == NULL) { 4101 ret = SA_NO_MEMORY; 4102 break; 4103 } 4104 } 4105 return (ret); 4106 } 4107 4108 /* 4109 * sa_free_protoset(optionset) 4110 * 4111 * Free the protocol property optionset. 4112 */ 4113 static void 4114 sa_free_protoset(sa_optionset_t optionset) 4115 { 4116 if (optionset != NULL) { 4117 xmlUnlinkNode((xmlNodePtr) optionset); 4118 xmlFreeNode((xmlNodePtr) optionset); 4119 } 4120 } 4121 4122 /* 4123 * sa_optionset_t sa_get_active_protocols(object) 4124 * 4125 * Return a list of the protocols that are active for the object. 4126 * This is currently an internal helper function, but could be 4127 * made visible if there is enough demand for it. 4128 * 4129 * The function finds the parent group and extracts the protocol 4130 * optionsets creating a new optionset with the protocols as properties. 4131 * 4132 * The caller must free the returned optionset. 4133 */ 4134 4135 static sa_optionset_t 4136 sa_get_active_protocols(void *object) 4137 { 4138 sa_optionset_t options; 4139 sa_share_t share = NULL; 4140 sa_group_t group = NULL; 4141 sa_resource_t resource = NULL; 4142 int ret = SA_OK; 4143 4144 if (object == NULL) 4145 return (NULL); 4146 options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset"); 4147 if (options == NULL) 4148 return (NULL); 4149 4150 /* 4151 * Find the objects up the tree that might have protocols 4152 * enabled on them. 4153 */ 4154 if (sa_is_resource(object)) { 4155 resource = (sa_resource_t)object; 4156 share = sa_get_resource_parent(resource); 4157 group = sa_get_parent_group(share); 4158 } else if (sa_is_share(object)) { 4159 share = (sa_share_t)object; 4160 group = sa_get_parent_group(share); 4161 } else { 4162 group = (sa_group_t)group; 4163 } 4164 if (resource != NULL) 4165 ret = get_protocol_list(options, resource); 4166 if (ret == SA_OK && share != NULL) 4167 ret = get_protocol_list(options, share); 4168 if (ret == SA_OK && group != NULL) 4169 ret = get_protocol_list(options, group); 4170 4171 /* 4172 * If there was an error, we won't have a complete list so 4173 * abandon everything. The caller will have to deal with the 4174 * issue. 4175 */ 4176 if (ret != SA_OK) { 4177 sa_free_protoset(options); 4178 options = NULL; 4179 } 4180 return (options); 4181 } 4182 4183 /* 4184 * sa_enable_resource, protocol) 4185 * Disable the specified share to the specified protocol. 4186 * If protocol is NULL, then all protocols. 4187 */ 4188 int 4189 sa_enable_resource(sa_resource_t resource, char *protocol) 4190 { 4191 int ret = SA_OK; 4192 4193 if (protocol != NULL) { 4194 ret = sa_proto_share_resource(protocol, resource); 4195 } else { 4196 sa_optionset_t protoset; 4197 sa_property_t prop; 4198 char *proto; 4199 int err; 4200 4201 /* need to do all protocols */ 4202 protoset = sa_get_active_protocols(resource); 4203 if (protoset == NULL) 4204 return (SA_NO_MEMORY); 4205 for (prop = sa_get_property(protoset, NULL); 4206 prop != NULL; 4207 prop = sa_get_next_property(prop)) { 4208 proto = sa_get_property_attr(prop, "type"); 4209 if (proto == NULL) { 4210 ret = SA_NO_MEMORY; 4211 continue; 4212 } 4213 err = sa_proto_share_resource(proto, resource); 4214 if (err != SA_OK) 4215 ret = err; 4216 sa_free_attr_string(proto); 4217 } 4218 sa_free_protoset(protoset); 4219 } 4220 if (ret == SA_OK) 4221 (void) sa_set_resource_attr(resource, "shared", NULL); 4222 4223 return (ret); 4224 } 4225 4226 /* 4227 * sa_disable_resource(resource, protocol) 4228 * 4229 * Disable the specified share for the specified protocol. If 4230 * protocol is NULL, then all protocols. If the underlying 4231 * protocol doesn't implement disable at the resource level, we 4232 * disable at the share level. 4233 */ 4234 int 4235 sa_disable_resource(sa_resource_t resource, char *protocol) 4236 { 4237 int ret = SA_OK; 4238 4239 if (protocol != NULL) { 4240 ret = sa_proto_unshare_resource(protocol, resource); 4241 if (ret == SA_NOT_IMPLEMENTED) { 4242 sa_share_t parent; 4243 /* 4244 * The protocol doesn't implement unshare 4245 * resource. That implies that resource names are 4246 * simple aliases for this protocol so we need to 4247 * unshare the share. 4248 */ 4249 parent = sa_get_resource_parent(resource); 4250 if (parent != NULL) 4251 ret = sa_disable_share(parent, protocol); 4252 else 4253 ret = SA_CONFIG_ERR; 4254 } 4255 } else { 4256 sa_optionset_t protoset; 4257 sa_property_t prop; 4258 char *proto; 4259 int err; 4260 4261 /* need to do all protocols */ 4262 protoset = sa_get_active_protocols(resource); 4263 if (protoset == NULL) 4264 return (SA_NO_MEMORY); 4265 for (prop = sa_get_property(protoset, NULL); 4266 prop != NULL; 4267 prop = sa_get_next_property(prop)) { 4268 proto = sa_get_property_attr(prop, "type"); 4269 if (proto == NULL) { 4270 ret = SA_NO_MEMORY; 4271 continue; 4272 } 4273 err = sa_proto_unshare_resource(proto, resource); 4274 if (err == SA_NOT_SUPPORTED) { 4275 sa_share_t parent; 4276 parent = sa_get_resource_parent(resource); 4277 if (parent != NULL) 4278 err = sa_disable_share(parent, proto); 4279 else 4280 err = SA_CONFIG_ERR; 4281 } 4282 if (err != SA_OK) 4283 ret = err; 4284 sa_free_attr_string(proto); 4285 } 4286 sa_free_protoset(protoset); 4287 } 4288 if (ret == SA_OK) 4289 (void) sa_set_resource_attr(resource, "shared", NULL); 4290 4291 return (ret); 4292 } 4293 4294 /* 4295 * sa_set_resource_description(resource, content) 4296 * 4297 * Set the description of share to content. 4298 */ 4299 4300 int 4301 sa_set_resource_description(sa_resource_t resource, char *content) 4302 { 4303 xmlNodePtr node; 4304 sa_group_t group; 4305 sa_share_t share; 4306 int ret = SA_OK; 4307 4308 for (node = ((xmlNodePtr)resource)->children; 4309 node != NULL; 4310 node = node->next) { 4311 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 4312 break; 4313 } 4314 } 4315 4316 /* no existing description but want to add */ 4317 if (node == NULL && content != NULL) { 4318 /* add a description */ 4319 node = _sa_set_share_description(resource, content); 4320 } else if (node != NULL && content != NULL) { 4321 /* update a description */ 4322 xmlNodeSetContent(node, (xmlChar *)content); 4323 } else if (node != NULL && content == NULL) { 4324 /* remove an existing description */ 4325 xmlUnlinkNode(node); 4326 xmlFreeNode(node); 4327 } 4328 share = sa_get_resource_parent(resource); 4329 group = sa_get_parent_group(share); 4330 if (group != NULL && sa_is_persistent(share)) { 4331 sa_handle_impl_t impl_handle; 4332 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 4333 if (impl_handle != NULL) 4334 ret = sa_commit_share(impl_handle->scfhandle, 4335 group, share); 4336 else 4337 ret = SA_SYSTEM_ERR; 4338 } 4339 return (ret); 4340 } 4341 4342 /* 4343 * sa_get_resource_description(share) 4344 * 4345 * Return the description text for the specified share if it 4346 * exists. NULL if no description exists. 4347 */ 4348 4349 char * 4350 sa_get_resource_description(sa_resource_t resource) 4351 { 4352 xmlChar *description = NULL; 4353 xmlNodePtr node; 4354 4355 for (node = ((xmlNodePtr)resource)->children; node != NULL; 4356 node = node->next) { 4357 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) 4358 break; 4359 } 4360 if (node != NULL) { 4361 description = xmlNodeGetContent(node); 4362 fixproblemchars((char *)description); 4363 } 4364 return ((char *)description); 4365 } 4366