1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Share control API 31 */ 32 #include <stdio.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <unistd.h> 39 #include <libxml/parser.h> 40 #include <libxml/tree.h> 41 #include "libshare.h" 42 #include "libshare_impl.h" 43 #include <libscf.h> 44 #include "scfutil.h" 45 #include <ctype.h> 46 #include <libintl.h> 47 #include <thread.h> 48 #include <synch.h> 49 50 #if _NOT_SMF 51 #define CONFIG_FILE "/var/tmp/share.cfg" 52 #define CONFIG_FILE_TMP "/var/tmp/share.cfg.tmp" 53 #endif 54 #define TSTAMP(tm) (uint64_t)(((uint64_t)tm.tv_sec << 32) | \ 55 (tm.tv_nsec & 0xffffffff)) 56 57 #define DFS_LOCK_FILE "/etc/dfs/fstypes" 58 59 /* 60 * internal data structures 61 */ 62 63 extern struct sa_proto_plugin *sap_proto_list; 64 65 /* current SMF/SVC repository handle */ 66 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *); 67 extern int gettransients(sa_handle_impl_t, xmlNodePtr *); 68 extern int sa_valid_property(void *, char *, sa_property_t); 69 extern char *sa_fstype(char *); 70 extern int sa_is_share(void *); 71 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ 72 extern int sa_group_is_zfs(sa_group_t); 73 extern int sa_path_is_zfs(char *); 74 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); 75 extern void update_legacy_config(sa_handle_t); 76 extern int issubdir(char *, char *); 77 extern void sa_zfs_init(sa_handle_impl_t); 78 extern void sa_zfs_fini(sa_handle_impl_t); 79 extern void sablocksigs(sigset_t *); 80 extern void saunblocksigs(sigset_t *); 81 82 /* 83 * Data structures for finding/managing the document root to access 84 * handle mapping. The list isn't expected to grow very large so a 85 * simple list is acceptable. The purpose is to provide a way to start 86 * with a group or share and find the library handle needed for 87 * various operations. 88 */ 89 mutex_t sa_global_lock; 90 struct doc2handle { 91 struct doc2handle *next; 92 xmlNodePtr root; 93 sa_handle_impl_t handle; 94 }; 95 96 static struct doc2handle *sa_global_handles = NULL; 97 98 /* helper functions */ 99 100 /* 101 * sa_errorstr(err) 102 * 103 * convert an error value to an error string 104 */ 105 106 char * 107 sa_errorstr(int err) 108 { 109 static char errstr[32]; 110 char *ret = NULL; 111 112 switch (err) { 113 case SA_OK: 114 ret = dgettext(TEXT_DOMAIN, "ok"); 115 break; 116 case SA_NO_SUCH_PATH: 117 ret = dgettext(TEXT_DOMAIN, "path doesn't exist"); 118 break; 119 case SA_NO_MEMORY: 120 ret = dgettext(TEXT_DOMAIN, "no memory"); 121 break; 122 case SA_DUPLICATE_NAME: 123 ret = dgettext(TEXT_DOMAIN, "name in use"); 124 break; 125 case SA_BAD_PATH: 126 ret = dgettext(TEXT_DOMAIN, "bad path"); 127 break; 128 case SA_NO_SUCH_GROUP: 129 ret = dgettext(TEXT_DOMAIN, "no such group"); 130 break; 131 case SA_CONFIG_ERR: 132 ret = dgettext(TEXT_DOMAIN, "configuration error"); 133 break; 134 case SA_SYSTEM_ERR: 135 ret = dgettext(TEXT_DOMAIN, "system error"); 136 break; 137 case SA_SYNTAX_ERR: 138 ret = dgettext(TEXT_DOMAIN, "syntax error"); 139 break; 140 case SA_NO_PERMISSION: 141 ret = dgettext(TEXT_DOMAIN, "no permission"); 142 break; 143 case SA_BUSY: 144 ret = dgettext(TEXT_DOMAIN, "busy"); 145 break; 146 case SA_NO_SUCH_PROP: 147 ret = dgettext(TEXT_DOMAIN, "no such property"); 148 break; 149 case SA_INVALID_NAME: 150 ret = dgettext(TEXT_DOMAIN, "invalid name"); 151 break; 152 case SA_INVALID_PROTOCOL: 153 ret = dgettext(TEXT_DOMAIN, "invalid protocol"); 154 break; 155 case SA_NOT_ALLOWED: 156 ret = dgettext(TEXT_DOMAIN, "operation not allowed"); 157 break; 158 case SA_BAD_VALUE: 159 ret = dgettext(TEXT_DOMAIN, "bad property value"); 160 break; 161 case SA_INVALID_SECURITY: 162 ret = dgettext(TEXT_DOMAIN, "invalid security type"); 163 break; 164 case SA_NO_SUCH_SECURITY: 165 ret = dgettext(TEXT_DOMAIN, "security type not found"); 166 break; 167 case SA_VALUE_CONFLICT: 168 ret = dgettext(TEXT_DOMAIN, "property value conflict"); 169 break; 170 case SA_NOT_IMPLEMENTED: 171 ret = dgettext(TEXT_DOMAIN, "not implemented"); 172 break; 173 case SA_INVALID_PATH: 174 ret = dgettext(TEXT_DOMAIN, "invalid path"); 175 break; 176 case SA_NOT_SUPPORTED: 177 ret = dgettext(TEXT_DOMAIN, "operation not supported"); 178 break; 179 case SA_PROP_SHARE_ONLY: 180 ret = dgettext(TEXT_DOMAIN, "property not valid for group"); 181 break; 182 case SA_NOT_SHARED: 183 ret = dgettext(TEXT_DOMAIN, "not shared"); 184 break; 185 default: 186 (void) snprintf(errstr, sizeof (errstr), 187 dgettext(TEXT_DOMAIN, "unknown %d"), err); 188 ret = errstr; 189 } 190 return (ret); 191 } 192 193 /* 194 * Document root to active handle mapping functions. These are only 195 * used internally. A mutex is used to prevent access while the list 196 * is changing. In general, the list will be relatively short - one 197 * item per thread that has called sa_init(). 198 */ 199 200 sa_handle_impl_t 201 get_handle_for_root(xmlNodePtr root) 202 { 203 struct doc2handle *item; 204 205 (void) mutex_lock(&sa_global_lock); 206 for (item = sa_global_handles; item != NULL; item = item->next) { 207 if (item->root == root) 208 break; 209 } 210 (void) mutex_unlock(&sa_global_lock); 211 if (item != NULL) 212 return (item->handle); 213 return (NULL); 214 } 215 216 static int 217 add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle) 218 { 219 struct doc2handle *item; 220 int ret = SA_NO_MEMORY; 221 222 item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1); 223 if (item != NULL) { 224 item->root = root; 225 item->handle = handle; 226 (void) mutex_lock(&sa_global_lock); 227 item->next = sa_global_handles; 228 sa_global_handles = item; 229 (void) mutex_unlock(&sa_global_lock); 230 ret = SA_OK; 231 } 232 return (ret); 233 } 234 235 /* 236 * remove_handle_for_root(root) 237 * 238 * Walks the list of handles and removes the one for this "root" from 239 * the list. It is up to the caller to free the data. 240 */ 241 242 static void 243 remove_handle_for_root(xmlNodePtr root) 244 { 245 struct doc2handle *item, *prev; 246 247 (void) mutex_lock(&sa_global_lock); 248 for (prev = NULL, item = sa_global_handles; item != NULL; 249 item = item->next) { 250 if (item->root == root) { 251 if (prev == NULL) { 252 /* first in the list */ 253 sa_global_handles = sa_global_handles->next; 254 } else { 255 prev->next = item->next; 256 } 257 /* Item is out of the list so free the list structure */ 258 free(item); 259 break; 260 } 261 prev = item; 262 } 263 (void) mutex_unlock(&sa_global_lock); 264 } 265 266 /* 267 * sa_find_group_handle(sa_group_t group) 268 * 269 * Find the sa_handle_t for the configuration associated with this 270 * group. 271 */ 272 sa_handle_t 273 sa_find_group_handle(sa_group_t group) 274 { 275 xmlNodePtr node = (xmlNodePtr)group; 276 sa_handle_t handle; 277 278 while (node != NULL) { 279 if (strcmp((char *)(node->name), "sharecfg") == 0) { 280 /* have the root so get the handle */ 281 handle = (sa_handle_t)get_handle_for_root(node); 282 return (handle); 283 } 284 node = node->parent; 285 } 286 return (NULL); 287 } 288 289 /* 290 * set_legacy_timestamp(root, path, timevalue) 291 * 292 * add the current timestamp value to the configuration for use in 293 * determining when to update the legacy files. For SMF, this 294 * property is kept in default/operation/legacy_timestamp 295 */ 296 297 static void 298 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval) 299 { 300 xmlNodePtr node; 301 xmlChar *lpath = NULL; 302 sa_handle_impl_t handle; 303 304 /* Have to have a handle or else we weren't initialized. */ 305 handle = get_handle_for_root(root); 306 if (handle == NULL) 307 return; 308 309 for (node = root->xmlChildrenNode; node != NULL; 310 node = node->next) { 311 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) { 312 /* a possible legacy node for this path */ 313 lpath = xmlGetProp(node, (xmlChar *)"path"); 314 if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) { 315 xmlFree(lpath); 316 break; 317 } 318 if (lpath != NULL) 319 xmlFree(lpath); 320 } 321 } 322 if (node == NULL) { 323 /* need to create the first legacy timestamp node */ 324 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL); 325 } 326 if (node != NULL) { 327 char tstring[32]; 328 int ret; 329 330 (void) snprintf(tstring, sizeof (tstring), "%lld", tval); 331 xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring); 332 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path); 333 /* now commit to SMF */ 334 ret = sa_get_instance(handle->scfhandle, "default"); 335 if (ret == SA_OK) { 336 ret = sa_start_transaction(handle->scfhandle, "operation"); 337 if (ret == SA_OK) { 338 ret = sa_set_property(handle->scfhandle, "legacy-timestamp", 339 tstring); 340 if (ret == SA_OK) { 341 (void) sa_end_transaction(handle->scfhandle); 342 } else { 343 sa_abort_transaction(handle->scfhandle); 344 } 345 } 346 } 347 } 348 } 349 350 /* 351 * is_shared(share) 352 * 353 * determine if the specified share is currently shared or not. 354 */ 355 static int 356 is_shared(sa_share_t share) 357 { 358 char *shared; 359 int result = 0; /* assume not */ 360 361 shared = sa_get_share_attr(share, "shared"); 362 if (shared != NULL) { 363 if (strcmp(shared, "true") == 0) 364 result = 1; 365 sa_free_attr_string(shared); 366 } 367 return (result); 368 } 369 370 /* 371 * checksubdirgroup(group, newpath, strictness) 372 * 373 * check all the specified newpath against all the paths in the 374 * group. This is a helper function for checksubdir to make it easier 375 * to also check ZFS subgroups. 376 * The strictness values mean: 377 * SA_CHECK_NORMAL == only check newpath against shares that are active 378 * SA_CHECK_STRICT == check newpath against both active shares and those 379 * stored in the repository 380 */ 381 static int 382 checksubdirgroup(sa_group_t group, char *newpath, int strictness) 383 { 384 sa_share_t share; 385 char *path; 386 int issub = SA_OK; 387 388 for (share = sa_get_share(group, NULL); share != NULL; 389 share = sa_get_next_share(share)) { 390 /* 391 * The original behavior of share never checked 392 * against the permanent configuration 393 * (/etc/dfs/dfstab). PIT has a number of cases where 394 * it depends on this older behavior even though it 395 * could be considered incorrect. We may tighten this 396 * up in the future. 397 */ 398 if (strictness == SA_CHECK_NORMAL && !is_shared(share)) 399 continue; 400 401 path = sa_get_share_attr(share, "path"); 402 /* 403 * If path is NULL, then a share is in the process of 404 * construction or someone has modified the property 405 * group inappropriately. It should be 406 * ignored. issubdir() comes from the original share 407 * implementation and does the difficult part of 408 * checking subdirectories. 409 */ 410 if (path == NULL) 411 continue; 412 if (newpath != NULL && 413 (strcmp(path, newpath) == 0 || issubdir(newpath, path) || 414 issubdir(path, newpath))) { 415 sa_free_attr_string(path); 416 path = NULL; 417 issub = SA_INVALID_PATH; 418 break; 419 } 420 sa_free_attr_string(path); 421 path = NULL; 422 } 423 return (issub); 424 } 425 426 /* 427 * checksubdir(newpath, strictness) 428 * 429 * checksubdir determines if the specified path (newpath) is a 430 * subdirectory of another share. It calls checksubdirgroup() to do 431 * the complicated work. The strictness parameter determines how 432 * strict a check to make against the path. The strictness values 433 * mean: SA_CHECK_NORMAL == only check newpath against shares that are 434 * active SA_CHECK_STRICT == check newpath against both active shares 435 * and those * stored in the repository 436 */ 437 static int 438 checksubdir(sa_handle_t handle, char *newpath, int strictness) 439 { 440 sa_group_t group; 441 int issub; 442 char *path = NULL; 443 444 for (issub = 0, group = sa_get_group(handle, NULL); 445 group != NULL && !issub; 446 group = sa_get_next_group(group)) { 447 if (sa_group_is_zfs(group)) { 448 sa_group_t subgroup; 449 for (subgroup = sa_get_sub_group(group); 450 subgroup != NULL && !issub; 451 subgroup = sa_get_next_group(subgroup)) 452 issub = checksubdirgroup(subgroup, newpath, strictness); 453 } else { 454 issub = checksubdirgroup(group, newpath, strictness); 455 } 456 } 457 if (path != NULL) 458 sa_free_attr_string(path); 459 return (issub); 460 } 461 462 /* 463 * validpath(path, strictness) 464 * determine if the provided path is valid for a share. It shouldn't 465 * be a sub-dir of an already shared path or the parent directory of a 466 * share path. 467 */ 468 static int 469 validpath(sa_handle_t handle, char *path, int strictness) 470 { 471 int error = SA_OK; 472 struct stat st; 473 sa_share_t share; 474 char *fstype; 475 476 if (*path != '/') { 477 return (SA_BAD_PATH); 478 } 479 if (stat(path, &st) < 0) { 480 error = SA_NO_SUCH_PATH; 481 } else { 482 share = sa_find_share(handle, path); 483 if (share != NULL) { 484 error = SA_DUPLICATE_NAME; 485 } 486 if (error == SA_OK) { 487 /* 488 * check for special case with file system that might 489 * have restrictions. For now, ZFS is the only case 490 * since it has its own idea of how to configure 491 * shares. We do this before subdir checking since 492 * things like ZFS will do that for us. This should 493 * also be done via plugin interface. 494 */ 495 fstype = sa_fstype(path); 496 if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 497 if (sa_zfs_is_shared(handle, path)) 498 error = SA_INVALID_NAME; 499 } 500 if (fstype != NULL) 501 sa_free_fstype(fstype); 502 } 503 if (error == SA_OK) { 504 error = checksubdir(handle, path, strictness); 505 } 506 } 507 return (error); 508 } 509 510 /* 511 * check to see if group/share is persistent. 512 */ 513 static int 514 is_persistent(sa_group_t group) 515 { 516 char *type; 517 int persist = 1; 518 519 type = sa_get_group_attr(group, "type"); 520 if (type != NULL && strcmp(type, "transient") == 0) 521 persist = 0; 522 if (type != NULL) 523 sa_free_attr_string(type); 524 return (persist); 525 } 526 527 /* 528 * sa_valid_group_name(name) 529 * 530 * check that the "name" contains only valid characters and otherwise 531 * fits the required naming conventions. Valid names must start with 532 * an alphabetic and the remainder may consist of only alphanumeric 533 * plus the '-' and '_' characters. This name limitation comes from 534 * inherent limitations in SMF. 535 */ 536 537 int 538 sa_valid_group_name(char *name) 539 { 540 int ret = 1; 541 ssize_t len; 542 543 if (name != NULL && isalpha(*name)) { 544 char c; 545 len = strlen(name); 546 if (len < (scf_max_name_len - sizeof ("group:"))) { 547 for (c = *name++; c != '\0' && ret != 0; c = *name++) { 548 if (!isalnum(c) && c != '-' && c != '_') 549 ret = 0; 550 } 551 } else { 552 ret = 0; 553 } 554 } else { 555 ret = 0; 556 } 557 return (ret); 558 } 559 560 561 /* 562 * is_zfs_group(group) 563 * Determine if the specified group is a ZFS sharenfs group 564 */ 565 static int 566 is_zfs_group(sa_group_t group) 567 { 568 int ret = 0; 569 xmlNodePtr parent; 570 xmlChar *zfs; 571 572 if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) { 573 parent = (xmlNodePtr)sa_get_parent_group(group); 574 } else { 575 parent = (xmlNodePtr)group; 576 } 577 zfs = xmlGetProp(parent, (xmlChar *)"zfs"); 578 if (zfs != NULL) { 579 xmlFree(zfs); 580 ret = 1; 581 } 582 return (ret); 583 } 584 585 /* 586 * sa_optionset_name(optionset, oname, len, id) 587 * return the SMF name for the optionset. If id is not NULL, it 588 * will have the GUID value for a share and should be used 589 * instead of the keyword "optionset" which is used for 590 * groups. If the optionset doesn't have a protocol type 591 * associated with it, "default" is used. This shouldn't happen 592 * at this point but may be desirable in the future if there are 593 * protocol independent properties added. The name is returned in 594 * oname. 595 */ 596 597 static int 598 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) 599 { 600 char *proto; 601 602 if (id == NULL) 603 id = "optionset"; 604 605 proto = sa_get_optionset_attr(optionset, "type"); 606 len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default"); 607 608 if (proto != NULL) 609 sa_free_attr_string(proto); 610 return (len); 611 } 612 613 /* 614 * sa_security_name(optionset, oname, len, id) 615 * 616 * return the SMF name for the security. If id is not NULL, it will 617 * have the GUID value for a share and should be used instead of the 618 * keyword "optionset" which is used for groups. If the optionset 619 * doesn't have a protocol type associated with it, "default" is 620 * used. This shouldn't happen at this point but may be desirable in 621 * the future if there are protocol independent properties added. The 622 * name is returned in oname. The security type is also encoded into 623 * the name. In the future, this wil *be handled a bit differently. 624 */ 625 626 static int 627 sa_security_name(sa_security_t security, char *oname, size_t len, char *id) 628 { 629 char *proto; 630 char *sectype; 631 632 if (id == NULL) 633 id = "optionset"; 634 635 proto = sa_get_security_attr(security, "type"); 636 sectype = sa_get_security_attr(security, "sectype"); 637 len = snprintf(oname, len, "%s_%s_%s", id, 638 proto ? proto : "default", 639 sectype ? sectype : "default"); 640 if (proto != NULL) 641 sa_free_attr_string(proto); 642 if (sectype != NULL) 643 sa_free_attr_string(sectype); 644 return (len); 645 } 646 647 /* 648 * sa_init(init_service) 649 * Initialize the API 650 * find all the shared objects 651 * init the tables with all objects 652 * read in the current configuration 653 */ 654 655 sa_handle_t 656 sa_init(int init_service) 657 { 658 struct stat st; 659 int legacy = 0; 660 uint64_t tval = 0; 661 int lockfd; 662 sigset_t old; 663 int updatelegacy = B_FALSE; 664 scf_simple_prop_t *prop; 665 sa_handle_impl_t handle; 666 int err; 667 668 handle = calloc(sizeof (struct sa_handle_impl), 1); 669 670 if (handle != NULL) { 671 /* get protocol specific structures */ 672 (void) proto_plugin_init(); 673 if (init_service & SA_INIT_SHARE_API) { 674 /* 675 * initialize access into libzfs. We use this when 676 * collecting info about ZFS datasets and shares. 677 */ 678 sa_zfs_init(handle); 679 /* 680 * since we want to use SMF, initialize an svc handle 681 * and find out what is there. 682 */ 683 handle->scfhandle = sa_scf_init(handle); 684 if (handle->scfhandle != NULL) { 685 /* 686 * Need to lock the extraction of the 687 * configuration if the dfstab file has 688 * changed. Lock everything now and release if 689 * not needed. Use a file that isn't being 690 * manipulated by other parts of the system in 691 * order to not interfere with locking. Using 692 * dfstab doesn't work. 693 */ 694 sablocksigs(&old); 695 lockfd = open(DFS_LOCK_FILE, O_RDWR); 696 if (lockfd >= 0) { 697 extern int errno; 698 errno = 0; 699 (void) lockf(lockfd, F_LOCK, 0); 700 /* 701 * Check whether we are going to need to merge 702 * any dfstab changes. This is done by 703 * comparing the value of legacy-timestamp 704 * with the current st_ctim of the file. If 705 * they are different, an update is needed and 706 * the file must remain locked until the merge 707 * is done in order to prevent multiple 708 * startups from changing the SMF repository 709 * at the same time. The first to get the 710 * lock will make any changes before the 711 * others can read the repository. 712 */ 713 prop = scf_simple_prop_get(handle->scfhandle->handle, 714 (const char *) 715 SA_SVC_FMRI_BASE ":default", 716 "operation", 717 "legacy-timestamp"); 718 if (prop != NULL) { 719 char *i64; 720 i64 = scf_simple_prop_next_astring(prop); 721 if (i64 != NULL) { 722 tval = strtoull(i64, NULL, 0); 723 } 724 if (stat(SA_LEGACY_DFSTAB, &st) >= 0 && 725 tval != TSTAMP(st.st_ctim)) { 726 updatelegacy = B_TRUE; 727 } 728 } else { 729 /* We haven't set the timestamp before so do it. */ 730 updatelegacy = B_TRUE; 731 } 732 } 733 if (updatelegacy == B_FALSE) { 734 /* Don't need the lock anymore */ 735 (void) lockf(lockfd, F_ULOCK, 0); 736 (void) close(lockfd); 737 } 738 739 /* 740 * It is essential that the document tree and 741 * the internal list of roots to handles be 742 * setup before anything that might try to 743 * create a new object is called. The document 744 * tree is the combination of handle->doc and 745 * handle->tree. This allows searches, 746 * etc. when all you have is an object in the 747 * tree. 748 */ 749 handle->doc = xmlNewDoc((xmlChar *)"1.0"); 750 handle->tree = xmlNewNode(NULL, (xmlChar *)"sharecfg"); 751 if (handle->doc != NULL && handle->tree != NULL) { 752 xmlDocSetRootElement(handle->doc, handle->tree); 753 err = add_handle_for_root(handle->tree, handle); 754 if (err == SA_OK) 755 err = sa_get_config(handle->scfhandle, 756 handle->tree, handle); 757 } else { 758 if (handle->doc != NULL) 759 xmlFreeDoc(handle->doc); 760 if (handle->tree != NULL) 761 xmlFreeNode(handle->tree); 762 err = SA_NO_MEMORY; 763 } 764 765 saunblocksigs(&old); 766 767 if (err != SA_OK) { 768 /* 769 * If we couldn't add the tree handle 770 * to the list, then things are going 771 * to fail badly. Might as well undo 772 * everything now and fail the 773 * sa_init(). 774 */ 775 sa_fini(handle); 776 return (NULL); 777 } 778 779 if (tval == 0) { 780 /* first time so make sure default is setup */ 781 sa_group_t defgrp; 782 sa_optionset_t opt; 783 defgrp = sa_get_group(handle, "default"); 784 if (defgrp != NULL) { 785 opt = sa_get_optionset(defgrp, NULL); 786 if (opt == NULL) 787 /* NFS is the default for default */ 788 opt = sa_create_optionset(defgrp, "nfs"); 789 } 790 } 791 792 if (updatelegacy == B_TRUE) { 793 sablocksigs(&old); 794 getlegacyconfig((sa_handle_t)handle, 795 SA_LEGACY_DFSTAB, &handle->tree); 796 if (stat(SA_LEGACY_DFSTAB, &st) >= 0) 797 set_legacy_timestamp(handle->tree, 798 SA_LEGACY_DFSTAB, 799 TSTAMP(st.st_ctim)); 800 saunblocksigs(&old); 801 /* Safe to unlock now to allow others to run */ 802 (void) lockf(lockfd, F_ULOCK, 0); 803 (void) close(lockfd); 804 } 805 legacy |= sa_get_zfs_shares(handle, "zfs"); 806 legacy |= gettransients(handle, &handle->tree); 807 } 808 } 809 } 810 return ((sa_handle_t)handle); 811 } 812 813 /* 814 * sa_fini(handle) 815 * Uninitialize the API structures including the configuration 816 * data structures and ZFS related data. 817 */ 818 819 void 820 sa_fini(sa_handle_t handle) 821 { 822 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 823 824 if (impl_handle != NULL) { 825 /* 826 * Free the config trees and any other data structures 827 * used in the handle. 828 */ 829 if (impl_handle->doc != NULL) 830 xmlFreeDoc(impl_handle->doc); 831 sa_scf_fini(impl_handle->scfhandle); 832 sa_zfs_fini(impl_handle); 833 834 /* Remove and free the entry in the global list. */ 835 remove_handle_for_root(impl_handle->tree); 836 837 /* Make sure we free the handle */ 838 free(impl_handle); 839 840 /* 841 * If this was the last handle to release, unload the 842 * plugins that were loaded. 843 */ 844 if (sa_global_handles == NULL) 845 (void) proto_plugin_fini(); 846 847 } 848 } 849 850 /* 851 * sa_get_protocols(char **protocol) 852 * Get array of protocols that are supported 853 * Returns pointer to an allocated and NULL terminated 854 * array of strings. Caller must free. 855 * This really should be determined dynamically. 856 * If there aren't any defined, return -1. 857 * Use free() to return memory. 858 */ 859 860 int 861 sa_get_protocols(char ***protocols) 862 { 863 int numproto = -1; 864 865 if (protocols != NULL) { 866 struct sa_proto_plugin *plug; 867 for (numproto = 0, plug = sap_proto_list; plug != NULL; 868 plug = plug->plugin_next) { 869 numproto++; 870 } 871 872 *protocols = calloc(numproto + 1, sizeof (char *)); 873 if (*protocols != NULL) { 874 int ret = 0; 875 for (plug = sap_proto_list; plug != NULL; 876 plug = plug->plugin_next) { 877 /* faking for now */ 878 (*protocols)[ret++] = plug->plugin_ops->sa_protocol; 879 } 880 } else { 881 numproto = -1; 882 } 883 } 884 return (numproto); 885 } 886 887 /* 888 * find_group_by_name(node, group) 889 * 890 * search the XML document subtree specified by node to find the group 891 * specified by group. Searching subtree allows subgroups to be 892 * searched for. 893 */ 894 895 static xmlNodePtr 896 find_group_by_name(xmlNodePtr node, xmlChar *group) 897 { 898 xmlChar *name = NULL; 899 900 for (node = node->xmlChildrenNode; node != NULL; 901 node = node->next) { 902 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) { 903 /* if no groupname, return the first found */ 904 if (group == NULL) 905 break; 906 name = xmlGetProp(node, (xmlChar *)"name"); 907 if (name != NULL && 908 xmlStrcmp(name, group) == 0) { 909 break; 910 } 911 if (name != NULL) { 912 xmlFree(name); 913 name = NULL; 914 } 915 } 916 } 917 if (name != NULL) 918 xmlFree(name); 919 return (node); 920 } 921 922 /* 923 * sa_get_group(groupname) 924 * Return the "group" specified. If groupname is NULL, 925 * return the first group of the list of groups. 926 */ 927 sa_group_t 928 sa_get_group(sa_handle_t handle, char *groupname) 929 { 930 xmlNodePtr node = NULL; 931 char *subgroup = NULL; 932 char *group = NULL; 933 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 934 935 if (impl_handle != NULL && impl_handle->tree != NULL) { 936 if (groupname != NULL) { 937 group = strdup(groupname); 938 subgroup = strchr(group, '/'); 939 if (subgroup != NULL) 940 *subgroup++ = '\0'; 941 } 942 node = find_group_by_name(impl_handle->tree, (xmlChar *)group); 943 /* if a subgroup, find it before returning */ 944 if (subgroup != NULL && node != NULL) { 945 node = find_group_by_name(node, (xmlChar *)subgroup); 946 } 947 } 948 if (node != NULL && (char *)group != NULL) 949 (void) sa_get_instance(impl_handle->scfhandle, (char *)group); 950 if (group != NULL) 951 free(group); 952 return ((sa_group_t)(node)); 953 } 954 955 /* 956 * sa_get_next_group(group) 957 * Return the "next" group after the specified group from 958 * the internal group list. NULL if there are no more. 959 */ 960 sa_group_t 961 sa_get_next_group(sa_group_t group) 962 { 963 xmlNodePtr ngroup = NULL; 964 if (group != NULL) { 965 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL; 966 ngroup = ngroup->next) { 967 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0) 968 break; 969 } 970 } 971 return ((sa_group_t)ngroup); 972 } 973 974 /* 975 * sa_get_share(group, sharepath) 976 * Return the share object for the share specified. The share 977 * must be in the specified group. Return NULL if not found. 978 */ 979 sa_share_t 980 sa_get_share(sa_group_t group, char *sharepath) 981 { 982 xmlNodePtr node = NULL; 983 xmlChar *path; 984 985 /* 986 * For future scalability, this should end up building a cache 987 * since it will get called regularly by the mountd and info 988 * services. 989 */ 990 if (group != NULL) { 991 for (node = ((xmlNodePtr)group)->children; node != NULL; 992 node = node->next) { 993 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 994 if (sharepath == NULL) { 995 break; 996 } else { 997 /* is it the correct share? */ 998 path = xmlGetProp(node, (xmlChar *)"path"); 999 if (path != NULL && 1000 xmlStrcmp(path, (xmlChar *)sharepath) == 0) { 1001 xmlFree(path); 1002 break; 1003 } 1004 xmlFree(path); 1005 } 1006 } 1007 } 1008 } 1009 return ((sa_share_t)node); 1010 } 1011 1012 /* 1013 * sa_get_next_share(share) 1014 * Return the next share following the specified share 1015 * from the internal list of shares. Returns NULL if there 1016 * are no more shares. The list is relative to the same 1017 * group. 1018 */ 1019 sa_share_t 1020 sa_get_next_share(sa_share_t share) 1021 { 1022 xmlNodePtr node = NULL; 1023 1024 if (share != NULL) { 1025 for (node = ((xmlNodePtr)share)->next; node != NULL; 1026 node = node->next) { 1027 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1028 break; 1029 } 1030 } 1031 } 1032 return ((sa_share_t)node); 1033 } 1034 1035 /* 1036 * _sa_get_child_node(node, type) 1037 * 1038 * find the child node of the specified node that has "type". This is 1039 * used to implement several internal functions. 1040 */ 1041 1042 static xmlNodePtr 1043 _sa_get_child_node(xmlNodePtr node, xmlChar *type) 1044 { 1045 xmlNodePtr child; 1046 for (child = node->xmlChildrenNode; child != NULL; 1047 child = child->next) 1048 if (xmlStrcmp(child->name, type) == 0) 1049 return (child); 1050 return ((xmlNodePtr)NULL); 1051 } 1052 1053 /* 1054 * find_share(group, path) 1055 * 1056 * Search all the shares in the specified group for one that has the 1057 * specified path. 1058 */ 1059 1060 static sa_share_t 1061 find_share(sa_group_t group, char *sharepath) 1062 { 1063 sa_share_t share; 1064 char *path; 1065 1066 for (share = sa_get_share(group, NULL); share != NULL; 1067 share = sa_get_next_share(share)) { 1068 path = sa_get_share_attr(share, "path"); 1069 if (path != NULL && strcmp(path, sharepath) == 0) { 1070 sa_free_attr_string(path); 1071 break; 1072 } 1073 if (path != NULL) 1074 sa_free_attr_string(path); 1075 } 1076 return (share); 1077 } 1078 1079 /* 1080 * sa_get_sub_group(group) 1081 * 1082 * Get the first sub-group of group. The sa_get_next_group() function 1083 * can be used to get the rest. This is currently only used for ZFS 1084 * sub-groups but could be used to implement a more general mechanism. 1085 */ 1086 1087 sa_group_t 1088 sa_get_sub_group(sa_group_t group) 1089 { 1090 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1091 (xmlChar *)"group")); 1092 } 1093 1094 /* 1095 * sa_find_share(sharepath) 1096 * Finds a share regardless of group. In the future, this 1097 * function should utilize a cache and hash table of some kind. 1098 * The current assumption is that a path will only be shared 1099 * once. In the future, this may change as implementation of 1100 * resource names comes into being. 1101 */ 1102 sa_share_t 1103 sa_find_share(sa_handle_t handle, char *sharepath) 1104 { 1105 sa_group_t group; 1106 sa_group_t zgroup; 1107 sa_share_t share = NULL; 1108 int done = 0; 1109 1110 for (group = sa_get_group(handle, NULL); group != NULL && !done; 1111 group = sa_get_next_group(group)) { 1112 if (is_zfs_group(group)) { 1113 for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1114 (xmlChar *)"group"); 1115 zgroup != NULL; zgroup = sa_get_next_group(zgroup)) { 1116 share = find_share(zgroup, sharepath); 1117 if (share != NULL) 1118 break; 1119 } 1120 } else { 1121 share = find_share(group, sharepath); 1122 } 1123 if (share != NULL) 1124 break; 1125 } 1126 return (share); 1127 } 1128 1129 /* 1130 * sa_check_path(group, path, strictness) 1131 * 1132 * check that path is a valid path relative to the group. Currently, 1133 * we are ignoring the group and checking only the NFS rules. Later, 1134 * we may want to use the group to then check against the protocols 1135 * enabled on the group. The strictness values mean: 1136 * SA_CHECK_NORMAL == only check newpath against shares that are active 1137 * SA_CHECK_STRICT == check newpath against both active shares and those 1138 * stored in the repository 1139 */ 1140 1141 int 1142 sa_check_path(sa_group_t group, char *path, int strictness) 1143 { 1144 sa_handle_t handle; 1145 1146 handle = sa_find_group_handle(group); 1147 return (validpath(handle, path, strictness)); 1148 } 1149 1150 /* 1151 * _sa_add_share(group, sharepath, persist, *error) 1152 * 1153 * common code for all types of add_share. sa_add_share() is the 1154 * public API, we also need to be able to do this when parsing legacy 1155 * files and construction of the internal configuration while 1156 * extracting config info from SMF. 1157 */ 1158 1159 sa_share_t 1160 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1161 { 1162 xmlNodePtr node = NULL; 1163 int err; 1164 1165 err = SA_OK; /* assume success */ 1166 1167 node = xmlNewChild((xmlNodePtr)group, NULL, 1168 (xmlChar *)"share", NULL); 1169 if (node != NULL) { 1170 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); 1171 xmlSetProp(node, (xmlChar *)"type", persist ? 1172 (xmlChar *)"persist" : (xmlChar *)"transient"); 1173 if (persist != SA_SHARE_TRANSIENT) { 1174 /* 1175 * persistent shares come in two flavors: SMF and 1176 * ZFS. Sort this one out based on target group and 1177 * path type. Currently, only NFS is supported in the 1178 * ZFS group and it is always on. 1179 */ 1180 if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) { 1181 err = sa_zfs_set_sharenfs(group, sharepath, 1); 1182 } else { 1183 sa_handle_impl_t impl_handle; 1184 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1185 if (impl_handle != NULL) 1186 err = sa_commit_share(impl_handle->scfhandle, group, 1187 (sa_share_t)node); 1188 else 1189 err = SA_SYSTEM_ERR; 1190 } 1191 } 1192 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { 1193 /* called by the dfstab parser so could be a show */ 1194 err = SA_OK; 1195 } 1196 if (err != SA_OK) { 1197 /* 1198 * we couldn't commit to the repository so undo 1199 * our internal state to reflect reality. 1200 */ 1201 xmlUnlinkNode(node); 1202 xmlFreeNode(node); 1203 node = NULL; 1204 } 1205 } else { 1206 err = SA_NO_MEMORY; 1207 } 1208 if (error != NULL) 1209 *error = err; 1210 return (node); 1211 } 1212 1213 /* 1214 * sa_add_share(group, sharepath, persist, *error) 1215 * 1216 * Add a new share object to the specified group. The share will 1217 * have the specified sharepath and will only be constructed if 1218 * it is a valid path to be shared. NULL is returned on error 1219 * and a detailed error value will be returned via the error 1220 * pointer. 1221 */ 1222 sa_share_t 1223 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1224 { 1225 xmlNodePtr node = NULL; 1226 sa_share_t dup; 1227 int strictness = SA_CHECK_NORMAL; 1228 sa_handle_t handle; 1229 1230 /* 1231 * If the share is to be permanent, use strict checking so a 1232 * bad config doesn't get created. Transient shares only need 1233 * to check against the currently active 1234 * shares. SA_SHARE_PARSER is a modifier used internally to 1235 * indicate that we are being called by the dfstab parser and 1236 * that we need strict checking in all cases. Normally persist 1237 * is in integer value but SA_SHARE_PARSER may be or'd into 1238 * it as an override. 1239 */ 1240 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT) 1241 strictness = SA_CHECK_STRICT; 1242 1243 handle = sa_find_group_handle(group); 1244 1245 if ((dup = sa_find_share(handle, sharepath)) == NULL && 1246 (*error = sa_check_path(group, sharepath, strictness)) == 1247 SA_OK) { 1248 node = _sa_add_share(group, sharepath, persist, error); 1249 } 1250 if (dup != NULL) 1251 *error = SA_DUPLICATE_NAME; 1252 1253 return ((sa_share_t)node); 1254 } 1255 1256 /* 1257 * sa_enable_share(share, protocol) 1258 * Enable the specified share to the specified protocol. 1259 * If protocol is NULL, then all protocols. 1260 */ 1261 int 1262 sa_enable_share(sa_share_t share, char *protocol) 1263 { 1264 char *sharepath; 1265 struct stat st; 1266 int err = 0; 1267 1268 sharepath = sa_get_share_attr(share, "path"); 1269 if (stat(sharepath, &st) < 0) { 1270 err = SA_NO_SUCH_PATH; 1271 } else { 1272 /* tell the server about the share */ 1273 if (protocol != NULL) { 1274 /* lookup protocol specific handler */ 1275 err = sa_proto_share(protocol, share); 1276 if (err == SA_OK) 1277 (void) sa_set_share_attr(share, "shared", "true"); 1278 } else { 1279 /* tell all protocols */ 1280 err = sa_proto_share("nfs", share); /* only NFS for now */ 1281 (void) sa_set_share_attr(share, "shared", "true"); 1282 } 1283 } 1284 if (sharepath != NULL) 1285 sa_free_attr_string(sharepath); 1286 return (err); 1287 } 1288 1289 /* 1290 * sa_disable_share(share, protocol) 1291 * Disable the specified share to the specified protocol. 1292 * If protocol is NULL, then all protocols. 1293 */ 1294 int 1295 sa_disable_share(sa_share_t share, char *protocol) 1296 { 1297 char *path; 1298 char *shared; 1299 int ret = SA_OK; 1300 1301 path = sa_get_share_attr(share, "path"); 1302 shared = sa_get_share_attr(share, "shared"); 1303 1304 if (protocol != NULL) { 1305 ret = sa_proto_unshare(protocol, path); 1306 } else { 1307 /* need to do all protocols */ 1308 ret = sa_proto_unshare("nfs", path); 1309 } 1310 if (ret == SA_OK) 1311 (void) sa_set_share_attr(share, "shared", NULL); 1312 if (path != NULL) 1313 sa_free_attr_string(path); 1314 if (shared != NULL) 1315 sa_free_attr_string(shared); 1316 return (ret); 1317 } 1318 1319 /* 1320 * sa_remove_share(share) 1321 * 1322 * remove the specified share from its containing group. 1323 * Remove from the SMF or ZFS configuration space. 1324 */ 1325 1326 int 1327 sa_remove_share(sa_share_t share) 1328 { 1329 sa_group_t group; 1330 int ret = SA_OK; 1331 char *type; 1332 int transient = 0; 1333 char *groupname; 1334 char *zfs; 1335 1336 type = sa_get_share_attr(share, "type"); 1337 group = sa_get_parent_group(share); 1338 zfs = sa_get_group_attr(group, "zfs"); 1339 groupname = sa_get_group_attr(group, "name"); 1340 if (type != NULL && strcmp(type, "persist") != 0) 1341 transient = 1; 1342 if (type != NULL) 1343 sa_free_attr_string(type); 1344 1345 /* remove the node from its group then free the memory */ 1346 1347 /* 1348 * need to test if "busy" 1349 */ 1350 /* only do SMF action if permanent */ 1351 if (!transient || zfs != NULL) { 1352 /* remove from legacy dfstab as well as possible SMF */ 1353 ret = sa_delete_legacy(share); 1354 if (ret == SA_OK) { 1355 if (!sa_group_is_zfs(group)) { 1356 sa_handle_impl_t impl_handle; 1357 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1358 if (impl_handle != NULL) 1359 ret = sa_delete_share(impl_handle->scfhandle, 1360 group, share); 1361 else 1362 ret = SA_SYSTEM_ERR; 1363 } else { 1364 char *sharepath = sa_get_share_attr(share, "path"); 1365 if (sharepath != NULL) { 1366 ret = sa_zfs_set_sharenfs(group, sharepath, 0); 1367 sa_free_attr_string(sharepath); 1368 } 1369 } 1370 } 1371 } 1372 if (groupname != NULL) 1373 sa_free_attr_string(groupname); 1374 if (zfs != NULL) 1375 sa_free_attr_string(zfs); 1376 1377 xmlUnlinkNode((xmlNodePtr)share); 1378 xmlFreeNode((xmlNodePtr)share); 1379 return (ret); 1380 } 1381 1382 /* 1383 * sa_move_share(group, share) 1384 * 1385 * move the specified share to the specified group. Update SMF 1386 * appropriately. 1387 */ 1388 1389 int 1390 sa_move_share(sa_group_t group, sa_share_t share) 1391 { 1392 sa_group_t oldgroup; 1393 int ret = SA_OK; 1394 1395 /* remove the node from its group then free the memory */ 1396 1397 oldgroup = sa_get_parent_group(share); 1398 if (oldgroup != group) { 1399 sa_handle_impl_t impl_handle; 1400 xmlUnlinkNode((xmlNodePtr)share); 1401 /* now that the share isn't in its old group, add to the new one */ 1402 xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share); 1403 /* need to deal with SMF */ 1404 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1405 if (impl_handle != NULL) { 1406 /* 1407 * need to remove from old group first and then add to 1408 * new group. Ideally, we would do the other order but 1409 * need to avoid having the share in two groups at the 1410 * same time. 1411 */ 1412 ret = sa_delete_share(impl_handle->scfhandle, oldgroup, share); 1413 if (ret == SA_OK) 1414 ret = sa_commit_share(impl_handle->scfhandle, group, share); 1415 } else { 1416 ret = SA_SYSTEM_ERR; 1417 } 1418 } 1419 return (ret); 1420 } 1421 1422 /* 1423 * sa_get_parent_group(share) 1424 * 1425 * Return the containg group for the share. If a group was actually 1426 * passed in, we don't want a parent so return NULL. 1427 */ 1428 1429 sa_group_t 1430 sa_get_parent_group(sa_share_t share) 1431 { 1432 xmlNodePtr node = NULL; 1433 if (share != NULL) { 1434 node = ((xmlNodePtr)share)->parent; 1435 /* 1436 * make sure parent is a group and not sharecfg since 1437 * we may be cheating and passing in a group. 1438 * Eventually, groups of groups might come into being. 1439 */ 1440 if (node == NULL || 1441 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0) 1442 node = NULL; 1443 } 1444 return ((sa_group_t)node); 1445 } 1446 1447 /* 1448 * _sa_create_group(impl_handle, groupname) 1449 * 1450 * Create a group in the document. The caller will need to deal with 1451 * configuration store and activation. 1452 */ 1453 1454 sa_group_t 1455 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname) 1456 { 1457 xmlNodePtr node = NULL; 1458 1459 if (sa_valid_group_name(groupname)) { 1460 node = xmlNewChild(impl_handle->tree, NULL, 1461 (xmlChar *)"group", NULL); 1462 if (node != NULL) { 1463 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1464 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1465 } 1466 } 1467 return ((sa_group_t)node); 1468 } 1469 1470 /* 1471 * _sa_create_zfs_group(group, groupname) 1472 * 1473 * Create a ZFS subgroup under the specified group. This may 1474 * eventually form the basis of general sub-groups, but is currently 1475 * restricted to ZFS. 1476 */ 1477 sa_group_t 1478 _sa_create_zfs_group(sa_group_t group, char *groupname) 1479 { 1480 xmlNodePtr node = NULL; 1481 1482 node = xmlNewChild((xmlNodePtr)group, NULL, 1483 (xmlChar *)"group", NULL); 1484 if (node != NULL) { 1485 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1486 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1487 } 1488 1489 return ((sa_group_t)node); 1490 } 1491 1492 /* 1493 * sa_create_group(groupname, *error) 1494 * 1495 * Create a new group with groupname. Need to validate that it is a 1496 * legal name for SMF and the construct the SMF service instance of 1497 * svc:/network/shares/group to implement the group. All necessary 1498 * operational properties must be added to the group at this point 1499 * (via the SMF transaction model). 1500 */ 1501 sa_group_t 1502 sa_create_group(sa_handle_t handle, char *groupname, int *error) 1503 { 1504 xmlNodePtr node = NULL; 1505 sa_group_t group; 1506 int ret; 1507 char rbacstr[256]; 1508 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 1509 1510 ret = SA_OK; 1511 1512 if (impl_handle == NULL || impl_handle->scfhandle == NULL) { 1513 ret = SA_SYSTEM_ERR; 1514 goto err; 1515 } 1516 1517 group = sa_get_group(handle, groupname); 1518 if (group != NULL) { 1519 ret = SA_DUPLICATE_NAME; 1520 } else { 1521 if (sa_valid_group_name(groupname)) { 1522 node = xmlNewChild(impl_handle->tree, NULL, 1523 (xmlChar *)"group", NULL); 1524 if (node != NULL) { 1525 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1526 /* default to the group being enabled */ 1527 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1528 ret = sa_create_instance(impl_handle->scfhandle, groupname); 1529 if (ret == SA_OK) { 1530 ret = sa_start_transaction(impl_handle->scfhandle, 1531 "operation"); 1532 } 1533 if (ret == SA_OK) { 1534 ret = sa_set_property(impl_handle->scfhandle, 1535 "state", "enabled"); 1536 if (ret == SA_OK) { 1537 ret = sa_end_transaction(impl_handle->scfhandle); 1538 } else { 1539 sa_abort_transaction(impl_handle->scfhandle); 1540 } 1541 } 1542 if (ret == SA_OK) { 1543 /* initialize the RBAC strings */ 1544 ret = sa_start_transaction(impl_handle->scfhandle, 1545 "general"); 1546 if (ret == SA_OK) { 1547 (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 1548 SA_RBAC_MANAGE, groupname); 1549 ret = sa_set_property(impl_handle->scfhandle, 1550 "action_authorization", 1551 rbacstr); 1552 } 1553 if (ret == SA_OK) { 1554 (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 1555 SA_RBAC_VALUE, groupname); 1556 ret = sa_set_property(impl_handle->scfhandle, 1557 "value_authorization", 1558 rbacstr); 1559 } 1560 if (ret == SA_OK) { 1561 ret = sa_end_transaction(impl_handle->scfhandle); 1562 } else { 1563 sa_abort_transaction(impl_handle->scfhandle); 1564 } 1565 } 1566 if (ret != SA_OK) { 1567 /* 1568 * Couldn't commit the group so we need to 1569 * undo internally. 1570 */ 1571 xmlUnlinkNode(node); 1572 xmlFreeNode(node); 1573 node = NULL; 1574 } 1575 } else { 1576 ret = SA_NO_MEMORY; 1577 } 1578 } else { 1579 ret = SA_INVALID_NAME; 1580 } 1581 } 1582 err: 1583 if (error != NULL) 1584 *error = ret; 1585 return ((sa_group_t)node); 1586 } 1587 1588 /* 1589 * sa_remove_group(group) 1590 * 1591 * Remove the specified group. This deletes from the SMF repository. 1592 * All property groups and properties are removed. 1593 */ 1594 1595 int 1596 sa_remove_group(sa_group_t group) 1597 { 1598 char *name; 1599 int ret = SA_OK; 1600 sa_handle_impl_t impl_handle; 1601 1602 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1603 if (impl_handle != NULL) { 1604 name = sa_get_group_attr(group, "name"); 1605 if (name != NULL) { 1606 ret = sa_delete_instance(impl_handle->scfhandle, name); 1607 sa_free_attr_string(name); 1608 } 1609 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */ 1610 xmlFreeNode((xmlNodePtr)group); /* now it is gone */ 1611 } else { 1612 ret = SA_SYSTEM_ERR; 1613 } 1614 return (ret); 1615 } 1616 1617 /* 1618 * sa_update_config() 1619 * 1620 * Used to update legacy files that need to be updated in bulk 1621 * Currently, this is a placeholder and will go away in a future 1622 * release. 1623 */ 1624 1625 int 1626 sa_update_config(sa_handle_t handle) 1627 { 1628 /* 1629 * do legacy files first so we can tell when they change. 1630 * This will go away when we start updating individual records 1631 * rather than the whole file. 1632 */ 1633 update_legacy_config(handle); 1634 return (SA_OK); 1635 } 1636 1637 /* 1638 * get_node_attr(node, tag) 1639 * 1640 * Get the speficied tag(attribute) if it exists on the node. This is 1641 * used internally by a number of attribute oriented functions. 1642 */ 1643 1644 static char * 1645 get_node_attr(void *nodehdl, char *tag) 1646 { 1647 xmlNodePtr node = (xmlNodePtr)nodehdl; 1648 xmlChar *name = NULL; 1649 1650 if (node != NULL) { 1651 name = xmlGetProp(node, (xmlChar *)tag); 1652 } 1653 return ((char *)name); 1654 } 1655 1656 /* 1657 * get_node_attr(node, tag) 1658 * 1659 * Set the speficied tag(attribute) to the specified value This is 1660 * used internally by a number of attribute oriented functions. It 1661 * doesn't update the repository, only the internal document state. 1662 */ 1663 1664 void 1665 set_node_attr(void *nodehdl, char *tag, char *value) 1666 { 1667 xmlNodePtr node = (xmlNodePtr)nodehdl; 1668 if (node != NULL && tag != NULL) { 1669 if (value != NULL) { 1670 xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value); 1671 } else { 1672 xmlUnsetProp(node, (xmlChar *)tag); 1673 } 1674 } 1675 } 1676 1677 /* 1678 * sa_get_group_attr(group, tag) 1679 * 1680 * Get the specied attribute, if defined, for the group. 1681 */ 1682 1683 char * 1684 sa_get_group_attr(sa_group_t group, char *tag) 1685 { 1686 return (get_node_attr((void *)group, tag)); 1687 } 1688 1689 /* 1690 * sa_set_group_attr(group, tag, value) 1691 * 1692 * set the specified tag/attribute on the group using value as its 1693 * value. 1694 * 1695 * This will result in setting the property in the SMF repository as 1696 * well as in the internal document. 1697 */ 1698 1699 int 1700 sa_set_group_attr(sa_group_t group, char *tag, char *value) 1701 { 1702 int ret; 1703 char *groupname; 1704 sa_handle_impl_t impl_handle; 1705 1706 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1707 if (impl_handle != NULL) { 1708 groupname = sa_get_group_attr(group, "name"); 1709 ret = sa_get_instance(impl_handle->scfhandle, groupname); 1710 if (ret == SA_OK) { 1711 set_node_attr((void *)group, tag, value); 1712 ret = sa_start_transaction(impl_handle->scfhandle, "operation"); 1713 if (ret == SA_OK) { 1714 ret = sa_set_property(impl_handle->scfhandle, tag, value); 1715 if (ret == SA_OK) 1716 (void) sa_end_transaction(impl_handle->scfhandle); 1717 else { 1718 sa_abort_transaction(impl_handle->scfhandle); 1719 } 1720 } 1721 } 1722 if (groupname != NULL) 1723 sa_free_attr_string(groupname); 1724 } else { 1725 ret = SA_SYSTEM_ERR; 1726 } 1727 return (ret); 1728 } 1729 1730 /* 1731 * sa_get_share_attr(share, tag) 1732 * 1733 * Return the value of the tag/attribute set on the specified 1734 * share. Returns NULL if the tag doesn't exist. 1735 */ 1736 1737 char * 1738 sa_get_share_attr(sa_share_t share, char *tag) 1739 { 1740 return (get_node_attr((void *)share, tag)); 1741 } 1742 1743 /* 1744 * sa_get_resource(group, resource) 1745 * 1746 * Search all the shares in the speified group for a share with a 1747 * resource name matching the one specified. 1748 * 1749 * In the future, it may be advantageous to allow group to be NULL and 1750 * search all groups but that isn't needed at present. 1751 */ 1752 1753 sa_share_t 1754 sa_get_resource(sa_group_t group, char *resource) 1755 { 1756 sa_share_t share = NULL; 1757 char *name = NULL; 1758 1759 if (resource != NULL) { 1760 for (share = sa_get_share(group, NULL); share != NULL; 1761 share = sa_get_next_share(share)) { 1762 name = sa_get_share_attr(share, "resource"); 1763 if (name != NULL) { 1764 if (strcmp(name, resource) == 0) 1765 break; 1766 sa_free_attr_string(name); 1767 name = NULL; 1768 } 1769 } 1770 if (name != NULL) 1771 sa_free_attr_string(name); 1772 } 1773 return ((sa_share_t)share); 1774 } 1775 1776 /* 1777 * _sa_set_share_description(share, description) 1778 * 1779 * Add a description tag with text contents to the specified share. 1780 * A separate XML tag is used rather than a property. 1781 */ 1782 1783 xmlNodePtr 1784 _sa_set_share_description(sa_share_t share, char *content) 1785 { 1786 xmlNodePtr node; 1787 node = xmlNewChild((xmlNodePtr)share, 1788 NULL, (xmlChar *)"description", NULL); 1789 xmlNodeSetContent(node, (xmlChar *)content); 1790 return (node); 1791 } 1792 1793 /* 1794 * sa_set_share_attr(share, tag, value) 1795 * 1796 * Set the share attribute specified by tag to the specified value. In 1797 * the case of "resource", enforce a no duplicates in a group rule. If 1798 * the share is not transient, commit the changes to the repository 1799 * else just update the share internally. 1800 */ 1801 1802 int 1803 sa_set_share_attr(sa_share_t share, char *tag, char *value) 1804 { 1805 sa_group_t group; 1806 sa_share_t resource; 1807 int ret = SA_OK; 1808 1809 group = sa_get_parent_group(share); 1810 1811 /* 1812 * There are some attributes that may have specific 1813 * restrictions on them. Initially, only "resource" has 1814 * special meaning that needs to be checked. Only one instance 1815 * of a resource name may exist within a group. 1816 */ 1817 1818 if (strcmp(tag, "resource") == 0) { 1819 resource = sa_get_resource(group, value); 1820 if (resource != share && resource != NULL) 1821 ret = SA_DUPLICATE_NAME; 1822 } 1823 if (ret == SA_OK) { 1824 set_node_attr((void *)share, tag, value); 1825 if (group != NULL) { 1826 char *type; 1827 /* we can probably optimize this some */ 1828 type = sa_get_share_attr(share, "type"); 1829 if (type == NULL || strcmp(type, "transient") != 0) { 1830 sa_handle_impl_t impl_handle; 1831 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1832 if (impl_handle != NULL) 1833 ret = sa_commit_share(impl_handle->scfhandle, 1834 group, share); 1835 else 1836 ret = SA_SYSTEM_ERR; 1837 } 1838 if (type != NULL) 1839 sa_free_attr_string(type); 1840 } 1841 } 1842 return (ret); 1843 } 1844 1845 /* 1846 * sa_get_property_attr(prop, tag) 1847 * 1848 * Get the value of the specified property attribute. Standard 1849 * attributes are "type" and "value". 1850 */ 1851 1852 char * 1853 sa_get_property_attr(sa_property_t prop, char *tag) 1854 { 1855 return (get_node_attr((void *)prop, tag)); 1856 } 1857 1858 /* 1859 * sa_get_optionset_attr(prop, tag) 1860 * 1861 * Get the value of the specified property attribute. Standard 1862 * attribute is "type". 1863 */ 1864 1865 char * 1866 sa_get_optionset_attr(sa_property_t optionset, char *tag) 1867 { 1868 return (get_node_attr((void *)optionset, tag)); 1869 1870 } 1871 1872 /* 1873 * sa_set_optionset_attr(optionset, tag, value) 1874 * 1875 * Set the specified attribute(tag) to the specified value on the 1876 * optionset. 1877 */ 1878 1879 void 1880 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 1881 { 1882 set_node_attr((void *)optionset, tag, value); 1883 } 1884 1885 /* 1886 * sa_free_attr_string(string) 1887 * 1888 * Free the string that was returned in one of the sa_get_*_attr() 1889 * functions. 1890 */ 1891 1892 void 1893 sa_free_attr_string(char *string) 1894 { 1895 xmlFree((xmlChar *)string); 1896 } 1897 1898 /* 1899 * sa_get_optionset(group, proto) 1900 * 1901 * Return the optionset, if it exists, that is associated with the 1902 * specified protocol. 1903 */ 1904 1905 sa_optionset_t 1906 sa_get_optionset(void *group, char *proto) 1907 { 1908 xmlNodePtr node; 1909 xmlChar *value = NULL; 1910 1911 for (node = ((xmlNodePtr)group)->children; node != NULL; 1912 node = node->next) { 1913 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 1914 value = xmlGetProp(node, (xmlChar *)"type"); 1915 if (proto != NULL) { 1916 if (value != NULL && 1917 xmlStrcmp(value, (xmlChar *)proto) == 0) { 1918 break; 1919 } 1920 if (value != NULL) { 1921 xmlFree(value); 1922 value = NULL; 1923 } 1924 } else { 1925 break; 1926 } 1927 } 1928 } 1929 if (value != NULL) 1930 xmlFree(value); 1931 return ((sa_optionset_t)node); 1932 } 1933 1934 /* 1935 * sa_get_next_optionset(optionset) 1936 * 1937 * Return the next optionset in the group. NULL if this was the last. 1938 */ 1939 1940 sa_optionset_t 1941 sa_get_next_optionset(sa_optionset_t optionset) 1942 { 1943 xmlNodePtr node; 1944 1945 for (node = ((xmlNodePtr)optionset)->next; node != NULL; 1946 node = node->next) { 1947 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 1948 break; 1949 } 1950 } 1951 return ((sa_optionset_t)node); 1952 } 1953 1954 /* 1955 * sa_get_security(group, sectype, proto) 1956 * 1957 * Return the security optionset. The internal name is a hold over 1958 * from the implementation and will be changed before the API is 1959 * finalized. This is really a named optionset that can be negotiated 1960 * as a group of properties (like NFS security options). 1961 */ 1962 1963 sa_security_t 1964 sa_get_security(sa_group_t group, char *sectype, char *proto) 1965 { 1966 xmlNodePtr node; 1967 xmlChar *value = NULL; 1968 1969 for (node = ((xmlNodePtr)group)->children; node != NULL; 1970 node = node->next) { 1971 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 1972 if (proto != NULL) { 1973 value = xmlGetProp(node, (xmlChar *)"type"); 1974 if (value == NULL || 1975 (value != NULL && 1976 xmlStrcmp(value, (xmlChar *)proto) != 0)) { 1977 /* it doesn't match so continue */ 1978 xmlFree(value); 1979 value = NULL; 1980 continue; 1981 } 1982 } 1983 if (value != NULL) { 1984 xmlFree(value); 1985 value = NULL; 1986 } 1987 /* potential match */ 1988 if (sectype != NULL) { 1989 value = xmlGetProp(node, (xmlChar *)"sectype"); 1990 if (value != NULL && 1991 xmlStrcmp(value, (xmlChar *)sectype) == 0) { 1992 break; 1993 } 1994 } else { 1995 break; 1996 } 1997 } 1998 if (value != NULL) { 1999 xmlFree(value); 2000 value = NULL; 2001 } 2002 } 2003 if (value != NULL) 2004 xmlFree(value); 2005 return ((sa_security_t)node); 2006 } 2007 2008 /* 2009 * sa_get_next_security(security) 2010 * 2011 * Get the next security optionset if one exists. 2012 */ 2013 2014 sa_security_t 2015 sa_get_next_security(sa_security_t security) 2016 { 2017 xmlNodePtr node; 2018 2019 for (node = ((xmlNodePtr)security)->next; node != NULL; 2020 node = node->next) { 2021 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2022 break; 2023 } 2024 } 2025 return ((sa_security_t)node); 2026 } 2027 2028 /* 2029 * sa_get_property(optionset, prop) 2030 * 2031 * Get the property object with the name specified in prop from the 2032 * optionset. 2033 */ 2034 2035 sa_property_t 2036 sa_get_property(sa_optionset_t optionset, char *prop) 2037 { 2038 xmlNodePtr node = (xmlNodePtr)optionset; 2039 xmlChar *value = NULL; 2040 2041 if (optionset == NULL) 2042 return (NULL); 2043 2044 for (node = node->children; node != NULL; 2045 node = node->next) { 2046 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2047 if (prop == NULL) 2048 break; 2049 value = xmlGetProp(node, (xmlChar *)"type"); 2050 if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) { 2051 break; 2052 } 2053 if (value != NULL) { 2054 xmlFree(value); 2055 value = NULL; 2056 } 2057 } 2058 } 2059 if (value != NULL) 2060 xmlFree(value); 2061 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2062 /* avoid a non option node -- it is possible to be a text node */ 2063 node = NULL; 2064 } 2065 return ((sa_property_t)node); 2066 } 2067 2068 /* 2069 * sa_get_next_property(property) 2070 * 2071 * Get the next property following the specified property. NULL if 2072 * this was the last. 2073 */ 2074 2075 sa_property_t 2076 sa_get_next_property(sa_property_t property) 2077 { 2078 xmlNodePtr node; 2079 2080 for (node = ((xmlNodePtr)property)->next; node != NULL; 2081 node = node->next) { 2082 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2083 break; 2084 } 2085 } 2086 return ((sa_property_t)node); 2087 } 2088 2089 /* 2090 * sa_set_share_description(share, content) 2091 * 2092 * Set the description of share to content. 2093 */ 2094 2095 int 2096 sa_set_share_description(sa_share_t share, char *content) 2097 { 2098 xmlNodePtr node; 2099 sa_group_t group; 2100 int ret = SA_OK; 2101 2102 for (node = ((xmlNodePtr)share)->children; node != NULL; 2103 node = node->next) { 2104 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2105 break; 2106 } 2107 } 2108 group = sa_get_parent_group(share); 2109 /* no existing description but want to add */ 2110 if (node == NULL && content != NULL) { 2111 /* add a description */ 2112 node = _sa_set_share_description(share, content); 2113 } else if (node != NULL && content != NULL) { 2114 /* update a description */ 2115 xmlNodeSetContent(node, (xmlChar *)content); 2116 } else if (node != NULL && content == NULL) { 2117 /* remove an existing description */ 2118 xmlUnlinkNode(node); 2119 xmlFreeNode(node); 2120 } 2121 if (group != NULL && is_persistent((sa_group_t)share)) { 2122 sa_handle_impl_t impl_handle; 2123 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2124 if (impl_handle != NULL) 2125 ret = sa_commit_share(impl_handle->scfhandle, group, share); 2126 else 2127 ret = SA_SYSTEM_ERR; 2128 } 2129 return (ret); 2130 } 2131 2132 /* 2133 * fixproblemchars(string) 2134 * 2135 * don't want any newline or tab characters in the text since these 2136 * could break display of data and legacy file formats. 2137 */ 2138 static void 2139 fixproblemchars(char *str) 2140 { 2141 int c; 2142 for (c = *str; c != '\0'; c = *++str) { 2143 if (c == '\t' || c == '\n') 2144 *str = ' '; 2145 else if (c == '"') 2146 *str = '\''; 2147 } 2148 } 2149 2150 /* 2151 * sa_get_share_description(share) 2152 * 2153 * Return the description text for the specified share if it 2154 * exists. NULL if no description exists. 2155 */ 2156 2157 char * 2158 sa_get_share_description(sa_share_t share) 2159 { 2160 xmlChar *description = NULL; 2161 xmlNodePtr node; 2162 2163 for (node = ((xmlNodePtr)share)->children; node != NULL; 2164 node = node->next) { 2165 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2166 break; 2167 } 2168 } 2169 if (node != NULL) { 2170 description = xmlNodeGetContent((xmlNodePtr)share); 2171 fixproblemchars((char *)description); 2172 } 2173 return ((char *)description); 2174 } 2175 2176 /* 2177 * sa_free(share_description(description) 2178 * 2179 * Free the description string. 2180 */ 2181 2182 void 2183 sa_free_share_description(char *description) 2184 { 2185 xmlFree((xmlChar *)description); 2186 } 2187 2188 /* 2189 * sa_create_optionset(group, proto) 2190 * 2191 * Create an optionset for the specified protocol in the specied 2192 * group. This is manifested as a property group within SMF. 2193 */ 2194 2195 sa_optionset_t 2196 sa_create_optionset(sa_group_t group, char *proto) 2197 { 2198 sa_optionset_t optionset; 2199 sa_group_t parent = group; 2200 2201 optionset = sa_get_optionset(group, proto); 2202 if (optionset != NULL) { 2203 /* can't have a duplicate protocol */ 2204 optionset = NULL; 2205 } else { 2206 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 2207 NULL, 2208 (xmlChar *)"optionset", 2209 NULL); 2210 /* 2211 * only put to repository if on a group and we were 2212 * able to create an optionset. 2213 */ 2214 if (optionset != NULL) { 2215 char oname[256]; 2216 char *groupname; 2217 char *id = NULL; 2218 2219 if (sa_is_share(group)) 2220 parent = sa_get_parent_group((sa_share_t)group); 2221 2222 sa_set_optionset_attr(optionset, "type", proto); 2223 2224 if (sa_is_share(group)) { 2225 id = sa_get_share_attr((sa_share_t)group, "id"); 2226 } 2227 (void) sa_optionset_name(optionset, oname, 2228 sizeof (oname), id); 2229 groupname = sa_get_group_attr(parent, "name"); 2230 if (groupname != NULL && is_persistent(group)) { 2231 sa_handle_impl_t impl_handle; 2232 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2233 assert(impl_handle != NULL); 2234 if (impl_handle != NULL) { 2235 (void) sa_get_instance(impl_handle->scfhandle, 2236 groupname); 2237 (void) sa_create_pgroup(impl_handle->scfhandle, oname); 2238 } 2239 } 2240 if (groupname != NULL) 2241 sa_free_attr_string(groupname); 2242 if (id != NULL) 2243 sa_free_attr_string(id); 2244 } 2245 } 2246 return (optionset); 2247 } 2248 2249 /* 2250 * sa_get_property_parent(property) 2251 * 2252 * Given a property, return the object it is a property of. This will 2253 * be an optionset of some type. 2254 */ 2255 2256 static sa_optionset_t 2257 sa_get_property_parent(sa_property_t property) 2258 { 2259 xmlNodePtr node = NULL; 2260 2261 if (property != NULL) { 2262 node = ((xmlNodePtr)property)->parent; 2263 } 2264 return ((sa_optionset_t)node); 2265 } 2266 2267 /* 2268 * sa_get_optionset_parent(optionset) 2269 * 2270 * Return the parent of the specified optionset. This could be a group 2271 * or a share. 2272 */ 2273 2274 static sa_group_t 2275 sa_get_optionset_parent(sa_optionset_t optionset) 2276 { 2277 xmlNodePtr node = NULL; 2278 2279 if (optionset != NULL) { 2280 node = ((xmlNodePtr)optionset)->parent; 2281 } 2282 return ((sa_group_t)node); 2283 } 2284 2285 /* 2286 * zfs_needs_update(share) 2287 * 2288 * In order to avoid making multiple updates to a ZFS share when 2289 * setting properties, the share attribute "changed" will be set to 2290 * true when a property is added or modifed. When done adding 2291 * properties, we can then detect that an update is needed. We then 2292 * clear the state here to detect additional changes. 2293 */ 2294 2295 static int 2296 zfs_needs_update(sa_share_t share) 2297 { 2298 char *attr; 2299 int result = 0; 2300 2301 attr = sa_get_share_attr(share, "changed"); 2302 if (attr != NULL) { 2303 sa_free_attr_string(attr); 2304 result = 1; 2305 } 2306 set_node_attr((void *)share, "changed", NULL); 2307 return (result); 2308 } 2309 2310 /* 2311 * zfs_set_update(share) 2312 * 2313 * Set the changed attribute of the share to true. 2314 */ 2315 2316 static void 2317 zfs_set_update(sa_share_t share) 2318 { 2319 set_node_attr((void *)share, "changed", "true"); 2320 } 2321 2322 /* 2323 * sa_commit_properties(optionset, clear) 2324 * 2325 * Check if SMF or ZFS config and either update or abort the pending 2326 * changes. 2327 */ 2328 2329 int 2330 sa_commit_properties(sa_optionset_t optionset, int clear) 2331 { 2332 sa_group_t group; 2333 sa_group_t parent; 2334 int zfs = 0; 2335 int needsupdate = 0; 2336 int ret = SA_OK; 2337 sa_handle_impl_t impl_handle; 2338 2339 group = sa_get_optionset_parent(optionset); 2340 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 2341 /* only update ZFS if on a share */ 2342 parent = sa_get_parent_group(group); 2343 zfs++; 2344 if (parent != NULL && is_zfs_group(parent)) { 2345 needsupdate = zfs_needs_update(group); 2346 } else { 2347 zfs = 0; 2348 } 2349 } 2350 if (zfs) { 2351 if (!clear && needsupdate) 2352 ret = sa_zfs_update((sa_share_t)group); 2353 } else { 2354 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2355 if (impl_handle != NULL) { 2356 if (clear) 2357 (void) sa_abort_transaction(impl_handle->scfhandle); 2358 else 2359 ret = sa_end_transaction(impl_handle->scfhandle); 2360 } else { 2361 ret = SA_SYSTEM_ERR; 2362 } 2363 } 2364 return (ret); 2365 } 2366 2367 /* 2368 * sa_destroy_optionset(optionset) 2369 * 2370 * Remove the optionset from its group. Update the repostory to 2371 * reflect this change. 2372 */ 2373 2374 int 2375 sa_destroy_optionset(sa_optionset_t optionset) 2376 { 2377 char name[256]; 2378 int len; 2379 int ret; 2380 char *id = NULL; 2381 sa_group_t group; 2382 int ispersist = 1; 2383 2384 /* now delete the prop group */ 2385 group = sa_get_optionset_parent(optionset); 2386 if (group != NULL && sa_is_share(group)) { 2387 ispersist = is_persistent(group); 2388 id = sa_get_share_attr((sa_share_t)group, "id"); 2389 } 2390 if (ispersist) { 2391 sa_handle_impl_t impl_handle; 2392 len = sa_optionset_name(optionset, name, sizeof (name), id); 2393 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2394 if (impl_handle != NULL) { 2395 if (len > 0) { 2396 ret = sa_delete_pgroup(impl_handle->scfhandle, name); 2397 } 2398 } else { 2399 ret = SA_SYSTEM_ERR; 2400 } 2401 } 2402 xmlUnlinkNode((xmlNodePtr)optionset); 2403 xmlFreeNode((xmlNodePtr)optionset); 2404 if (id != NULL) 2405 sa_free_attr_string(id); 2406 return (ret); 2407 } 2408 2409 /* private to the implementation */ 2410 int 2411 _sa_remove_optionset(sa_optionset_t optionset) 2412 { 2413 int ret = SA_OK; 2414 2415 xmlUnlinkNode((xmlNodePtr)optionset); 2416 xmlFreeNode((xmlNodePtr)optionset); 2417 return (ret); 2418 } 2419 2420 /* 2421 * sa_create_security(group, sectype, proto) 2422 * 2423 * Create a security optionset (one that has a type name and a 2424 * proto). Security is left over from a pure NFS implementation. The 2425 * naming will change in the future when the API is released. 2426 */ 2427 sa_security_t 2428 sa_create_security(sa_group_t group, char *sectype, char *proto) 2429 { 2430 sa_security_t security; 2431 char *id = NULL; 2432 sa_group_t parent; 2433 char *groupname = NULL; 2434 2435 if (group != NULL && sa_is_share(group)) { 2436 id = sa_get_share_attr((sa_share_t)group, "id"); 2437 parent = sa_get_parent_group(group); 2438 if (parent != NULL) 2439 groupname = sa_get_group_attr(parent, "name"); 2440 } else if (group != NULL) { 2441 groupname = sa_get_group_attr(group, "name"); 2442 } 2443 2444 security = sa_get_security(group, sectype, proto); 2445 if (security != NULL) { 2446 /* can't have a duplicate security option */ 2447 security = NULL; 2448 } else { 2449 security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 2450 NULL, 2451 (xmlChar *)"security", 2452 NULL); 2453 if (security != NULL) { 2454 char oname[256]; 2455 sa_set_security_attr(security, "type", proto); 2456 2457 sa_set_security_attr(security, "sectype", sectype); 2458 (void) sa_security_name(security, oname, 2459 sizeof (oname), id); 2460 if (groupname != NULL && is_persistent(group)) { 2461 sa_handle_impl_t impl_handle; 2462 impl_handle = 2463 (sa_handle_impl_t)sa_find_group_handle(group); 2464 if (impl_handle != NULL) { 2465 (void) sa_get_instance(impl_handle->scfhandle, 2466 groupname); 2467 (void) sa_create_pgroup(impl_handle->scfhandle, 2468 oname); 2469 } 2470 } 2471 } 2472 } 2473 if (groupname != NULL) 2474 sa_free_attr_string(groupname); 2475 return (security); 2476 } 2477 2478 /* 2479 * sa_destroy_security(security) 2480 * 2481 * Remove the specified optionset from the document and the 2482 * configuration. 2483 */ 2484 2485 int 2486 sa_destroy_security(sa_security_t security) 2487 { 2488 char name[256]; 2489 int len; 2490 int ret = SA_OK; 2491 char *id = NULL; 2492 sa_group_t group; 2493 int iszfs = 0; 2494 int ispersist = 1; 2495 2496 group = sa_get_optionset_parent(security); 2497 2498 if (group != NULL) 2499 iszfs = sa_group_is_zfs(group); 2500 2501 if (group != NULL && !iszfs) { 2502 if (sa_is_share(group)) 2503 ispersist = is_persistent(group); 2504 id = sa_get_share_attr((sa_share_t)group, "id"); 2505 } 2506 if (ispersist) { 2507 len = sa_security_name(security, name, sizeof (name), id); 2508 if (!iszfs && len > 0) { 2509 sa_handle_impl_t impl_handle; 2510 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2511 if (impl_handle != NULL) { 2512 ret = sa_delete_pgroup(impl_handle->scfhandle, name); 2513 } else { 2514 ret = SA_SYSTEM_ERR; 2515 } 2516 } 2517 } 2518 xmlUnlinkNode((xmlNodePtr)security); 2519 xmlFreeNode((xmlNodePtr)security); 2520 if (iszfs) { 2521 ret = sa_zfs_update(group); 2522 } 2523 if (id != NULL) 2524 sa_free_attr_string(id); 2525 return (ret); 2526 } 2527 2528 /* 2529 * sa_get_security_attr(optionset, tag) 2530 * 2531 * Return the specified attribute value from the optionset. 2532 */ 2533 2534 char * 2535 sa_get_security_attr(sa_property_t optionset, char *tag) 2536 { 2537 return (get_node_attr((void *)optionset, tag)); 2538 2539 } 2540 2541 /* 2542 * sa_set_security_attr(optionset, tag, value) 2543 * 2544 * Set the optioset attribute specied by tag to the specified value. 2545 */ 2546 2547 void 2548 sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 2549 { 2550 set_node_attr((void *)optionset, tag, value); 2551 } 2552 2553 /* 2554 * is_nodetype(node, type) 2555 * 2556 * Check to see if node is of the type specified. 2557 */ 2558 2559 static int 2560 is_nodetype(void *node, char *type) 2561 { 2562 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 2563 } 2564 2565 /* 2566 * sa_set_prop_by_prop(optionset, group, prop, type) 2567 * 2568 * Add/remove/update the specified property prop into the optionset or 2569 * share. If a share, sort out which property group based on GUID. In 2570 * all cases, the appropriate transaction is set (or ZFS share is 2571 * marked as needing an update) 2572 */ 2573 2574 #define SA_PROP_OP_REMOVE 1 2575 #define SA_PROP_OP_ADD 2 2576 #define SA_PROP_OP_UPDATE 3 2577 static int 2578 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 2579 sa_property_t prop, int type) 2580 { 2581 char *name; 2582 char *valstr; 2583 int ret = SA_OK; 2584 scf_transaction_entry_t *entry; 2585 scf_value_t *value; 2586 int opttype; /* 1 == optionset, 0 == security */ 2587 char *id = NULL; 2588 int iszfs = 0; 2589 int isshare = 0; 2590 sa_group_t parent = NULL; 2591 sa_handle_impl_t impl_handle; 2592 scfutilhandle_t *scf_handle; 2593 2594 if (!is_persistent(group)) { 2595 /* 2596 * if the group/share is not persistent we don't need 2597 * to do anything here 2598 */ 2599 return (SA_OK); 2600 } 2601 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2602 if (impl_handle == NULL || impl_handle->scfhandle == NULL) { 2603 return (SA_SYSTEM_ERR); 2604 } 2605 scf_handle = impl_handle->scfhandle; 2606 name = sa_get_property_attr(prop, "type"); 2607 valstr = sa_get_property_attr(prop, "value"); 2608 entry = scf_entry_create(scf_handle->handle); 2609 opttype = is_nodetype((void *)optionset, "optionset"); 2610 2611 if (valstr != NULL && entry != NULL) { 2612 if (sa_is_share(group)) { 2613 isshare = 1; 2614 parent = sa_get_parent_group(group); 2615 if (parent != NULL) { 2616 iszfs = is_zfs_group(parent); 2617 } 2618 } else { 2619 iszfs = is_zfs_group(group); 2620 } 2621 if (!iszfs) { 2622 if (scf_handle->trans == NULL) { 2623 char oname[256]; 2624 char *groupname = NULL; 2625 if (isshare) { 2626 if (parent != NULL) { 2627 groupname = sa_get_group_attr(parent, "name"); 2628 } 2629 id = sa_get_share_attr((sa_share_t)group, "id"); 2630 } else { 2631 groupname = sa_get_group_attr(group, "name"); 2632 } 2633 if (groupname != NULL) { 2634 ret = sa_get_instance(scf_handle, groupname); 2635 sa_free_attr_string(groupname); 2636 } 2637 if (opttype) 2638 (void) sa_optionset_name(optionset, oname, 2639 sizeof (oname), id); 2640 else 2641 (void) sa_security_name(optionset, oname, 2642 sizeof (oname), id); 2643 ret = sa_start_transaction(scf_handle, oname); 2644 } 2645 if (ret == SA_OK) { 2646 switch (type) { 2647 case SA_PROP_OP_REMOVE: 2648 ret = scf_transaction_property_delete( 2649 scf_handle->trans, 2650 entry, name); 2651 break; 2652 case SA_PROP_OP_ADD: 2653 case SA_PROP_OP_UPDATE: 2654 value = scf_value_create(scf_handle->handle); 2655 if (value != NULL) { 2656 if (type == SA_PROP_OP_ADD) 2657 ret = scf_transaction_property_new( 2658 scf_handle->trans, 2659 entry, 2660 name, 2661 SCF_TYPE_ASTRING); 2662 else 2663 ret = scf_transaction_property_change( 2664 scf_handle->trans, 2665 entry, 2666 name, 2667 SCF_TYPE_ASTRING); 2668 if (ret == 0) { 2669 ret = scf_value_set_astring(value, valstr); 2670 if (ret == 0) 2671 ret = scf_entry_add_value(entry, value); 2672 if (ret != 0) { 2673 scf_value_destroy(value); 2674 ret = SA_SYSTEM_ERR; 2675 } 2676 } else { 2677 scf_entry_destroy(entry); 2678 ret = SA_SYSTEM_ERR; 2679 } 2680 break; 2681 } 2682 } 2683 } 2684 } else { 2685 /* 2686 * ZFS update. The calling function would have updated 2687 * the internal XML structure. Just need to flag it as 2688 * changed for ZFS. 2689 */ 2690 zfs_set_update((sa_share_t)group); 2691 } 2692 } 2693 2694 if (name != NULL) 2695 sa_free_attr_string(name); 2696 if (valstr != NULL) 2697 sa_free_attr_string(valstr); 2698 else if (entry != NULL) 2699 scf_entry_destroy(entry); 2700 2701 if (ret == -1) 2702 ret = SA_SYSTEM_ERR; 2703 2704 return (ret); 2705 } 2706 2707 /* 2708 * sa_create_property(name, value) 2709 * 2710 * Create a new property with the specified name and value. 2711 */ 2712 2713 sa_property_t 2714 sa_create_property(char *name, char *value) 2715 { 2716 xmlNodePtr node; 2717 2718 node = xmlNewNode(NULL, (xmlChar *)"option"); 2719 if (node != NULL) { 2720 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 2721 xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 2722 } 2723 return ((sa_property_t)node); 2724 } 2725 2726 /* 2727 * sa_add_property(object, property) 2728 * 2729 * Add the specified property to the object. Issue the appropriate 2730 * transaction or mark a ZFS object as needing an update. 2731 */ 2732 2733 int 2734 sa_add_property(void *object, sa_property_t property) 2735 { 2736 int ret = SA_OK; 2737 sa_group_t parent; 2738 sa_group_t group; 2739 char *proto; 2740 2741 proto = sa_get_optionset_attr(object, "type"); 2742 if (property != NULL) { 2743 if ((ret = sa_valid_property(object, proto, property)) == SA_OK) { 2744 property = (sa_property_t)xmlAddChild((xmlNodePtr)object, 2745 (xmlNodePtr)property); 2746 } else { 2747 if (proto != NULL) 2748 sa_free_attr_string(proto); 2749 return (ret); 2750 } 2751 } 2752 2753 if (proto != NULL) 2754 sa_free_attr_string(proto); 2755 2756 parent = sa_get_parent_group(object); 2757 if (!is_persistent(parent)) { 2758 return (ret); 2759 } 2760 2761 if (sa_is_share(parent)) 2762 group = sa_get_parent_group(parent); 2763 else 2764 group = parent; 2765 2766 if (property == NULL) 2767 ret = SA_NO_MEMORY; 2768 else { 2769 char oname[256]; 2770 2771 if (!is_zfs_group(group)) { 2772 char *id = NULL; 2773 sa_handle_impl_t impl_handle; 2774 scfutilhandle_t *scf_handle; 2775 2776 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2777 if (impl_handle == NULL || impl_handle->scfhandle == NULL) 2778 ret = SA_SYSTEM_ERR; 2779 if (ret == SA_OK) { 2780 scf_handle = impl_handle->scfhandle; 2781 if (sa_is_share((sa_group_t)parent)) { 2782 id = sa_get_share_attr((sa_share_t)parent, "id"); 2783 } 2784 if (scf_handle->trans == NULL) { 2785 if (is_nodetype(object, "optionset")) 2786 (void) sa_optionset_name((sa_optionset_t)object, 2787 oname, sizeof (oname), id); 2788 else 2789 (void) sa_security_name((sa_optionset_t)object, 2790 oname, sizeof (oname), id); 2791 ret = sa_start_transaction(scf_handle, oname); 2792 } 2793 if (ret == SA_OK) { 2794 char *name; 2795 char *value; 2796 name = sa_get_property_attr(property, "type"); 2797 value = sa_get_property_attr(property, "value"); 2798 if (name != NULL && value != NULL) { 2799 if (scf_handle->scf_state == SCH_STATE_INIT) 2800 ret = sa_set_property(scf_handle, name, value); 2801 } else 2802 ret = SA_CONFIG_ERR; 2803 if (name != NULL) 2804 sa_free_attr_string(name); 2805 if (value != NULL) 2806 sa_free_attr_string(value); 2807 } 2808 if (id != NULL) 2809 sa_free_attr_string(id); 2810 } 2811 } else { 2812 /* 2813 * ZFS is a special case. We do want to allow editing 2814 * property/security lists since we can have a better 2815 * syntax and we also want to keep things consistent 2816 * when possible. 2817 * 2818 * Right now, we defer until the sa_commit_properties 2819 * so we can get them all at once. We do need to mark 2820 * the share as "changed" 2821 */ 2822 zfs_set_update((sa_share_t)parent); 2823 } 2824 } 2825 return (ret); 2826 } 2827 2828 /* 2829 * sa_remove_property(property) 2830 * 2831 * Remove the specied property from its containing object. Update the 2832 * repository as appropriate. 2833 */ 2834 2835 int 2836 sa_remove_property(sa_property_t property) 2837 { 2838 int ret = SA_OK; 2839 2840 if (property != NULL) { 2841 sa_optionset_t optionset; 2842 sa_group_t group; 2843 optionset = sa_get_property_parent(property); 2844 if (optionset != NULL) { 2845 group = sa_get_optionset_parent(optionset); 2846 if (group != NULL) { 2847 ret = sa_set_prop_by_prop(optionset, group, property, 2848 SA_PROP_OP_REMOVE); 2849 } 2850 } 2851 xmlUnlinkNode((xmlNodePtr)property); 2852 xmlFreeNode((xmlNodePtr)property); 2853 } else { 2854 ret = SA_NO_SUCH_PROP; 2855 } 2856 return (ret); 2857 } 2858 2859 /* 2860 * sa_update_property(property, value) 2861 * 2862 * Update the specified property to the new value. If value is NULL, 2863 * we currently treat this as a remove. 2864 */ 2865 2866 int 2867 sa_update_property(sa_property_t property, char *value) 2868 { 2869 int ret = SA_OK; 2870 if (value == NULL) { 2871 return (sa_remove_property(property)); 2872 } else { 2873 sa_optionset_t optionset; 2874 sa_group_t group; 2875 set_node_attr((void *)property, "value", value); 2876 optionset = sa_get_property_parent(property); 2877 if (optionset != NULL) { 2878 group = sa_get_optionset_parent(optionset); 2879 if (group != NULL) { 2880 ret = sa_set_prop_by_prop(optionset, group, property, 2881 SA_PROP_OP_UPDATE); 2882 } 2883 } else { 2884 ret = SA_NO_SUCH_PROP; 2885 } 2886 } 2887 return (ret); 2888 } 2889 2890 /* 2891 * sa_get_protocol_property(propset, prop) 2892 * 2893 * Get the specified protocol specific property. These are global to 2894 * the protocol and not specific to a group or share. 2895 */ 2896 2897 sa_property_t 2898 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 2899 { 2900 xmlNodePtr node = (xmlNodePtr)propset; 2901 xmlChar *value = NULL; 2902 2903 for (node = node->children; node != NULL; 2904 node = node->next) { 2905 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2906 if (prop == NULL) 2907 break; 2908 value = xmlGetProp(node, (xmlChar *)"type"); 2909 if (value != NULL && 2910 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 2911 break; 2912 } 2913 if (value != NULL) { 2914 xmlFree(value); 2915 value = NULL; 2916 } 2917 } 2918 } 2919 if (value != NULL) 2920 xmlFree(value); 2921 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2922 /* avoid a non option node -- it is possible to be a text node */ 2923 node = NULL; 2924 } 2925 return ((sa_property_t)node); 2926 } 2927 2928 /* 2929 * sa_get_next_protocol_property(prop) 2930 * 2931 * Get the next protocol specific property in the list. 2932 */ 2933 2934 sa_property_t 2935 sa_get_next_protocol_property(sa_property_t prop) 2936 { 2937 xmlNodePtr node; 2938 2939 for (node = ((xmlNodePtr)prop)->next; node != NULL; 2940 node = node->next) { 2941 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2942 break; 2943 } 2944 } 2945 return ((sa_property_t)node); 2946 } 2947 2948 /* 2949 * sa_set_protocol_property(prop, value) 2950 * 2951 * Set the specified property to have the new value. The protocol 2952 * specific plugin will then be called to update the property. 2953 */ 2954 2955 int 2956 sa_set_protocol_property(sa_property_t prop, char *value) 2957 { 2958 sa_protocol_properties_t propset; 2959 char *proto; 2960 int ret = SA_INVALID_PROTOCOL; 2961 2962 propset = ((xmlNodePtr)prop)->parent; 2963 if (propset != NULL) { 2964 proto = sa_get_optionset_attr(propset, "type"); 2965 if (proto != NULL) { 2966 set_node_attr((xmlNodePtr)prop, "value", value); 2967 ret = sa_proto_set_property(proto, prop); 2968 sa_free_attr_string(proto); 2969 } 2970 } 2971 return (ret); 2972 } 2973 2974 /* 2975 * sa_add_protocol_property(propset, prop) 2976 * 2977 * Add a new property to the protocol sepcific property set. 2978 */ 2979 2980 int 2981 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 2982 { 2983 xmlNodePtr node; 2984 2985 /* should check for legitimacy */ 2986 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 2987 if (node != NULL) 2988 return (SA_OK); 2989 return (SA_NO_MEMORY); 2990 } 2991 2992 /* 2993 * sa_create_protocol_properties(proto) 2994 * 2995 * Create a protocol specifity property set. 2996 */ 2997 2998 sa_protocol_properties_t 2999 sa_create_protocol_properties(char *proto) 3000 { 3001 xmlNodePtr node; 3002 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 3003 if (node != NULL) { 3004 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 3005 } 3006 return (node); 3007 } 3008