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