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