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