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