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 }