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