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