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 2014 Nexenta Systems, Inc. All rights reserved. 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_impl_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 int 746 sa_is_security(char *optname, char *proto) 747 { 748 int ret = 0; 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 if the object is of type "share". 777 */ 778 779 int 780 sa_is_share(void *object) 781 { 782 if (object != NULL) { 783 if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0) 784 return (1); 785 } 786 return (0); 787 } 788 /* 789 * sa_is_resource(object) 790 * 791 * returns true if the object is of type "resource". 792 */ 793 794 int 795 sa_is_resource(void *object) 796 { 797 if (object != NULL) { 798 if (strcmp((char *)((xmlNodePtr)object)->name, "resource") == 0) 799 return (1); 800 } 801 return (0); 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 (sa_handle_impl_t) 1523 handle, 1524 "zfs"); 1525 } 1526 if (group != NULL) { 1527 (void) sa_create_optionset( 1528 group, tmplist->fstype); 1529 (void) sa_set_group_attr(group, 1530 "zfs", "true"); 1531 } 1532 } 1533 if (group != NULL) { 1534 share = _sa_add_share(group, 1535 tmplist->path, SA_SHARE_TRANSIENT, 1536 &err, (uint64_t)SA_FEATURE_NONE); 1537 } 1538 } else { 1539 share = _sa_add_share(lgroup, tmplist->path, 1540 SA_SHARE_TRANSIENT, &err, 1541 (uint64_t)SA_FEATURE_NONE); 1542 } 1543 } 1544 if (share == NULL) 1545 (void) printf(dgettext(TEXT_DOMAIN, 1546 "Problem with transient: %s\n"), sa_errorstr(err)); 1547 if (share != NULL) 1548 set_node_attr(share, "shared", "true"); 1549 if (err == SA_OK) { 1550 if (tmplist->options != NULL && 1551 strlen(tmplist->options) > 0) { 1552 (void) sa_parse_legacy_options(share, 1553 tmplist->options, tmplist->fstype); 1554 } 1555 if (tmplist->resource != NULL && 1556 strcmp(tmplist->resource, "-") != 0) 1557 set_node_attr(share, "resource", 1558 tmplist->resource); 1559 if (tmplist->description != NULL) { 1560 xmlNodePtr node; 1561 node = xmlNewChild((xmlNodePtr)share, NULL, 1562 (xmlChar *)"description", NULL); 1563 xmlNodeSetContent(node, 1564 (xmlChar *)tmplist->description); 1565 } 1566 legacy = 1; 1567 } 1568 } 1569 dfs_free_list(list); 1570 return (legacy); 1571 } 1572 1573 /* 1574 * Get the transient shares from the sharetab (or other) file. since 1575 * these are transient, they only appear in the working file and not 1576 * in a repository. 1577 */ 1578 int 1579 gettransients(sa_handle_impl_t ihandle, xmlNodePtr *root) 1580 { 1581 int legacy = 0; 1582 int numproto; 1583 char **protocols = NULL; 1584 int i; 1585 1586 if (root != NULL) { 1587 if (*root == NULL) 1588 *root = xmlNewNode(NULL, (xmlChar *)"sharecfg"); 1589 if (*root != NULL) { 1590 legacy = parse_sharetab(ihandle); 1591 numproto = sa_get_protocols(&protocols); 1592 for (i = 0; i < numproto; i++) 1593 legacy |= sa_proto_get_transients( 1594 (sa_handle_t)ihandle, protocols[i]); 1595 if (protocols != NULL) 1596 free(protocols); 1597 } 1598 } 1599 return (legacy); 1600 } 1601 1602 /* 1603 * sa_has_prop(optionset, prop) 1604 * 1605 * Is the specified property a member of the optionset? 1606 */ 1607 1608 int 1609 sa_has_prop(sa_optionset_t optionset, sa_property_t prop) 1610 { 1611 char *name; 1612 sa_property_t otherprop; 1613 int result = 0; 1614 1615 if (optionset != NULL) { 1616 name = sa_get_property_attr(prop, "type"); 1617 if (name != NULL) { 1618 otherprop = sa_get_property(optionset, name); 1619 if (otherprop != NULL) 1620 result = 1; 1621 sa_free_attr_string(name); 1622 } 1623 } 1624 return (result); 1625 } 1626 1627 /* 1628 * Update legacy files 1629 * 1630 * Provides functions to add/remove/modify individual entries 1631 * in dfstab and sharetab 1632 */ 1633 1634 void 1635 update_legacy_config(sa_handle_t handle) 1636 { 1637 /* 1638 * no longer used -- this is a placeholder in case we need to 1639 * add it back later. 1640 */ 1641 #ifdef lint 1642 handle = handle; 1643 #endif 1644 } 1645 1646 /* 1647 * sa_valid_property(handle, object, proto, property) 1648 * 1649 * check to see if the specified property is valid relative to the 1650 * specified protocol. The protocol plugin is called to do the work. 1651 */ 1652 1653 int 1654 sa_valid_property(sa_handle_t handle, void *object, char *proto, 1655 sa_property_t property) 1656 { 1657 int ret = SA_OK; 1658 1659 if (proto != NULL && property != NULL) { 1660 ret = sa_proto_valid_prop(handle, proto, property, object); 1661 } 1662 1663 return (ret); 1664 } 1665 1666 /* 1667 * sa_fstype(path) 1668 * 1669 * Given path, return the string representing the path's file system 1670 * type. This is used to discover ZFS shares. 1671 */ 1672 1673 char * 1674 sa_fstype(char *path) 1675 { 1676 int err; 1677 struct stat st; 1678 1679 err = stat(path, &st); 1680 if (err < 0) 1681 err = SA_NO_SUCH_PATH; 1682 else 1683 err = SA_OK; 1684 1685 /* 1686 * If we have a valid path at this point ret, return the fstype. 1687 */ 1688 if (err == SA_OK) 1689 return (strdup(st.st_fstype)); 1690 1691 return (NULL); 1692 } 1693 1694 void 1695 sa_free_fstype(char *type) 1696 { 1697 free(type); 1698 } 1699 1700 /* 1701 * sa_get_derived_optionset(object, proto, hier) 1702 * 1703 * Work backward to the top of the share object tree and start 1704 * copying protocol specific optionsets into a newly created 1705 * optionset that doesn't have a parent (it will be freed 1706 * later). This provides for the property inheritance model. That 1707 * is, properties closer to the share take precedence over group 1708 * level. This also provides for groups of groups in the future. 1709 */ 1710 1711 sa_optionset_t 1712 sa_get_derived_optionset(void *object, char *proto, int hier) 1713 { 1714 sa_optionset_t newoptionset; 1715 sa_optionset_t optionset; 1716 sa_group_t group; 1717 1718 if (hier && 1719 (group = sa_get_parent_group((sa_share_t)object)) != NULL) { 1720 newoptionset = sa_get_derived_optionset((void *)group, proto, 1721 hier); 1722 } else { 1723 newoptionset = (sa_optionset_t)xmlNewNode(NULL, 1724 (xmlChar *)"optionset"); 1725 if (newoptionset != NULL) { 1726 sa_set_optionset_attr(newoptionset, "type", proto); 1727 } 1728 } 1729 /* Dont' do anything if memory wasn't allocated */ 1730 if (newoptionset == NULL) 1731 return (NULL); 1732 1733 /* Found the top so working back down the stack */ 1734 optionset = sa_get_optionset((sa_optionset_t)object, proto); 1735 if (optionset != NULL) { 1736 sa_property_t prop; 1737 /* add optionset to the newoptionset */ 1738 for (prop = sa_get_property(optionset, NULL); 1739 prop != NULL; 1740 prop = sa_get_next_property(prop)) { 1741 sa_property_t newprop; 1742 char *name; 1743 char *value; 1744 name = sa_get_property_attr(prop, "type"); 1745 value = sa_get_property_attr(prop, "value"); 1746 if (name == NULL) 1747 continue; 1748 newprop = sa_get_property(newoptionset, name); 1749 /* Replace the value with the new value */ 1750 if (newprop != NULL) { 1751 /* 1752 * Only set if value is non NULL, old value ok 1753 * if it is NULL. 1754 */ 1755 if (value != NULL) 1756 set_node_attr(newprop, "value", value); 1757 } else { 1758 /* an entirely new property */ 1759 if (value != NULL) { 1760 newprop = sa_create_property(name, 1761 value); 1762 if (newprop != NULL) { 1763 newprop = (sa_property_t) 1764 xmlAddChild( 1765 (xmlNodePtr)newoptionset, 1766 (xmlNodePtr)newprop); 1767 } 1768 } 1769 } 1770 sa_free_attr_string(name); 1771 1772 if (value != NULL) 1773 sa_free_attr_string(value); 1774 } 1775 } 1776 return (newoptionset); 1777 } 1778 1779 void 1780 sa_free_derived_optionset(sa_optionset_t optionset) 1781 { 1782 /* While it shouldn't be linked, it doesn't hurt */ 1783 if (optionset != NULL) { 1784 xmlUnlinkNode((xmlNodePtr) optionset); 1785 xmlFreeNode((xmlNodePtr) optionset); 1786 } 1787 } 1788 1789 /* 1790 * sa_get_all_security_types(object, proto, hier) 1791 * 1792 * Find all the security types set for this object. This is 1793 * preliminary to getting a derived security set. The return value is an 1794 * optionset containg properties which are the sectype values found by 1795 * walking up the XML document structure. The returned optionset 1796 * is a derived optionset. 1797 * 1798 * If hier is 0, only look at object. If non-zero, walk up the tree. 1799 */ 1800 sa_optionset_t 1801 sa_get_all_security_types(void *object, char *proto, int hier) 1802 { 1803 sa_optionset_t options; 1804 sa_security_t security; 1805 sa_group_t group; 1806 sa_property_t prop; 1807 1808 options = NULL; 1809 1810 if (hier && 1811 (group = sa_get_parent_group((sa_share_t)object)) != NULL) 1812 options = sa_get_all_security_types((void *)group, proto, hier); 1813 else 1814 options = (sa_optionset_t)xmlNewNode(NULL, 1815 (xmlChar *)"optionset"); 1816 1817 if (options == NULL) 1818 return (options); 1819 1820 /* Hit the top so collect the security types working back. */ 1821 for (security = sa_get_security((sa_group_t)object, NULL, NULL); 1822 security != NULL; 1823 security = sa_get_next_security(security)) { 1824 char *type; 1825 char *sectype; 1826 1827 type = sa_get_security_attr(security, "type"); 1828 if (type != NULL) { 1829 if (strcmp(type, proto) != 0) { 1830 sa_free_attr_string(type); 1831 continue; 1832 } 1833 sectype = sa_get_security_attr(security, "sectype"); 1834 if (sectype != NULL) { 1835 /* 1836 * Have a security type, check to see if 1837 * already present in optionset and add if it 1838 * isn't. 1839 */ 1840 if (sa_get_property(options, sectype) == NULL) { 1841 prop = sa_create_property(sectype, 1842 "true"); 1843 if (prop != NULL) 1844 prop = (sa_property_t) 1845 xmlAddChild( 1846 (xmlNodePtr)options, 1847 (xmlNodePtr)prop); 1848 } 1849 sa_free_attr_string(sectype); 1850 } 1851 sa_free_attr_string(type); 1852 } 1853 } 1854 1855 return (options); 1856 } 1857 1858 /* 1859 * sa_get_derived_security(object, sectype, proto, hier) 1860 * 1861 * Get the derived security(named optionset) for the object given the 1862 * sectype and proto. If hier is non-zero, walk up the tree to get all 1863 * properties defined for this object, otherwise just those on the 1864 * object. 1865 */ 1866 1867 sa_security_t 1868 sa_get_derived_security(void *object, char *sectype, char *proto, int hier) 1869 { 1870 sa_security_t newsecurity; 1871 sa_security_t security; 1872 sa_group_t group; 1873 sa_property_t prop; 1874 1875 if (hier && 1876 (group = sa_get_parent_group((sa_share_t)object)) != NULL) { 1877 newsecurity = sa_get_derived_security((void *)group, 1878 sectype, proto, hier); 1879 } else { 1880 newsecurity = (sa_security_t)xmlNewNode(NULL, 1881 (xmlChar *)"security"); 1882 if (newsecurity != NULL) { 1883 sa_set_security_attr(newsecurity, "type", proto); 1884 sa_set_security_attr(newsecurity, "sectype", sectype); 1885 } 1886 } 1887 /* Don't do anything if memory wasn't allocated */ 1888 if (newsecurity == NULL) 1889 return (newsecurity); 1890 1891 /* Found the top so working back down the stack. */ 1892 security = sa_get_security((sa_security_t)object, sectype, proto); 1893 if (security == NULL) 1894 return (newsecurity); 1895 1896 /* add security to the newsecurity */ 1897 for (prop = sa_get_property(security, NULL); 1898 prop != NULL; prop = sa_get_next_property(prop)) { 1899 sa_property_t newprop; 1900 char *name; 1901 char *value; 1902 name = sa_get_property_attr(prop, "type"); 1903 value = sa_get_property_attr(prop, "value"); 1904 if (name != NULL) { 1905 newprop = sa_get_property(newsecurity, name); 1906 /* Replace the value with the new value */ 1907 if (newprop != NULL) { 1908 /* 1909 * Only set if value is non NULL, old 1910 * value ok if it is NULL. The value 1911 * must be associated with the "value" 1912 * tag within XML. 1913 */ 1914 if (value != NULL) 1915 set_node_attr(newprop, "value", value); 1916 } else { 1917 /* An entirely new property */ 1918 if (value != NULL) { 1919 newprop = sa_create_property(name, 1920 value); 1921 newprop = (sa_property_t) 1922 xmlAddChild((xmlNodePtr)newsecurity, 1923 (xmlNodePtr)newprop); 1924 } 1925 } 1926 sa_free_attr_string(name); 1927 } 1928 if (value != NULL) 1929 sa_free_attr_string(value); 1930 } 1931 return (newsecurity); 1932 } 1933 1934 void 1935 sa_free_derived_security(sa_security_t security) 1936 { 1937 /* while it shouldn't be linked, it doesn't hurt */ 1938 if (security != NULL) { 1939 xmlUnlinkNode((xmlNodePtr)security); 1940 xmlFreeNode((xmlNodePtr)security); 1941 } 1942 } 1943 1944 /* 1945 * sharetab utility functions 1946 * 1947 * Makes use of the original sharetab.c from fs.d/nfs/lib 1948 */ 1949 1950 /* 1951 * sa_fillshare(share, proto, sh) 1952 * 1953 * Fill the struct share with values obtained from the share object. 1954 */ 1955 void 1956 sa_fillshare(sa_share_t share, char *proto, struct share *sh) 1957 { 1958 char *groupname = NULL; 1959 char *value; 1960 sa_group_t group; 1961 char *buff; 1962 char *zfs; 1963 sa_resource_t resource; 1964 char *rsrcname = NULL; 1965 char *defprop; 1966 1967 /* 1968 * We only want to deal with the path level shares for the 1969 * sharetab file. If a resource, get the parent. 1970 */ 1971 if (sa_is_resource(share)) { 1972 resource = (sa_resource_t)share; 1973 share = sa_get_resource_parent(resource); 1974 rsrcname = sa_get_resource_attr(resource, "name"); 1975 } 1976 1977 group = sa_get_parent_group(share); 1978 if (group != NULL) { 1979 zfs = sa_get_group_attr(group, "zfs"); 1980 groupname = sa_get_group_attr(group, "name"); 1981 1982 if (groupname != NULL && 1983 (strcmp(groupname, "default") == 0 || zfs != NULL)) { 1984 /* 1985 * since the groupname is either "default" or the 1986 * group is a ZFS group, we don't want to keep 1987 * groupname. We do want it if it is any other type of 1988 * group. 1989 */ 1990 sa_free_attr_string(groupname); 1991 groupname = NULL; 1992 } 1993 if (zfs != NULL) 1994 sa_free_attr_string(zfs); 1995 } 1996 1997 value = sa_get_share_attr(share, "path"); 1998 if (value != NULL) { 1999 sh->sh_path = strdup(value); 2000 sa_free_attr_string(value); 2001 } 2002 2003 if (rsrcname != NULL || groupname != NULL) { 2004 int len = 0; 2005 2006 if (rsrcname != NULL) 2007 len += strlen(rsrcname); 2008 if (groupname != NULL) 2009 len += strlen(groupname); 2010 len += 3; /* worst case */ 2011 buff = malloc(len); 2012 (void) snprintf(buff, len, "%s%s%s", 2013 (rsrcname != NULL && 2014 strlen(rsrcname) > 0) ? rsrcname : "-", 2015 groupname != NULL ? "@" : "", 2016 groupname != NULL ? groupname : ""); 2017 sh->sh_res = buff; 2018 if (rsrcname != NULL) 2019 sa_free_attr_string(rsrcname); 2020 if (groupname != NULL) 2021 sa_free_attr_string(groupname); 2022 } else { 2023 sh->sh_res = strdup("-"); 2024 } 2025 2026 /* 2027 * Get correct default prop string. NFS uses "rw", others use 2028 * "". 2029 */ 2030 if (strcmp(proto, "nfs") != 0) 2031 defprop = "\"\""; 2032 else 2033 defprop = "rw"; 2034 2035 sh->sh_fstype = strdup(proto); 2036 value = sa_proto_legacy_format(proto, share, 1); 2037 if (value != NULL) { 2038 if (strlen(value) > 0) 2039 sh->sh_opts = strdup(value); 2040 else 2041 sh->sh_opts = strdup(defprop); 2042 free(value); 2043 } else { 2044 sh->sh_opts = strdup(defprop); 2045 } 2046 2047 value = sa_get_share_description(share); 2048 if (value != NULL) { 2049 sh->sh_descr = strdup(value); 2050 sa_free_share_description(value); 2051 } else { 2052 sh->sh_descr = strdup(""); 2053 } 2054 } 2055 2056 /* 2057 * sa_emptyshare(sh) 2058 * 2059 * Free the strings in the non-NULL members of sh. 2060 */ 2061 2062 void 2063 sa_emptyshare(struct share *sh) 2064 { 2065 if (sh->sh_path != NULL) 2066 free(sh->sh_path); 2067 sh->sh_path = NULL; 2068 if (sh->sh_res != NULL) 2069 free(sh->sh_res); 2070 sh->sh_res = NULL; 2071 if (sh->sh_fstype != NULL) 2072 free(sh->sh_fstype); 2073 sh->sh_fstype = NULL; 2074 if (sh->sh_opts != NULL) 2075 free(sh->sh_opts); 2076 sh->sh_opts = NULL; 2077 if (sh->sh_descr != NULL) 2078 free(sh->sh_descr); 2079 sh->sh_descr = NULL; 2080 } 2081 2082 /* 2083 * sa_update_sharetab_ts(handle) 2084 * 2085 * Update the internal timestamp of when sharetab was last 2086 * changed. This needs to be public for ZFS to get at it. 2087 */ 2088 2089 void 2090 sa_update_sharetab_ts(sa_handle_t handle) 2091 { 2092 struct stat st; 2093 sa_handle_impl_t implhandle = (sa_handle_impl_t)handle; 2094 2095 if (implhandle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0) 2096 implhandle->tssharetab = TSTAMP(st.st_mtim); 2097 } 2098 2099 /* 2100 * sa_update_sharetab(share, proto) 2101 * 2102 * Update the sharetab file with info from the specified share. 2103 * This could be an update or add. 2104 */ 2105 2106 int 2107 sa_update_sharetab(sa_share_t share, char *proto) 2108 { 2109 int ret = SA_OK; 2110 share_t sh; 2111 char *path; 2112 sa_handle_t handle; 2113 2114 path = sa_get_share_attr(share, "path"); 2115 if (path != NULL) { 2116 (void) memset(&sh, '\0', sizeof (sh)); 2117 2118 handle = sa_find_group_handle((sa_group_t)share); 2119 if (handle != NULL) { 2120 /* 2121 * Fill in share structure and send it to the kernel. 2122 */ 2123 (void) sa_fillshare(share, proto, &sh); 2124 (void) _sharefs(SHAREFS_ADD, &sh); 2125 /* 2126 * We need the timestamp of the sharetab file right 2127 * after the update was done. This lets us detect a 2128 * change that made by a different process. 2129 */ 2130 sa_update_sharetab_ts(handle); 2131 sa_emptyshare(&sh); 2132 } else { 2133 ret = SA_CONFIG_ERR; 2134 } 2135 sa_free_attr_string(path); 2136 } 2137 2138 return (ret); 2139 } 2140 2141 /* 2142 * sa_delete_sharetab(handle, path, proto) 2143 * 2144 * remove the specified share from sharetab. 2145 */ 2146 2147 int 2148 sa_delete_sharetab(sa_handle_t handle, char *path, char *proto) 2149 { 2150 int ret = SA_OK; 2151 struct stat st; 2152 2153 share_t sh; 2154 /* 2155 * Both the path and the proto are 2156 * keys into the sharetab. 2157 */ 2158 if (path != NULL && proto != NULL) { 2159 (void) memset(&sh, '\0', sizeof (sh)); 2160 sh.sh_path = path; 2161 sh.sh_fstype = proto; 2162 2163 ret = _sharefs(SHAREFS_REMOVE, &sh); 2164 if (handle != NULL && stat(SA_LEGACY_SHARETAB, &st) == 0) 2165 sa_update_sharetab_ts(handle); 2166 } 2167 return (ret); 2168 } 2169 2170 /* 2171 * sa_needs_refresh(handle) 2172 * 2173 * Returns B_TRUE if the internal cache needs to be refreshed do to a 2174 * change by another process. B_FALSE returned otherwise. 2175 */ 2176 boolean_t 2177 sa_needs_refresh(sa_handle_t handle) 2178 { 2179 sa_handle_impl_t implhandle = (sa_handle_impl_t)handle; 2180 struct stat st; 2181 char *str; 2182 uint64_t tstamp; 2183 scf_simple_prop_t *prop; 2184 2185 if (handle == NULL) 2186 return (B_TRUE); 2187 2188 /* 2189 * If sharetab has changed, then there was an external 2190 * change. Check sharetab first since it is updated by ZFS as 2191 * well as sharemgr. This is where external ZFS changes are 2192 * caught. 2193 */ 2194 if (stat(SA_LEGACY_SHARETAB, &st) == 0 && 2195 TSTAMP(st.st_mtim) != implhandle->tssharetab) 2196 return (B_TRUE); 2197 2198 /* 2199 * If sharetab wasn't changed, check whether there were any 2200 * SMF transactions that modified the config but didn't 2201 * initiate a share. This is less common but does happen. 2202 */ 2203 prop = scf_simple_prop_get(implhandle->scfhandle->handle, 2204 (const char *)SA_SVC_FMRI_BASE ":default", "state", 2205 "lastupdate"); 2206 if (prop != NULL) { 2207 str = scf_simple_prop_next_astring(prop); 2208 if (str != NULL) 2209 tstamp = strtoull(str, NULL, 0); 2210 else 2211 tstamp = 0; 2212 scf_simple_prop_free(prop); 2213 if (tstamp != implhandle->tstrans) 2214 return (B_TRUE); 2215 } 2216 2217 return (B_FALSE); 2218 } 2219 2220 /* 2221 * sa_fix_resource_name(path) 2222 * 2223 * Convert invalid characters in a resource name (SMB share name) 2224 * to underscores ('_'). The list of invalid characters includes 2225 * control characters and the following: 2226 * 2227 * " / \ [ ] : | < > + ; , ? * = 2228 * 2229 * The caller must pass a valid path. Leading and trailing slashes 2230 * are stripped from the path before converting invalid characters. 2231 * Resource names are restricted to SA_MAX_RESOURCE_NAME characters. 2232 */ 2233 void 2234 sa_fix_resource_name(char *path) 2235 { 2236 char *invalid = "\"/\\[]:|<>+;,?*="; 2237 char *p = path; 2238 char *q; 2239 size_t len; 2240 2241 assert(path != NULL); 2242 2243 /* 2244 * Strip leading and trailing /'s. 2245 */ 2246 p += strspn(p, "/"); 2247 q = strchr(p, '\0'); 2248 if (q != NULL && q != path) { 2249 while ((--q, *q == '/')) 2250 *q = '\0'; 2251 } 2252 2253 if (*p == '\0') { 2254 (void) strcpy(path, "_"); 2255 return; 2256 } 2257 2258 /* 2259 * Stride over path components until the remaining 2260 * path is no longer than SA_MAX_RESOURCE_NAME. 2261 */ 2262 q = p; 2263 while ((q != NULL) && (strlen(q) > SA_MAX_RESOURCE_NAME)) { 2264 if ((q = strchr(q, '/')) != NULL) { 2265 ++q; 2266 p = q; 2267 } 2268 } 2269 2270 /* 2271 * If the path is still longer than SA_MAX_RESOURCE_NAME, 2272 * take the trailing SA_MAX_RESOURCE_NAME characters. 2273 */ 2274 if ((len = strlen(p)) > SA_MAX_RESOURCE_NAME) { 2275 len = SA_MAX_RESOURCE_NAME; 2276 p = strchr(p, '\0') - (SA_MAX_RESOURCE_NAME - 1); 2277 } 2278 2279 (void) memmove(path, p, len); 2280 path[len] = '\0'; 2281 2282 for (p = path; *p != '\0'; ++p) { 2283 if ((iscntrl(*p)) || strchr(invalid, *p)) 2284 *p = '_'; 2285 } 2286 } 2287