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 2007 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 (void) sa_end_transaction( 2076 impl_handle->scfhandle); 2077 else 2078 sa_abort_transaction( 2079 impl_handle->scfhandle); 2080 } 2081 } 2082 if (groupname != NULL) 2083 sa_free_attr_string(groupname); 2084 } else { 2085 ret = SA_SYSTEM_ERR; 2086 } 2087 return (ret); 2088 } 2089 2090 /* 2091 * sa_get_share_attr(share, tag) 2092 * 2093 * Return the value of the tag/attribute set on the specified 2094 * share. Returns NULL if the tag doesn't exist. 2095 */ 2096 2097 char * 2098 sa_get_share_attr(sa_share_t share, char *tag) 2099 { 2100 return (get_node_attr((void *)share, tag)); 2101 } 2102 2103 /* 2104 * _sa_set_share_description(share, description) 2105 * 2106 * Add a description tag with text contents to the specified share. A 2107 * separate XML tag is used rather than a property. This can also be 2108 * used with resources. 2109 */ 2110 2111 xmlNodePtr 2112 _sa_set_share_description(void *share, char *content) 2113 { 2114 xmlNodePtr node; 2115 node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description", 2116 NULL); 2117 xmlNodeSetContent(node, (xmlChar *)content); 2118 return (node); 2119 } 2120 2121 /* 2122 * sa_set_share_attr(share, tag, value) 2123 * 2124 * Set the share attribute specified by tag to the specified value. In 2125 * the case of "resource", enforce a no duplicates in a group rule. If 2126 * the share is not transient, commit the changes to the repository 2127 * else just update the share internally. 2128 */ 2129 2130 int 2131 sa_set_share_attr(sa_share_t share, char *tag, char *value) 2132 { 2133 sa_group_t group; 2134 sa_share_t resource; 2135 int ret = SA_OK; 2136 2137 group = sa_get_parent_group(share); 2138 2139 /* 2140 * There are some attributes that may have specific 2141 * restrictions on them. Initially, only "resource" has 2142 * special meaning that needs to be checked. Only one instance 2143 * of a resource name may exist within a group. 2144 */ 2145 2146 if (strcmp(tag, "resource") == 0) { 2147 resource = sa_get_resource(group, value); 2148 if (resource != share && resource != NULL) 2149 ret = SA_DUPLICATE_NAME; 2150 } 2151 if (ret == SA_OK) { 2152 set_node_attr((void *)share, tag, value); 2153 if (group != NULL) { 2154 char *type; 2155 /* we can probably optimize this some */ 2156 type = sa_get_share_attr(share, "type"); 2157 if (type == NULL || strcmp(type, "transient") != 0) { 2158 sa_handle_impl_t impl_handle; 2159 impl_handle = 2160 (sa_handle_impl_t)sa_find_group_handle( 2161 group); 2162 if (impl_handle != NULL) { 2163 ret = sa_commit_share( 2164 impl_handle->scfhandle, group, 2165 share); 2166 } else { 2167 ret = SA_SYSTEM_ERR; 2168 } 2169 } 2170 if (type != NULL) 2171 sa_free_attr_string(type); 2172 } 2173 } 2174 return (ret); 2175 } 2176 2177 /* 2178 * sa_get_property_attr(prop, tag) 2179 * 2180 * Get the value of the specified property attribute. Standard 2181 * attributes are "type" and "value". 2182 */ 2183 2184 char * 2185 sa_get_property_attr(sa_property_t prop, char *tag) 2186 { 2187 return (get_node_attr((void *)prop, tag)); 2188 } 2189 2190 /* 2191 * sa_get_optionset_attr(prop, tag) 2192 * 2193 * Get the value of the specified property attribute. Standard 2194 * attribute is "type". 2195 */ 2196 2197 char * 2198 sa_get_optionset_attr(sa_property_t optionset, char *tag) 2199 { 2200 return (get_node_attr((void *)optionset, tag)); 2201 2202 } 2203 2204 /* 2205 * sa_set_optionset_attr(optionset, tag, value) 2206 * 2207 * Set the specified attribute(tag) to the specified value on the 2208 * optionset. 2209 */ 2210 2211 void 2212 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 2213 { 2214 set_node_attr((void *)optionset, tag, value); 2215 } 2216 2217 /* 2218 * sa_free_attr_string(string) 2219 * 2220 * Free the string that was returned in one of the sa_get_*_attr() 2221 * functions. 2222 */ 2223 2224 void 2225 sa_free_attr_string(char *string) 2226 { 2227 xmlFree((xmlChar *)string); 2228 } 2229 2230 /* 2231 * sa_get_optionset(group, proto) 2232 * 2233 * Return the optionset, if it exists, that is associated with the 2234 * specified protocol. 2235 */ 2236 2237 sa_optionset_t 2238 sa_get_optionset(void *group, char *proto) 2239 { 2240 xmlNodePtr node; 2241 xmlChar *value = NULL; 2242 2243 for (node = ((xmlNodePtr)group)->children; node != NULL; 2244 node = node->next) { 2245 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2246 value = xmlGetProp(node, (xmlChar *)"type"); 2247 if (proto != NULL) { 2248 if (value != NULL && 2249 xmlStrcmp(value, (xmlChar *)proto) == 0) { 2250 break; 2251 } 2252 if (value != NULL) { 2253 xmlFree(value); 2254 value = NULL; 2255 } 2256 } else { 2257 break; 2258 } 2259 } 2260 } 2261 if (value != NULL) 2262 xmlFree(value); 2263 return ((sa_optionset_t)node); 2264 } 2265 2266 /* 2267 * sa_get_next_optionset(optionset) 2268 * 2269 * Return the next optionset in the group. NULL if this was the last. 2270 */ 2271 2272 sa_optionset_t 2273 sa_get_next_optionset(sa_optionset_t optionset) 2274 { 2275 xmlNodePtr node; 2276 2277 for (node = ((xmlNodePtr)optionset)->next; node != NULL; 2278 node = node->next) { 2279 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2280 break; 2281 } 2282 } 2283 return ((sa_optionset_t)node); 2284 } 2285 2286 /* 2287 * sa_get_security(group, sectype, proto) 2288 * 2289 * Return the security optionset. The internal name is a hold over 2290 * from the implementation and will be changed before the API is 2291 * finalized. This is really a named optionset that can be negotiated 2292 * as a group of properties (like NFS security options). 2293 */ 2294 2295 sa_security_t 2296 sa_get_security(sa_group_t group, char *sectype, char *proto) 2297 { 2298 xmlNodePtr node; 2299 xmlChar *value = NULL; 2300 2301 for (node = ((xmlNodePtr)group)->children; node != NULL; 2302 node = node->next) { 2303 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2304 if (proto != NULL) { 2305 value = xmlGetProp(node, (xmlChar *)"type"); 2306 if (value == NULL || 2307 (value != NULL && 2308 xmlStrcmp(value, (xmlChar *)proto) != 0)) { 2309 /* it doesn't match so continue */ 2310 xmlFree(value); 2311 value = NULL; 2312 continue; 2313 } 2314 } 2315 if (value != NULL) { 2316 xmlFree(value); 2317 value = NULL; 2318 } 2319 /* potential match */ 2320 if (sectype != NULL) { 2321 value = xmlGetProp(node, (xmlChar *)"sectype"); 2322 if (value != NULL && 2323 xmlStrcmp(value, (xmlChar *)sectype) == 0) { 2324 break; 2325 } 2326 } else { 2327 break; 2328 } 2329 } 2330 if (value != NULL) { 2331 xmlFree(value); 2332 value = NULL; 2333 } 2334 } 2335 if (value != NULL) 2336 xmlFree(value); 2337 return ((sa_security_t)node); 2338 } 2339 2340 /* 2341 * sa_get_next_security(security) 2342 * 2343 * Get the next security optionset if one exists. 2344 */ 2345 2346 sa_security_t 2347 sa_get_next_security(sa_security_t security) 2348 { 2349 xmlNodePtr node; 2350 2351 for (node = ((xmlNodePtr)security)->next; node != NULL; 2352 node = node->next) { 2353 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2354 break; 2355 } 2356 } 2357 return ((sa_security_t)node); 2358 } 2359 2360 /* 2361 * sa_get_property(optionset, prop) 2362 * 2363 * Get the property object with the name specified in prop from the 2364 * optionset. 2365 */ 2366 2367 sa_property_t 2368 sa_get_property(sa_optionset_t optionset, char *prop) 2369 { 2370 xmlNodePtr node = (xmlNodePtr)optionset; 2371 xmlChar *value = NULL; 2372 2373 if (optionset == NULL) 2374 return (NULL); 2375 2376 for (node = node->children; node != NULL; 2377 node = node->next) { 2378 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2379 if (prop == NULL) 2380 break; 2381 value = xmlGetProp(node, (xmlChar *)"type"); 2382 if (value != NULL && 2383 xmlStrcmp(value, (xmlChar *)prop) == 0) { 2384 break; 2385 } 2386 if (value != NULL) { 2387 xmlFree(value); 2388 value = NULL; 2389 } 2390 } 2391 } 2392 if (value != NULL) 2393 xmlFree(value); 2394 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2395 /* 2396 * avoid a non option node -- it is possible to be a 2397 * text node 2398 */ 2399 node = NULL; 2400 } 2401 return ((sa_property_t)node); 2402 } 2403 2404 /* 2405 * sa_get_next_property(property) 2406 * 2407 * Get the next property following the specified property. NULL if 2408 * this was the last. 2409 */ 2410 2411 sa_property_t 2412 sa_get_next_property(sa_property_t property) 2413 { 2414 xmlNodePtr node; 2415 2416 for (node = ((xmlNodePtr)property)->next; node != NULL; 2417 node = node->next) { 2418 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2419 break; 2420 } 2421 } 2422 return ((sa_property_t)node); 2423 } 2424 2425 /* 2426 * sa_set_share_description(share, content) 2427 * 2428 * Set the description of share to content. 2429 */ 2430 2431 int 2432 sa_set_share_description(sa_share_t share, char *content) 2433 { 2434 xmlNodePtr node; 2435 sa_group_t group; 2436 int ret = SA_OK; 2437 2438 for (node = ((xmlNodePtr)share)->children; node != NULL; 2439 node = node->next) { 2440 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2441 break; 2442 } 2443 } 2444 /* no existing description but want to add */ 2445 if (node == NULL && content != NULL) { 2446 /* add a description */ 2447 node = _sa_set_share_description(share, content); 2448 } else if (node != NULL && content != NULL) { 2449 /* update a description */ 2450 xmlNodeSetContent(node, (xmlChar *)content); 2451 } else if (node != NULL && content == NULL) { 2452 /* remove an existing description */ 2453 xmlUnlinkNode(node); 2454 xmlFreeNode(node); 2455 } 2456 group = sa_get_parent_group(share); 2457 if (group != NULL && sa_is_persistent(share)) { 2458 sa_handle_impl_t impl_handle; 2459 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2460 if (impl_handle != NULL) { 2461 ret = sa_commit_share(impl_handle->scfhandle, group, 2462 share); 2463 } else { 2464 ret = SA_SYSTEM_ERR; 2465 } 2466 } 2467 return (ret); 2468 } 2469 2470 /* 2471 * fixproblemchars(string) 2472 * 2473 * don't want any newline or tab characters in the text since these 2474 * could break display of data and legacy file formats. 2475 */ 2476 static void 2477 fixproblemchars(char *str) 2478 { 2479 int c; 2480 for (c = *str; c != '\0'; c = *++str) { 2481 if (c == '\t' || c == '\n') 2482 *str = ' '; 2483 else if (c == '"') 2484 *str = '\''; 2485 } 2486 } 2487 2488 /* 2489 * sa_get_share_description(share) 2490 * 2491 * Return the description text for the specified share if it 2492 * exists. NULL if no description exists. 2493 */ 2494 2495 char * 2496 sa_get_share_description(sa_share_t share) 2497 { 2498 xmlChar *description = NULL; 2499 xmlNodePtr node; 2500 2501 for (node = ((xmlNodePtr)share)->children; node != NULL; 2502 node = node->next) { 2503 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2504 break; 2505 } 2506 } 2507 if (node != NULL) { 2508 description = xmlNodeGetContent(node); 2509 fixproblemchars((char *)description); 2510 } 2511 return ((char *)description); 2512 } 2513 2514 /* 2515 * sa_free(share_description(description) 2516 * 2517 * Free the description string. 2518 */ 2519 2520 void 2521 sa_free_share_description(char *description) 2522 { 2523 xmlFree((xmlChar *)description); 2524 } 2525 2526 /* 2527 * sa_create_optionset(group, proto) 2528 * 2529 * Create an optionset for the specified protocol in the specied 2530 * group. This is manifested as a property group within SMF. 2531 */ 2532 2533 sa_optionset_t 2534 sa_create_optionset(sa_group_t group, char *proto) 2535 { 2536 sa_optionset_t optionset; 2537 sa_group_t parent = group; 2538 sa_share_t share = NULL; 2539 int err = SA_OK; 2540 char *id = NULL; 2541 2542 optionset = sa_get_optionset(group, proto); 2543 if (optionset != NULL) { 2544 /* can't have a duplicate protocol */ 2545 optionset = NULL; 2546 } else { 2547 /* 2548 * Account for resource names being slightly 2549 * different. 2550 */ 2551 if (sa_is_share(group)) { 2552 /* 2553 * Transient shares do not have an "id" so not an 2554 * error to not find one. 2555 */ 2556 id = sa_get_share_attr((sa_share_t)group, "id"); 2557 } else if (sa_is_resource(group)) { 2558 share = sa_get_resource_parent( 2559 (sa_resource_t)group); 2560 id = sa_get_resource_attr(share, "id"); 2561 2562 /* id can be NULL if the group is transient (ZFS) */ 2563 if (id == NULL && sa_is_persistent(group)) 2564 err = SA_NO_MEMORY; 2565 } 2566 if (err == SA_NO_MEMORY) { 2567 /* 2568 * Couldn't get the id for the share or 2569 * resource. While this could be a 2570 * configuration issue, it is most likely an 2571 * out of memory. In any case, fail the create. 2572 */ 2573 return (NULL); 2574 } 2575 2576 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 2577 NULL, (xmlChar *)"optionset", NULL); 2578 /* 2579 * only put to repository if on a group and we were 2580 * able to create an optionset. 2581 */ 2582 if (optionset != NULL) { 2583 char oname[SA_STRSIZE]; 2584 char *groupname; 2585 2586 /* 2587 * Need to get parent group in all cases, but also get 2588 * the share if this is a resource. 2589 */ 2590 if (sa_is_share(group)) { 2591 parent = sa_get_parent_group((sa_share_t)group); 2592 } else if (sa_is_resource(group)) { 2593 share = sa_get_resource_parent( 2594 (sa_resource_t)group); 2595 parent = sa_get_parent_group(share); 2596 } 2597 2598 sa_set_optionset_attr(optionset, "type", proto); 2599 2600 (void) sa_optionset_name(optionset, oname, 2601 sizeof (oname), id); 2602 groupname = sa_get_group_attr(parent, "name"); 2603 if (groupname != NULL && sa_is_persistent(group)) { 2604 sa_handle_impl_t impl_handle; 2605 impl_handle = 2606 (sa_handle_impl_t)sa_find_group_handle( 2607 group); 2608 assert(impl_handle != NULL); 2609 if (impl_handle != NULL) { 2610 (void) sa_get_instance( 2611 impl_handle->scfhandle, groupname); 2612 (void) sa_create_pgroup( 2613 impl_handle->scfhandle, oname); 2614 } 2615 } 2616 if (groupname != NULL) 2617 sa_free_attr_string(groupname); 2618 } 2619 } 2620 2621 if (id != NULL) 2622 sa_free_attr_string(id); 2623 return (optionset); 2624 } 2625 2626 /* 2627 * sa_get_property_parent(property) 2628 * 2629 * Given a property, return the object it is a property of. This will 2630 * be an optionset of some type. 2631 */ 2632 2633 static sa_optionset_t 2634 sa_get_property_parent(sa_property_t property) 2635 { 2636 xmlNodePtr node = NULL; 2637 2638 if (property != NULL) 2639 node = ((xmlNodePtr)property)->parent; 2640 return ((sa_optionset_t)node); 2641 } 2642 2643 /* 2644 * sa_get_optionset_parent(optionset) 2645 * 2646 * Return the parent of the specified optionset. This could be a group 2647 * or a share. 2648 */ 2649 2650 static sa_group_t 2651 sa_get_optionset_parent(sa_optionset_t optionset) 2652 { 2653 xmlNodePtr node = NULL; 2654 2655 if (optionset != NULL) 2656 node = ((xmlNodePtr)optionset)->parent; 2657 return ((sa_group_t)node); 2658 } 2659 2660 /* 2661 * zfs_needs_update(share) 2662 * 2663 * In order to avoid making multiple updates to a ZFS share when 2664 * setting properties, the share attribute "changed" will be set to 2665 * true when a property is added or modified. When done adding 2666 * properties, we can then detect that an update is needed. We then 2667 * clear the state here to detect additional changes. 2668 */ 2669 2670 static int 2671 zfs_needs_update(sa_share_t share) 2672 { 2673 char *attr; 2674 int result = 0; 2675 2676 attr = sa_get_share_attr(share, "changed"); 2677 if (attr != NULL) { 2678 sa_free_attr_string(attr); 2679 result = 1; 2680 } 2681 set_node_attr((void *)share, "changed", NULL); 2682 return (result); 2683 } 2684 2685 /* 2686 * zfs_set_update(share) 2687 * 2688 * Set the changed attribute of the share to true. 2689 */ 2690 2691 static void 2692 zfs_set_update(sa_share_t share) 2693 { 2694 set_node_attr((void *)share, "changed", "true"); 2695 } 2696 2697 /* 2698 * sa_commit_properties(optionset, clear) 2699 * 2700 * Check if SMF or ZFS config and either update or abort the pending 2701 * changes. 2702 */ 2703 2704 int 2705 sa_commit_properties(sa_optionset_t optionset, int clear) 2706 { 2707 sa_group_t group; 2708 sa_group_t parent; 2709 int zfs = 0; 2710 int needsupdate = 0; 2711 int ret = SA_OK; 2712 sa_handle_impl_t impl_handle; 2713 2714 group = sa_get_optionset_parent(optionset); 2715 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 2716 /* only update ZFS if on a share */ 2717 parent = sa_get_parent_group(group); 2718 zfs++; 2719 if (parent != NULL && is_zfs_group(parent)) 2720 needsupdate = zfs_needs_update(group); 2721 else 2722 zfs = 0; 2723 } 2724 if (zfs) { 2725 if (!clear && needsupdate) 2726 ret = sa_zfs_update((sa_share_t)group); 2727 } else { 2728 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2729 if (impl_handle != NULL) { 2730 if (clear) { 2731 (void) sa_abort_transaction( 2732 impl_handle->scfhandle); 2733 } else { 2734 ret = sa_end_transaction( 2735 impl_handle->scfhandle); 2736 } 2737 } else { 2738 ret = SA_SYSTEM_ERR; 2739 } 2740 } 2741 return (ret); 2742 } 2743 2744 /* 2745 * sa_destroy_optionset(optionset) 2746 * 2747 * Remove the optionset from its group. Update the repository to 2748 * reflect this change. 2749 */ 2750 2751 int 2752 sa_destroy_optionset(sa_optionset_t optionset) 2753 { 2754 char name[SA_STRSIZE]; 2755 int len; 2756 int ret; 2757 char *id = NULL; 2758 sa_group_t group; 2759 int ispersist = 1; 2760 2761 /* now delete the prop group */ 2762 group = sa_get_optionset_parent(optionset); 2763 if (group != NULL) { 2764 if (sa_is_resource(group)) { 2765 sa_resource_t resource = group; 2766 sa_share_t share = sa_get_resource_parent(resource); 2767 group = sa_get_parent_group(share); 2768 id = sa_get_share_attr(share, "id"); 2769 } else if (sa_is_share(group)) { 2770 id = sa_get_share_attr((sa_share_t)group, "id"); 2771 } 2772 ispersist = sa_is_persistent(group); 2773 } 2774 if (ispersist) { 2775 sa_handle_impl_t impl_handle; 2776 len = sa_optionset_name(optionset, name, sizeof (name), id); 2777 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2778 if (impl_handle != NULL) { 2779 if (len > 0) { 2780 ret = sa_delete_pgroup(impl_handle->scfhandle, 2781 name); 2782 } 2783 } else { 2784 ret = SA_SYSTEM_ERR; 2785 } 2786 } 2787 xmlUnlinkNode((xmlNodePtr)optionset); 2788 xmlFreeNode((xmlNodePtr)optionset); 2789 if (id != NULL) 2790 sa_free_attr_string(id); 2791 return (ret); 2792 } 2793 2794 /* private to the implementation */ 2795 int 2796 _sa_remove_optionset(sa_optionset_t optionset) 2797 { 2798 int ret = SA_OK; 2799 2800 xmlUnlinkNode((xmlNodePtr)optionset); 2801 xmlFreeNode((xmlNodePtr)optionset); 2802 return (ret); 2803 } 2804 2805 /* 2806 * sa_create_security(group, sectype, proto) 2807 * 2808 * Create a security optionset (one that has a type name and a 2809 * proto). Security is left over from a pure NFS implementation. The 2810 * naming will change in the future when the API is released. 2811 */ 2812 sa_security_t 2813 sa_create_security(sa_group_t group, char *sectype, char *proto) 2814 { 2815 sa_security_t security; 2816 char *id = NULL; 2817 sa_group_t parent; 2818 char *groupname = NULL; 2819 2820 if (group != NULL && sa_is_share(group)) { 2821 id = sa_get_share_attr((sa_share_t)group, "id"); 2822 parent = sa_get_parent_group(group); 2823 if (parent != NULL) 2824 groupname = sa_get_group_attr(parent, "name"); 2825 } else if (group != NULL) { 2826 groupname = sa_get_group_attr(group, "name"); 2827 } 2828 2829 security = sa_get_security(group, sectype, proto); 2830 if (security != NULL) { 2831 /* can't have a duplicate security option */ 2832 security = NULL; 2833 } else { 2834 security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 2835 NULL, (xmlChar *)"security", NULL); 2836 if (security != NULL) { 2837 char oname[SA_STRSIZE]; 2838 sa_set_security_attr(security, "type", proto); 2839 2840 sa_set_security_attr(security, "sectype", sectype); 2841 (void) sa_security_name(security, oname, 2842 sizeof (oname), id); 2843 if (groupname != NULL && sa_is_persistent(group)) { 2844 sa_handle_impl_t impl_handle; 2845 impl_handle = 2846 (sa_handle_impl_t)sa_find_group_handle( 2847 group); 2848 if (impl_handle != NULL) { 2849 (void) sa_get_instance( 2850 impl_handle->scfhandle, groupname); 2851 (void) sa_create_pgroup( 2852 impl_handle->scfhandle, oname); 2853 } 2854 } 2855 } 2856 } 2857 if (groupname != NULL) 2858 sa_free_attr_string(groupname); 2859 return (security); 2860 } 2861 2862 /* 2863 * sa_destroy_security(security) 2864 * 2865 * Remove the specified optionset from the document and the 2866 * configuration. 2867 */ 2868 2869 int 2870 sa_destroy_security(sa_security_t security) 2871 { 2872 char name[SA_STRSIZE]; 2873 int len; 2874 int ret = SA_OK; 2875 char *id = NULL; 2876 sa_group_t group; 2877 int iszfs = 0; 2878 int ispersist = 1; 2879 2880 group = sa_get_optionset_parent(security); 2881 2882 if (group != NULL) 2883 iszfs = sa_group_is_zfs(group); 2884 2885 if (group != NULL && !iszfs) { 2886 if (sa_is_share(group)) 2887 ispersist = sa_is_persistent(group); 2888 id = sa_get_share_attr((sa_share_t)group, "id"); 2889 } 2890 if (ispersist) { 2891 len = sa_security_name(security, name, sizeof (name), id); 2892 if (!iszfs && len > 0) { 2893 sa_handle_impl_t impl_handle; 2894 impl_handle = 2895 (sa_handle_impl_t)sa_find_group_handle(group); 2896 if (impl_handle != NULL) { 2897 ret = sa_delete_pgroup(impl_handle->scfhandle, 2898 name); 2899 } else { 2900 ret = SA_SYSTEM_ERR; 2901 } 2902 } 2903 } 2904 xmlUnlinkNode((xmlNodePtr)security); 2905 xmlFreeNode((xmlNodePtr)security); 2906 if (iszfs) 2907 ret = sa_zfs_update(group); 2908 if (id != NULL) 2909 sa_free_attr_string(id); 2910 return (ret); 2911 } 2912 2913 /* 2914 * sa_get_security_attr(optionset, tag) 2915 * 2916 * Return the specified attribute value from the optionset. 2917 */ 2918 2919 char * 2920 sa_get_security_attr(sa_property_t optionset, char *tag) 2921 { 2922 return (get_node_attr((void *)optionset, tag)); 2923 2924 } 2925 2926 /* 2927 * sa_set_security_attr(optionset, tag, value) 2928 * 2929 * Set the optioset attribute specied by tag to the specified value. 2930 */ 2931 2932 void 2933 sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 2934 { 2935 set_node_attr((void *)optionset, tag, value); 2936 } 2937 2938 /* 2939 * is_nodetype(node, type) 2940 * 2941 * Check to see if node is of the type specified. 2942 */ 2943 2944 static int 2945 is_nodetype(void *node, char *type) 2946 { 2947 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 2948 } 2949 2950 /* 2951 * add_or_update() 2952 * 2953 * Add or update a property. Pulled out of sa_set_prop_by_prop for 2954 * readability. 2955 */ 2956 static int 2957 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value, 2958 scf_transaction_entry_t *entry, char *name, char *valstr) 2959 { 2960 int ret = SA_SYSTEM_ERR; 2961 2962 if (value != NULL) { 2963 if (type == SA_PROP_OP_ADD) 2964 ret = scf_transaction_property_new(scf_handle->trans, 2965 entry, name, SCF_TYPE_ASTRING); 2966 else 2967 ret = scf_transaction_property_change(scf_handle->trans, 2968 entry, name, SCF_TYPE_ASTRING); 2969 if (ret == 0) { 2970 ret = scf_value_set_astring(value, valstr); 2971 if (ret == 0) 2972 ret = scf_entry_add_value(entry, value); 2973 if (ret == 0) 2974 return (ret); 2975 scf_value_destroy(value); 2976 } else { 2977 scf_entry_destroy(entry); 2978 } 2979 } 2980 return (SA_SYSTEM_ERR); 2981 } 2982 2983 /* 2984 * sa_set_prop_by_prop(optionset, group, prop, type) 2985 * 2986 * Add/remove/update the specified property prop into the optionset or 2987 * share. If a share, sort out which property group based on GUID. In 2988 * all cases, the appropriate transaction is set (or ZFS share is 2989 * marked as needing an update) 2990 */ 2991 2992 static int 2993 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 2994 sa_property_t prop, int type) 2995 { 2996 char *name; 2997 char *valstr; 2998 int ret = SA_OK; 2999 scf_transaction_entry_t *entry; 3000 scf_value_t *value; 3001 int opttype; /* 1 == optionset, 0 == security */ 3002 char *id = NULL; 3003 int iszfs = 0; 3004 sa_group_t parent = NULL; 3005 sa_share_t share = NULL; 3006 sa_handle_impl_t impl_handle; 3007 scfutilhandle_t *scf_handle; 3008 3009 if (!sa_is_persistent(group)) { 3010 /* 3011 * if the group/share is not persistent we don't need 3012 * to do anything here 3013 */ 3014 return (SA_OK); 3015 } 3016 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 3017 if (impl_handle == NULL || impl_handle->scfhandle == NULL) 3018 return (SA_SYSTEM_ERR); 3019 scf_handle = impl_handle->scfhandle; 3020 name = sa_get_property_attr(prop, "type"); 3021 valstr = sa_get_property_attr(prop, "value"); 3022 entry = scf_entry_create(scf_handle->handle); 3023 opttype = is_nodetype((void *)optionset, "optionset"); 3024 3025 /* 3026 * Check for share vs. resource since they need slightly 3027 * different treatment given the hierarchy. 3028 */ 3029 if (valstr != NULL && entry != NULL) { 3030 if (sa_is_share(group)) { 3031 parent = sa_get_parent_group(group); 3032 share = (sa_share_t)group; 3033 if (parent != NULL) 3034 iszfs = is_zfs_group(parent); 3035 } else if (sa_is_resource(group)) { 3036 share = sa_get_parent_group(group); 3037 if (share != NULL) 3038 parent = sa_get_parent_group(share); 3039 } else { 3040 iszfs = is_zfs_group(group); 3041 } 3042 if (!iszfs) { 3043 if (scf_handle->trans == NULL) { 3044 char oname[SA_STRSIZE]; 3045 char *groupname = NULL; 3046 if (share != NULL) { 3047 if (parent != NULL) 3048 groupname = 3049 sa_get_group_attr(parent, 3050 "name"); 3051 id = sa_get_share_attr( 3052 (sa_share_t)share, "id"); 3053 } else { 3054 groupname = sa_get_group_attr(group, 3055 "name"); 3056 } 3057 if (groupname != NULL) { 3058 ret = sa_get_instance(scf_handle, 3059 groupname); 3060 sa_free_attr_string(groupname); 3061 } 3062 if (opttype) 3063 (void) sa_optionset_name(optionset, 3064 oname, sizeof (oname), id); 3065 else 3066 (void) sa_security_name(optionset, 3067 oname, sizeof (oname), id); 3068 ret = sa_start_transaction(scf_handle, oname); 3069 } 3070 if (ret == SA_OK) { 3071 switch (type) { 3072 case SA_PROP_OP_REMOVE: 3073 ret = scf_transaction_property_delete( 3074 scf_handle->trans, entry, name); 3075 break; 3076 case SA_PROP_OP_ADD: 3077 case SA_PROP_OP_UPDATE: 3078 value = scf_value_create( 3079 scf_handle->handle); 3080 ret = add_or_update(scf_handle, type, 3081 value, entry, name, valstr); 3082 break; 3083 } 3084 } 3085 } else { 3086 /* 3087 * ZFS update. The calling function would have updated 3088 * the internal XML structure. Just need to flag it as 3089 * changed for ZFS. 3090 */ 3091 zfs_set_update((sa_share_t)group); 3092 } 3093 } 3094 3095 if (name != NULL) 3096 sa_free_attr_string(name); 3097 if (valstr != NULL) 3098 sa_free_attr_string(valstr); 3099 else if (entry != NULL) 3100 scf_entry_destroy(entry); 3101 3102 if (ret == -1) 3103 ret = SA_SYSTEM_ERR; 3104 3105 return (ret); 3106 } 3107 3108 /* 3109 * sa_create_property(name, value) 3110 * 3111 * Create a new property with the specified name and value. 3112 */ 3113 3114 sa_property_t 3115 sa_create_property(char *name, char *value) 3116 { 3117 xmlNodePtr node; 3118 3119 node = xmlNewNode(NULL, (xmlChar *)"option"); 3120 if (node != NULL) { 3121 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 3122 xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 3123 } 3124 return ((sa_property_t)node); 3125 } 3126 3127 /* 3128 * sa_add_property(object, property) 3129 * 3130 * Add the specified property to the object. Issue the appropriate 3131 * transaction or mark a ZFS object as needing an update. 3132 */ 3133 3134 int 3135 sa_add_property(void *object, sa_property_t property) 3136 { 3137 int ret = SA_OK; 3138 sa_group_t parent; 3139 sa_group_t group; 3140 char *proto; 3141 3142 proto = sa_get_optionset_attr(object, "type"); 3143 if (property != NULL) { 3144 if ((ret = sa_valid_property(object, proto, property)) == 3145 SA_OK) { 3146 property = (sa_property_t)xmlAddChild( 3147 (xmlNodePtr)object, (xmlNodePtr)property); 3148 } else { 3149 if (proto != NULL) 3150 sa_free_attr_string(proto); 3151 return (ret); 3152 } 3153 } 3154 3155 if (proto != NULL) 3156 sa_free_attr_string(proto); 3157 3158 parent = sa_get_parent_group(object); 3159 if (!sa_is_persistent(parent)) 3160 return (ret); 3161 3162 if (sa_is_resource(parent)) { 3163 /* 3164 * Resources are children of share. Need to go up two 3165 * levels to find the group but the parent needs to be 3166 * the share at this point in order to get the "id". 3167 */ 3168 parent = sa_get_parent_group(parent); 3169 group = sa_get_parent_group(parent); 3170 } else if (sa_is_share(parent)) { 3171 group = sa_get_parent_group(parent); 3172 } else { 3173 group = parent; 3174 } 3175 3176 if (property == NULL) { 3177 ret = SA_NO_MEMORY; 3178 } else { 3179 char oname[SA_STRSIZE]; 3180 3181 if (!is_zfs_group(group)) { 3182 char *id = NULL; 3183 sa_handle_impl_t impl_handle; 3184 scfutilhandle_t *scf_handle; 3185 3186 impl_handle = (sa_handle_impl_t)sa_find_group_handle( 3187 group); 3188 if (impl_handle == NULL || 3189 impl_handle->scfhandle == NULL) 3190 ret = SA_SYSTEM_ERR; 3191 if (ret == SA_OK) { 3192 scf_handle = impl_handle->scfhandle; 3193 if (sa_is_share((sa_group_t)parent)) { 3194 id = sa_get_share_attr( 3195 (sa_share_t)parent, "id"); 3196 } 3197 if (scf_handle->trans == NULL) { 3198 if (is_nodetype(object, "optionset")) { 3199 (void) sa_optionset_name( 3200 (sa_optionset_t)object, 3201 oname, sizeof (oname), id); 3202 } else { 3203 (void) sa_security_name( 3204 (sa_optionset_t)object, 3205 oname, sizeof (oname), id); 3206 } 3207 ret = sa_start_transaction(scf_handle, 3208 oname); 3209 } 3210 if (ret == SA_OK) { 3211 char *name; 3212 char *value; 3213 name = sa_get_property_attr(property, 3214 "type"); 3215 value = sa_get_property_attr(property, 3216 "value"); 3217 if (name != NULL && value != NULL) { 3218 if (scf_handle->scf_state == 3219 SCH_STATE_INIT) { 3220 ret = sa_set_property( 3221 scf_handle, name, 3222 value); 3223 } 3224 } else { 3225 ret = SA_CONFIG_ERR; 3226 } 3227 if (name != NULL) 3228 sa_free_attr_string( 3229 name); 3230 if (value != NULL) 3231 sa_free_attr_string(value); 3232 } 3233 if (id != NULL) 3234 sa_free_attr_string(id); 3235 } 3236 } else { 3237 /* 3238 * ZFS is a special case. We do want 3239 * to allow editing property/security 3240 * lists since we can have a better 3241 * syntax and we also want to keep 3242 * things consistent when possible. 3243 * 3244 * Right now, we defer until the 3245 * sa_commit_properties so we can get 3246 * them all at once. We do need to 3247 * mark the share as "changed" 3248 */ 3249 zfs_set_update((sa_share_t)parent); 3250 } 3251 } 3252 return (ret); 3253 } 3254 3255 /* 3256 * sa_remove_property(property) 3257 * 3258 * Remove the specied property from its containing object. Update the 3259 * repository as appropriate. 3260 */ 3261 3262 int 3263 sa_remove_property(sa_property_t property) 3264 { 3265 int ret = SA_OK; 3266 3267 if (property != NULL) { 3268 sa_optionset_t optionset; 3269 sa_group_t group; 3270 optionset = sa_get_property_parent(property); 3271 if (optionset != NULL) { 3272 group = sa_get_optionset_parent(optionset); 3273 if (group != NULL) { 3274 ret = sa_set_prop_by_prop(optionset, group, 3275 property, SA_PROP_OP_REMOVE); 3276 } 3277 } 3278 xmlUnlinkNode((xmlNodePtr)property); 3279 xmlFreeNode((xmlNodePtr)property); 3280 } else { 3281 ret = SA_NO_SUCH_PROP; 3282 } 3283 return (ret); 3284 } 3285 3286 /* 3287 * sa_update_property(property, value) 3288 * 3289 * Update the specified property to the new value. If value is NULL, 3290 * we currently treat this as a remove. 3291 */ 3292 3293 int 3294 sa_update_property(sa_property_t property, char *value) 3295 { 3296 int ret = SA_OK; 3297 if (value == NULL) { 3298 return (sa_remove_property(property)); 3299 } else { 3300 sa_optionset_t optionset; 3301 sa_group_t group; 3302 set_node_attr((void *)property, "value", value); 3303 optionset = sa_get_property_parent(property); 3304 if (optionset != NULL) { 3305 group = sa_get_optionset_parent(optionset); 3306 if (group != NULL) { 3307 ret = sa_set_prop_by_prop(optionset, group, 3308 property, SA_PROP_OP_UPDATE); 3309 } 3310 } else { 3311 ret = SA_NO_SUCH_PROP; 3312 } 3313 } 3314 return (ret); 3315 } 3316 3317 /* 3318 * sa_get_protocol_property(propset, prop) 3319 * 3320 * Get the specified protocol specific property. These are global to 3321 * the protocol and not specific to a group or share. 3322 */ 3323 3324 sa_property_t 3325 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 3326 { 3327 xmlNodePtr node = (xmlNodePtr)propset; 3328 xmlChar *value = NULL; 3329 3330 for (node = node->children; node != NULL; 3331 node = node->next) { 3332 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3333 if (prop == NULL) 3334 break; 3335 value = xmlGetProp(node, (xmlChar *)"type"); 3336 if (value != NULL && 3337 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 3338 break; 3339 } 3340 if (value != NULL) { 3341 xmlFree(value); 3342 value = NULL; 3343 } 3344 } 3345 } 3346 if (value != NULL) 3347 xmlFree(value); 3348 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 3349 /* 3350 * avoid a non option node -- it is possible to be a 3351 * text node 3352 */ 3353 node = NULL; 3354 } 3355 return ((sa_property_t)node); 3356 } 3357 3358 /* 3359 * sa_get_next_protocol_property(prop) 3360 * 3361 * Get the next protocol specific property in the list. 3362 */ 3363 3364 sa_property_t 3365 sa_get_next_protocol_property(sa_property_t prop) 3366 { 3367 xmlNodePtr node; 3368 3369 for (node = ((xmlNodePtr)prop)->next; node != NULL; 3370 node = node->next) { 3371 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3372 break; 3373 } 3374 } 3375 return ((sa_property_t)node); 3376 } 3377 3378 /* 3379 * sa_set_protocol_property(prop, value) 3380 * 3381 * Set the specified property to have the new value. The protocol 3382 * specific plugin will then be called to update the property. 3383 */ 3384 3385 int 3386 sa_set_protocol_property(sa_property_t prop, char *value) 3387 { 3388 sa_protocol_properties_t propset; 3389 char *proto; 3390 int ret = SA_INVALID_PROTOCOL; 3391 3392 propset = ((xmlNodePtr)prop)->parent; 3393 if (propset != NULL) { 3394 proto = sa_get_optionset_attr(propset, "type"); 3395 if (proto != NULL) { 3396 set_node_attr((xmlNodePtr)prop, "value", value); 3397 ret = sa_proto_set_property(proto, prop); 3398 sa_free_attr_string(proto); 3399 } 3400 } 3401 return (ret); 3402 } 3403 3404 /* 3405 * sa_add_protocol_property(propset, prop) 3406 * 3407 * Add a new property to the protocol specific property set. 3408 */ 3409 3410 int 3411 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 3412 { 3413 xmlNodePtr node; 3414 3415 /* should check for legitimacy */ 3416 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 3417 if (node != NULL) 3418 return (SA_OK); 3419 return (SA_NO_MEMORY); 3420 } 3421 3422 /* 3423 * sa_create_protocol_properties(proto) 3424 * 3425 * Create a protocol specific property set. 3426 */ 3427 3428 sa_protocol_properties_t 3429 sa_create_protocol_properties(char *proto) 3430 { 3431 xmlNodePtr node; 3432 3433 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 3434 if (node != NULL) 3435 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 3436 return (node); 3437 } 3438 3439 /* 3440 * sa_get_share_resource(share, resource) 3441 * 3442 * Get the named resource from the share, if it exists. If resource is 3443 * NULL, get the first resource. 3444 */ 3445 3446 sa_resource_t 3447 sa_get_share_resource(sa_share_t share, char *resource) 3448 { 3449 xmlNodePtr node = NULL; 3450 xmlChar *name; 3451 3452 if (share != NULL) { 3453 for (node = ((xmlNodePtr)share)->children; node != NULL; 3454 node = node->next) { 3455 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) { 3456 if (resource == NULL) { 3457 /* 3458 * We are looking for the first 3459 * resource node and not a names 3460 * resource. 3461 */ 3462 break; 3463 } else { 3464 /* is it the correct share? */ 3465 name = xmlGetProp(node, 3466 (xmlChar *)"name"); 3467 if (name != NULL && 3468 xmlStrcasecmp(name, 3469 (xmlChar *)resource) == 0) { 3470 xmlFree(name); 3471 break; 3472 } 3473 xmlFree(name); 3474 } 3475 } 3476 } 3477 } 3478 return ((sa_resource_t)node); 3479 } 3480 3481 /* 3482 * sa_get_next_resource(resource) 3483 * Return the next share following the specified share 3484 * from the internal list of shares. Returns NULL if there 3485 * are no more shares. The list is relative to the same 3486 * group. 3487 */ 3488 sa_share_t 3489 sa_get_next_resource(sa_resource_t resource) 3490 { 3491 xmlNodePtr node = NULL; 3492 3493 if (resource != NULL) { 3494 for (node = ((xmlNodePtr)resource)->next; node != NULL; 3495 node = node->next) { 3496 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) 3497 break; 3498 } 3499 } 3500 return ((sa_share_t)node); 3501 } 3502 3503 /* 3504 * _sa_get_next_resource_index(share) 3505 * 3506 * get the next resource index number (one greater then current largest) 3507 */ 3508 3509 static int 3510 _sa_get_next_resource_index(sa_share_t share) 3511 { 3512 sa_resource_t resource; 3513 int index = 0; 3514 char *id; 3515 3516 for (resource = sa_get_share_resource(share, NULL); 3517 resource != NULL; 3518 resource = sa_get_next_resource(resource)) { 3519 id = get_node_attr((void *)resource, "id"); 3520 if (id != NULL) { 3521 int val; 3522 val = atoi(id); 3523 if (val > index) 3524 index = val; 3525 sa_free_attr_string(id); 3526 } 3527 } 3528 return (index + 1); 3529 } 3530 3531 3532 /* 3533 * sa_add_resource(share, resource, persist, &err) 3534 * 3535 * Adds a new resource name associated with share. The resource name 3536 * must be unique in the system and will be case insensitive (eventually). 3537 */ 3538 3539 sa_resource_t 3540 sa_add_resource(sa_share_t share, char *resource, int persist, int *error) 3541 { 3542 xmlNodePtr node; 3543 int err = SA_OK; 3544 sa_resource_t res; 3545 sa_group_t group; 3546 sa_handle_t handle; 3547 char istring[8]; /* just big enough for an integer value */ 3548 int index; 3549 3550 group = sa_get_parent_group(share); 3551 handle = sa_find_group_handle(group); 3552 res = sa_find_resource(handle, resource); 3553 if (res != NULL) { 3554 err = SA_DUPLICATE_NAME; 3555 res = NULL; 3556 } else { 3557 node = xmlNewChild((xmlNodePtr)share, NULL, 3558 (xmlChar *)"resource", NULL); 3559 if (node != NULL) { 3560 xmlSetProp(node, (xmlChar *)"name", 3561 (xmlChar *)resource); 3562 xmlSetProp(node, (xmlChar *)"type", persist ? 3563 (xmlChar *)"persist" : (xmlChar *)"transient"); 3564 if (persist != SA_SHARE_TRANSIENT) { 3565 index = _sa_get_next_resource_index(share); 3566 (void) snprintf(istring, sizeof (istring), "%d", 3567 index); 3568 xmlSetProp(node, (xmlChar *)"id", 3569 (xmlChar *)istring); 3570 if (!sa_group_is_zfs(group) && 3571 sa_is_persistent((sa_group_t)share)) { 3572 /* ZFS doesn't use resource names */ 3573 sa_handle_impl_t ihandle; 3574 ihandle = (sa_handle_impl_t) 3575 sa_find_group_handle( 3576 group); 3577 if (ihandle != NULL) 3578 err = sa_commit_share( 3579 ihandle->scfhandle, group, 3580 share); 3581 else 3582 err = SA_SYSTEM_ERR; 3583 } 3584 } 3585 } 3586 } 3587 if (error != NULL) 3588 *error = err; 3589 return ((sa_resource_t)node); 3590 } 3591 3592 /* 3593 * sa_remove_resource(resource) 3594 * 3595 * Remove the resource name from the share (and the system) 3596 */ 3597 3598 int 3599 sa_remove_resource(sa_resource_t resource) 3600 { 3601 sa_share_t share; 3602 sa_group_t group; 3603 char *type; 3604 int ret = SA_OK; 3605 int transient = 0; 3606 3607 share = sa_get_resource_parent(resource); 3608 type = sa_get_share_attr(share, "type"); 3609 group = sa_get_parent_group(share); 3610 3611 3612 if (type != NULL) { 3613 if (strcmp(type, "persist") != 0) 3614 transient = 1; 3615 sa_free_attr_string(type); 3616 } 3617 3618 /* Remove from the share */ 3619 xmlUnlinkNode((xmlNode *)resource); 3620 xmlFreeNode((xmlNode *)resource); 3621 3622 /* only do SMF action if permanent and not ZFS */ 3623 if (!transient && !sa_group_is_zfs(group)) { 3624 sa_handle_impl_t ihandle; 3625 ihandle = (sa_handle_impl_t)sa_find_group_handle(group); 3626 if (ihandle != NULL) 3627 ret = sa_commit_share(ihandle->scfhandle, group, share); 3628 else 3629 ret = SA_SYSTEM_ERR; 3630 } 3631 return (ret); 3632 } 3633 3634 /* 3635 * proto_resource_rename(handle, group, resource, newname) 3636 * 3637 * Helper function for sa_rename_resource that notifies the protocol 3638 * of a resource name change prior to a config repository update. 3639 */ 3640 static int 3641 proto_rename_resource(sa_handle_t handle, sa_group_t group, 3642 sa_resource_t resource, char *newname) 3643 { 3644 sa_optionset_t optionset; 3645 int ret = SA_OK; 3646 int err; 3647 3648 for (optionset = sa_get_optionset(group, NULL); 3649 optionset != NULL; 3650 optionset = sa_get_next_optionset(optionset)) { 3651 char *type; 3652 type = sa_get_optionset_attr(optionset, "type"); 3653 if (type != NULL) { 3654 err = sa_proto_rename_resource(handle, type, resource, 3655 newname); 3656 if (err != SA_OK) 3657 ret = err; 3658 sa_free_attr_string(type); 3659 } 3660 } 3661 return (ret); 3662 } 3663 3664 /* 3665 * sa_rename_resource(resource, newname) 3666 * 3667 * Rename the resource to the new name, if it is unique. 3668 */ 3669 3670 int 3671 sa_rename_resource(sa_resource_t resource, char *newname) 3672 { 3673 sa_share_t share; 3674 sa_group_t group = NULL; 3675 sa_resource_t target; 3676 int ret = SA_CONFIG_ERR; 3677 sa_handle_t handle = NULL; 3678 3679 share = sa_get_resource_parent(resource); 3680 if (share == NULL) 3681 return (ret); 3682 3683 group = sa_get_parent_group(share); 3684 if (group == NULL) 3685 return (ret); 3686 3687 handle = (sa_handle_impl_t)sa_find_group_handle(group); 3688 if (handle == NULL) 3689 return (ret); 3690 3691 target = sa_find_resource(handle, newname); 3692 if (target != NULL) { 3693 ret = SA_DUPLICATE_NAME; 3694 } else { 3695 /* 3696 * Everything appears to be valid at this 3697 * point. Change the name of the active share and then 3698 * update the share in the appropriate repository. 3699 */ 3700 ret = proto_rename_resource(handle, group, resource, newname); 3701 set_node_attr(resource, "name", newname); 3702 if (!sa_group_is_zfs(group) && 3703 sa_is_persistent((sa_group_t)share)) { 3704 sa_handle_impl_t ihandle = (sa_handle_impl_t)handle; 3705 ret = sa_commit_share(ihandle->scfhandle, group, 3706 share); 3707 } 3708 } 3709 return (ret); 3710 } 3711 3712 /* 3713 * sa_get_resource_attr(resource, tag) 3714 * 3715 * Get the named attribute of the resource. "name" and "id" are 3716 * currently defined. NULL if tag not defined. 3717 */ 3718 3719 char * 3720 sa_get_resource_attr(sa_resource_t resource, char *tag) 3721 { 3722 return (get_node_attr((void *)resource, tag)); 3723 } 3724 3725 /* 3726 * sa_set_resource_attr(resource, tag, value) 3727 * 3728 * Get the named attribute of the resource. "name" and "id" are 3729 * currently defined. NULL if tag not defined. Currently we don't do 3730 * much, but additional checking may be needed in the future. 3731 */ 3732 3733 int 3734 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value) 3735 { 3736 set_node_attr((void *)resource, tag, value); 3737 return (SA_OK); 3738 } 3739 3740 /* 3741 * sa_get_resource_parent(resource_t) 3742 * 3743 * Returns the share associated with the resource. 3744 */ 3745 3746 sa_share_t 3747 sa_get_resource_parent(sa_resource_t resource) 3748 { 3749 sa_share_t share = NULL; 3750 3751 if (resource != NULL) 3752 share = (sa_share_t)((xmlNodePtr)resource)->parent; 3753 return (share); 3754 } 3755 3756 /* 3757 * find_resource(group, name) 3758 * 3759 * Find the resource within the group. 3760 */ 3761 3762 static sa_resource_t 3763 find_resource(sa_group_t group, char *resname) 3764 { 3765 sa_share_t share; 3766 sa_resource_t resource = NULL; 3767 char *name; 3768 3769 /* Iterate over all the shares and resources in the group. */ 3770 for (share = sa_get_share(group, NULL); 3771 share != NULL && resource == NULL; 3772 share = sa_get_next_share(share)) { 3773 for (resource = sa_get_share_resource(share, NULL); 3774 resource != NULL; 3775 resource = sa_get_next_resource(resource)) { 3776 name = sa_get_resource_attr(resource, "name"); 3777 if (name != NULL && xmlStrcasecmp((xmlChar*)name, 3778 (xmlChar*)resname) == 0) { 3779 sa_free_attr_string(name); 3780 break; 3781 } 3782 if (name != NULL) { 3783 sa_free_attr_string(name); 3784 } 3785 } 3786 } 3787 return (resource); 3788 } 3789 3790 /* 3791 * sa_find_resource(name) 3792 * 3793 * Find the named resource in the system. 3794 */ 3795 3796 sa_resource_t 3797 sa_find_resource(sa_handle_t handle, char *name) 3798 { 3799 sa_group_t group; 3800 sa_group_t zgroup; 3801 sa_resource_t resource = NULL; 3802 3803 /* 3804 * Iterate over all groups and zfs subgroups and check for 3805 * resource name in them. 3806 */ 3807 for (group = sa_get_group(handle, NULL); group != NULL; 3808 group = sa_get_next_group(group)) { 3809 3810 if (is_zfs_group(group)) { 3811 for (zgroup = 3812 (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 3813 (xmlChar *)"group"); 3814 zgroup != NULL && resource == NULL; 3815 zgroup = sa_get_next_group(zgroup)) { 3816 resource = find_resource(zgroup, name); 3817 } 3818 } else { 3819 resource = find_resource(group, name); 3820 } 3821 if (resource != NULL) 3822 break; 3823 } 3824 return (resource); 3825 } 3826 3827 /* 3828 * sa_get_resource(group, resource) 3829 * 3830 * Search all the shares in the specified group for a share with a 3831 * resource name matching the one specified. 3832 * 3833 * In the future, it may be advantageous to allow group to be NULL and 3834 * search all groups but that isn't needed at present. 3835 */ 3836 3837 sa_resource_t 3838 sa_get_resource(sa_group_t group, char *resource) 3839 { 3840 sa_share_t share = NULL; 3841 sa_resource_t res = NULL; 3842 3843 if (resource != NULL) { 3844 for (share = sa_get_share(group, NULL); 3845 share != NULL && res == NULL; 3846 share = sa_get_next_share(share)) { 3847 res = sa_get_share_resource(share, resource); 3848 } 3849 } 3850 return (res); 3851 } 3852 3853 /* 3854 * sa_enable_resource, protocol) 3855 * Disable the specified share to the specified protocol. 3856 * If protocol is NULL, then all protocols. 3857 */ 3858 int 3859 sa_enable_resource(sa_resource_t resource, char *protocol) 3860 { 3861 int ret = SA_OK; 3862 char **protocols; 3863 int numproto; 3864 3865 if (protocol != NULL) { 3866 ret = sa_proto_share_resource(protocol, resource); 3867 } else { 3868 /* need to do all protocols */ 3869 if ((numproto = sa_get_protocols(&protocols)) >= 0) { 3870 int i, err; 3871 for (i = 0; i < numproto; i++) { 3872 err = sa_proto_share_resource( 3873 protocols[i], resource); 3874 if (err != SA_OK) 3875 ret = err; 3876 } 3877 free(protocols); 3878 } 3879 } 3880 if (ret == SA_OK) 3881 (void) sa_set_resource_attr(resource, "shared", NULL); 3882 3883 return (ret); 3884 } 3885 3886 /* 3887 * sa_disable_resource(resource, protocol) 3888 * 3889 * Disable the specified share for the specified protocol. If 3890 * protocol is NULL, then all protocols. If the underlying 3891 * protocol doesn't implement disable at the resource level, we 3892 * disable at the share level. 3893 */ 3894 int 3895 sa_disable_resource(sa_resource_t resource, char *protocol) 3896 { 3897 int ret = SA_OK; 3898 char **protocols; 3899 int numproto; 3900 3901 if (protocol != NULL) { 3902 ret = sa_proto_unshare_resource(protocol, resource); 3903 if (ret == SA_NOT_IMPLEMENTED) { 3904 sa_share_t parent; 3905 /* 3906 * The protocol doesn't implement unshare 3907 * resource. That implies that resource names are 3908 * simple aliases for this protocol so we need to 3909 * unshare the share. 3910 */ 3911 parent = sa_get_resource_parent(resource); 3912 if (parent != NULL) 3913 ret = sa_disable_share(parent, protocol); 3914 else 3915 ret = SA_CONFIG_ERR; 3916 } 3917 } else { 3918 /* need to do all protocols */ 3919 if ((numproto = sa_get_protocols(&protocols)) >= 0) { 3920 int i, err; 3921 for (i = 0; i < numproto; i++) { 3922 err = sa_proto_unshare_resource(protocols[i], 3923 resource); 3924 if (err == SA_NOT_SUPPORTED) { 3925 sa_share_t parent; 3926 parent = sa_get_resource_parent( 3927 resource); 3928 if (parent != NULL) 3929 err = sa_disable_share(parent, 3930 protocols[i]); 3931 else 3932 err = SA_CONFIG_ERR; 3933 } 3934 if (err != SA_OK) 3935 ret = err; 3936 } 3937 free(protocols); 3938 } 3939 } 3940 if (ret == SA_OK) 3941 (void) sa_set_resource_attr(resource, "shared", NULL); 3942 3943 return (ret); 3944 } 3945 3946 /* 3947 * sa_set_resource_description(resource, content) 3948 * 3949 * Set the description of share to content. 3950 */ 3951 3952 int 3953 sa_set_resource_description(sa_resource_t resource, char *content) 3954 { 3955 xmlNodePtr node; 3956 sa_group_t group; 3957 sa_share_t share; 3958 int ret = SA_OK; 3959 3960 for (node = ((xmlNodePtr)resource)->children; 3961 node != NULL; 3962 node = node->next) { 3963 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 3964 break; 3965 } 3966 } 3967 3968 /* no existing description but want to add */ 3969 if (node == NULL && content != NULL) { 3970 /* add a description */ 3971 node = _sa_set_share_description(resource, content); 3972 } else if (node != NULL && content != NULL) { 3973 /* update a description */ 3974 xmlNodeSetContent(node, (xmlChar *)content); 3975 } else if (node != NULL && content == NULL) { 3976 /* remove an existing description */ 3977 xmlUnlinkNode(node); 3978 xmlFreeNode(node); 3979 } 3980 share = sa_get_resource_parent(resource); 3981 group = sa_get_parent_group(share); 3982 if (group != NULL && sa_is_persistent(share)) { 3983 sa_handle_impl_t impl_handle; 3984 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 3985 if (impl_handle != NULL) 3986 ret = sa_commit_share(impl_handle->scfhandle, 3987 group, share); 3988 else 3989 ret = SA_SYSTEM_ERR; 3990 } 3991 return (ret); 3992 } 3993 3994 /* 3995 * sa_get_resource_description(share) 3996 * 3997 * Return the description text for the specified share if it 3998 * exists. NULL if no description exists. 3999 */ 4000 4001 char * 4002 sa_get_resource_description(sa_resource_t resource) 4003 { 4004 xmlChar *description = NULL; 4005 xmlNodePtr node; 4006 4007 for (node = ((xmlNodePtr)resource)->children; node != NULL; 4008 node = node->next) { 4009 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) 4010 break; 4011 } 4012 if (node != NULL) { 4013 description = xmlNodeGetContent(node); 4014 fixproblemchars((char *)description); 4015 } 4016 return ((char *)description); 4017 } 4018