1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2013 RackTop Systems. 25 */ 26 27 /* 28 * core library for common functions across all config store types 29 * and file systems to be exported. This includes legacy dfstab/sharetab 30 * parsing. Need to eliminate XML where possible. 31 */ 32 33 #include <stdio.h> 34 #include <string.h> 35 #include <ctype.h> 36 #include <unistd.h> 37 #include <limits.h> 38 #include <errno.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <libxml/parser.h> 42 #include <libxml/tree.h> 43 #include "libshare.h" 44 #include "libshare_impl.h" 45 #include <fcntl.h> 46 #include <thread.h> 47 #include <grp.h> 48 #include <limits.h> 49 #include <sys/param.h> 50 #include <signal.h> 51 #include <libintl.h> 52 #include <dirent.h> 53 54 #include <sharefs/share.h> 55 #include "sharetab.h" 56 57 #define DFSTAB_NOTICE_LINES 5 58 static char *notice[DFSTAB_NOTICE_LINES] = { 59 "# Do not modify this file directly.\n", 60 "# Use the sharemgr(1m) command for all share management\n", 61 "# This file is reconstructed and only maintained for backward\n", 62 "# compatibility. Configuration lines could be lost.\n", 63 "#\n" 64 }; 65 66 #define STRNCAT(x, y, z) (xmlChar *)strncat((char *)x, (char *)y, z) 67 68 /* will be much smaller, but this handles bad syntax in the file */ 69 #define MAXARGSFORSHARE 256 70 71 static mutex_t sharetab_lock = DEFAULTMUTEX; 72 extern mutex_t sa_dfstab_lock; 73 74 /* used internally only */ 75 typedef 76 struct sharelist { 77 struct sharelist *next; 78 int persist; 79 char *path; 80 char *resource; 81 char *fstype; 82 char *options; 83 char *description; 84 char *group; 85 char *origline; 86 int lineno; 87 } xfs_sharelist_t; 88 static void parse_dfstab(sa_handle_t, char *, xmlNodePtr); 89 extern char *_sa_get_token(char *); 90 static void dfs_free_list(xfs_sharelist_t *); 91 /* prototypes */ 92 void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *); 93 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t); 94 extern sa_group_t _sa_create_group(sa_handle_t, char *); 95 static void outdfstab(FILE *, xfs_sharelist_t *); 96 extern int _sa_remove_optionset(sa_optionset_t); 97 extern int set_node_share(void *, char *, char *); 98 extern void set_node_attr(void *, char *, char *); 99 100 /* 101 * sablocksigs(*sigs) 102 * 103 * block important signals for a critical region. Arg is a pointer to 104 * a sigset_t that is used later for the unblock. 105 */ 106 void 107 sablocksigs(sigset_t *sigs) 108 { 109 sigset_t new; 110 111 if (sigs != NULL) { 112 (void) sigprocmask(SIG_BLOCK, NULL, &new); 113 (void) sigaddset(&new, SIGHUP); 114 (void) sigaddset(&new, SIGINT); 115 (void) sigaddset(&new, SIGQUIT); 116 (void) sigaddset(&new, SIGTSTP); 117 (void) sigprocmask(SIG_SETMASK, &new, sigs); 118 } 119 } 120 121 /* 122 * saunblocksigs(*sigs) 123 * 124 * unblock previously blocked signals from the sigs arg. 125 */ 126 void 127 saunblocksigs(sigset_t *sigs) 128 { 129 if (sigs != NULL) 130 (void) sigprocmask(SIG_SETMASK, sigs, NULL); 131 } 132 133 /* 134 * alloc_sharelist() 135 * 136 * allocator function to return an zfs_sharelist_t 137 */ 138 139 static xfs_sharelist_t * 140 alloc_sharelist() 141 { 142 xfs_sharelist_t *item; 143 144 item = (xfs_sharelist_t *)malloc(sizeof (xfs_sharelist_t)); 145 if (item != NULL) 146 (void) memset(item, '\0', sizeof (xfs_sharelist_t)); 147 return (item); 148 } 149 150 /* 151 * fix_notice(list) 152 * 153 * Look at the beginning of the current /etc/dfs/dfstab file and add 154 * the do not modify notice if it doesn't exist. 155 */ 156 157 static xfs_sharelist_t * 158 fix_notice(xfs_sharelist_t *list) 159 { 160 xfs_sharelist_t *item, *prev; 161 int i; 162 163 if (list == NULL) { 164 /* zero length dfstab */ 165 list = alloc_sharelist(); 166 if (list == NULL) 167 return (NULL); 168 list->description = strdup("#\n"); 169 } 170 if (list->path == NULL && list->description != NULL && 171 strcmp(list->description, notice[0]) != 0) { 172 for (prev = NULL, i = 0; i < DFSTAB_NOTICE_LINES; i++) { 173 item = alloc_sharelist(); 174 if (item != NULL) { 175 item->description = strdup(notice[i]); 176 if (prev == NULL) { 177 item->next = list; 178 prev = item; 179 list = item; 180 } else { 181 item->next = prev->next; 182 prev->next = item; 183 prev = item; 184 } 185 } 186 } 187 } 188 return (list); 189 } 190 191 /* 192 * getdfstab(dfs) 193 * 194 * Returns an zfs_sharelist_t list of lines from the dfstab file 195 * pointed to by the FILE pointer dfs. Each entry is parsed and the 196 * original line is also preserved. Used in parsing and updating the 197 * dfstab file. 198 */ 199 200 static xfs_sharelist_t * 201 getdfstab(FILE *dfs) 202 { 203 char buff[_POSIX_ARG_MAX]; /* reasonable size given syntax of share */ 204 char *bp; 205 char *token; 206 char *args[MAXARGSFORSHARE]; 207 int argc; 208 int c; 209 static int line = 0; 210 xfs_sharelist_t *item = NULL, *first = NULL, *last; 211 212 if (dfs != NULL) { 213 first = NULL; 214 line = 0; 215 while (fgets(buff, sizeof (buff), dfs) != NULL) { 216 line++; 217 bp = buff; 218 if (buff[0] == '#') { 219 item = alloc_sharelist(); 220 if (item != NULL) { 221 /* if no path, then comment */ 222 item->lineno = line; 223 item->description = strdup(buff); 224 if (first == NULL) { 225 first = item; 226 last = item; 227 } else { 228 last->next = item; 229 last = item; 230 } 231 } else { 232 break; 233 } 234 continue; 235 } else if (buff[0] == '\n') { 236 continue; 237 } 238 optind = 1; 239 item = alloc_sharelist(); 240 if (item == NULL) { 241 break; 242 } else if (first == NULL) { 243 first = item; 244 last = item; 245 } else { 246 last->next = item; 247 last = item; 248 } 249 item->lineno = line; 250 item->origline = strdup(buff); 251 (void) _sa_get_token(NULL); /* reset to new pointers */ 252 argc = 0; 253 while ((token = _sa_get_token(bp)) != NULL) { 254 if (argc < MAXARGSFORSHARE) 255 args[argc++] = token; 256 } 257 while ((c = getopt(argc, args, "F:o:d:pg:")) != -1) { 258 switch (c) { 259 case 'p': 260 item->persist = 1; 261 break; 262 case 'F': 263 item->fstype = strdup(optarg); 264 break; 265 case 'o': 266 item->options = strdup(optarg); 267 break; 268 case 'd': 269 item->description = strdup(optarg); 270 break; 271 case 'g': 272 item->group = strdup(optarg); 273 break; 274 default: 275 break; 276 } 277 } 278 if (optind < argc) { 279 item->path = strdup(args[optind]); 280 optind++; 281 if (optind < argc) { 282 char *resource; 283 char *optgroup; 284 /* resource and/or groupname */ 285 resource = args[optind]; 286 optgroup = strchr(resource, '@'); 287 if (optgroup != NULL) 288 *optgroup++ = '\0'; 289 if (optgroup != NULL) 290 item->group = strdup(optgroup); 291 if (resource != NULL && 292 strlen(resource) > 0) 293 item->resource = 294 strdup(resource); 295 } 296 } 297 /* NFS is the default if none defined */ 298 if (item != NULL && item->fstype == NULL) 299 item->fstype = strdup("nfs"); 300 } 301 } 302 first = fix_notice(first); 303 return (first); 304 } 305 306 /* 307 * finddfsentry(list, path) 308 * 309 * Look for path in the zfs_sharelist_t list and return the entry if it 310 * exists. 311 */ 312 313 static xfs_sharelist_t * 314 finddfsentry(xfs_sharelist_t *list, char *path) 315 { 316 xfs_sharelist_t *item; 317 318 for (item = list; item != NULL; item = item->next) { 319 if (item->path != NULL && strcmp(item->path, path) == 0) 320 return (item); 321 } 322 return (NULL); 323 } 324 325 /* 326 * remdfsentry(list, path, proto) 327 * 328 * Remove the specified path (with protocol) from the list. This will 329 * remove it from dfstab when the file is rewritten. 330 */ 331 332 static xfs_sharelist_t * 333 remdfsentry(xfs_sharelist_t *list, char *path, char *proto) 334 { 335 xfs_sharelist_t *item, *prev = NULL; 336 337 338 for (item = prev = list; item != NULL; item = item->next) { 339 /* skip comment entry but don't lose it */ 340 if (item->path == NULL) { 341 prev = item; 342 continue; 343 } 344 /* if proto is NULL, remove all protocols */ 345 if (proto == NULL || (strcmp(item->path, path) == 0 && 346 (item->fstype != NULL && strcmp(item->fstype, proto) == 0))) 347 break; 348 if (item->fstype == NULL && 349 (proto == NULL || strcmp(proto, "nfs") == 0)) 350 break; 351 prev = item; 352 } 353 if (item != NULL) { 354 if (item == prev) 355 list = item->next; /* this must be the first one */ 356 else 357 prev->next = item->next; 358 item->next = NULL; 359 dfs_free_list(item); 360 } 361 return (list); 362 } 363 364 /* 365 * remdfsline(list, line) 366 * 367 * Remove the line specified from the list. 368 */ 369 370 static xfs_sharelist_t * 371 remdfsline(xfs_sharelist_t *list, char *line) 372 { 373 xfs_sharelist_t *item, *prev = NULL; 374 375 for (item = prev = list; item != NULL; item = item->next) { 376 /* skip comment entry but don't lose it */ 377 if (item->path == NULL) { 378 prev = item; 379 continue; 380 } 381 if (strcmp(item->origline, line) == 0) 382 break; 383 prev = item; 384 } 385 if (item != NULL) { 386 if (item == prev) 387 list = item->next; /* this must be the first one */ 388 else 389 prev->next = item->next; 390 item->next = NULL; 391 dfs_free_list(item); 392 } 393 return (list); 394 } 395 396 /* 397 * adddfsentry(list, share, proto) 398 * 399 * Add an entry to the dfstab list for share (relative to proto). This 400 * is used to update dfstab for legacy purposes. 401 */ 402 403 static xfs_sharelist_t * 404 adddfsentry(xfs_sharelist_t *list, sa_share_t share, char *proto) 405 { 406 xfs_sharelist_t *item, *tmp; 407 sa_group_t parent; 408 char *groupname; 409 410 item = alloc_sharelist(); 411 if (item != NULL) { 412 parent = sa_get_parent_group(share); 413 groupname = sa_get_group_attr(parent, "name"); 414 if (groupname != NULL && strcmp(groupname, "default") == 0) { 415 sa_free_attr_string(groupname); 416 groupname = NULL; 417 } 418 item->path = sa_get_share_attr(share, "path"); 419 item->resource = sa_get_share_attr(share, "resource"); 420 item->group = groupname; 421 item->fstype = strdup(proto); 422 item->options = sa_proto_legacy_format(proto, share, 1); 423 if (item->options != NULL && strlen(item->options) == 0) { 424 free(item->options); 425 item->options = NULL; 426 } 427 item->description = sa_get_share_description(share); 428 if (item->description != NULL && 429 strlen(item->description) == 0) { 430 sa_free_share_description(item->description); 431 item->description = NULL; 432 } 433 if (list == NULL) { 434 list = item; 435 } else { 436 for (tmp = list; tmp->next != NULL; tmp = tmp->next) 437 /* do nothing */; 438 tmp->next = item; 439 } 440 } 441 return (list); 442 } 443 444 /* 445 * outdfstab(dfstab, list) 446 * 447 * Output the list to dfstab making sure the file is truncated. 448 * Comments and errors are preserved. 449 */ 450 451 static void 452 outdfstab(FILE *dfstab, xfs_sharelist_t *list) 453 { 454 xfs_sharelist_t *item; 455 456 (void) ftruncate(fileno(dfstab), 0); 457 458 for (item = list; item != NULL; item = item->next) { 459 if (item->path != NULL) { 460 if (*item->path == '/') { 461 (void) fprintf(dfstab, 462 "share %s%s%s%s%s%s%s %s%s%s%s%s\n", 463 (item->fstype != NULL) ? "-F " : "", 464 (item->fstype != NULL) ? item->fstype : "", 465 (item->options != NULL) ? " -o " : "", 466 (item->options != NULL) ? 467 item->options : "", 468 (item->description != NULL) ? 469 " -d \"" : "", 470 (item->description != NULL) ? 471 item->description : "", 472 (item->description != NULL) ? "\"" : "", 473 item->path, 474 ((item->resource != NULL) || 475 (item->group != NULL)) ? " " : "", 476 (item->resource != NULL) ? 477 item->resource : "", 478 item->group != NULL ? "@" : "", 479 item->group != NULL ? item->group : ""); 480 } else { 481 (void) fprintf(dfstab, "%s", item->origline); 482 } 483 } else { 484 if (item->description != NULL) 485 (void) fprintf(dfstab, "%s", item->description); 486 else 487 (void) fprintf(dfstab, "%s", item->origline); 488 } 489 } 490 } 491 492 /* 493 * open_dfstab(file) 494 * 495 * Open the specified dfstab file. If the owner/group/perms are wrong, 496 * fix them. 497 */ 498 499 static FILE * 500 open_dfstab(char *file) 501 { 502 struct group *grp; 503 struct group group; 504 char *buff; 505 int grsize; 506 FILE *dfstab; 507 508 dfstab = fopen(file, "r+"); 509 if (dfstab == NULL) { 510 dfstab = fopen(file, "w+"); 511 } 512 if (dfstab != NULL) { 513 grsize = sysconf(_SC_GETGR_R_SIZE_MAX); 514 buff = malloc(grsize); 515 if (buff != NULL) 516 grp = getgrnam_r(SA_DEFAULT_FILE_GRP, &group, buff, 517 grsize); 518 else 519 grp = getgrnam(SA_DEFAULT_FILE_GRP); 520 (void) fchmod(fileno(dfstab), 0644); 521 (void) fchown(fileno(dfstab), 0, 522 grp != NULL ? grp->gr_gid : 3); 523 if (buff != NULL) 524 free(buff); 525 rewind(dfstab); 526 } 527 return (dfstab); 528 } 529 530 /* 531 * sa_comment_line(line, err) 532 * 533 * Add a comment to the dfstab file with err as a prefix to the 534 * original line. 535 */ 536 537 static void 538 sa_comment_line(char *line, char *err) 539 { 540 FILE *dfstab; 541 xfs_sharelist_t *list; 542 sigset_t old; 543 544 dfstab = open_dfstab(SA_LEGACY_DFSTAB); 545 if (dfstab != NULL) { 546 (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8); 547 sablocksigs(&old); 548 (void) lockf(fileno(dfstab), F_LOCK, 0); 549 (void) mutex_lock(&sa_dfstab_lock); 550 list = getdfstab(dfstab); 551 rewind(dfstab); 552 /* 553 * don't ignore the return since the list could have 554 * gone to NULL if the file only had one line in it. 555 */ 556 list = remdfsline(list, line); 557 outdfstab(dfstab, list); 558 (void) fprintf(dfstab, "# Error: %s: %s", err, line); 559 (void) fsync(fileno(dfstab)); 560 (void) mutex_unlock(&sa_dfstab_lock); 561 (void) lockf(fileno(dfstab), F_ULOCK, 0); 562 (void) fclose(dfstab); 563 saunblocksigs(&old); 564 if (list != NULL) 565 dfs_free_list(list); 566 } 567 } 568 569 /* 570 * sa_delete_legacy(share, protocol) 571 * 572 * Delete the specified share from the legacy config file. 573 */ 574 575 int 576 sa_delete_legacy(sa_share_t share, char *protocol) 577 { 578 FILE *dfstab; 579 int err; 580 int ret = SA_OK; 581 xfs_sharelist_t *list; 582 char *path; 583 sa_optionset_t optionset; 584 sa_group_t parent; 585 sigset_t old; 586 587 /* 588 * Protect against shares that don't have paths. This is not 589 * really an error at this point. 590 */ 591 path = sa_get_share_attr(share, "path"); 592 if (path == NULL) 593 return (ret); 594 595 dfstab = open_dfstab(SA_LEGACY_DFSTAB); 596 if (dfstab != NULL) { 597 (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8); 598 sablocksigs(&old); 599 parent = sa_get_parent_group(share); 600 if (parent != NULL) { 601 (void) lockf(fileno(dfstab), F_LOCK, 0); 602 (void) mutex_lock(&sa_dfstab_lock); 603 list = getdfstab(dfstab); 604 rewind(dfstab); 605 if (protocol != NULL) { 606 if (list != NULL) 607 list = remdfsentry(list, path, 608 protocol); 609 } else { 610 for (optionset = sa_get_optionset(parent, NULL); 611 optionset != NULL; 612 optionset = 613 sa_get_next_optionset(optionset)) { 614 char *proto = sa_get_optionset_attr( 615 optionset, "type"); 616 617 if (list != NULL && proto != NULL) 618 list = remdfsentry(list, path, 619 proto); 620 if (proto == NULL) 621 ret = SA_NO_MEMORY; 622 /* 623 * may want to only do the dfstab if 624 * this call returns NOT IMPLEMENTED 625 * but it shouldn't hurt. 626 */ 627 if (ret == SA_OK) { 628 err = sa_proto_delete_legacy( 629 proto, share); 630 if (err != SA_NOT_IMPLEMENTED) 631 ret = err; 632 } 633 if (proto != NULL) 634 sa_free_attr_string(proto); 635 } 636 } 637 outdfstab(dfstab, list); 638 if (list != NULL) 639 dfs_free_list(list); 640 (void) fflush(dfstab); 641 (void) mutex_unlock(&sa_dfstab_lock); 642 (void) lockf(fileno(dfstab), F_ULOCK, 0); 643 } 644 (void) fsync(fileno(dfstab)); 645 saunblocksigs(&old); 646 (void) fclose(dfstab); 647 } else { 648 if (errno == EACCES || errno == EPERM) 649 ret = SA_NO_PERMISSION; 650 else 651 ret = SA_CONFIG_ERR; 652 } 653 654 if (path != NULL) 655 sa_free_attr_string(path); 656 657 return (ret); 658 } 659 660 /* 661 * sa_update_legacy(share, proto) 662 * 663 * There is an assumption that dfstab will be the most common form of 664 * legacy configuration file for shares, but not the only one. Because 665 * of that, dfstab handling is done in the main code with calls to 666 * this function and protocol specific calls to deal with formatting 667 * options into dfstab/share compatible syntax. Since not everything 668 * will be dfstab, there is a provision for calling a protocol 669 * specific plugin interface that allows the protocol plugin to do its 670 * own legacy files and skip the dfstab update. 671 */ 672 673 int 674 sa_update_legacy(sa_share_t share, char *proto) 675 { 676 FILE *dfstab; 677 int ret = SA_OK; 678 xfs_sharelist_t *list; 679 char *path; 680 sigset_t old; 681 char *persist; 682 uint64_t features; 683 684 ret = sa_proto_update_legacy(proto, share); 685 if (ret != SA_NOT_IMPLEMENTED) 686 return (ret); 687 688 features = sa_proto_get_featureset(proto); 689 if (!(features & SA_FEATURE_DFSTAB)) 690 return (ret); 691 692 /* do the dfstab format */ 693 persist = sa_get_share_attr(share, "type"); 694 /* 695 * only update if the share is not transient -- no share type 696 * set or the type is not "transient". 697 */ 698 if (persist == NULL || strcmp(persist, "transient") != 0) { 699 path = sa_get_share_attr(share, "path"); 700 if (path == NULL) { 701 ret = SA_NO_MEMORY; 702 goto out; 703 } 704 dfstab = open_dfstab(SA_LEGACY_DFSTAB); 705 if (dfstab != NULL) { 706 (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8); 707 sablocksigs(&old); 708 (void) lockf(fileno(dfstab), F_LOCK, 0); 709 (void) mutex_lock(&sa_dfstab_lock); 710 list = getdfstab(dfstab); 711 rewind(dfstab); 712 if (list != NULL) 713 list = remdfsentry(list, path, proto); 714 list = adddfsentry(list, share, proto); 715 outdfstab(dfstab, list); 716 (void) fflush(dfstab); 717 (void) mutex_unlock(&sa_dfstab_lock); 718 (void) lockf(fileno(dfstab), F_ULOCK, 0); 719 (void) fsync(fileno(dfstab)); 720 saunblocksigs(&old); 721 (void) fclose(dfstab); 722 if (list != NULL) 723 dfs_free_list(list); 724 } else { 725 if (errno == EACCES || errno == EPERM) 726 ret = SA_NO_PERMISSION; 727 else 728 ret = SA_CONFIG_ERR; 729 } 730 sa_free_attr_string(path); 731 } 732 out: 733 if (persist != NULL) 734 sa_free_attr_string(persist); 735 return (ret); 736 } 737 738 /* 739 * sa_is_security(optname, proto) 740 * 741 * Check to see if optname is a security (named optionset) specific 742 * property for the specified protocol. 743 */ 744 745 boolean_t 746 sa_is_security(char *optname, char *proto) 747 { 748 int ret = B_FALSE; 749 if (proto != NULL) 750 ret = sa_proto_security_prop(proto, optname); 751 return (ret); 752 } 753 754 /* 755 * add_syntax_comment(root, line, err, todfstab) 756 * 757 * Add a comment to the document indicating a syntax error. If 758 * todfstab is set, write it back to the dfstab file as well. 759 */ 760 761 static void 762 add_syntax_comment(xmlNodePtr root, char *line, char *err, int todfstab) 763 { 764 xmlNodePtr node; 765 766 node = xmlNewChild(root, NULL, (xmlChar *)"error", (xmlChar *)line); 767 if (node != NULL) 768 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)err); 769 if (todfstab) 770 sa_comment_line(line, err); 771 } 772 773 /* 774 * sa_is_share(object) 775 * 776 * returns true of the object is of type "share". 777 */ 778 779 boolean_t 780 sa_is_share(void *object) 781 { 782 if (object != NULL) { 783 if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0) 784 return (B_TRUE); 785 } 786 return (B_FALSE); 787 } 788 /* 789 * sa_is_resource(object) 790 * 791 * returns true of the object is of type "share". 792 */ 793 794 boolean_t 795 sa_is_resource(void *object) 796 { 797 if (object != NULL) { 798 if (strcmp((char *)((xmlNodePtr)object)->name, "resource") == 0) 799 return (B_TRUE); 800 } 801 return (B_FALSE); 802 } 803 804 /* 805 * _sa_remove_property(property) 806 * 807 * remove a property only from the document. 808 */ 809 810 static void 811 _sa_remove_property(sa_property_t property) 812 { 813 xmlUnlinkNode((xmlNodePtr)property); 814 xmlFreeNode((xmlNodePtr)property); 815 } 816 817 /* 818 * _sa_create_dummy_share() 819 * 820 * Create a share entry suitable for parsing but not tied to any real 821 * config tree. Need to have a parent as well as the node to parse 822 * on. Free using _sa_free_dummy_share(share); 823 */ 824 825 static sa_group_t 826 _sa_create_dummy_share() 827 { 828 xmlNodePtr parent_node = NULL; 829 xmlNodePtr child_node = NULL; 830 831 parent_node = xmlNewNode(NULL, (xmlChar *)"group"); 832 if (parent_node != NULL) { 833 child_node = xmlNewChild(parent_node, NULL, (xmlChar *)"share", 834 NULL); 835 if (child_node != NULL) { 836 /* 837 * Use a "zfs" tag since that will make sure nothing 838 * really attempts to put values into the 839 * repository. Also ZFS is currently the only user of 840 * this interface. 841 */ 842 set_node_attr(parent_node, "type", "transient"); 843 set_node_attr(parent_node, "zfs", "true"); 844 set_node_attr(child_node, "type", "transient"); 845 set_node_attr(child_node, "zfs", "true"); 846 } else { 847 xmlFreeNode(parent_node); 848 } 849 } 850 return (child_node); 851 } 852 853 /* 854 * _sa_free_dummy_share(share) 855 * 856 * Free the dummy share and its parent. It is an error to try and 857 * free something that isn't a dummy. 858 */ 859 860 static int 861 _sa_free_dummy_share(sa_share_t share) 862 { 863 xmlNodePtr node = (xmlNodePtr)share; 864 xmlNodePtr parent; 865 int ret = SA_OK; 866 char *name; 867 868 if (node != NULL) { 869 parent = node->parent; 870 name = (char *)xmlGetProp(node, (xmlChar *)"path"); 871 if (name != NULL) { 872 /* Real shares always have a path but a dummy doesn't */ 873 ret = SA_NOT_ALLOWED; 874 sa_free_attr_string(name); 875 } else { 876 /* 877 * If there is a parent, do the free on that since 878 * xmlFreeNode is a recursive function and free's an 879 * child nodes. 880 */ 881 if (parent != NULL) { 882 node = parent; 883 } 884 xmlUnlinkNode(node); 885 xmlFreeNode(node); 886 } 887 } 888 return (ret); 889 } 890 891 892 /* 893 * sa_parse_legacy_options(group, options, proto) 894 * 895 * In order to support legacy configurations, we allow the protocol 896 * specific plugin to parse legacy syntax options (like those in 897 * /etc/dfs/dfstab). This adds a new optionset to the group (or 898 * share). 899 * 900 * Once the optionset has been created, we then get the derived 901 * optionset of the parent (options from the optionset of the parent 902 * and any parent it might have) and remove those from the created 903 * optionset. This avoids duplication of options. 904 */ 905 906 int 907 sa_parse_legacy_options(sa_group_t group, char *options, char *proto) 908 { 909 int ret = SA_INVALID_PROTOCOL; 910 sa_group_t parent; 911 int using_dummy = B_FALSE; 912 char *pvalue; 913 sa_optionset_t optionset; 914 sa_property_t popt, prop; 915 sa_optionset_t localoptions; 916 917 /* 918 * If "group" is NULL, this is just a parse without saving 919 * anything in either SMF or ZFS. Create a dummy group to 920 * handle this case. 921 */ 922 if (group == NULL) { 923 group = (sa_group_t)_sa_create_dummy_share(); 924 using_dummy = B_TRUE; 925 } 926 927 parent = sa_get_parent_group(group); 928 929 if (proto != NULL) 930 ret = sa_proto_legacy_opts(proto, group, options); 931 932 if (using_dummy) { 933 /* Since this is a dummy parse, cleanup and quit here */ 934 (void) _sa_free_dummy_share(parent); 935 return (ret); 936 } 937 938 if (ret != SA_OK) 939 return (ret); 940 941 /* 942 * If in a group, remove the inherited options and security 943 */ 944 945 if (parent == NULL) 946 return (ret); 947 948 /* Find parent options to remove from child */ 949 optionset = sa_get_derived_optionset(parent, proto, 1); 950 localoptions = sa_get_optionset(group, proto); 951 if (optionset != NULL) { 952 for (popt = sa_get_property(optionset, NULL); 953 popt != NULL; 954 popt = sa_get_next_property(popt)) { 955 char *tag; 956 char *value; 957 tag = sa_get_property_attr(popt, "type"); 958 if (tag == NULL) 959 continue; 960 prop = sa_get_property(localoptions, tag); 961 if (prop != NULL) { 962 value = sa_get_property_attr(popt, 963 "value"); 964 pvalue = sa_get_property_attr(prop, 965 "value"); 966 if (value != NULL && pvalue != NULL && 967 strcmp(value, pvalue) == 0) { 968 /* 969 * Remove the property 970 * from the 971 * child. While we 972 * removed it, we 973 * don't need to reset 974 * as we do below 975 * since we always 976 * search from the 977 * beginning. 978 */ 979 (void) _sa_remove_property( 980 prop); 981 } 982 if (value != NULL) 983 sa_free_attr_string(value); 984 if (pvalue != NULL) 985 sa_free_attr_string(pvalue); 986 } 987 sa_free_attr_string(tag); 988 } 989 prop = sa_get_property(localoptions, NULL); 990 if (prop == NULL && sa_is_share(group)) { 991 /* 992 * All properties removed so remove the 993 * optionset if it is on a share 994 */ 995 (void) _sa_remove_optionset(localoptions); 996 } 997 sa_free_derived_optionset(optionset); 998 } 999 /* 1000 * Need to remove security here. If there are no 1001 * security options on the local group/share, don't 1002 * bother since those are the only ones that would be 1003 * affected. 1004 */ 1005 localoptions = sa_get_all_security_types(group, proto, 0); 1006 if (localoptions != NULL) { 1007 for (prop = sa_get_property(localoptions, NULL); 1008 prop != NULL; 1009 prop = sa_get_next_property(prop)) { 1010 char *tag; 1011 sa_security_t security; 1012 tag = sa_get_property_attr(prop, "type"); 1013 if (tag != NULL) { 1014 sa_property_t nextpopt = NULL; 1015 security = sa_get_security(group, tag, proto); 1016 sa_free_attr_string(tag); 1017 /* 1018 * prop's value only changes outside this loop 1019 */ 1020 pvalue = sa_get_property_attr(prop, "value"); 1021 for (popt = sa_get_property(security, NULL); 1022 popt != NULL; 1023 popt = nextpopt) { 1024 char *value; 1025 /* 1026 * Need to get the next prop 1027 * now since we could break 1028 * the list during removal. 1029 */ 1030 nextpopt = sa_get_next_property(popt); 1031 /* remove Duplicates from this level */ 1032 value = sa_get_property_attr(popt, 1033 "value"); 1034 if (value != NULL && pvalue != NULL && 1035 strcmp(value, pvalue) == 0) { 1036 /* 1037 * remove the property 1038 * from the child 1039 */ 1040 (void) _sa_remove_property 1041 (popt); 1042 } 1043 if (value != NULL) 1044 sa_free_attr_string(value); 1045 } 1046 if (pvalue != NULL) 1047 sa_free_attr_string(pvalue); 1048 } 1049 } 1050 (void) sa_destroy_optionset(localoptions); 1051 } 1052 return (ret); 1053 } 1054 1055 /* 1056 * dfs_free_list(list) 1057 * 1058 * Free the data in each list entry of the list as well as freeing the 1059 * entries themselves. We need to avoid memory leaks and don't want to 1060 * dereference any NULL members. 1061 */ 1062 1063 static void 1064 dfs_free_list(xfs_sharelist_t *list) 1065 { 1066 xfs_sharelist_t *entry; 1067 for (entry = list; entry != NULL; entry = list) { 1068 if (entry->path != NULL) 1069 free(entry->path); 1070 if (entry->resource != NULL) 1071 free(entry->resource); 1072 if (entry->fstype != NULL) 1073 free(entry->fstype); 1074 if (entry->options != NULL) 1075 free(entry->options); 1076 if (entry->description != NULL) 1077 free(entry->description); 1078 if (entry->origline != NULL) 1079 free(entry->origline); 1080 if (entry->group != NULL) 1081 free(entry->group); 1082 list = list->next; 1083 free(entry); 1084 } 1085 } 1086 1087 /* 1088 * parse_dfstab(dfstab, root) 1089 * 1090 * Open and read the existing dfstab, parsing each line and adding it 1091 * to the internal configuration. Make sure syntax errors, etc are 1092 * preserved as comments. 1093 */ 1094 1095 static void 1096 parse_dfstab(sa_handle_t handle, char *dfstab, xmlNodePtr root) 1097 { 1098 sa_share_t share; 1099 sa_group_t group; 1100 sa_group_t sgroup = NULL; 1101 sa_group_t defgroup; 1102 xfs_sharelist_t *head, *list; 1103 int err; 1104 int defined_group; 1105 FILE *dfs; 1106 char *oldprops; 1107 1108 /* read the dfstab format file and fill in the doc tree */ 1109 1110 dfs = fopen(dfstab, "r"); 1111 if (dfs == NULL) 1112 return; 1113 1114 defgroup = sa_get_group(handle, "default"); 1115 1116 for (head = list = getdfstab(dfs); 1117 list != NULL; 1118 list = list->next) { 1119 share = NULL; 1120 group = NULL; 1121 defined_group = 0; 1122 err = 0; 1123 1124 if (list->origline == NULL) { 1125 /* 1126 * Comment line that we will likely skip. 1127 * If the line has the syntax: 1128 * # error: string: string 1129 * It should be preserved until manually deleted. 1130 */ 1131 if (list->description != NULL && 1132 strncmp(list->description, "# Error: ", 9) == 0) { 1133 char *line; 1134 char *error; 1135 char *cmd; 1136 line = strdup(list->description); 1137 if (line != NULL) { 1138 error = line + 9; 1139 cmd = strchr(error, ':'); 1140 if (cmd != NULL) { 1141 int len; 1142 *cmd = '\0'; 1143 cmd += 2; 1144 len = strlen(cmd); 1145 cmd[len - 1] = '\0'; 1146 add_syntax_comment(root, cmd, 1147 error, 0); 1148 } 1149 free(line); 1150 } 1151 } 1152 continue; 1153 } 1154 if (list->path != NULL && strlen(list->path) > 0 && 1155 *list->path == '/') { 1156 share = sa_find_share(handle, list->path); 1157 if (share != NULL) 1158 sgroup = sa_get_parent_group(share); 1159 else 1160 sgroup = NULL; 1161 } else { 1162 (void) printf(dgettext(TEXT_DOMAIN, 1163 "No share specified in dfstab: " 1164 "line %d: %s\n"), 1165 list->lineno, list->origline); 1166 add_syntax_comment(root, list->origline, 1167 dgettext(TEXT_DOMAIN, "No share specified"), 1); 1168 continue; 1169 } 1170 if (list->group != NULL && strlen(list->group) > 0) { 1171 group = sa_get_group(handle, list->group); 1172 defined_group = 1; 1173 } else { 1174 group = defgroup; 1175 } 1176 if (defined_group && group == NULL) { 1177 (void) printf(dgettext(TEXT_DOMAIN, 1178 "Unknown group used in dfstab: line %d: %s\n"), 1179 list->lineno, list->origline); 1180 add_syntax_comment(root, list->origline, 1181 dgettext(TEXT_DOMAIN, "Unknown group specified"), 1182 1); 1183 continue; 1184 } 1185 if (group == NULL) { 1186 /* Shouldn't happen unless an SMF error */ 1187 err = SA_CONFIG_ERR; 1188 continue; 1189 } 1190 if (share == NULL) { 1191 if (defined_group || group != defgroup) 1192 continue; 1193 /* This is an OK add for legacy */ 1194 share = sa_add_share(defgroup, list->path, 1195 SA_SHARE_PERMANENT | SA_SHARE_PARSER, &err); 1196 if (share != NULL) { 1197 if (list->description != NULL && 1198 strlen(list->description) > 0) 1199 (void) sa_set_share_description(share, 1200 list->description); 1201 if (list->options != NULL && 1202 strlen(list->options) > 0) { 1203 (void) sa_parse_legacy_options(share, 1204 list->options, list->fstype); 1205 } 1206 if (list->resource != NULL) 1207 (void) sa_set_share_attr(share, 1208 "resource", list->resource); 1209 } else { 1210 (void) printf(dgettext(TEXT_DOMAIN, 1211 "Error in dfstab: line %d: %s\n"), 1212 list->lineno, list->origline); 1213 if (err != SA_BAD_PATH) 1214 add_syntax_comment(root, list->origline, 1215 dgettext(TEXT_DOMAIN, "Syntax"), 1); 1216 else 1217 add_syntax_comment(root, list->origline, 1218 dgettext(TEXT_DOMAIN, 1219 "Path"), 1); 1220 continue; 1221 } 1222 } else { 1223 if (group != sgroup) { 1224 (void) printf(dgettext(TEXT_DOMAIN, 1225 "Attempt to change configuration in " 1226 "dfstab: line %d: %s\n"), 1227 list->lineno, list->origline); 1228 add_syntax_comment(root, list->origline, 1229 dgettext(TEXT_DOMAIN, 1230 "Attempt to change configuration"), 1); 1231 continue; 1232 } 1233 /* 1234 * It is the same group but could have changed 1235 * options. Make sure we include the group's 1236 * properties so we don't end up moving them to 1237 * the share inadvertantly. The last arg being 1238 * true says to get the inherited properties as well 1239 * as the local properties. 1240 */ 1241 oldprops = sa_proto_legacy_format(list->fstype, share, 1242 B_TRUE); 1243 1244 if (oldprops == NULL) 1245 continue; 1246 1247 if (list->options != NULL && 1248 strcmp(oldprops, list->options) != 0) { 1249 sa_optionset_t opts; 1250 sa_security_t secs; 1251 1252 /* possibly different values */ 1253 opts = sa_get_optionset((sa_group_t) 1254 share, list->fstype); 1255 (void) sa_destroy_optionset(opts); 1256 1257 for (secs = sa_get_security( 1258 (sa_group_t)share, NULL, list->fstype); 1259 secs != NULL; 1260 secs = sa_get_security((sa_group_t)share, 1261 NULL, list->fstype)) { 1262 (void) sa_destroy_security( 1263 secs); 1264 } 1265 (void) sa_parse_legacy_options(share, 1266 list->options, list->fstype); 1267 } 1268 sa_format_free(oldprops); 1269 } 1270 } 1271 dfs_free_list(head); 1272 } 1273 1274 /* 1275 * legacy_removes(group, file) 1276 * 1277 * Find any shares that are "missing" from the legacy file. These 1278 * should be removed from the configuration since they are likely from 1279 * a legacy app or the admin modified the dfstab file directly. We 1280 * have to support this even if it is not the recommended way to do 1281 * things. 1282 */ 1283 1284 static void 1285 legacy_removes(sa_group_t group, char *file) 1286 { 1287 sa_share_t share; 1288 char *path; 1289 xfs_sharelist_t *list, *item; 1290 FILE *dfstab; 1291 1292 dfstab = fopen(file, "r"); 1293 if (dfstab != NULL) { 1294 list = getdfstab(dfstab); 1295 (void) fclose(dfstab); 1296 retry: 1297 for (share = sa_get_share(group, NULL); 1298 share != NULL; 1299 share = sa_get_next_share(share)) { 1300 /* now see if the share is in the dfstab file */ 1301 path = sa_get_share_attr(share, "path"); 1302 if (path != NULL) { 1303 item = finddfsentry(list, path); 1304 sa_free_attr_string(path); 1305 if (item == NULL) { 1306 /* The share was removed this way */ 1307 (void) sa_remove_share(share); 1308 1309 /* 1310 * Start over since the list was broken 1311 */ 1312 goto retry; 1313 } 1314 } 1315 } 1316 if (list != NULL) 1317 dfs_free_list(list); 1318 } 1319 } 1320 1321 /* 1322 * getlegacyconfig(path, root) 1323 * 1324 * Parse dfstab and build the legacy configuration. This only gets 1325 * called when a change was detected. 1326 */ 1327 1328 void 1329 getlegacyconfig(sa_handle_t handle, char *path, xmlNodePtr *root) 1330 { 1331 sa_group_t defgroup; 1332 1333 if (root != NULL) { 1334 if (*root == NULL) 1335 *root = xmlNewNode(NULL, (xmlChar *)"sharecfg"); 1336 if (*root != NULL) { 1337 if (strcmp(path, SA_LEGACY_DFSTAB) == 0) { 1338 /* 1339 * Walk the default shares and find anything 1340 * missing. we do this first to make sure it 1341 * is cleaned up since there may be legacy 1342 * code add/del via dfstab and we need to 1343 * cleanup SMF. 1344 */ 1345 defgroup = sa_get_group(handle, "default"); 1346 if (defgroup != NULL) 1347 legacy_removes(defgroup, path); 1348 /* Parse the dfstab and add anything new */ 1349 parse_dfstab(handle, path, *root); 1350 } 1351 } 1352 } 1353 } 1354 1355 /* 1356 * get_share_list(&err) 1357 * 1358 * Get a linked list of all the shares on the system from 1359 * /etc/dfs/sharetab. This is partially copied from libfsmgt which we 1360 * can't use due to package dependencies. 1361 */ 1362 static xfs_sharelist_t * 1363 get_share_list(int *errp) 1364 { 1365 xfs_sharelist_t *newp; 1366 xfs_sharelist_t *headp; 1367 xfs_sharelist_t *tailp; 1368 FILE *fp; 1369 1370 headp = NULL; 1371 tailp = NULL; 1372 1373 if ((fp = fopen(SHARETAB, "r")) != NULL) { 1374 struct share *sharetab_entry; 1375 1376 (void) lockf(fileno(fp), F_LOCK, 0); 1377 (void) mutex_lock(&sharetab_lock); 1378 1379 while (getshare(fp, &sharetab_entry) > 0) { 1380 newp = alloc_sharelist(); 1381 if (newp == NULL) { 1382 (void) mutex_unlock(&sharetab_lock); 1383 (void) lockf(fileno(fp), F_ULOCK, 0); 1384 goto err; 1385 } 1386 1387 /* 1388 * Link into the list here so we don't leak 1389 * memory on a failure from strdup(). 1390 */ 1391 if (headp == NULL) { 1392 headp = newp; 1393 tailp = newp; 1394 } else { 1395 tailp->next = newp; 1396 tailp = newp; 1397 } 1398 1399 newp->path = strdup(sharetab_entry->sh_path); 1400 newp->resource = strdup(sharetab_entry->sh_res); 1401 newp->fstype = strdup(sharetab_entry->sh_fstype); 1402 newp->options = strdup(sharetab_entry->sh_opts); 1403 newp->description = strdup(sharetab_entry->sh_descr); 1404 1405 if (newp->path == NULL || newp->resource == NULL || 1406 newp->fstype == NULL || newp->options == NULL || 1407 newp->description == NULL) { 1408 (void) mutex_unlock(&sharetab_lock); 1409 (void) lockf(fileno(fp), F_ULOCK, 0); 1410 goto err; 1411 } 1412 } 1413 1414 (void) mutex_unlock(&sharetab_lock); 1415 (void) lockf(fileno(fp), F_ULOCK, 0); 1416 (void) fclose(fp); 1417 } else { 1418 *errp = errno; 1419 } 1420 1421 /* 1422 * Caller must free the mount list 1423 */ 1424 return (headp); 1425 err: 1426 /* 1427 * Out of memory so cleanup and leave. 1428 */ 1429 dfs_free_list(headp); 1430 (void) fclose(fp); 1431 return (NULL); 1432 } 1433 1434 /* 1435 * parse_sharetab(handle) 1436 * 1437 * Read the /etc/dfs/sharetab file and see which entries don't exist 1438 * in the repository. These shares are marked transient. We also need 1439 * to see if they are ZFS shares since ZFS bypasses the SMF 1440 * repository. 1441 */ 1442 1443 int 1444 parse_sharetab(sa_handle_t handle) 1445 { 1446 xfs_sharelist_t *list, *tmplist; 1447 int err = 0; 1448 sa_share_t share; 1449 sa_group_t group; 1450 sa_group_t lgroup; 1451 char *groupname; 1452 int legacy = 0; 1453 char shareopts[MAXNAMLEN]; 1454 1455 list = get_share_list(&err); 1456 if (list == NULL) 1457 return (legacy); 1458 1459 lgroup = sa_get_group(handle, "default"); 1460 1461 for (tmplist = list; tmplist != NULL; tmplist = tmplist->next) { 1462 group = NULL; 1463 share = sa_find_share(handle, tmplist->path); 1464 if (share != NULL) { 1465 /* 1466 * If this is a legacy share, mark as shared so we 1467 * only update sharetab appropriately. We also keep 1468 * the sharetab options in order to display for legacy 1469 * share with no arguments. 1470 */ 1471 set_node_attr(share, "shared", "true"); 1472 (void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s", 1473 tmplist->fstype); 1474 set_node_attr(share, shareopts, tmplist->options); 1475 continue; 1476 } 1477 1478 /* 1479 * This share is transient so needs to be 1480 * added. Initially, this will be under 1481 * default(legacy) unless it is a ZFS 1482 * share. If zfs, we need a zfs group. 1483 */ 1484 if (tmplist->resource != NULL && 1485 (groupname = strchr(tmplist->resource, '@')) != NULL) { 1486 /* There is a defined group */ 1487 *groupname++ = '\0'; 1488 group = sa_get_group(handle, groupname); 1489 if (group != NULL) { 1490 share = _sa_add_share(group, tmplist->path, 1491 SA_SHARE_TRANSIENT, &err, 1492 (uint64_t)SA_FEATURE_NONE); 1493 } else { 1494 /* 1495 * While this case shouldn't 1496 * occur very often, it does 1497 * occur out of a "zfs set 1498 * sharenfs=off" when the 1499 * dataset is also set to 1500 * canmount=off. A warning 1501 * will then cause the zfs 1502 * command to abort. Since we 1503 * add it to the default list, 1504 * everything works properly 1505 * anyway and the library 1506 * doesn't need to give a 1507 * warning. 1508 */ 1509 share = _sa_add_share(lgroup, 1510 tmplist->path, SA_SHARE_TRANSIENT, 1511 &err, (uint64_t)SA_FEATURE_NONE); 1512 } 1513 } else { 1514 if (sa_zfs_is_shared(handle, tmplist->path)) { 1515 group = sa_get_group(handle, "zfs"); 1516 if (group == NULL) { 1517 group = sa_create_group(handle, 1518 "zfs", &err); 1519 if (group == NULL && 1520 err == SA_NO_PERMISSION) { 1521 group = _sa_create_group( 1522 handle, "zfs"); 1523 } 1524 if (group != NULL) { 1525 (void) sa_create_optionset( 1526 group, tmplist->fstype); 1527 (void) sa_set_group_attr(group, 1528 "zfs", "true"); 1529 } 1530 } 1531 if (group != NULL) { 1532 share = _sa_add_share(group, 1533 tmplist->path, SA_SHARE_TRANSIENT, 1534 &err, (uint64_t)SA_FEATURE_NONE); 1535 } 1536 } else { 1537 share = _sa_add_share(lgroup, tmplist->path, 1538 SA_SHARE_TRANSIENT, &err, 1539 (uint64_t)SA_FEATURE_NONE); 1540 } 1541 } 1542 if (share == NULL) 1543 (void) printf(dgettext(TEXT_DOMAIN, 1544 "Problem with transient: %s\n"), sa_errorstr(err)); 1545 if (share != NULL) 1546 set_node_attr(share, "shared", "true"); 1547 if (err == SA_OK) { 1548 if (tmplist->options != NULL && 1549 strlen(tmplist->options) > 0) { 1550 (void) sa_parse_legacy_options(share, 1551 tmplist->options, tmplist->fstype); 1552 } 1553 if (tmplist->resource != NULL && 1554 strcmp(tmplist->resource, "-") != 0) 1555 set_node_attr(share, "resource", 1556 tmplist->resource); 1557 if (tmplist->description != NULL) { 1558 xmlNodePtr node; 1559 node = xmlNewChild((xmlNodePtr)share, NULL, 1560 (xmlChar *)"description", NULL); 1561 xmlNodeSetContent(node, 1562 (xmlChar *)tmplist->description); 1563 } 1564 legacy = 1; 1565 } 1566 } 1567 dfs_free_list(list); 1568 return (legacy); 1569 } 1570 1571 /* 1572 * Get the transient shares from the sharetab (or other) file. since 1573 * these are transient, they only appear in the working file and not 1574 * in a repository. 1575 */ 1576 int 1577 gettransients(sa_handle_t handle, xmlNodePtr *root) 1578 { 1579 int legacy = 0; 1580 int numproto; 1581 char **protocols = NULL; 1582 int i; 1583 1584 if (root != NULL) { 1585 if (*root == NULL) 1586 *root = xmlNewNode(NULL, (xmlChar *)"sharecfg"); 1587 if (*root != NULL) { 1588 legacy = parse_sharetab(handle); 1589 numproto = sa_get_protocols(&protocols); 1590 for (i = 0; i < numproto; i++) 1591 legacy |= sa_proto_get_transients( 1592 handle, protocols[i]); 1593 if (protocols != NULL) 1594 free(protocols); 1595 } 1596 } 1597 return (legacy); 1598 } 1599 1600 /* 1601 * sa_has_prop(optionset, prop) 1602 * 1603 * Is the specified property a member of the optionset? 1604 */ 1605 1606 int 1607 sa_has_prop(sa_optionset_t optionset, sa_property_t prop) 1608 { 1609 char *name; 1610 sa_property_t otherprop; 1611 int result = 0; 1612 1613 if (optionset != NULL) { 1614 name = sa_get_property_attr(prop, "type"); 1615 if (name != NULL) { 1616 otherprop = sa_get_property(optionset, name); 1617 if (otherprop != NULL) 1618 result = 1; 1619 sa_free_attr_string(name); 1620 } 1621 } 1622 return (result); 1623 } 1624 1625 /* 1626 * Update legacy files 1627 * 1628 * Provides functions to add/remove/modify individual entries 1629 * in dfstab and sharetab 1630 */ 1631 1632 void 1633 update_legacy_config(sa_handle_t handle) 1634 { 1635 /* 1636 * no longer used -- this is a placeholder in case we need to 1637 * add it back later. 1638 */ 1639 #ifdef lint 1640 handle = handle; 1641 #endif 1642 } 1643 1644 /* 1645 * sa_valid_property(handle, object, proto, property) 1646 * 1647 * check to see if the specified property is valid relative to the 1648 * specified protocol. The protocol plugin is called to do the work. 1649 */ 1650 1651 int 1652 sa_valid_property(sa_handle_t handle, void *object, char *proto, 1653 sa_property_t property) 1654 { 1655 int ret = SA_OK; 1656 1657 if (proto != NULL && property != NULL) { 1658 ret = sa_proto_valid_prop(handle, proto, property, object); 1659 } 1660 1661 return (ret); 1662 } 1663 1664 /* 1665 * sa_fstype(path) 1666 * 1667 * Given path, return the string representing the path's file system 1668 * type. This is used to discover ZFS shares. 1669 */ 1670 1671 char * 1672 sa_fstype(char *path) 1673 { 1674 int err; 1675 struct stat st; 1676 1677 err = stat(path, &st); 1678 if (err < 0) 1679 err = SA_NO_SUCH_PATH; 1680 else 1681 err = SA_OK; 1682 1683 /* 1684 * If we have a valid path at this point ret, return the fstype. 1685 */ 1686 if (err == SA_OK) 1687 return (strdup(st.st_fstype)); 1688 1689 return (NULL); 1690 } 1691 1692 void 1693 sa_free_fstype(char *type) 1694 { 1695 free(type); 1696 } 1697 1698 /* 1699 * sa_get_derived_optionset(object, proto, hier) 1700 * 1701 * Work backward to the top of the share object tree and start 1702 * copying protocol specific optionsets into a newly created 1703 * optionset that doesn't have a parent (it will be freed 1704 * later). This provides for the property inheritance model. That 1705 * is, properties closer to the share take precedence over group 1706 * level. This also provides for groups of groups in the future. 1707 */ 1708 1709 sa_optionset_t 1710 sa_get_derived_optionset(void *object, char *proto, int hier) 1711 { 1712 sa_optionset_t newoptionset; 1713 sa_optionset_t optionset; 1714 sa_group_t group; 1715 1716 if (hier && 1717 (group = sa_get_parent_group((sa_share_t)object)) != NULL) { 1718 newoptionset = sa_get_derived_optionset((void *)group, proto, 1719 hier); 1720 } else { 1721 newoptionset = (sa_optionset_t)xmlNewNode(NULL, 1722 (xmlChar *)"optionset"); 1723 if (newoptionset != NULL) { 1724 sa_set_optionset_attr(newoptionset, "type", proto); 1725 } 1726 } 1727 /* Dont' do anything if memory wasn't allocated */ 1728 if (newoptionset == NULL) 1729 return (NULL); 1730 1731 /* Found the top so working back down the stack */ 1732 optionset = sa_get_optionset((sa_optionset_t)object, proto); 1733 if (optionset != NULL) { 1734 sa_property_t prop; 1735 /* add optionset to the newoptionset */ 1736 for (prop = sa_get_property(optionset, NULL); 1737 prop != NULL; 1738 prop = sa_get_next_property(prop)) { 1739 sa_property_t newprop; 1740 char *name; 1741 char *value; 1742 name = sa_get_property_attr(prop, "type"); 1743 value = sa_get_property_attr(prop, "value"); 1744 if (name == NULL) 1745 continue; 1746 newprop = sa_get_property(newoptionset, name); 1747 /* Replace the value with the new value */ 1748 if (newprop != NULL) { 1749 /* 1750 * Only set if value is non NULL, old value ok 1751 * if it is NULL. 1752 */ 1753 if (value != NULL) 1754 set_node_attr(newprop, "value", value); 1755 } else { 1756 /* an entirely new property */ 1757 if (value != NULL) { 1758 newprop = sa_create_property(name, 1759 value); 1760 if (newprop != NULL) { 1761 newprop = (sa_property_t) 1762 xmlAddChild( 1763 (xmlNodePtr)newoptionset, 1764 (xmlNodePtr)newprop); 1765 } 1766 } 1767 } 1768 sa_free_attr_string(name); 1769 1770 if (value != NULL) 1771 sa_free_attr_string(value); 1772 } 1773 } 1774 return (newoptionset); 1775 } 1776 1777 void 1778 sa_free_derived_optionset(sa_optionset_t optionset) 1779 { 1780 /* While it shouldn't be linked, it doesn't hurt */ 1781 if (optionset != NULL) { 1782 xmlUnlinkNode((xmlNodePtr) optionset); 1783 xmlFreeNode((xmlNodePtr) optionset); 1784 } 1785 } 1786 1787 /* 1788 * sa_get_all_security_types(object, proto, hier) 1789 * 1790 * Find all the security types set for this object. This is 1791 * preliminary to getting a derived security set. The return value is an 1792 * optionset containg properties which are the sectype values found by 1793 * walking up the XML document structure. The returned optionset 1794 * is a derived optionset. 1795 * 1796 * If hier is 0, only look at object. If non-zero, walk up the tree. 1797 */ 1798 sa_optionset_t 1799 sa_get_all_security_types(void *object, char *proto, int hier) 1800 { 1801 sa_optionset_t options; 1802 sa_security_t security; 1803 sa_group_t group; 1804 sa_property_t prop; 1805 1806 options = NULL; 1807 1808 if (hier && 1809 (group = sa_get_parent_group((sa_share_t)object)) != NULL) 1810 options = sa_get_all_security_types((void *)group, proto, hier); 1811 else 1812 options = (sa_optionset_t)xmlNewNode(NULL, 1813 (xmlChar *)"optionset"); 1814 1815 if (options == NULL) 1816 return (options); 1817 1818 /* Hit the top so collect the security types working back. */ 1819 for (security = sa_get_security((sa_group_t)object, NULL, NULL); 1820 security != NULL; 1821 security = sa_get_next_security(security)) { 1822 char *type; 1823 char *sectype; 1824 1825 type = sa_get_security_attr(security, "type"); 1826 if (type != NULL) { 1827 if (strcmp(type, proto) != 0) { 1828 sa_free_attr_string(type); 1829 continue; 1830 } 1831 sectype = sa_get_security_attr(security, "sectype"); 1832 if (sectype != NULL) { 1833 /* 1834 * Have a security type, check to see if 1835 * already present in optionset and add if it 1836 * isn't. 1837 */ 1838 if (sa_get_property(options, sectype) == NULL) { 1839 prop = sa_create_property(sectype, 1840 "true"); 1841 if (prop != NULL) 1842 prop = (sa_property_t) 1843 xmlAddChild( 1844 (xmlNodePtr)options, 1845 (xmlNodePtr)prop); 1846 } 1847 sa_free_attr_string(sectype); 1848 } 1849 sa_free_attr_string(type); 1850 } 1851 } 1852 1853 return (options); 1854 } 1855 1856 /* 1857 * sa_get_derived_security(object, sectype, proto, hier) 1858 * 1859 * Get the derived security(named optionset) for the object given the 1860 * sectype and proto. If hier is non-zero, walk up the tree to get all 1861 * properties defined for this object, otherwise just those on the 1862 * object. 1863 */ 1864 1865 sa_security_t 1866 sa_get_derived_security(void *object, char *sectype, char *proto, int hier) 1867 { 1868 sa_security_t newsecurity; 1869 sa_security_t security; 1870 sa_group_t group; 1871 sa_property_t prop; 1872 1873 if (hier && 1874 (group = sa_get_parent_group((sa_share_t)object)) != NULL) { 1875 newsecurity = sa_get_derived_security((void *)group, 1876 sectype, proto, hier); 1877 } else { 1878 newsecurity = (sa_security_t)xmlNewNode(NULL, 1879 (xmlChar *)"security"); 1880 if (newsecurity != NULL) { 1881 sa_set_security_attr(newsecurity, "type", proto); 1882 sa_set_security_attr(newsecurity, "sectype", sectype); 1883 } 1884 } 1885 /* Don't do anything if memory wasn't allocated */ 1886 if (newsecurity == NULL) 1887 return (newsecurity); 1888 1889 /* Found the top so working back down the stack. */ 1890 security = sa_get_security((sa_security_t)object, sectype, proto); 1891 if (security == NULL) 1892 return (newsecurity); 1893 1894 /* add security to the newsecurity */ 1895 for (prop = sa_get_property(security, NULL); 1896 prop != NULL; prop = sa_get_next_property(prop)) { 1897 sa_property_t newprop; 1898 char *name; 1899 char *value; 1900 name = sa_get_property_attr(prop, "type"); 1901 value = sa_get_property_attr(prop, "value"); 1902 if (name != NULL) { 1903 newprop = sa_get_property(newsecurity, name); 1904 /* Replace the value with the new value */ 1905 if (newprop != NULL) { 1906 /* 1907 * Only set if value is non NULL, old 1908 * value ok if it is NULL. The value 1909 * must be associated with the "value" 1910 * tag within XML. 1911 */ 1912 if (value != NULL) 1913 set_node_attr(newprop, "value", value); 1914 } else { 1915 /* An entirely new property */ 1916 if (value != NULL) { 1917 newprop = sa_create_property(name, 1918 value); 1919 newprop = (sa_property_t) 1920 xmlAddChild((xmlNodePtr)newsecurity, 1921 (xmlNodePtr)newprop); 1922 } 1923 } 1924 sa_free_attr_string(name); 1925 } 1926 if (value != NULL) 1927 sa_free_attr_string(value); 1928 } 1929 return (newsecurity); 1930 } 1931 1932 void 1933 sa_free_derived_security(sa_security_t security) 1934 { 1935 /* while it shouldn't be linked, it doesn't hurt */ 1936 if (security != NULL) { 1937 xmlUnlinkNode((xmlNodePtr)security); 1938 xmlFreeNode((xmlNodePtr)security); 1939 } 1940 } 1941 1942 /* 1943 * sharetab utility functions 1944 * 1945 * Makes use of the original sharetab.c from fs.d/nfs/lib 1946 */ 1947 1948 /* 1949 * sa_fillshare(share, proto, sh) 1950 * 1951 * Fill the struct share with values obtained from the share object. 1952 */ 1953 void 1954 sa_fillshare(sa_share_t share, char *proto, struct share *sh) 1955 { 1956 char *groupname = NULL; 1957 char *value; 1958 sa_group_t group; 1959 char *buff; 1960 char *zfs; 1961 sa_resource_t resource; 1962 char *rsrcname = NULL; 1963 char *defprop; 1964 1965 /* 1966 * We only want to deal with the path level shares for the 1967 * sharetab file. If a resource, get the parent. 1968 */ 1969 if (sa_is_resource(share)) { 1970 resource = (sa_resource_t)share; 1971 share = sa_get_resource_parent(resource); 1972 rsrcname = sa_get_resource_attr(resource, "name"); 1973 } 1974 1975 group = sa_get_parent_group(share); 1976 if (group != NULL) { 1977 zfs = sa_get_group_attr(group, "zfs"); 1978 groupname = sa_get_group_attr(group, "name"); 1979 1980 if (groupname != NULL && 1981 (strcmp(groupname, "default") == 0 || zfs != NULL)) { 1982 /* 1983 * since the groupname is either "default" or the 1984 * group is a ZFS group, we don't want to keep 1985 * groupname. We do want it if it is any other type of 1986 * group. 1987 */ 1988 sa_free_attr_string(groupname); 1989 groupname = NULL; 1990 } 1991 if (zfs != NULL) 1992 sa_free_attr_string(zfs); 1993 } 1994 1995 value = sa_get_share_attr(share, "path"); 1996 if (value != NULL) { 1997 sh->sh_path = strdup(value); 1998 sa_free_attr_string(value); 1999 } 2000 2001 if (rsrcname != NULL || groupname != NULL) { 2002 int len = 0; 2003 2004 if (rsrcname != NULL) 2005 len += strlen(rsrcname); 2006 if (groupname != NULL) 2007 len += strlen(groupname); 2008 len += 3; /* worst case */ 2009 buff = malloc(len); 2010 (void) snprintf(buff, len, "%s%s%s", 2011 (rsrcname != NULL && 2012 strlen(rsrcname) > 0) ? rsrcname : "-", 2013 groupname != NULL ? "@" : "", 2014 groupname != NULL ? groupname : ""); 2015 sh->sh_res = buff; 2016 if (rsrcname != NULL) 2017 sa_free_attr_string(rsrcname); 2018 if (groupname != NULL) 2019 sa_free_attr_string(groupname); 2020 } else { 2021 sh->sh_res = strdup("-"); 2022 } 2023 2024 /* 2025 * Get correct default prop string. NFS uses "rw", others use 2026 * "". 2027 */ 2028 if (strcmp(proto, "nfs") != 0) 2029 defprop = "\"\""; 2030 else 2031 defprop = "rw"; 2032 2033 sh->sh_fstype = strdup(proto); 2034 value = sa_proto_legacy_format(proto, share, 1); 2035 if (value != NULL) { 2036 if (strlen(value) > 0) 2037 sh->sh_opts = strdup(value); 2038 else 2039 sh->sh_opts = strdup(defprop); 2040 free(value); 2041 } else { 2042 sh->sh_opts = strdup(defprop); 2043 } 2044 2045 value = sa_get_share_description(share); 2046 if (value != NULL) { 2047 sh->sh_descr = strdup(value); 2048 sa_free_share_description(value); 2049 } else { 2050 sh->sh_descr = strdup(""); 2051 } 2052 } 2053 2054 /* 2055 * sa_emptyshare(sh) 2056 * 2057 * Free the strings in the non-NULL members of sh. 2058 */ 2059 2060 void 2061 sa_emptyshare(struct share *sh) 2062 { 2063 if (sh->sh_path != NULL) 2064 free(sh->sh_path); 2065 sh->sh_path = NULL; 2066 if (sh->sh_res != NULL) 2067 free(sh->sh_res); 2068 sh->sh_res = NULL; 2069 if (sh->sh_fstype != NULL) 2070 free(sh->sh_fstype); 2071 sh->sh_fstype = NULL; 2072 if (sh->sh_opts != NULL) 2073 free(sh->sh_opts); 2074 sh->sh_opts = NULL; 2075 if (sh->sh_descr != NULL) 2076 free(sh->sh_descr); 2077 sh->sh_descr = NULL; 2078 } 2079 2080 /* 2081 * sa_update_sharetab_ts(handle) 2082 * 2083 * Update the internal timestamp of when sharetab was last 2084 * changed. This needs to be public for ZFS to get at it. 2085 */ 2086 2087 void 2088 sa_update_sharetab_ts(sa_handle_t handle) 2089 { 2090 struct stat st; 2091 2092 if (handle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0) 2093 handle->tssharetab = TSTAMP(st.st_mtim); 2094 } 2095 2096 /* 2097 * sa_update_sharetab(share, proto) 2098 * 2099 * Update the sharetab file with info from the specified share. 2100 * This could be an update or add. 2101 */ 2102 2103 int 2104 sa_update_sharetab(sa_share_t share, char *proto) 2105 { 2106 int ret = SA_OK; 2107 share_t sh; 2108 char *path; 2109 sa_handle_t handle; 2110 2111 path = sa_get_share_attr(share, "path"); 2112 if (path != NULL) { 2113 (void) memset(&sh, '\0', sizeof (sh)); 2114 2115 handle = sa_find_group_handle((sa_group_t)share); 2116 if (handle != NULL) { 2117 /* 2118 * Fill in share structure and send it to the kernel. 2119 */ 2120 (void) sa_fillshare(share, proto, &sh); 2121 (void) _sharefs(SHAREFS_ADD, &sh); 2122 /* 2123 * We need the timestamp of the sharetab file right 2124 * after the update was done. This lets us detect a 2125 * change that made by a different process. 2126 */ 2127 sa_update_sharetab_ts(handle); 2128 sa_emptyshare(&sh); 2129 } else { 2130 ret = SA_CONFIG_ERR; 2131 } 2132 sa_free_attr_string(path); 2133 } 2134 2135 return (ret); 2136 } 2137 2138 /* 2139 * sa_delete_sharetab(handle, path, proto) 2140 * 2141 * remove the specified share from sharetab. 2142 */ 2143 2144 int 2145 sa_delete_sharetab(sa_handle_t handle, char *path, char *proto) 2146 { 2147 int ret = SA_OK; 2148 struct stat st; 2149 2150 share_t sh; 2151 /* 2152 * Both the path and the proto are 2153 * keys into the sharetab. 2154 */ 2155 if (path != NULL && proto != NULL) { 2156 (void) memset(&sh, '\0', sizeof (sh)); 2157 sh.sh_path = path; 2158 sh.sh_fstype = proto; 2159 2160 ret = _sharefs(SHAREFS_REMOVE, &sh); 2161 if (handle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0) 2162 sa_update_sharetab_ts(handle); 2163 } 2164 return (ret); 2165 } 2166 2167 /* 2168 * sa_needs_refresh(handle) 2169 * 2170 * Returns B_TRUE if the internal cache needs to be refreshed do to a 2171 * change by another process. B_FALSE returned otherwise. 2172 */ 2173 boolean_t 2174 sa_needs_refresh(sa_handle_t handle) 2175 { 2176 struct stat st; 2177 char *str; 2178 uint64_t tstamp; 2179 scf_simple_prop_t *prop; 2180 2181 if (handle == NULL) 2182 return (B_TRUE); 2183 2184 /* 2185 * If sharetab has changed, then there was an external 2186 * change. Check sharetab first since it is updated by ZFS as 2187 * well as sharemgr. This is where external ZFS changes are 2188 * caught. 2189 */ 2190 if (stat(SA_LEGACY_SHARETAB, &st) == 0 && 2191 TSTAMP(st.st_mtim) != handle->tssharetab) 2192 return (B_TRUE); 2193 2194 /* 2195 * If sharetab wasn't changed, check whether there were any 2196 * SMF transactions that modified the config but didn't 2197 * initiate a share. This is less common but does happen. 2198 */ 2199 prop = scf_simple_prop_get(handle->scfhandle->handle, 2200 (const char *)SA_SVC_FMRI_BASE ":default", "state", 2201 "lastupdate"); 2202 if (prop != NULL) { 2203 str = scf_simple_prop_next_astring(prop); 2204 if (str != NULL) 2205 tstamp = strtoull(str, NULL, 0); 2206 else 2207 tstamp = 0; 2208 scf_simple_prop_free(prop); 2209 if (tstamp != handle->tstrans) 2210 return (B_TRUE); 2211 } 2212 2213 return (B_FALSE); 2214 } 2215 2216 /* 2217 * sa_fix_resource_name(path) 2218 * 2219 * Convert invalid characters in a resource name (SMB share name) 2220 * to underscores ('_'). The list of invalid characters includes 2221 * control characters and the following: 2222 * 2223 * " / \ [ ] : | < > + ; , ? * = 2224 * 2225 * The caller must pass a valid path. Leading and trailing slashes 2226 * are stripped from the path before converting invalid characters. 2227 * Resource names are restricted to SA_MAX_RESOURCE_NAME characters. 2228 */ 2229 void 2230 sa_fix_resource_name(char *path) 2231 { 2232 char *invalid = "\"/\\[]:|<>+;,?*="; 2233 char *p = path; 2234 char *q; 2235 size_t len; 2236 2237 assert(path != NULL); 2238 2239 /* 2240 * Strip leading and trailing /'s. 2241 */ 2242 p += strspn(p, "/"); 2243 q = strchr(p, '\0'); 2244 if (q != NULL && q != path) { 2245 while ((--q, *q == '/')) 2246 *q = '\0'; 2247 } 2248 2249 if (*p == '\0') { 2250 (void) strcpy(path, "_"); 2251 return; 2252 } 2253 2254 /* 2255 * Stride over path components until the remaining 2256 * path is no longer than SA_MAX_RESOURCE_NAME. 2257 */ 2258 q = p; 2259 while ((q != NULL) && (strlen(q) > SA_MAX_RESOURCE_NAME)) { 2260 if ((q = strchr(q, '/')) != NULL) { 2261 ++q; 2262 p = q; 2263 } 2264 } 2265 2266 /* 2267 * If the path is still longer than SA_MAX_RESOURCE_NAME, 2268 * take the trailing SA_MAX_RESOURCE_NAME characters. 2269 */ 2270 if ((len = strlen(p)) > SA_MAX_RESOURCE_NAME) { 2271 len = SA_MAX_RESOURCE_NAME; 2272 p = strchr(p, '\0') - (SA_MAX_RESOURCE_NAME - 1); 2273 } 2274 2275 (void) memmove(path, p, len); 2276 path[len] = '\0'; 2277 2278 for (p = path; *p != '\0'; ++p) { 2279 if ((iscntrl(*p)) || strchr(invalid, *p)) 2280 *p = '_'; 2281 } 2282 }