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