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 (void) sa_get_config(handle->scfhandle, &handle->tree, 739 &handle->doc, handle); 740 /* need to setup for later searchs when using group/share */ 741 err = add_handle_for_root(handle->tree, handle); 742 saunblocksigs(&old); 743 744 if (err != SA_OK) { 745 /* 746 * If we couldn't add the tree handle to the 747 * list, then things are going to fail 748 * badly. Might as well undo everything now 749 * and fail the sa_init(). 750 */ 751 sa_fini(handle); 752 return (NULL); 753 } 754 755 if (tval == 0) { 756 /* first time so make sure default is setup */ 757 sa_group_t defgrp; 758 sa_optionset_t opt; 759 defgrp = sa_get_group(handle, "default"); 760 if (defgrp != NULL) { 761 opt = sa_get_optionset(defgrp, NULL); 762 if (opt == NULL) 763 /* NFS is the default for default */ 764 opt = sa_create_optionset(defgrp, "nfs"); 765 } 766 } 767 if (updatelegacy == B_TRUE) { 768 sablocksigs(&old); 769 getlegacyconfig((sa_handle_t)handle, 770 SA_LEGACY_DFSTAB, &handle->tree); 771 if (stat(SA_LEGACY_DFSTAB, &st) >= 0) 772 set_legacy_timestamp(handle->tree, 773 SA_LEGACY_DFSTAB, 774 TSTAMP(st.st_ctim)); 775 saunblocksigs(&old); 776 /* Safe to unlock now to allow others to run */ 777 (void) lockf(lockfd, F_ULOCK, 0); 778 (void) close(lockfd); 779 } 780 legacy |= sa_get_zfs_shares(handle, "zfs"); 781 legacy |= gettransients(handle, &handle->tree); 782 } 783 } 784 } 785 return ((sa_handle_t)handle); 786 } 787 788 /* 789 * sa_fini(handle) 790 * Uninitialize the API structures including the configuration 791 * data structures and ZFS related data. 792 */ 793 794 void 795 sa_fini(sa_handle_t handle) 796 { 797 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 798 799 if (impl_handle != NULL) { 800 /* 801 * Free the config trees and any other data structures 802 * used in the handle. 803 */ 804 if (impl_handle->doc != NULL) 805 xmlFreeDoc(impl_handle->doc); 806 sa_scf_fini(impl_handle->scfhandle); 807 sa_zfs_fini(impl_handle); 808 809 /* Remove and free the entry in the global list. */ 810 remove_handle_for_root(impl_handle->tree); 811 812 /* Make sure we free the handle */ 813 free(impl_handle); 814 815 /* 816 * If this was the last handle to release, unload the 817 * plugins that were loaded. 818 */ 819 if (sa_global_handles == NULL) 820 (void) proto_plugin_fini(); 821 822 } 823 } 824 825 /* 826 * sa_get_protocols(char **protocol) 827 * Get array of protocols that are supported 828 * Returns pointer to an allocated and NULL terminated 829 * array of strings. Caller must free. 830 * This really should be determined dynamically. 831 * If there aren't any defined, return -1. 832 * Use free() to return memory. 833 */ 834 835 int 836 sa_get_protocols(char ***protocols) 837 { 838 int numproto = -1; 839 840 if (protocols != NULL) { 841 struct sa_proto_plugin *plug; 842 for (numproto = 0, plug = sap_proto_list; plug != NULL; 843 plug = plug->plugin_next) { 844 numproto++; 845 } 846 847 *protocols = calloc(numproto + 1, sizeof (char *)); 848 if (*protocols != NULL) { 849 int ret = 0; 850 for (plug = sap_proto_list; plug != NULL; 851 plug = plug->plugin_next) { 852 /* faking for now */ 853 (*protocols)[ret++] = plug->plugin_ops->sa_protocol; 854 } 855 } else { 856 numproto = -1; 857 } 858 } 859 return (numproto); 860 } 861 862 /* 863 * find_group_by_name(node, group) 864 * 865 * search the XML document subtree specified by node to find the group 866 * specified by group. Searching subtree allows subgroups to be 867 * searched for. 868 */ 869 870 static xmlNodePtr 871 find_group_by_name(xmlNodePtr node, xmlChar *group) 872 { 873 xmlChar *name = NULL; 874 875 for (node = node->xmlChildrenNode; node != NULL; 876 node = node->next) { 877 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) { 878 /* if no groupname, return the first found */ 879 if (group == NULL) 880 break; 881 name = xmlGetProp(node, (xmlChar *)"name"); 882 if (name != NULL && 883 xmlStrcmp(name, group) == 0) { 884 break; 885 } 886 if (name != NULL) { 887 xmlFree(name); 888 name = NULL; 889 } 890 } 891 } 892 if (name != NULL) 893 xmlFree(name); 894 return (node); 895 } 896 897 /* 898 * sa_get_group(groupname) 899 * Return the "group" specified. If groupname is NULL, 900 * return the first group of the list of groups. 901 */ 902 sa_group_t 903 sa_get_group(sa_handle_t handle, char *groupname) 904 { 905 xmlNodePtr node = NULL; 906 char *subgroup = NULL; 907 char *group = NULL; 908 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 909 910 if (impl_handle != NULL && impl_handle->tree != NULL) { 911 if (groupname != NULL) { 912 group = strdup(groupname); 913 subgroup = strchr(group, '/'); 914 if (subgroup != NULL) 915 *subgroup++ = '\0'; 916 } 917 node = find_group_by_name(impl_handle->tree, (xmlChar *)group); 918 /* if a subgroup, find it before returning */ 919 if (subgroup != NULL && node != NULL) { 920 node = find_group_by_name(node, (xmlChar *)subgroup); 921 } 922 } 923 if (node != NULL && (char *)group != NULL) 924 (void) sa_get_instance(impl_handle->scfhandle, (char *)group); 925 if (group != NULL) 926 free(group); 927 return ((sa_group_t)(node)); 928 } 929 930 /* 931 * sa_get_next_group(group) 932 * Return the "next" group after the specified group from 933 * the internal group list. NULL if there are no more. 934 */ 935 sa_group_t 936 sa_get_next_group(sa_group_t group) 937 { 938 xmlNodePtr ngroup = NULL; 939 if (group != NULL) { 940 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL; 941 ngroup = ngroup->next) { 942 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0) 943 break; 944 } 945 } 946 return ((sa_group_t)ngroup); 947 } 948 949 /* 950 * sa_get_share(group, sharepath) 951 * Return the share object for the share specified. The share 952 * must be in the specified group. Return NULL if not found. 953 */ 954 sa_share_t 955 sa_get_share(sa_group_t group, char *sharepath) 956 { 957 xmlNodePtr node = NULL; 958 xmlChar *path; 959 960 /* 961 * For future scalability, this should end up building a cache 962 * since it will get called regularly by the mountd and info 963 * services. 964 */ 965 if (group != NULL) { 966 for (node = ((xmlNodePtr)group)->children; node != NULL; 967 node = node->next) { 968 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 969 if (sharepath == NULL) { 970 break; 971 } else { 972 /* is it the correct share? */ 973 path = xmlGetProp(node, (xmlChar *)"path"); 974 if (path != NULL && 975 xmlStrcmp(path, (xmlChar *)sharepath) == 0) { 976 xmlFree(path); 977 break; 978 } 979 xmlFree(path); 980 } 981 } 982 } 983 } 984 return ((sa_share_t)node); 985 } 986 987 /* 988 * sa_get_next_share(share) 989 * Return the next share following the specified share 990 * from the internal list of shares. Returns NULL if there 991 * are no more shares. The list is relative to the same 992 * group. 993 */ 994 sa_share_t 995 sa_get_next_share(sa_share_t share) 996 { 997 xmlNodePtr node = NULL; 998 999 if (share != NULL) { 1000 for (node = ((xmlNodePtr)share)->next; node != NULL; 1001 node = node->next) { 1002 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1003 break; 1004 } 1005 } 1006 } 1007 return ((sa_share_t)node); 1008 } 1009 1010 /* 1011 * _sa_get_child_node(node, type) 1012 * 1013 * find the child node of the specified node that has "type". This is 1014 * used to implement several internal functions. 1015 */ 1016 1017 static xmlNodePtr 1018 _sa_get_child_node(xmlNodePtr node, xmlChar *type) 1019 { 1020 xmlNodePtr child; 1021 for (child = node->xmlChildrenNode; child != NULL; 1022 child = child->next) 1023 if (xmlStrcmp(child->name, type) == 0) 1024 return (child); 1025 return ((xmlNodePtr)NULL); 1026 } 1027 1028 /* 1029 * find_share(group, path) 1030 * 1031 * Search all the shares in the specified group for one that has the 1032 * specified path. 1033 */ 1034 1035 static sa_share_t 1036 find_share(sa_group_t group, char *sharepath) 1037 { 1038 sa_share_t share; 1039 char *path; 1040 1041 for (share = sa_get_share(group, NULL); share != NULL; 1042 share = sa_get_next_share(share)) { 1043 path = sa_get_share_attr(share, "path"); 1044 if (path != NULL && strcmp(path, sharepath) == 0) { 1045 sa_free_attr_string(path); 1046 break; 1047 } 1048 if (path != NULL) 1049 sa_free_attr_string(path); 1050 } 1051 return (share); 1052 } 1053 1054 /* 1055 * sa_get_sub_group(group) 1056 * 1057 * Get the first sub-group of group. The sa_get_next_group() function 1058 * can be used to get the rest. This is currently only used for ZFS 1059 * sub-groups but could be used to implement a more general mechanism. 1060 */ 1061 1062 sa_group_t 1063 sa_get_sub_group(sa_group_t group) 1064 { 1065 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1066 (xmlChar *)"group")); 1067 } 1068 1069 /* 1070 * sa_find_share(sharepath) 1071 * Finds a share regardless of group. In the future, this 1072 * function should utilize a cache and hash table of some kind. 1073 * The current assumption is that a path will only be shared 1074 * once. In the future, this may change as implementation of 1075 * resource names comes into being. 1076 */ 1077 sa_share_t 1078 sa_find_share(sa_handle_t handle, char *sharepath) 1079 { 1080 sa_group_t group; 1081 sa_group_t zgroup; 1082 sa_share_t share = NULL; 1083 int done = 0; 1084 1085 for (group = sa_get_group(handle, NULL); group != NULL && !done; 1086 group = sa_get_next_group(group)) { 1087 if (is_zfs_group(group)) { 1088 for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1089 (xmlChar *)"group"); 1090 zgroup != NULL; zgroup = sa_get_next_group(zgroup)) { 1091 share = find_share(zgroup, sharepath); 1092 if (share != NULL) 1093 break; 1094 } 1095 } else { 1096 share = find_share(group, sharepath); 1097 } 1098 if (share != NULL) 1099 break; 1100 } 1101 return (share); 1102 } 1103 1104 /* 1105 * sa_check_path(group, path, strictness) 1106 * 1107 * check that path is a valid path relative to the group. Currently, 1108 * we are ignoring the group and checking only the NFS rules. Later, 1109 * we may want to use the group to then check against the protocols 1110 * enabled on the group. The strictness values mean: 1111 * SA_CHECK_NORMAL == only check newpath against shares that are active 1112 * SA_CHECK_STRICT == check newpath against both active shares and those 1113 * stored in the repository 1114 */ 1115 1116 int 1117 sa_check_path(sa_group_t group, char *path, int strictness) 1118 { 1119 sa_handle_t handle; 1120 1121 handle = sa_find_group_handle(group); 1122 return (validpath(handle, path, strictness)); 1123 } 1124 1125 /* 1126 * _sa_add_share(group, sharepath, persist, *error) 1127 * 1128 * common code for all types of add_share. sa_add_share() is the 1129 * public API, we also need to be able to do this when parsing legacy 1130 * files and construction of the internal configuration while 1131 * extracting config info from SMF. 1132 */ 1133 1134 sa_share_t 1135 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1136 { 1137 xmlNodePtr node = NULL; 1138 int err; 1139 1140 err = SA_OK; /* assume success */ 1141 1142 node = xmlNewChild((xmlNodePtr)group, NULL, 1143 (xmlChar *)"share", NULL); 1144 if (node != NULL) { 1145 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); 1146 xmlSetProp(node, (xmlChar *)"type", persist ? 1147 (xmlChar *)"persist" : (xmlChar *)"transient"); 1148 if (persist != SA_SHARE_TRANSIENT) { 1149 /* 1150 * persistent shares come in two flavors: SMF and 1151 * ZFS. Sort this one out based on target group and 1152 * path type. Currently, only NFS is supported in the 1153 * ZFS group and it is always on. 1154 */ 1155 if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) { 1156 err = sa_zfs_set_sharenfs(group, sharepath, 1); 1157 } else { 1158 sa_handle_impl_t impl_handle; 1159 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1160 if (impl_handle != NULL) 1161 err = sa_commit_share(impl_handle->scfhandle, group, 1162 (sa_share_t)node); 1163 else 1164 err = SA_SYSTEM_ERR; 1165 } 1166 } 1167 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { 1168 /* called by the dfstab parser so could be a show */ 1169 err = SA_OK; 1170 } 1171 if (err != SA_OK) { 1172 /* 1173 * we couldn't commit to the repository so undo 1174 * our internal state to reflect reality. 1175 */ 1176 xmlUnlinkNode(node); 1177 xmlFreeNode(node); 1178 node = NULL; 1179 } 1180 } else { 1181 err = SA_NO_MEMORY; 1182 } 1183 if (error != NULL) 1184 *error = err; 1185 return (node); 1186 } 1187 1188 /* 1189 * sa_add_share(group, sharepath, persist, *error) 1190 * 1191 * Add a new share object to the specified group. The share will 1192 * have the specified sharepath and will only be constructed if 1193 * it is a valid path to be shared. NULL is returned on error 1194 * and a detailed error value will be returned via the error 1195 * pointer. 1196 */ 1197 sa_share_t 1198 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1199 { 1200 xmlNodePtr node = NULL; 1201 sa_share_t dup; 1202 int strictness = SA_CHECK_NORMAL; 1203 sa_handle_t handle; 1204 1205 /* 1206 * If the share is to be permanent, use strict checking so a 1207 * bad config doesn't get created. Transient shares only need 1208 * to check against the currently active 1209 * shares. SA_SHARE_PARSER is a modifier used internally to 1210 * indicate that we are being called by the dfstab parser and 1211 * that we need strict checking in all cases. Normally persist 1212 * is in integer value but SA_SHARE_PARSER may be or'd into 1213 * it as an override. 1214 */ 1215 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT) 1216 strictness = SA_CHECK_STRICT; 1217 1218 handle = sa_find_group_handle(group); 1219 1220 if ((dup = sa_find_share(handle, sharepath)) == NULL && 1221 (*error = sa_check_path(group, sharepath, strictness)) == 1222 SA_OK) { 1223 node = _sa_add_share(group, sharepath, persist, error); 1224 } 1225 if (dup != NULL) 1226 *error = SA_DUPLICATE_NAME; 1227 1228 return ((sa_share_t)node); 1229 } 1230 1231 /* 1232 * sa_enable_share(share, protocol) 1233 * Enable the specified share to the specified protocol. 1234 * If protocol is NULL, then all protocols. 1235 */ 1236 int 1237 sa_enable_share(sa_share_t share, char *protocol) 1238 { 1239 char *sharepath; 1240 struct stat st; 1241 int err = 0; 1242 1243 sharepath = sa_get_share_attr(share, "path"); 1244 if (stat(sharepath, &st) < 0) { 1245 err = SA_NO_SUCH_PATH; 1246 } else { 1247 /* tell the server about the share */ 1248 if (protocol != NULL) { 1249 /* lookup protocol specific handler */ 1250 err = sa_proto_share(protocol, share); 1251 if (err == SA_OK) 1252 (void) sa_set_share_attr(share, "shared", "true"); 1253 } else { 1254 /* tell all protocols */ 1255 err = sa_proto_share("nfs", share); /* only NFS for now */ 1256 (void) sa_set_share_attr(share, "shared", "true"); 1257 } 1258 } 1259 if (sharepath != NULL) 1260 sa_free_attr_string(sharepath); 1261 return (err); 1262 } 1263 1264 /* 1265 * sa_disable_share(share, protocol) 1266 * Disable the specified share to the specified protocol. 1267 * If protocol is NULL, then all protocols. 1268 */ 1269 int 1270 sa_disable_share(sa_share_t share, char *protocol) 1271 { 1272 char *path; 1273 char *shared; 1274 int ret = SA_OK; 1275 1276 path = sa_get_share_attr(share, "path"); 1277 shared = sa_get_share_attr(share, "shared"); 1278 1279 if (protocol != NULL) { 1280 ret = sa_proto_unshare(protocol, path); 1281 } else { 1282 /* need to do all protocols */ 1283 ret = sa_proto_unshare("nfs", path); 1284 } 1285 if (ret == SA_OK) 1286 (void) sa_set_share_attr(share, "shared", NULL); 1287 if (path != NULL) 1288 sa_free_attr_string(path); 1289 if (shared != NULL) 1290 sa_free_attr_string(shared); 1291 return (ret); 1292 } 1293 1294 /* 1295 * sa_remove_share(share) 1296 * 1297 * remove the specified share from its containing group. 1298 * Remove from the SMF or ZFS configuration space. 1299 */ 1300 1301 int 1302 sa_remove_share(sa_share_t share) 1303 { 1304 sa_group_t group; 1305 int ret = SA_OK; 1306 char *type; 1307 int transient = 0; 1308 char *groupname; 1309 char *zfs; 1310 1311 type = sa_get_share_attr(share, "type"); 1312 group = sa_get_parent_group(share); 1313 zfs = sa_get_group_attr(group, "zfs"); 1314 groupname = sa_get_group_attr(group, "name"); 1315 if (type != NULL && strcmp(type, "persist") != 0) 1316 transient = 1; 1317 if (type != NULL) 1318 sa_free_attr_string(type); 1319 1320 /* remove the node from its group then free the memory */ 1321 1322 /* 1323 * need to test if "busy" 1324 */ 1325 /* only do SMF action if permanent */ 1326 if (!transient || zfs != NULL) { 1327 /* remove from legacy dfstab as well as possible SMF */ 1328 ret = sa_delete_legacy(share); 1329 if (ret == SA_OK) { 1330 if (!sa_group_is_zfs(group)) { 1331 sa_handle_impl_t impl_handle; 1332 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1333 if (impl_handle != NULL) 1334 ret = sa_delete_share(impl_handle->scfhandle, 1335 group, share); 1336 else 1337 ret = SA_SYSTEM_ERR; 1338 } else { 1339 char *sharepath = sa_get_share_attr(share, "path"); 1340 if (sharepath != NULL) { 1341 ret = sa_zfs_set_sharenfs(group, sharepath, 0); 1342 sa_free_attr_string(sharepath); 1343 } 1344 } 1345 } 1346 } 1347 if (groupname != NULL) 1348 sa_free_attr_string(groupname); 1349 if (zfs != NULL) 1350 sa_free_attr_string(zfs); 1351 1352 xmlUnlinkNode((xmlNodePtr)share); 1353 xmlFreeNode((xmlNodePtr)share); 1354 return (ret); 1355 } 1356 1357 /* 1358 * sa_move_share(group, share) 1359 * 1360 * move the specified share to the specified group. Update SMF 1361 * appropriately. 1362 */ 1363 1364 int 1365 sa_move_share(sa_group_t group, sa_share_t share) 1366 { 1367 sa_group_t oldgroup; 1368 int ret = SA_OK; 1369 1370 /* remove the node from its group then free the memory */ 1371 1372 oldgroup = sa_get_parent_group(share); 1373 if (oldgroup != group) { 1374 sa_handle_impl_t impl_handle; 1375 xmlUnlinkNode((xmlNodePtr)share); 1376 /* now that the share isn't in its old group, add to the new one */ 1377 xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share); 1378 /* need to deal with SMF */ 1379 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1380 if (impl_handle != NULL) { 1381 /* 1382 * need to remove from old group first and then add to 1383 * new group. Ideally, we would do the other order but 1384 * need to avoid having the share in two groups at the 1385 * same time. 1386 */ 1387 ret = sa_delete_share(impl_handle->scfhandle, oldgroup, share); 1388 if (ret == SA_OK) 1389 ret = sa_commit_share(impl_handle->scfhandle, group, share); 1390 } else { 1391 ret = SA_SYSTEM_ERR; 1392 } 1393 } 1394 return (ret); 1395 } 1396 1397 /* 1398 * sa_get_parent_group(share) 1399 * 1400 * Return the containg group for the share. If a group was actually 1401 * passed in, we don't want a parent so return NULL. 1402 */ 1403 1404 sa_group_t 1405 sa_get_parent_group(sa_share_t share) 1406 { 1407 xmlNodePtr node = NULL; 1408 if (share != NULL) { 1409 node = ((xmlNodePtr)share)->parent; 1410 /* 1411 * make sure parent is a group and not sharecfg since 1412 * we may be cheating and passing in a group. 1413 * Eventually, groups of groups might come into being. 1414 */ 1415 if (node == NULL || 1416 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0) 1417 node = NULL; 1418 } 1419 return ((sa_group_t)node); 1420 } 1421 1422 /* 1423 * _sa_create_group(impl_handle, groupname) 1424 * 1425 * Create a group in the document. The caller will need to deal with 1426 * configuration store and activation. 1427 */ 1428 1429 sa_group_t 1430 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname) 1431 { 1432 xmlNodePtr node = NULL; 1433 1434 if (sa_valid_group_name(groupname)) { 1435 node = xmlNewChild(impl_handle->tree, NULL, 1436 (xmlChar *)"group", NULL); 1437 if (node != NULL) { 1438 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1439 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1440 } 1441 } 1442 return ((sa_group_t)node); 1443 } 1444 1445 /* 1446 * _sa_create_zfs_group(group, groupname) 1447 * 1448 * Create a ZFS subgroup under the specified group. This may 1449 * eventually form the basis of general sub-groups, but is currently 1450 * restricted to ZFS. 1451 */ 1452 sa_group_t 1453 _sa_create_zfs_group(sa_group_t group, char *groupname) 1454 { 1455 xmlNodePtr node = NULL; 1456 1457 node = xmlNewChild((xmlNodePtr)group, NULL, 1458 (xmlChar *)"group", NULL); 1459 if (node != NULL) { 1460 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1461 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1462 } 1463 1464 return ((sa_group_t)node); 1465 } 1466 1467 /* 1468 * sa_create_group(groupname, *error) 1469 * 1470 * Create a new group with groupname. Need to validate that it is a 1471 * legal name for SMF and the construct the SMF service instance of 1472 * svc:/network/shares/group to implement the group. All necessary 1473 * operational properties must be added to the group at this point 1474 * (via the SMF transaction model). 1475 */ 1476 sa_group_t 1477 sa_create_group(sa_handle_t handle, char *groupname, int *error) 1478 { 1479 xmlNodePtr node = NULL; 1480 sa_group_t group; 1481 int ret; 1482 char rbacstr[256]; 1483 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 1484 1485 ret = SA_OK; 1486 1487 if (impl_handle == NULL || impl_handle->scfhandle == NULL) { 1488 ret = SA_SYSTEM_ERR; 1489 goto err; 1490 } 1491 1492 group = sa_get_group(handle, groupname); 1493 if (group != NULL) { 1494 ret = SA_DUPLICATE_NAME; 1495 } else { 1496 if (sa_valid_group_name(groupname)) { 1497 node = xmlNewChild(impl_handle->tree, NULL, 1498 (xmlChar *)"group", NULL); 1499 if (node != NULL) { 1500 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1501 /* default to the group being enabled */ 1502 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1503 ret = sa_create_instance(impl_handle->scfhandle, groupname); 1504 if (ret == SA_OK) { 1505 ret = sa_start_transaction(impl_handle->scfhandle, 1506 "operation"); 1507 } 1508 if (ret == SA_OK) { 1509 ret = sa_set_property(impl_handle->scfhandle, 1510 "state", "enabled"); 1511 if (ret == SA_OK) { 1512 ret = sa_end_transaction(impl_handle->scfhandle); 1513 } else { 1514 sa_abort_transaction(impl_handle->scfhandle); 1515 } 1516 } 1517 if (ret == SA_OK) { 1518 /* initialize the RBAC strings */ 1519 ret = sa_start_transaction(impl_handle->scfhandle, 1520 "general"); 1521 if (ret == SA_OK) { 1522 (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 1523 SA_RBAC_MANAGE, groupname); 1524 ret = sa_set_property(impl_handle->scfhandle, 1525 "action_authorization", 1526 rbacstr); 1527 } 1528 if (ret == SA_OK) { 1529 (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 1530 SA_RBAC_VALUE, groupname); 1531 ret = sa_set_property(impl_handle->scfhandle, 1532 "value_authorization", 1533 rbacstr); 1534 } 1535 if (ret == SA_OK) { 1536 ret = sa_end_transaction(impl_handle->scfhandle); 1537 } else { 1538 sa_abort_transaction(impl_handle->scfhandle); 1539 } 1540 } 1541 if (ret != SA_OK) { 1542 /* 1543 * Couldn't commit the group so we need to 1544 * undo internally. 1545 */ 1546 xmlUnlinkNode(node); 1547 xmlFreeNode(node); 1548 node = NULL; 1549 } 1550 } else { 1551 ret = SA_NO_MEMORY; 1552 } 1553 } else { 1554 ret = SA_INVALID_NAME; 1555 } 1556 } 1557 err: 1558 if (error != NULL) 1559 *error = ret; 1560 return ((sa_group_t)node); 1561 } 1562 1563 /* 1564 * sa_remove_group(group) 1565 * 1566 * Remove the specified group. This deletes from the SMF repository. 1567 * All property groups and properties are removed. 1568 */ 1569 1570 int 1571 sa_remove_group(sa_group_t group) 1572 { 1573 char *name; 1574 int ret = SA_OK; 1575 sa_handle_impl_t impl_handle; 1576 1577 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1578 if (impl_handle != NULL) { 1579 name = sa_get_group_attr(group, "name"); 1580 if (name != NULL) { 1581 ret = sa_delete_instance(impl_handle->scfhandle, name); 1582 sa_free_attr_string(name); 1583 } 1584 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */ 1585 xmlFreeNode((xmlNodePtr)group); /* now it is gone */ 1586 } else { 1587 ret = SA_SYSTEM_ERR; 1588 } 1589 return (ret); 1590 } 1591 1592 /* 1593 * sa_update_config() 1594 * 1595 * Used to update legacy files that need to be updated in bulk 1596 * Currently, this is a placeholder and will go away in a future 1597 * release. 1598 */ 1599 1600 int 1601 sa_update_config(sa_handle_t handle) 1602 { 1603 /* 1604 * do legacy files first so we can tell when they change. 1605 * This will go away when we start updating individual records 1606 * rather than the whole file. 1607 */ 1608 update_legacy_config(handle); 1609 return (SA_OK); 1610 } 1611 1612 /* 1613 * get_node_attr(node, tag) 1614 * 1615 * Get the speficied tag(attribute) if it exists on the node. This is 1616 * used internally by a number of attribute oriented functions. 1617 */ 1618 1619 static char * 1620 get_node_attr(void *nodehdl, char *tag) 1621 { 1622 xmlNodePtr node = (xmlNodePtr)nodehdl; 1623 xmlChar *name = NULL; 1624 1625 if (node != NULL) { 1626 name = xmlGetProp(node, (xmlChar *)tag); 1627 } 1628 return ((char *)name); 1629 } 1630 1631 /* 1632 * get_node_attr(node, tag) 1633 * 1634 * Set the speficied tag(attribute) to the specified value This is 1635 * used internally by a number of attribute oriented functions. It 1636 * doesn't update the repository, only the internal document state. 1637 */ 1638 1639 void 1640 set_node_attr(void *nodehdl, char *tag, char *value) 1641 { 1642 xmlNodePtr node = (xmlNodePtr)nodehdl; 1643 if (node != NULL && tag != NULL) { 1644 if (value != NULL) { 1645 xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value); 1646 } else { 1647 xmlUnsetProp(node, (xmlChar *)tag); 1648 } 1649 } 1650 } 1651 1652 /* 1653 * sa_get_group_attr(group, tag) 1654 * 1655 * Get the specied attribute, if defined, for the group. 1656 */ 1657 1658 char * 1659 sa_get_group_attr(sa_group_t group, char *tag) 1660 { 1661 return (get_node_attr((void *)group, tag)); 1662 } 1663 1664 /* 1665 * sa_set_group_attr(group, tag, value) 1666 * 1667 * set the specified tag/attribute on the group using value as its 1668 * value. 1669 * 1670 * This will result in setting the property in the SMF repository as 1671 * well as in the internal document. 1672 */ 1673 1674 int 1675 sa_set_group_attr(sa_group_t group, char *tag, char *value) 1676 { 1677 int ret; 1678 char *groupname; 1679 sa_handle_impl_t impl_handle; 1680 1681 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1682 if (impl_handle != NULL) { 1683 groupname = sa_get_group_attr(group, "name"); 1684 ret = sa_get_instance(impl_handle->scfhandle, groupname); 1685 if (ret == SA_OK) { 1686 set_node_attr((void *)group, tag, value); 1687 ret = sa_start_transaction(impl_handle->scfhandle, "operation"); 1688 if (ret == SA_OK) { 1689 ret = sa_set_property(impl_handle->scfhandle, tag, value); 1690 if (ret == SA_OK) 1691 (void) sa_end_transaction(impl_handle->scfhandle); 1692 else { 1693 sa_abort_transaction(impl_handle->scfhandle); 1694 } 1695 } 1696 } 1697 if (groupname != NULL) 1698 sa_free_attr_string(groupname); 1699 } else { 1700 ret = SA_SYSTEM_ERR; 1701 } 1702 return (ret); 1703 } 1704 1705 /* 1706 * sa_get_share_attr(share, tag) 1707 * 1708 * Return the value of the tag/attribute set on the specified 1709 * share. Returns NULL if the tag doesn't exist. 1710 */ 1711 1712 char * 1713 sa_get_share_attr(sa_share_t share, char *tag) 1714 { 1715 return (get_node_attr((void *)share, tag)); 1716 } 1717 1718 /* 1719 * sa_get_resource(group, resource) 1720 * 1721 * Search all the shares in the speified group for a share with a 1722 * resource name matching the one specified. 1723 * 1724 * In the future, it may be advantageous to allow group to be NULL and 1725 * search all groups but that isn't needed at present. 1726 */ 1727 1728 sa_share_t 1729 sa_get_resource(sa_group_t group, char *resource) 1730 { 1731 sa_share_t share = NULL; 1732 char *name = NULL; 1733 1734 if (resource != NULL) { 1735 for (share = sa_get_share(group, NULL); share != NULL; 1736 share = sa_get_next_share(share)) { 1737 name = sa_get_share_attr(share, "resource"); 1738 if (name != NULL) { 1739 if (strcmp(name, resource) == 0) 1740 break; 1741 sa_free_attr_string(name); 1742 name = NULL; 1743 } 1744 } 1745 if (name != NULL) 1746 sa_free_attr_string(name); 1747 } 1748 return ((sa_share_t)share); 1749 } 1750 1751 /* 1752 * _sa_set_share_description(share, description) 1753 * 1754 * Add a description tag with text contents to the specified share. 1755 * A separate XML tag is used rather than a property. 1756 */ 1757 1758 xmlNodePtr 1759 _sa_set_share_description(sa_share_t share, char *content) 1760 { 1761 xmlNodePtr node; 1762 node = xmlNewChild((xmlNodePtr)share, 1763 NULL, (xmlChar *)"description", NULL); 1764 xmlNodeSetContent(node, (xmlChar *)content); 1765 return (node); 1766 } 1767 1768 /* 1769 * sa_set_share_attr(share, tag, value) 1770 * 1771 * Set the share attribute specified by tag to the specified value. In 1772 * the case of "resource", enforce a no duplicates in a group rule. If 1773 * the share is not transient, commit the changes to the repository 1774 * else just update the share internally. 1775 */ 1776 1777 int 1778 sa_set_share_attr(sa_share_t share, char *tag, char *value) 1779 { 1780 sa_group_t group; 1781 sa_share_t resource; 1782 int ret = SA_OK; 1783 1784 group = sa_get_parent_group(share); 1785 1786 /* 1787 * There are some attributes that may have specific 1788 * restrictions on them. Initially, only "resource" has 1789 * special meaning that needs to be checked. Only one instance 1790 * of a resource name may exist within a group. 1791 */ 1792 1793 if (strcmp(tag, "resource") == 0) { 1794 resource = sa_get_resource(group, value); 1795 if (resource != share && resource != NULL) 1796 ret = SA_DUPLICATE_NAME; 1797 } 1798 if (ret == SA_OK) { 1799 set_node_attr((void *)share, tag, value); 1800 if (group != NULL) { 1801 char *type; 1802 /* we can probably optimize this some */ 1803 type = sa_get_share_attr(share, "type"); 1804 if (type == NULL || strcmp(type, "transient") != 0) { 1805 sa_handle_impl_t impl_handle; 1806 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1807 if (impl_handle != NULL) 1808 ret = sa_commit_share(impl_handle->scfhandle, 1809 group, share); 1810 else 1811 ret = SA_SYSTEM_ERR; 1812 } 1813 if (type != NULL) 1814 sa_free_attr_string(type); 1815 } 1816 } 1817 return (ret); 1818 } 1819 1820 /* 1821 * sa_get_property_attr(prop, tag) 1822 * 1823 * Get the value of the specified property attribute. Standard 1824 * attributes are "type" and "value". 1825 */ 1826 1827 char * 1828 sa_get_property_attr(sa_property_t prop, char *tag) 1829 { 1830 return (get_node_attr((void *)prop, tag)); 1831 } 1832 1833 /* 1834 * sa_get_optionset_attr(prop, tag) 1835 * 1836 * Get the value of the specified property attribute. Standard 1837 * attribute is "type". 1838 */ 1839 1840 char * 1841 sa_get_optionset_attr(sa_property_t optionset, char *tag) 1842 { 1843 return (get_node_attr((void *)optionset, tag)); 1844 1845 } 1846 1847 /* 1848 * sa_set_optionset_attr(optionset, tag, value) 1849 * 1850 * Set the specified attribute(tag) to the specified value on the 1851 * optionset. 1852 */ 1853 1854 void 1855 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 1856 { 1857 set_node_attr((void *)optionset, tag, value); 1858 } 1859 1860 /* 1861 * sa_free_attr_string(string) 1862 * 1863 * Free the string that was returned in one of the sa_get_*_attr() 1864 * functions. 1865 */ 1866 1867 void 1868 sa_free_attr_string(char *string) 1869 { 1870 xmlFree((xmlChar *)string); 1871 } 1872 1873 /* 1874 * sa_get_optionset(group, proto) 1875 * 1876 * Return the optionset, if it exists, that is associated with the 1877 * specified protocol. 1878 */ 1879 1880 sa_optionset_t 1881 sa_get_optionset(void *group, char *proto) 1882 { 1883 xmlNodePtr node; 1884 xmlChar *value = NULL; 1885 1886 for (node = ((xmlNodePtr)group)->children; node != NULL; 1887 node = node->next) { 1888 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 1889 value = xmlGetProp(node, (xmlChar *)"type"); 1890 if (proto != NULL) { 1891 if (value != NULL && 1892 xmlStrcmp(value, (xmlChar *)proto) == 0) { 1893 break; 1894 } 1895 if (value != NULL) { 1896 xmlFree(value); 1897 value = NULL; 1898 } 1899 } else { 1900 break; 1901 } 1902 } 1903 } 1904 if (value != NULL) 1905 xmlFree(value); 1906 return ((sa_optionset_t)node); 1907 } 1908 1909 /* 1910 * sa_get_next_optionset(optionset) 1911 * 1912 * Return the next optionset in the group. NULL if this was the last. 1913 */ 1914 1915 sa_optionset_t 1916 sa_get_next_optionset(sa_optionset_t optionset) 1917 { 1918 xmlNodePtr node; 1919 1920 for (node = ((xmlNodePtr)optionset)->next; node != NULL; 1921 node = node->next) { 1922 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 1923 break; 1924 } 1925 } 1926 return ((sa_optionset_t)node); 1927 } 1928 1929 /* 1930 * sa_get_security(group, sectype, proto) 1931 * 1932 * Return the security optionset. The internal name is a hold over 1933 * from the implementation and will be changed before the API is 1934 * finalized. This is really a named optionset that can be negotiated 1935 * as a group of properties (like NFS security options). 1936 */ 1937 1938 sa_security_t 1939 sa_get_security(sa_group_t group, char *sectype, char *proto) 1940 { 1941 xmlNodePtr node; 1942 xmlChar *value = NULL; 1943 1944 for (node = ((xmlNodePtr)group)->children; node != NULL; 1945 node = node->next) { 1946 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 1947 if (proto != NULL) { 1948 value = xmlGetProp(node, (xmlChar *)"type"); 1949 if (value == NULL || 1950 (value != NULL && 1951 xmlStrcmp(value, (xmlChar *)proto) != 0)) { 1952 /* it doesn't match so continue */ 1953 xmlFree(value); 1954 value = NULL; 1955 continue; 1956 } 1957 } 1958 if (value != NULL) { 1959 xmlFree(value); 1960 value = NULL; 1961 } 1962 /* potential match */ 1963 if (sectype != NULL) { 1964 value = xmlGetProp(node, (xmlChar *)"sectype"); 1965 if (value != NULL && 1966 xmlStrcmp(value, (xmlChar *)sectype) == 0) { 1967 break; 1968 } 1969 } else { 1970 break; 1971 } 1972 } 1973 if (value != NULL) { 1974 xmlFree(value); 1975 value = NULL; 1976 } 1977 } 1978 if (value != NULL) 1979 xmlFree(value); 1980 return ((sa_security_t)node); 1981 } 1982 1983 /* 1984 * sa_get_next_security(security) 1985 * 1986 * Get the next security optionset if one exists. 1987 */ 1988 1989 sa_security_t 1990 sa_get_next_security(sa_security_t security) 1991 { 1992 xmlNodePtr node; 1993 1994 for (node = ((xmlNodePtr)security)->next; node != NULL; 1995 node = node->next) { 1996 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 1997 break; 1998 } 1999 } 2000 return ((sa_security_t)node); 2001 } 2002 2003 /* 2004 * sa_get_property(optionset, prop) 2005 * 2006 * Get the property object with the name specified in prop from the 2007 * optionset. 2008 */ 2009 2010 sa_property_t 2011 sa_get_property(sa_optionset_t optionset, char *prop) 2012 { 2013 xmlNodePtr node = (xmlNodePtr)optionset; 2014 xmlChar *value = NULL; 2015 2016 if (optionset == NULL) 2017 return (NULL); 2018 2019 for (node = node->children; node != NULL; 2020 node = node->next) { 2021 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2022 if (prop == NULL) 2023 break; 2024 value = xmlGetProp(node, (xmlChar *)"type"); 2025 if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) { 2026 break; 2027 } 2028 if (value != NULL) { 2029 xmlFree(value); 2030 value = NULL; 2031 } 2032 } 2033 } 2034 if (value != NULL) 2035 xmlFree(value); 2036 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2037 /* avoid a non option node -- it is possible to be a text node */ 2038 node = NULL; 2039 } 2040 return ((sa_property_t)node); 2041 } 2042 2043 /* 2044 * sa_get_next_property(property) 2045 * 2046 * Get the next property following the specified property. NULL if 2047 * this was the last. 2048 */ 2049 2050 sa_property_t 2051 sa_get_next_property(sa_property_t property) 2052 { 2053 xmlNodePtr node; 2054 2055 for (node = ((xmlNodePtr)property)->next; node != NULL; 2056 node = node->next) { 2057 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2058 break; 2059 } 2060 } 2061 return ((sa_property_t)node); 2062 } 2063 2064 /* 2065 * sa_set_share_description(share, content) 2066 * 2067 * Set the description of share to content. 2068 */ 2069 2070 int 2071 sa_set_share_description(sa_share_t share, char *content) 2072 { 2073 xmlNodePtr node; 2074 sa_group_t group; 2075 int ret = SA_OK; 2076 2077 for (node = ((xmlNodePtr)share)->children; node != NULL; 2078 node = node->next) { 2079 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2080 break; 2081 } 2082 } 2083 group = sa_get_parent_group(share); 2084 /* no existing description but want to add */ 2085 if (node == NULL && content != NULL) { 2086 /* add a description */ 2087 node = _sa_set_share_description(share, content); 2088 } else if (node != NULL && content != NULL) { 2089 /* update a description */ 2090 xmlNodeSetContent(node, (xmlChar *)content); 2091 } else if (node != NULL && content == NULL) { 2092 /* remove an existing description */ 2093 xmlUnlinkNode(node); 2094 xmlFreeNode(node); 2095 } 2096 if (group != NULL && is_persistent((sa_group_t)share)) { 2097 sa_handle_impl_t impl_handle; 2098 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2099 if (impl_handle != NULL) 2100 ret = sa_commit_share(impl_handle->scfhandle, group, share); 2101 else 2102 ret = SA_SYSTEM_ERR; 2103 } 2104 return (ret); 2105 } 2106 2107 /* 2108 * fixproblemchars(string) 2109 * 2110 * don't want any newline or tab characters in the text since these 2111 * could break display of data and legacy file formats. 2112 */ 2113 static void 2114 fixproblemchars(char *str) 2115 { 2116 int c; 2117 for (c = *str; c != '\0'; c = *++str) { 2118 if (c == '\t' || c == '\n') 2119 *str = ' '; 2120 else if (c == '"') 2121 *str = '\''; 2122 } 2123 } 2124 2125 /* 2126 * sa_get_share_description(share) 2127 * 2128 * Return the description text for the specified share if it 2129 * exists. NULL if no description exists. 2130 */ 2131 2132 char * 2133 sa_get_share_description(sa_share_t share) 2134 { 2135 xmlChar *description = NULL; 2136 xmlNodePtr node; 2137 2138 for (node = ((xmlNodePtr)share)->children; node != NULL; 2139 node = node->next) { 2140 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2141 break; 2142 } 2143 } 2144 if (node != NULL) { 2145 description = xmlNodeGetContent((xmlNodePtr)share); 2146 fixproblemchars((char *)description); 2147 } 2148 return ((char *)description); 2149 } 2150 2151 /* 2152 * sa_free(share_description(description) 2153 * 2154 * Free the description string. 2155 */ 2156 2157 void 2158 sa_free_share_description(char *description) 2159 { 2160 xmlFree((xmlChar *)description); 2161 } 2162 2163 /* 2164 * sa_create_optionset(group, proto) 2165 * 2166 * Create an optionset for the specified protocol in the specied 2167 * group. This is manifested as a property group within SMF. 2168 */ 2169 2170 sa_optionset_t 2171 sa_create_optionset(sa_group_t group, char *proto) 2172 { 2173 sa_optionset_t optionset; 2174 sa_group_t parent = group; 2175 2176 optionset = sa_get_optionset(group, proto); 2177 if (optionset != NULL) { 2178 /* can't have a duplicate protocol */ 2179 optionset = NULL; 2180 } else { 2181 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 2182 NULL, 2183 (xmlChar *)"optionset", 2184 NULL); 2185 /* 2186 * only put to repository if on a group and we were 2187 * able to create an optionset. 2188 */ 2189 if (optionset != NULL) { 2190 char oname[256]; 2191 char *groupname; 2192 char *id = NULL; 2193 2194 if (sa_is_share(group)) 2195 parent = sa_get_parent_group((sa_share_t)group); 2196 2197 sa_set_optionset_attr(optionset, "type", proto); 2198 2199 if (sa_is_share(group)) { 2200 id = sa_get_share_attr((sa_share_t)group, "id"); 2201 } 2202 (void) sa_optionset_name(optionset, oname, 2203 sizeof (oname), id); 2204 groupname = sa_get_group_attr(parent, "name"); 2205 if (groupname != NULL && is_persistent(group)) { 2206 sa_handle_impl_t impl_handle; 2207 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2208 assert(impl_handle != NULL); 2209 if (impl_handle != NULL) { 2210 (void) sa_get_instance(impl_handle->scfhandle, 2211 groupname); 2212 (void) sa_create_pgroup(impl_handle->scfhandle, oname); 2213 } 2214 } 2215 if (groupname != NULL) 2216 sa_free_attr_string(groupname); 2217 if (id != NULL) 2218 sa_free_attr_string(id); 2219 } 2220 } 2221 return (optionset); 2222 } 2223 2224 /* 2225 * sa_get_property_parent(property) 2226 * 2227 * Given a property, return the object it is a property of. This will 2228 * be an optionset of some type. 2229 */ 2230 2231 static sa_optionset_t 2232 sa_get_property_parent(sa_property_t property) 2233 { 2234 xmlNodePtr node = NULL; 2235 2236 if (property != NULL) { 2237 node = ((xmlNodePtr)property)->parent; 2238 } 2239 return ((sa_optionset_t)node); 2240 } 2241 2242 /* 2243 * sa_get_optionset_parent(optionset) 2244 * 2245 * Return the parent of the specified optionset. This could be a group 2246 * or a share. 2247 */ 2248 2249 static sa_group_t 2250 sa_get_optionset_parent(sa_optionset_t optionset) 2251 { 2252 xmlNodePtr node = NULL; 2253 2254 if (optionset != NULL) { 2255 node = ((xmlNodePtr)optionset)->parent; 2256 } 2257 return ((sa_group_t)node); 2258 } 2259 2260 /* 2261 * zfs_needs_update(share) 2262 * 2263 * In order to avoid making multiple updates to a ZFS share when 2264 * setting properties, the share attribute "changed" will be set to 2265 * true when a property is added or modifed. When done adding 2266 * properties, we can then detect that an update is needed. We then 2267 * clear the state here to detect additional changes. 2268 */ 2269 2270 static int 2271 zfs_needs_update(sa_share_t share) 2272 { 2273 char *attr; 2274 int result = 0; 2275 2276 attr = sa_get_share_attr(share, "changed"); 2277 if (attr != NULL) { 2278 sa_free_attr_string(attr); 2279 result = 1; 2280 } 2281 set_node_attr((void *)share, "changed", NULL); 2282 return (result); 2283 } 2284 2285 /* 2286 * zfs_set_update(share) 2287 * 2288 * Set the changed attribute of the share to true. 2289 */ 2290 2291 static void 2292 zfs_set_update(sa_share_t share) 2293 { 2294 set_node_attr((void *)share, "changed", "true"); 2295 } 2296 2297 /* 2298 * sa_commit_properties(optionset, clear) 2299 * 2300 * Check if SMF or ZFS config and either update or abort the pending 2301 * changes. 2302 */ 2303 2304 int 2305 sa_commit_properties(sa_optionset_t optionset, int clear) 2306 { 2307 sa_group_t group; 2308 sa_group_t parent; 2309 int zfs = 0; 2310 int needsupdate = 0; 2311 int ret = SA_OK; 2312 sa_handle_impl_t impl_handle; 2313 2314 group = sa_get_optionset_parent(optionset); 2315 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 2316 /* only update ZFS if on a share */ 2317 parent = sa_get_parent_group(group); 2318 zfs++; 2319 if (parent != NULL && is_zfs_group(parent)) { 2320 needsupdate = zfs_needs_update(group); 2321 } else { 2322 zfs = 0; 2323 } 2324 } 2325 if (zfs) { 2326 if (!clear && needsupdate) 2327 ret = sa_zfs_update((sa_share_t)group); 2328 } else { 2329 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2330 if (impl_handle != NULL) { 2331 if (clear) 2332 (void) sa_abort_transaction(impl_handle->scfhandle); 2333 else 2334 ret = sa_end_transaction(impl_handle->scfhandle); 2335 } else { 2336 ret = SA_SYSTEM_ERR; 2337 } 2338 } 2339 return (ret); 2340 } 2341 2342 /* 2343 * sa_destroy_optionset(optionset) 2344 * 2345 * Remove the optionset from its group. Update the repostory to 2346 * reflect this change. 2347 */ 2348 2349 int 2350 sa_destroy_optionset(sa_optionset_t optionset) 2351 { 2352 char name[256]; 2353 int len; 2354 int ret; 2355 char *id = NULL; 2356 sa_group_t group; 2357 int ispersist = 1; 2358 2359 /* now delete the prop group */ 2360 group = sa_get_optionset_parent(optionset); 2361 if (group != NULL && sa_is_share(group)) { 2362 ispersist = is_persistent(group); 2363 id = sa_get_share_attr((sa_share_t)group, "id"); 2364 } 2365 if (ispersist) { 2366 sa_handle_impl_t impl_handle; 2367 len = sa_optionset_name(optionset, name, sizeof (name), id); 2368 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2369 if (impl_handle != NULL) { 2370 if (len > 0) { 2371 ret = sa_delete_pgroup(impl_handle->scfhandle, name); 2372 } 2373 } else { 2374 ret = SA_SYSTEM_ERR; 2375 } 2376 } 2377 xmlUnlinkNode((xmlNodePtr)optionset); 2378 xmlFreeNode((xmlNodePtr)optionset); 2379 if (id != NULL) 2380 sa_free_attr_string(id); 2381 return (ret); 2382 } 2383 2384 /* private to the implementation */ 2385 int 2386 _sa_remove_optionset(sa_optionset_t optionset) 2387 { 2388 int ret = SA_OK; 2389 2390 xmlUnlinkNode((xmlNodePtr)optionset); 2391 xmlFreeNode((xmlNodePtr)optionset); 2392 return (ret); 2393 } 2394 2395 /* 2396 * sa_create_security(group, sectype, proto) 2397 * 2398 * Create a security optionset (one that has a type name and a 2399 * proto). Security is left over from a pure NFS implementation. The 2400 * naming will change in the future when the API is released. 2401 */ 2402 sa_security_t 2403 sa_create_security(sa_group_t group, char *sectype, char *proto) 2404 { 2405 sa_security_t security; 2406 char *id = NULL; 2407 sa_group_t parent; 2408 char *groupname = NULL; 2409 2410 if (group != NULL && sa_is_share(group)) { 2411 id = sa_get_share_attr((sa_share_t)group, "id"); 2412 parent = sa_get_parent_group(group); 2413 if (parent != NULL) 2414 groupname = sa_get_group_attr(parent, "name"); 2415 } else if (group != NULL) { 2416 groupname = sa_get_group_attr(group, "name"); 2417 } 2418 2419 security = sa_get_security(group, sectype, proto); 2420 if (security != NULL) { 2421 /* can't have a duplicate security option */ 2422 security = NULL; 2423 } else { 2424 security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 2425 NULL, 2426 (xmlChar *)"security", 2427 NULL); 2428 if (security != NULL) { 2429 char oname[256]; 2430 sa_set_security_attr(security, "type", proto); 2431 2432 sa_set_security_attr(security, "sectype", sectype); 2433 (void) sa_security_name(security, oname, 2434 sizeof (oname), id); 2435 if (groupname != NULL && is_persistent(group)) { 2436 sa_handle_impl_t impl_handle; 2437 impl_handle = 2438 (sa_handle_impl_t)sa_find_group_handle(group); 2439 if (impl_handle != NULL) { 2440 (void) sa_get_instance(impl_handle->scfhandle, 2441 groupname); 2442 (void) sa_create_pgroup(impl_handle->scfhandle, 2443 oname); 2444 } 2445 } 2446 } 2447 } 2448 if (groupname != NULL) 2449 sa_free_attr_string(groupname); 2450 return (security); 2451 } 2452 2453 /* 2454 * sa_destroy_security(security) 2455 * 2456 * Remove the specified optionset from the document and the 2457 * configuration. 2458 */ 2459 2460 int 2461 sa_destroy_security(sa_security_t security) 2462 { 2463 char name[256]; 2464 int len; 2465 int ret = SA_OK; 2466 char *id = NULL; 2467 sa_group_t group; 2468 int iszfs = 0; 2469 int ispersist = 1; 2470 2471 group = sa_get_optionset_parent(security); 2472 2473 if (group != NULL) 2474 iszfs = sa_group_is_zfs(group); 2475 2476 if (group != NULL && !iszfs) { 2477 if (sa_is_share(group)) 2478 ispersist = is_persistent(group); 2479 id = sa_get_share_attr((sa_share_t)group, "id"); 2480 } 2481 if (ispersist) { 2482 len = sa_security_name(security, name, sizeof (name), id); 2483 if (!iszfs && len > 0) { 2484 sa_handle_impl_t impl_handle; 2485 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2486 if (impl_handle != NULL) { 2487 ret = sa_delete_pgroup(impl_handle->scfhandle, name); 2488 } else { 2489 ret = SA_SYSTEM_ERR; 2490 } 2491 } 2492 } 2493 xmlUnlinkNode((xmlNodePtr)security); 2494 xmlFreeNode((xmlNodePtr)security); 2495 if (iszfs) { 2496 ret = sa_zfs_update(group); 2497 } 2498 if (id != NULL) 2499 sa_free_attr_string(id); 2500 return (ret); 2501 } 2502 2503 /* 2504 * sa_get_security_attr(optionset, tag) 2505 * 2506 * Return the specified attribute value from the optionset. 2507 */ 2508 2509 char * 2510 sa_get_security_attr(sa_property_t optionset, char *tag) 2511 { 2512 return (get_node_attr((void *)optionset, tag)); 2513 2514 } 2515 2516 /* 2517 * sa_set_security_attr(optionset, tag, value) 2518 * 2519 * Set the optioset attribute specied by tag to the specified value. 2520 */ 2521 2522 void 2523 sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 2524 { 2525 set_node_attr((void *)optionset, tag, value); 2526 } 2527 2528 /* 2529 * is_nodetype(node, type) 2530 * 2531 * Check to see if node is of the type specified. 2532 */ 2533 2534 static int 2535 is_nodetype(void *node, char *type) 2536 { 2537 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 2538 } 2539 2540 /* 2541 * sa_set_prop_by_prop(optionset, group, prop, type) 2542 * 2543 * Add/remove/update the specified property prop into the optionset or 2544 * share. If a share, sort out which property group based on GUID. In 2545 * all cases, the appropriate transaction is set (or ZFS share is 2546 * marked as needing an update) 2547 */ 2548 2549 #define SA_PROP_OP_REMOVE 1 2550 #define SA_PROP_OP_ADD 2 2551 #define SA_PROP_OP_UPDATE 3 2552 static int 2553 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 2554 sa_property_t prop, int type) 2555 { 2556 char *name; 2557 char *valstr; 2558 int ret = SA_OK; 2559 scf_transaction_entry_t *entry; 2560 scf_value_t *value; 2561 int opttype; /* 1 == optionset, 0 == security */ 2562 char *id = NULL; 2563 int iszfs = 0; 2564 int isshare = 0; 2565 sa_group_t parent = NULL; 2566 sa_handle_impl_t impl_handle; 2567 scfutilhandle_t *scf_handle; 2568 2569 if (!is_persistent(group)) { 2570 /* 2571 * if the group/share is not persistent we don't need 2572 * to do anything here 2573 */ 2574 return (SA_OK); 2575 } 2576 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2577 if (impl_handle == NULL || impl_handle->scfhandle == NULL) { 2578 return (SA_SYSTEM_ERR); 2579 } 2580 scf_handle = impl_handle->scfhandle; 2581 name = sa_get_property_attr(prop, "type"); 2582 valstr = sa_get_property_attr(prop, "value"); 2583 entry = scf_entry_create(scf_handle->handle); 2584 opttype = is_nodetype((void *)optionset, "optionset"); 2585 2586 if (valstr != NULL && entry != NULL) { 2587 if (sa_is_share(group)) { 2588 isshare = 1; 2589 parent = sa_get_parent_group(group); 2590 if (parent != NULL) { 2591 iszfs = is_zfs_group(parent); 2592 } 2593 } else { 2594 iszfs = is_zfs_group(group); 2595 } 2596 if (!iszfs) { 2597 if (scf_handle->trans == NULL) { 2598 char oname[256]; 2599 char *groupname = NULL; 2600 if (isshare) { 2601 if (parent != NULL) { 2602 groupname = sa_get_group_attr(parent, "name"); 2603 } 2604 id = sa_get_share_attr((sa_share_t)group, "id"); 2605 } else { 2606 groupname = sa_get_group_attr(group, "name"); 2607 } 2608 if (groupname != NULL) { 2609 ret = sa_get_instance(scf_handle, groupname); 2610 sa_free_attr_string(groupname); 2611 } 2612 if (opttype) 2613 (void) sa_optionset_name(optionset, oname, 2614 sizeof (oname), id); 2615 else 2616 (void) sa_security_name(optionset, oname, 2617 sizeof (oname), id); 2618 ret = sa_start_transaction(scf_handle, oname); 2619 } 2620 if (ret == SA_OK) { 2621 switch (type) { 2622 case SA_PROP_OP_REMOVE: 2623 ret = scf_transaction_property_delete( 2624 scf_handle->trans, 2625 entry, name); 2626 break; 2627 case SA_PROP_OP_ADD: 2628 case SA_PROP_OP_UPDATE: 2629 value = scf_value_create(scf_handle->handle); 2630 if (value != NULL) { 2631 if (type == SA_PROP_OP_ADD) 2632 ret = scf_transaction_property_new( 2633 scf_handle->trans, 2634 entry, 2635 name, 2636 SCF_TYPE_ASTRING); 2637 else 2638 ret = scf_transaction_property_change( 2639 scf_handle->trans, 2640 entry, 2641 name, 2642 SCF_TYPE_ASTRING); 2643 if (ret == 0) { 2644 ret = scf_value_set_astring(value, valstr); 2645 if (ret == 0) 2646 ret = scf_entry_add_value(entry, value); 2647 if (ret != 0) { 2648 scf_value_destroy(value); 2649 ret = SA_SYSTEM_ERR; 2650 } 2651 } else { 2652 scf_entry_destroy(entry); 2653 ret = SA_SYSTEM_ERR; 2654 } 2655 break; 2656 } 2657 } 2658 } 2659 } else { 2660 /* 2661 * ZFS update. The calling function would have updated 2662 * the internal XML structure. Just need to flag it as 2663 * changed for ZFS. 2664 */ 2665 zfs_set_update((sa_share_t)group); 2666 } 2667 } 2668 2669 if (name != NULL) 2670 sa_free_attr_string(name); 2671 if (valstr != NULL) 2672 sa_free_attr_string(valstr); 2673 else if (entry != NULL) 2674 scf_entry_destroy(entry); 2675 2676 if (ret == -1) 2677 ret = SA_SYSTEM_ERR; 2678 2679 return (ret); 2680 } 2681 2682 /* 2683 * sa_create_property(name, value) 2684 * 2685 * Create a new property with the specified name and value. 2686 */ 2687 2688 sa_property_t 2689 sa_create_property(char *name, char *value) 2690 { 2691 xmlNodePtr node; 2692 2693 node = xmlNewNode(NULL, (xmlChar *)"option"); 2694 if (node != NULL) { 2695 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 2696 xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 2697 } 2698 return ((sa_property_t)node); 2699 } 2700 2701 /* 2702 * sa_add_property(object, property) 2703 * 2704 * Add the specified property to the object. Issue the appropriate 2705 * transaction or mark a ZFS object as needing an update. 2706 */ 2707 2708 int 2709 sa_add_property(void *object, sa_property_t property) 2710 { 2711 int ret = SA_OK; 2712 sa_group_t parent; 2713 sa_group_t group; 2714 char *proto; 2715 2716 proto = sa_get_optionset_attr(object, "type"); 2717 if (property != NULL) { 2718 if ((ret = sa_valid_property(object, proto, property)) == SA_OK) { 2719 property = (sa_property_t)xmlAddChild((xmlNodePtr)object, 2720 (xmlNodePtr)property); 2721 } else { 2722 if (proto != NULL) 2723 sa_free_attr_string(proto); 2724 return (ret); 2725 } 2726 } 2727 2728 if (proto != NULL) 2729 sa_free_attr_string(proto); 2730 2731 parent = sa_get_parent_group(object); 2732 if (!is_persistent(parent)) { 2733 return (ret); 2734 } 2735 2736 if (sa_is_share(parent)) 2737 group = sa_get_parent_group(parent); 2738 else 2739 group = parent; 2740 2741 if (property == NULL) 2742 ret = SA_NO_MEMORY; 2743 else { 2744 char oname[256]; 2745 2746 if (!is_zfs_group(group)) { 2747 char *id = NULL; 2748 sa_handle_impl_t impl_handle; 2749 scfutilhandle_t *scf_handle; 2750 2751 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2752 if (impl_handle == NULL || impl_handle->scfhandle == NULL) 2753 ret = SA_SYSTEM_ERR; 2754 if (ret == SA_OK) { 2755 scf_handle = impl_handle->scfhandle; 2756 if (sa_is_share((sa_group_t)parent)) { 2757 id = sa_get_share_attr((sa_share_t)parent, "id"); 2758 } 2759 if (scf_handle->trans == NULL) { 2760 if (is_nodetype(object, "optionset")) 2761 (void) sa_optionset_name((sa_optionset_t)object, 2762 oname, sizeof (oname), id); 2763 else 2764 (void) sa_security_name((sa_optionset_t)object, 2765 oname, sizeof (oname), id); 2766 ret = sa_start_transaction(scf_handle, oname); 2767 } 2768 if (ret == SA_OK) { 2769 char *name; 2770 char *value; 2771 name = sa_get_property_attr(property, "type"); 2772 value = sa_get_property_attr(property, "value"); 2773 if (name != NULL && value != NULL) { 2774 if (scf_handle->scf_state == SCH_STATE_INIT) 2775 ret = sa_set_property(scf_handle, name, value); 2776 } else 2777 ret = SA_CONFIG_ERR; 2778 if (name != NULL) 2779 sa_free_attr_string(name); 2780 if (value != NULL) 2781 sa_free_attr_string(value); 2782 } 2783 if (id != NULL) 2784 sa_free_attr_string(id); 2785 } 2786 } else { 2787 /* 2788 * ZFS is a special case. We do want to allow editing 2789 * property/security lists since we can have a better 2790 * syntax and we also want to keep things consistent 2791 * when possible. 2792 * 2793 * Right now, we defer until the sa_commit_properties 2794 * so we can get them all at once. We do need to mark 2795 * the share as "changed" 2796 */ 2797 zfs_set_update((sa_share_t)parent); 2798 } 2799 } 2800 return (ret); 2801 } 2802 2803 /* 2804 * sa_remove_property(property) 2805 * 2806 * Remove the specied property from its containing object. Update the 2807 * repository as appropriate. 2808 */ 2809 2810 int 2811 sa_remove_property(sa_property_t property) 2812 { 2813 int ret = SA_OK; 2814 2815 if (property != NULL) { 2816 sa_optionset_t optionset; 2817 sa_group_t group; 2818 optionset = sa_get_property_parent(property); 2819 if (optionset != NULL) { 2820 group = sa_get_optionset_parent(optionset); 2821 if (group != NULL) { 2822 ret = sa_set_prop_by_prop(optionset, group, property, 2823 SA_PROP_OP_REMOVE); 2824 } 2825 } 2826 xmlUnlinkNode((xmlNodePtr)property); 2827 xmlFreeNode((xmlNodePtr)property); 2828 } else { 2829 ret = SA_NO_SUCH_PROP; 2830 } 2831 return (ret); 2832 } 2833 2834 /* 2835 * sa_update_property(property, value) 2836 * 2837 * Update the specified property to the new value. If value is NULL, 2838 * we currently treat this as a remove. 2839 */ 2840 2841 int 2842 sa_update_property(sa_property_t property, char *value) 2843 { 2844 int ret = SA_OK; 2845 if (value == NULL) { 2846 return (sa_remove_property(property)); 2847 } else { 2848 sa_optionset_t optionset; 2849 sa_group_t group; 2850 set_node_attr((void *)property, "value", value); 2851 optionset = sa_get_property_parent(property); 2852 if (optionset != NULL) { 2853 group = sa_get_optionset_parent(optionset); 2854 if (group != NULL) { 2855 ret = sa_set_prop_by_prop(optionset, group, property, 2856 SA_PROP_OP_UPDATE); 2857 } 2858 } else { 2859 ret = SA_NO_SUCH_PROP; 2860 } 2861 } 2862 return (ret); 2863 } 2864 2865 /* 2866 * sa_get_protocol_property(propset, prop) 2867 * 2868 * Get the specified protocol specific property. These are global to 2869 * the protocol and not specific to a group or share. 2870 */ 2871 2872 sa_property_t 2873 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 2874 { 2875 xmlNodePtr node = (xmlNodePtr)propset; 2876 xmlChar *value = NULL; 2877 2878 for (node = node->children; node != NULL; 2879 node = node->next) { 2880 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2881 if (prop == NULL) 2882 break; 2883 value = xmlGetProp(node, (xmlChar *)"type"); 2884 if (value != NULL && 2885 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 2886 break; 2887 } 2888 if (value != NULL) { 2889 xmlFree(value); 2890 value = NULL; 2891 } 2892 } 2893 } 2894 if (value != NULL) 2895 xmlFree(value); 2896 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2897 /* avoid a non option node -- it is possible to be a text node */ 2898 node = NULL; 2899 } 2900 return ((sa_property_t)node); 2901 } 2902 2903 /* 2904 * sa_get_next_protocol_property(prop) 2905 * 2906 * Get the next protocol specific property in the list. 2907 */ 2908 2909 sa_property_t 2910 sa_get_next_protocol_property(sa_property_t prop) 2911 { 2912 xmlNodePtr node; 2913 2914 for (node = ((xmlNodePtr)prop)->next; node != NULL; 2915 node = node->next) { 2916 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2917 break; 2918 } 2919 } 2920 return ((sa_property_t)node); 2921 } 2922 2923 /* 2924 * sa_set_protocol_property(prop, value) 2925 * 2926 * Set the specified property to have the new value. The protocol 2927 * specific plugin will then be called to update the property. 2928 */ 2929 2930 int 2931 sa_set_protocol_property(sa_property_t prop, char *value) 2932 { 2933 sa_protocol_properties_t propset; 2934 char *proto; 2935 int ret = SA_INVALID_PROTOCOL; 2936 2937 propset = ((xmlNodePtr)prop)->parent; 2938 if (propset != NULL) { 2939 proto = sa_get_optionset_attr(propset, "type"); 2940 if (proto != NULL) { 2941 set_node_attr((xmlNodePtr)prop, "value", value); 2942 ret = sa_proto_set_property(proto, prop); 2943 sa_free_attr_string(proto); 2944 } 2945 } 2946 return (ret); 2947 } 2948 2949 /* 2950 * sa_add_protocol_property(propset, prop) 2951 * 2952 * Add a new property to the protocol sepcific property set. 2953 */ 2954 2955 int 2956 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 2957 { 2958 xmlNodePtr node; 2959 2960 /* should check for legitimacy */ 2961 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 2962 if (node != NULL) 2963 return (SA_OK); 2964 return (SA_NO_MEMORY); 2965 } 2966 2967 /* 2968 * sa_create_protocol_properties(proto) 2969 * 2970 * Create a protocol specifity property set. 2971 */ 2972 2973 sa_protocol_properties_t 2974 sa_create_protocol_properties(char *proto) 2975 { 2976 xmlNodePtr node; 2977 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 2978 if (node != NULL) { 2979 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 2980 } 2981 return (node); 2982 } 2983