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  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  27  * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
  28  */
  29 
  30 #include <stdio.h>
  31 #include <libzfs.h>
  32 #include <string.h>
  33 #include <strings.h>
  34 #include <errno.h>
  35 #include <libshare.h>
  36 #include "libshare_impl.h"
  37 #include <libintl.h>
  38 #include <sys/mnttab.h>
  39 #include <sys/mntent.h>
  40 #include <assert.h>
  41 
  42 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
  43 extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
  44 extern char *sa_fstype(char *);
  45 extern void set_node_attr(void *, char *, char *);
  46 extern int sa_is_share(void *);
  47 extern void sa_update_sharetab_ts(sa_handle_t);
  48 
  49 /*
  50  * File system specific code for ZFS. The original code was stolen
  51  * from the "zfs" command and modified to better suit this library's
  52  * usage.
  53  */
  54 
  55 typedef struct get_all_cbdata {
  56         zfs_handle_t    **cb_handles;
  57         size_t          cb_alloc;
  58         size_t          cb_used;
  59         uint_t          cb_types;
  60 } get_all_cbdata_t;
  61 
  62 /*
  63  * sa_zfs_init(impl_handle)
  64  *
  65  * Initialize an access handle into libzfs.  The handle needs to stay
  66  * around until sa_zfs_fini() in order to maintain the cache of
  67  * mounts.
  68  */
  69 
  70 int
  71 sa_zfs_init(sa_handle_impl_t impl_handle)
  72 {
  73         impl_handle->zfs_libhandle = libzfs_init();
  74         if (impl_handle->zfs_libhandle != NULL) {
  75                 libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
  76                 return (B_TRUE);
  77         }
  78         return (B_FALSE);
  79 }
  80 
  81 /*
  82  * sa_zfs_fini(impl_handle)
  83  *
  84  * cleanup data structures and the libzfs handle used for accessing
  85  * zfs file share info.
  86  */
  87 
  88 void
  89 sa_zfs_fini(sa_handle_impl_t impl_handle)
  90 {
  91         if (impl_handle->zfs_libhandle != NULL) {
  92                 if (impl_handle->zfs_list != NULL) {
  93                         zfs_handle_t **zhp = impl_handle->zfs_list;
  94                         size_t i;
  95 
  96                         /*
  97                          * Contents of zfs_list need to be freed so we
  98                          * don't lose ZFS handles.
  99                          */
 100                         for (i = 0; i < impl_handle->zfs_list_count; i++) {
 101                                 zfs_close(zhp[i]);
 102                         }
 103                         free(impl_handle->zfs_list);
 104                         impl_handle->zfs_list = NULL;
 105                         impl_handle->zfs_list_count = 0;
 106                 }
 107 
 108                 libzfs_fini(impl_handle->zfs_libhandle);
 109                 impl_handle->zfs_libhandle = NULL;
 110         }
 111 }
 112 
 113 /*
 114  * get_one_filesystem(zfs_handle_t, data)
 115  *
 116  * an iterator function called while iterating through the ZFS
 117  * root. It accumulates into an array of file system handles that can
 118  * be used to derive info about those file systems.
 119  *
 120  * Note that as this function is called, we close all zhp handles that
 121  * are not going to be places into the cp_handles list. We don't want
 122  * to close the ones we are keeping, but all others would be leaked if
 123  * not closed here.
 124  */
 125 
 126 static int
 127 get_one_filesystem(zfs_handle_t *zhp, void *data)
 128 {
 129         get_all_cbdata_t *cbp = data;
 130         zfs_type_t type = zfs_get_type(zhp);
 131 
 132         /*
 133          * Interate over any nested datasets.
 134          */
 135         if (type == ZFS_TYPE_FILESYSTEM &&
 136             zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) {
 137                 zfs_close(zhp);
 138                 return (1);
 139         }
 140 
 141         /*
 142          * Skip any datasets whose type does not match.
 143          */
 144         if ((type & cbp->cb_types) == 0) {
 145                 zfs_close(zhp);
 146                 return (0);
 147         }
 148 
 149         if (cbp->cb_alloc == cbp->cb_used) {
 150                 zfs_handle_t **handles;
 151 
 152                 if (cbp->cb_alloc == 0)
 153                         cbp->cb_alloc = 64;
 154                 else
 155                         cbp->cb_alloc *= 2;
 156 
 157                 handles = (zfs_handle_t **)calloc(1,
 158                     cbp->cb_alloc * sizeof (void *));
 159 
 160                 if (handles == NULL) {
 161                         zfs_close(zhp);
 162                         return (0);
 163                 }
 164                 if (cbp->cb_handles) {
 165                         bcopy(cbp->cb_handles, handles,
 166                             cbp->cb_used * sizeof (void *));
 167                         free(cbp->cb_handles);
 168                 }
 169 
 170                 cbp->cb_handles = handles;
 171         }
 172 
 173         cbp->cb_handles[cbp->cb_used++] = zhp;
 174 
 175         return (0);
 176 }
 177 
 178 /*
 179  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
 180  *
 181  * iterate through all ZFS file systems starting at the root. Returns
 182  * a count and an array of handle pointers. Allocating is only done
 183  * once. The caller does not need to free since it will be done at
 184  * sa_zfs_fini() time.
 185  */
 186 
 187 static void
 188 get_all_filesystems(sa_handle_impl_t impl_handle,
 189     zfs_handle_t ***fslist, size_t *count)
 190 {
 191         get_all_cbdata_t cb = { 0 };
 192         cb.cb_types = ZFS_TYPE_FILESYSTEM;
 193 
 194         if (impl_handle->zfs_list != NULL) {
 195                 *fslist = impl_handle->zfs_list;
 196                 *count = impl_handle->zfs_list_count;
 197                 return;
 198         }
 199 
 200         (void) zfs_iter_root(impl_handle->zfs_libhandle,
 201             get_one_filesystem, &cb);
 202 
 203         impl_handle->zfs_list = *fslist = cb.cb_handles;
 204         impl_handle->zfs_list_count = *count = cb.cb_used;
 205 }
 206 
 207 /*
 208  * mountpoint_compare(a, b)
 209  *
 210  * compares the mountpoint on two zfs file systems handles.
 211  * returns values following strcmp() model.
 212  */
 213 
 214 static int
 215 mountpoint_compare(const void *a, const void *b)
 216 {
 217         zfs_handle_t **za = (zfs_handle_t **)a;
 218         zfs_handle_t **zb = (zfs_handle_t **)b;
 219         char mounta[MAXPATHLEN];
 220         char mountb[MAXPATHLEN];
 221 
 222         verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
 223             sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
 224         verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
 225             sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
 226 
 227         return (strcmp(mounta, mountb));
 228 }
 229 
 230 /*
 231  * return legacy mountpoint.  Caller provides space for mountpoint and
 232  * dataset.
 233  */
 234 int
 235 get_legacy_mountpoint(const char *path, char *dataset, size_t dlen,
 236     char *mountpoint, size_t mlen)
 237 {
 238         FILE *fp;
 239         struct mnttab entry;
 240 
 241         if ((fp = fopen(MNTTAB, "r")) == NULL) {
 242                 return (1);
 243         }
 244 
 245         while (getmntent(fp, &entry) == 0) {
 246 
 247                 if (entry.mnt_fstype == NULL ||
 248                     strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
 249                         continue;
 250 
 251                 if (strcmp(entry.mnt_mountp, path) == 0) {
 252                         if (mlen > 0)
 253                                 (void) strlcpy(mountpoint, entry.mnt_mountp,
 254                                     mlen);
 255                         if (dlen > 0)
 256                                 (void) strlcpy(dataset, entry.mnt_special,
 257                                     dlen);
 258                         break;
 259                 }
 260         }
 261         (void) fclose(fp);
 262         return (1);
 263 }
 264 
 265 
 266 static char *
 267 verify_zfs_handle(zfs_handle_t *hdl, const char *path, boolean_t search_mnttab)
 268 {
 269         char mountpoint[ZFS_MAXPROPLEN];
 270         char canmount[ZFS_MAXPROPLEN] = { 0 };
 271         /* must have a mountpoint */
 272         if (zfs_prop_get(hdl, ZFS_PROP_MOUNTPOINT, mountpoint,
 273             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
 274                 /* no mountpoint */
 275                 return (NULL);
 276         }
 277 
 278         /* mountpoint must be a path */
 279         if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
 280             strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
 281                 /*
 282                  * Search mmttab for mountpoint and get dataset.
 283                  */
 284 
 285                 if (search_mnttab == B_TRUE &&
 286                     get_legacy_mountpoint(path, mountpoint,
 287                     sizeof (mountpoint), NULL, 0) == 0) {
 288                         return (strdup(mountpoint));
 289                 }
 290                 return (NULL);
 291         }
 292 
 293         /* canmount must be set */
 294         if (zfs_prop_get(hdl, ZFS_PROP_CANMOUNT, canmount,
 295             sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
 296             strcmp(canmount, "off") == 0)
 297                 return (NULL);
 298 
 299         /*
 300          * have a mountable handle but want to skip those marked none
 301          * and legacy
 302          */
 303         if (strcmp(mountpoint, path) == 0) {
 304                 return (strdup((char *)zfs_get_name(hdl)));
 305         }
 306 
 307         return (NULL);
 308 }
 309 
 310 /*
 311  * get_zfs_dataset(impl_handle, path)
 312  *
 313  * get the name of the ZFS dataset the path is equivalent to.  The
 314  * dataset name is used for get/set of ZFS properties since libzfs
 315  * requires a dataset to do a zfs_open().
 316  */
 317 
 318 static char *
 319 get_zfs_dataset(sa_handle_impl_t impl_handle, char *path,
 320     boolean_t search_mnttab)
 321 {
 322         size_t i, count = 0;
 323         zfs_handle_t **zlist;
 324         char *cutpath;
 325         zfs_handle_t *handle_from_path;
 326         char *ret = NULL;
 327 
 328         /*
 329          * First we optimistically assume that the mount path for the filesystem
 330          * is the same as the name of the filesystem (minus some number of
 331          * leading slashes). If this is true, then zfs_open should properly open
 332          * the filesystem. We duplicate the error checking done later in the
 333          * function for consistency. If anything fails, we resort to the
 334          * (extremely slow) search of all the filesystems.
 335          */
 336         cutpath = path + strspn(path, "/");
 337 
 338         assert(impl_handle->zfs_libhandle != NULL);
 339         if ((handle_from_path = zfs_open(impl_handle->zfs_libhandle, cutpath,
 340             ZFS_TYPE_FILESYSTEM)) != NULL)
 341                 if ((ret = verify_zfs_handle(handle_from_path, path,
 342                     search_mnttab)) != NULL)
 343                         return (ret);
 344         /*
 345          * Couldn't find a filesystem optimistically, check all the handles we
 346          * can.
 347          */
 348         get_all_filesystems(impl_handle, &zlist, &count);
 349         for (i = 0; i < count; i++) {
 350                 assert(zlist[i]);
 351                 if ((ret = verify_zfs_handle(zlist[i], path,
 352                     search_mnttab)) != NULL)
 353                         return (ret);
 354         }
 355 
 356         /* Couldn't find a matching dataset */
 357         return (NULL);
 358 }
 359 
 360 /*
 361  * get_zfs_property(dataset, property)
 362  *
 363  * Get the file system property specified from the ZFS dataset.
 364  */
 365 
 366 static char *
 367 get_zfs_property(char *dataset, zfs_prop_t property)
 368 {
 369         zfs_handle_t *handle = NULL;
 370         char shareopts[ZFS_MAXPROPLEN];
 371         libzfs_handle_t *libhandle;
 372 
 373         libhandle = libzfs_init();
 374         if (libhandle != NULL) {
 375                 handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
 376                 if (handle != NULL) {
 377                         if (zfs_prop_get(handle, property, shareopts,
 378                             sizeof (shareopts), NULL, NULL, 0,
 379                             B_FALSE) == 0) {
 380                                 zfs_close(handle);
 381                                 libzfs_fini(libhandle);
 382                                 return (strdup(shareopts));
 383                         }
 384                         zfs_close(handle);
 385                 }
 386                 libzfs_fini(libhandle);
 387         }
 388         return (NULL);
 389 }
 390 
 391 /*
 392  * sa_zfs_is_shared(handle, path)
 393  *
 394  * Check to see if the ZFS path provided has the sharenfs option set
 395  * or not.
 396  */
 397 
 398 int
 399 sa_zfs_is_shared(sa_handle_t sahandle, char *path)
 400 {
 401         int ret = 0;
 402         char *dataset;
 403         zfs_handle_t *handle = NULL;
 404         char shareopts[ZFS_MAXPROPLEN];
 405         libzfs_handle_t *libhandle;
 406 
 407         dataset = get_zfs_dataset((sa_handle_t)sahandle, path, B_FALSE);
 408         if (dataset != NULL) {
 409                 libhandle = libzfs_init();
 410                 if (libhandle != NULL) {
 411                         handle = zfs_open(libhandle, dataset,
 412                             ZFS_TYPE_FILESYSTEM);
 413                         if (handle != NULL) {
 414                                 if (zfs_prop_get(handle, ZFS_PROP_SHARENFS,
 415                                     shareopts, sizeof (shareopts), NULL, NULL,
 416                                     0, B_FALSE) == 0 &&
 417                                     strcmp(shareopts, "off") != 0) {
 418                                         ret = 1; /* it is shared */
 419                                 }
 420                                 zfs_close(handle);
 421                         }
 422                         libzfs_fini(libhandle);
 423                 }
 424                 free(dataset);
 425         }
 426         return (ret);
 427 }
 428 
 429 /*
 430  * find_or_create_group(handle, groupname, proto, *err)
 431  *
 432  * While walking the ZFS tree, we need to add shares to a defined
 433  * group. If the group doesn't exist, create it first, making sure it
 434  * is marked as a ZFS group.
 435  *
 436  * Note that all ZFS shares are in a subgroup of the top level group
 437  * called "zfs".
 438  */
 439 
 440 static sa_group_t
 441 find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
 442 {
 443         sa_group_t group;
 444         sa_optionset_t optionset;
 445         int ret = SA_OK;
 446 
 447         /*
 448          * we check to see if the "zfs" group exists. Since this
 449          * should be the top level group, we don't want the
 450          * parent. This is to make sure the zfs group has been created
 451          * and to created if it hasn't been.
 452          */
 453         group = sa_get_group(handle, groupname);
 454         if (group == NULL) {
 455                 group = sa_create_group(handle, groupname, &ret);
 456 
 457                 /* make sure this is flagged as a ZFS group */
 458                 if (group != NULL)
 459                         ret = sa_set_group_attr(group, "zfs", "true");
 460         }
 461         if (group != NULL) {
 462                 if (proto != NULL) {
 463                         optionset = sa_get_optionset(group, proto);
 464                         if (optionset == NULL)
 465                                 optionset = sa_create_optionset(group, proto);
 466                 }
 467         }
 468         if (err != NULL)
 469                 *err = ret;
 470         return (group);
 471 }
 472 
 473 /*
 474  * find_or_create_zfs_subgroup(groupname, optstring, *err)
 475  *
 476  * ZFS shares will be in a subgroup of the "zfs" master group.  This
 477  * function looks to see if the groupname exists and returns it if it
 478  * does or else creates a new one with the specified name and returns
 479  * that.  The "zfs" group will exist before we get here, but we make
 480  * sure just in case.
 481  *
 482  * err must be a valid pointer.
 483  */
 484 
 485 static sa_group_t
 486 find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, char *proto,
 487     char *optstring, int *err)
 488 {
 489         sa_group_t group = NULL;
 490         sa_group_t zfs;
 491         char *name;
 492         char *options;
 493 
 494         /* start with the top-level "zfs" group */
 495         zfs = sa_get_group(handle, "zfs");
 496         *err = SA_OK;
 497         if (zfs != NULL) {
 498                 for (group = sa_get_sub_group(zfs); group != NULL;
 499                     group = sa_get_next_group(group)) {
 500                         name = sa_get_group_attr(group, "name");
 501                         if (name != NULL && strcmp(name, groupname) == 0) {
 502                                 /* have the group so break out of here */
 503                                 sa_free_attr_string(name);
 504                                 break;
 505                         }
 506                         if (name != NULL)
 507                                 sa_free_attr_string(name);
 508                 }
 509 
 510                 if (group == NULL) {
 511                         /*
 512                          * Need to create the sub-group since it doesn't exist
 513                          */
 514                         group = _sa_create_zfs_group(zfs, groupname);
 515                         if (group == NULL) {
 516                                 *err = SA_NO_MEMORY;
 517                                 return (NULL);
 518                         }
 519                         set_node_attr(group, "zfs", "true");
 520                 }
 521                 if (strcmp(optstring, "on") == 0)
 522                         optstring = "rw";
 523                 options = strdup(optstring);
 524                 if (options != NULL) {
 525                         *err = sa_parse_legacy_options(group, options,
 526                             proto);
 527                         /* If no optionset, add one. */
 528                         if (sa_get_optionset(group, proto) == NULL)
 529                                 (void) sa_create_optionset(group, proto);
 530 
 531                         /*
 532                          * Do not forget to update an optionset of
 533                          * the parent group so that it contains
 534                          * all protocols its subgroups have.
 535                          */
 536                         if (sa_get_optionset(zfs, proto) == NULL)
 537                                 (void) sa_create_optionset(zfs, proto);
 538 
 539                         free(options);
 540                 } else {
 541                         *err = SA_NO_MEMORY;
 542                 }
 543         }
 544         return (group);
 545 }
 546 
 547 /*
 548  * zfs_construct_resource(share, name, base, dataset)
 549  *
 550  * Add a resource to the share using name as a template. If name ==
 551  * NULL, then construct a name based on the dataset value.
 552  * name.
 553  */
 554 static void
 555 zfs_construct_resource(sa_share_t share, char *dataset)
 556 {
 557         char buff[SA_MAX_RESOURCE_NAME + 1];
 558         int ret = SA_OK;
 559 
 560         (void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset);
 561         sa_fix_resource_name(buff);
 562         (void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret);
 563 }
 564 
 565 /*
 566  * zfs_inherited(handle, source, sourcestr)
 567  *
 568  * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares
 569  * for readability.
 570  */
 571 static int
 572 zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
 573     char *shareopts, char *mountpoint, char *proto, char *dataset)
 574 {
 575         int doshopt = 0;
 576         int err = SA_OK;
 577         sa_group_t group;
 578         sa_resource_t resource;
 579         uint64_t features;
 580 
 581         /*
 582          * Need to find the "real" parent sub-group. It may not be
 583          * mounted, but it was identified in the "sourcestr"
 584          * variable. The real parent not mounted can occur if
 585          * "canmount=off and sharenfs=on".
 586          */
 587         group = find_or_create_zfs_subgroup(handle, sourcestr, proto,
 588             shareopts, &doshopt);
 589         if (group != NULL) {
 590                 /*
 591                  * We may need the first share for resource
 592                  * prototype. We only care about it if it has a
 593                  * resource that sets a prefix value.
 594                  */
 595                 if (share == NULL)
 596                         share = _sa_add_share(group, mountpoint,
 597                             SA_SHARE_TRANSIENT, &err,
 598                             (uint64_t)SA_FEATURE_NONE);
 599                 /*
 600                  * some options may only be on shares. If the opt
 601                  * string contains one of those, we put it just on the
 602                  * share.
 603                  */
 604                 if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) {
 605                         char *options;
 606                         options = strdup(shareopts);
 607                         if (options != NULL) {
 608                                 set_node_attr(share, "dataset", dataset);
 609                                 err = sa_parse_legacy_options(share, options,
 610                                     proto);
 611                                 set_node_attr(share, "dataset", NULL);
 612                                 free(options);
 613                         }
 614                         if (sa_get_optionset(group, proto) == NULL)
 615                                 (void) sa_create_optionset(group, proto);
 616                 }
 617                 features = sa_proto_get_featureset(proto);
 618                 if (share != NULL && features & SA_FEATURE_RESOURCE) {
 619                         /*
 620                          * We have a share and the protocol requires
 621                          * that at least one resource exist (probably
 622                          * SMB). We need to make sure that there is at
 623                          * least one.
 624                          */
 625                         resource = sa_get_share_resource(share, NULL);
 626                         if (resource == NULL) {
 627                                 zfs_construct_resource(share, dataset);
 628                         }
 629                 }
 630         } else {
 631                 err = SA_NO_MEMORY;
 632         }
 633         return (err);
 634 }
 635 
 636 /*
 637  * zfs_notinherited(group, share, mountpoint, shareopts, proto, dataset,
 638  *     grouperr)
 639  *
 640  * handle case where this is the top of a sub-group in ZFS. Pulled out
 641  * of sa_get_zfs_shares for readability. We need the grouperr from the
 642  * creation of the subgroup to know whether to add the public
 643  * property, etc. to the specific share.
 644  */
 645 static int
 646 zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint,
 647     char *shareopts, char *proto, char *dataset, int grouperr)
 648 {
 649         int err = SA_OK;
 650         sa_resource_t resource;
 651         uint64_t features;
 652 
 653         set_node_attr(group, "zfs", "true");
 654         if (share == NULL)
 655                 share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
 656                     &err, (uint64_t)SA_FEATURE_NONE);
 657 
 658         if (err != SA_OK)
 659                 return (err);
 660 
 661         if (strcmp(shareopts, "on") == 0)
 662                 shareopts = "";
 663         if (shareopts != NULL) {
 664                 char *options;
 665                 if (grouperr == SA_PROP_SHARE_ONLY) {
 666                         /*
 667                          * Some properties may only be on shares, but
 668                          * due to the ZFS sub-groups being artificial,
 669                          * we sometimes get this and have to deal with
 670                          * it. We do it by attempting to put it on the
 671                          * share.
 672                          */
 673                         options = strdup(shareopts);
 674                         if (options != NULL) {
 675                                 err = sa_parse_legacy_options(share,
 676                                     options, proto);
 677                                 free(options);
 678                         }
 679                 }
 680                 /* Unmark the share's changed state */
 681                 set_node_attr(share, "changed", NULL);
 682         }
 683         features = sa_proto_get_featureset(proto);
 684         if (share != NULL && features & SA_FEATURE_RESOURCE) {
 685                 /*
 686                  * We have a share and the protocol requires that at
 687                  * least one resource exist (probably SMB). We need to
 688                  * make sure that there is at least one.
 689                  */
 690                 resource = sa_get_share_resource(share, NULL);
 691                 if (resource == NULL) {
 692                         zfs_construct_resource(share, dataset);
 693                 }
 694         }
 695         return (err);
 696 }
 697 
 698 /*
 699  * zfs_grp_error(err)
 700  *
 701  * Print group create error, but only once. If err is 0 do the
 702  * print else don't.
 703  */
 704 
 705 static void
 706 zfs_grp_error(int err)
 707 {
 708         if (err == 0) {
 709                 /* only print error once */
 710                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
 711                     "Cannot create ZFS subgroup during initialization:"
 712                     " %s\n"), sa_errorstr(SA_SYSTEM_ERR));
 713         }
 714 }
 715 
 716 /*
 717  * zfs_process_share(handle, share, mountpoint, proto, source,
 718  *     shareopts, sourcestr)
 719  *
 720  * Creates the subgroup, if necessary and adds shares, resources
 721  * and properties.
 722  */
 723 int
 724 sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
 725     char *mountpoint, char *proto, zprop_source_t source, char *shareopts,
 726     char *sourcestr, char *dataset)
 727 {
 728         int err = SA_OK;
 729 
 730         if (source & ZPROP_SRC_INHERITED) {
 731                 err = zfs_inherited(handle, share, sourcestr, shareopts,
 732                     mountpoint, proto, dataset);
 733         } else {
 734                 group = find_or_create_zfs_subgroup(handle, dataset, proto,
 735                     shareopts, &err);
 736                 if (group == NULL) {
 737                         static boolean_t reported_error = B_FALSE;
 738                         /*
 739                          * There is a problem, but we can't do
 740                          * anything about it at this point so we issue
 741                          * a warning and move on.
 742                          */
 743                         zfs_grp_error(reported_error);
 744                         reported_error = B_TRUE;
 745                 }
 746                 set_node_attr(group, "zfs", "true");
 747                 /*
 748                  * Add share with local opts via zfs_notinherited.
 749                  */
 750                 err = zfs_notinherited(group, share, mountpoint, shareopts,
 751                     proto, dataset, err);
 752         }
 753         return (err);
 754 }
 755 
 756 /*
 757  * sa_get_zfs_shares(handle, groupname)
 758  *
 759  * Walk the mnttab for all zfs mounts and determine which are
 760  * shared. Find or create the appropriate group/sub-group to contain
 761  * the shares.
 762  *
 763  * All shares are in a sub-group that will hold the properties. This
 764  * allows representing the inherited property model.
 765  *
 766  * One area of complication is if "sharenfs" is set at one level of
 767  * the directory tree and "sharesmb" is set at a different level, the
 768  * a sub-group must be formed at the lower level for both
 769  * protocols. That is the nature of the problem in CR 6667349.
 770  */
 771 
 772 int
 773 sa_get_zfs_shares(sa_handle_t handle, char *groupname)
 774 {
 775         sa_group_t zfsgroup;
 776         boolean_t nfs;
 777         boolean_t nfs_inherited;
 778         boolean_t smb;
 779         boolean_t smb_inherited;
 780         zfs_handle_t **zlist;
 781         char nfsshareopts[ZFS_MAXPROPLEN];
 782         char smbshareopts[ZFS_MAXPROPLEN];
 783         sa_share_t share;
 784         zprop_source_t source;
 785         char nfssourcestr[ZFS_MAXPROPLEN];
 786         char smbsourcestr[ZFS_MAXPROPLEN];
 787         char mountpoint[ZFS_MAXPROPLEN];
 788         size_t count = 0, i;
 789         libzfs_handle_t *zfs_libhandle;
 790         int err = SA_OK;
 791 
 792         /*
 793          * If we can't access libzfs, don't bother doing anything.
 794          */
 795         zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
 796         if (zfs_libhandle == NULL)
 797                 return (SA_SYSTEM_ERR);
 798 
 799         zfsgroup = find_or_create_group(handle, groupname, NULL, &err);
 800         /* Not an error, this could be a legacy condition */
 801         if (zfsgroup == NULL)
 802                 return (SA_OK);
 803 
 804         /*
 805          * need to walk the mounted ZFS pools and datasets to
 806          * find shares that are possible.
 807          */
 808         get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
 809         qsort(zlist, count, sizeof (void *), mountpoint_compare);
 810 
 811         for (i = 0; i < count; i++) {
 812                 char *dataset;
 813 
 814                 source = ZPROP_SRC_ALL;
 815                 /* If no mountpoint, skip. */
 816                 if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT,
 817                     mountpoint, sizeof (mountpoint), NULL, NULL, 0,
 818                     B_FALSE) != 0)
 819                         continue;
 820 
 821                 /*
 822                  * zfs_get_name value must not be freed. It is just a
 823                  * pointer to a value in the handle.
 824                  */
 825                 if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
 826                         continue;
 827 
 828                 /*
 829                  * only deal with "mounted" file systems since
 830                  * unmounted file systems can't actually be shared.
 831                  */
 832 
 833                 if (!zfs_is_mounted(zlist[i], NULL))
 834                         continue;
 835 
 836                 nfs = nfs_inherited = B_FALSE;
 837 
 838                 if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, nfsshareopts,
 839                     sizeof (nfsshareopts), &source, nfssourcestr,
 840                     ZFS_MAXPROPLEN, B_FALSE) == 0 &&
 841                     strcmp(nfsshareopts, "off") != 0) {
 842                         if (source & ZPROP_SRC_INHERITED)
 843                                 nfs_inherited = B_TRUE;
 844                         else
 845                                 nfs = B_TRUE;
 846                 }
 847 
 848                 smb = smb_inherited = B_FALSE;
 849                 if (zfs_prop_get(zlist[i], ZFS_PROP_SHARESMB, smbshareopts,
 850                     sizeof (smbshareopts), &source, smbsourcestr,
 851                     ZFS_MAXPROPLEN, B_FALSE) == 0 &&
 852                     strcmp(smbshareopts, "off") != 0) {
 853                         if (source & ZPROP_SRC_INHERITED)
 854                                 smb_inherited = B_TRUE;
 855                         else
 856                                 smb = B_TRUE;
 857                 }
 858 
 859                 /*
 860                  * If the mountpoint is already shared, it must be a
 861                  * non-ZFS share. We want to remove the share from its
 862                  * parent group and reshare it under ZFS.
 863                  */
 864                 share = sa_find_share(handle, mountpoint);
 865                 if (share != NULL &&
 866                     (nfs || smb || nfs_inherited || smb_inherited)) {
 867                         err = sa_remove_share(share);
 868                         share = NULL;
 869                 }
 870 
 871                 /*
 872                  * At this point, we have the information needed to
 873                  * determine what to do with the share.
 874                  *
 875                  * If smb or nfs is set, we have a new sub-group.
 876                  * If smb_inherit and/or nfs_inherit is set, then
 877                  * place on an existing sub-group. If both are set,
 878                  * the existing sub-group is the closest up the tree.
 879                  */
 880                 if (nfs || smb) {
 881                         /*
 882                          * Non-inherited is the straightforward
 883                          * case. sa_zfs_process_share handles it
 884                          * directly. Make sure that if the "other"
 885                          * protocol is inherited, that we treat it as
 886                          * non-inherited as well.
 887                          */
 888                         if (nfs || nfs_inherited) {
 889                                 err = sa_zfs_process_share(handle, zfsgroup,
 890                                     share, mountpoint, "nfs",
 891                                     0, nfsshareopts,
 892                                     nfssourcestr, dataset);
 893                                 share = sa_find_share(handle, mountpoint);
 894                         }
 895                         if (smb || smb_inherited) {
 896                                 err = sa_zfs_process_share(handle, zfsgroup,
 897                                     share, mountpoint, "smb",
 898                                     0, smbshareopts,
 899                                     smbsourcestr, dataset);
 900                         }
 901                 } else if (nfs_inherited || smb_inherited) {
 902                         char *grpdataset;
 903                         /*
 904                          * If we only have inherited groups, it is
 905                          * important to find the closer of the two if
 906                          * the protocols are set at different
 907                          * levels. The closest sub-group is the one we
 908                          * want to work with.
 909                          */
 910                         if (nfs_inherited && smb_inherited) {
 911                                 if (strcmp(nfssourcestr, smbsourcestr) <= 0)
 912                                         grpdataset = nfssourcestr;
 913                                 else
 914                                         grpdataset = smbsourcestr;
 915                         } else if (nfs_inherited) {
 916                                 grpdataset = nfssourcestr;
 917                         } else if (smb_inherited) {
 918                                 grpdataset = smbsourcestr;
 919                         }
 920                         if (nfs_inherited) {
 921                                 err = sa_zfs_process_share(handle, zfsgroup,
 922                                     share, mountpoint, "nfs",
 923                                     ZPROP_SRC_INHERITED, nfsshareopts,
 924                                     grpdataset, dataset);
 925                                 share = sa_find_share(handle, mountpoint);
 926                         }
 927                         if (smb_inherited) {
 928                                 err = sa_zfs_process_share(handle, zfsgroup,
 929                                     share, mountpoint, "smb",
 930                                     ZPROP_SRC_INHERITED, smbshareopts,
 931                                     grpdataset, dataset);
 932                         }
 933                 }
 934         }
 935         /*
 936          * Don't need to free the "zlist" variable since it is only a
 937          * pointer to a cached value that will be freed when
 938          * sa_fini() is called.
 939          */
 940         return (err);
 941 }
 942 
 943 #define COMMAND         "/usr/sbin/zfs"
 944 
 945 /*
 946  * sa_zfs_set_sharenfs(group, path, on)
 947  *
 948  * Update the "sharenfs" property on the path. If on is true, then set
 949  * to the properties on the group or "on" if no properties are
 950  * defined. Set to "off" if on is false.
 951  */
 952 
 953 int
 954 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
 955 {
 956         int ret = SA_NOT_IMPLEMENTED;
 957         char *command;
 958 
 959         command = malloc(ZFS_MAXPROPLEN * 2);
 960         if (command != NULL) {
 961                 char *opts = NULL;
 962                 char *dataset = NULL;
 963                 FILE *pfile;
 964                 sa_handle_impl_t impl_handle;
 965                 /* for now, NFS is always available for "zfs" */
 966                 if (on) {
 967                         opts = sa_proto_legacy_format("nfs", group, 1);
 968                         if (opts != NULL && strlen(opts) == 0) {
 969                                 free(opts);
 970                                 opts = strdup("on");
 971                         }
 972                 }
 973 
 974                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
 975                 assert(impl_handle != NULL);
 976                 if (impl_handle != NULL)
 977                         dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
 978                 else
 979                         ret = SA_SYSTEM_ERR;
 980 
 981                 if (dataset != NULL) {
 982                         (void) snprintf(command, ZFS_MAXPROPLEN * 2,
 983                             "%s set sharenfs=\"%s\" %s", COMMAND,
 984                             opts != NULL ? opts : "off", dataset);
 985                         pfile = popen(command, "r");
 986                         if (pfile != NULL) {
 987                                 ret = pclose(pfile);
 988                                 if (ret != 0)
 989                                         ret = SA_SYSTEM_ERR;
 990                         }
 991                 }
 992                 if (opts != NULL)
 993                         free(opts);
 994                 if (dataset != NULL)
 995                         free(dataset);
 996                 free(command);
 997         }
 998         return (ret);
 999 }
1000 
1001 /*
1002  * add_resources(share, opt)
1003  *
1004  * Add resource properties to those in "opt".  Resources are prefixed
1005  * with name=resourcename.
1006  */
1007 static char *
1008 add_resources(sa_share_t share, char *opt)
1009 {
1010         char *newopt = NULL;
1011         char *propstr;
1012         sa_resource_t resource;
1013 
1014         newopt = strdup(opt);
1015         if (newopt == NULL)
1016                 return (newopt);
1017 
1018         for (resource = sa_get_share_resource(share, NULL);
1019             resource != NULL;
1020             resource = sa_get_next_resource(resource)) {
1021                 char *name;
1022                 size_t size;
1023 
1024                 name = sa_get_resource_attr(resource, "name");
1025                 if (name == NULL) {
1026                         free(newopt);
1027                         return (NULL);
1028                 }
1029                 size = strlen(name) + strlen(opt) + sizeof ("name=") + 1;
1030                 newopt = calloc(1, size);
1031                 if (newopt != NULL)
1032                         (void) snprintf(newopt, size, "%s,name=%s", opt, name);
1033                 sa_free_attr_string(name);
1034                 free(opt);
1035                 opt = newopt;
1036                 propstr = sa_proto_legacy_format("smb", resource, 0);
1037                 if (propstr == NULL) {
1038                         free(opt);
1039                         return (NULL);
1040                 }
1041                 size = strlen(propstr) + strlen(opt) + 2;
1042                 newopt = calloc(1, size);
1043                 if (newopt != NULL)
1044                         (void) snprintf(newopt, size, "%s,%s", opt, propstr);
1045                 free(opt);
1046                 opt = newopt;
1047         }
1048         return (opt);
1049 }
1050 
1051 /*
1052  * sa_zfs_set_sharesmb(group, path, on)
1053  *
1054  * Update the "sharesmb" property on the path. If on is true, then set
1055  * to the properties on the group or "on" if no properties are
1056  * defined. Set to "off" if on is false.
1057  */
1058 
1059 int
1060 sa_zfs_set_sharesmb(sa_group_t group, char *path, int on)
1061 {
1062         int ret = SA_NOT_IMPLEMENTED;
1063         char *command;
1064         sa_share_t share;
1065 
1066         /* In case SMB not enabled */
1067         if (sa_get_optionset(group, "smb") == NULL)
1068                 return (SA_NOT_SUPPORTED);
1069 
1070         command = malloc(ZFS_MAXPROPLEN * 2);
1071         if (command != NULL) {
1072                 char *opts = NULL;
1073                 char *dataset = NULL;
1074                 FILE *pfile;
1075                 sa_handle_impl_t impl_handle;
1076 
1077                 if (on) {
1078                         char *newopt;
1079 
1080                         share = sa_get_share(group, NULL);
1081                         opts = sa_proto_legacy_format("smb", share, 1);
1082                         if (opts != NULL && strlen(opts) == 0) {
1083                                 free(opts);
1084                                 opts = strdup("on");
1085                         }
1086                         newopt = add_resources(opts, share);
1087                         free(opts);
1088                         opts = newopt;
1089                 }
1090 
1091                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1092                 assert(impl_handle != NULL);
1093                 if (impl_handle != NULL)
1094                         dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
1095                 else
1096                         ret = SA_SYSTEM_ERR;
1097 
1098                 if (dataset != NULL) {
1099                         (void) snprintf(command, ZFS_MAXPROPLEN * 2,
1100                             "echo %s set sharesmb=\"%s\" %s", COMMAND,
1101                             opts != NULL ? opts : "off", dataset);
1102                         pfile = popen(command, "r");
1103                         if (pfile != NULL) {
1104                                 ret = pclose(pfile);
1105                                 if (ret != 0)
1106                                         ret = SA_SYSTEM_ERR;
1107                         }
1108                 }
1109                 if (opts != NULL)
1110                         free(opts);
1111                 if (dataset != NULL)
1112                         free(dataset);
1113                 free(command);
1114         }
1115         return (ret);
1116 }
1117 
1118 /*
1119  * sa_zfs_update(group)
1120  *
1121  * call back to ZFS to update the share if necessary.
1122  * Don't do it if it isn't a real change.
1123  */
1124 int
1125 sa_zfs_update(sa_group_t group)
1126 {
1127         sa_optionset_t protopt;
1128         sa_group_t parent;
1129         char *command;
1130         char *optstring;
1131         int ret = SA_OK;
1132         int doupdate = 0;
1133         FILE *pfile;
1134 
1135         if (sa_is_share(group))
1136                 parent = sa_get_parent_group(group);
1137         else
1138                 parent = group;
1139 
1140         if (parent != NULL) {
1141                 command = malloc(ZFS_MAXPROPLEN * 2);
1142                 if (command == NULL)
1143                         return (SA_NO_MEMORY);
1144 
1145                 *command = '\0';
1146                 for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
1147                     protopt = sa_get_next_optionset(protopt)) {
1148 
1149                         char *proto = sa_get_optionset_attr(protopt, "type");
1150                         char *path;
1151                         char *dataset = NULL;
1152                         char *zfsopts = NULL;
1153 
1154                         if (sa_is_share(group)) {
1155                                 path = sa_get_share_attr((sa_share_t)group,
1156                                     "path");
1157                                 if (path != NULL) {
1158                                         sa_handle_impl_t impl_handle;
1159 
1160                                         impl_handle = sa_find_group_handle(
1161                                             group);
1162                                         if (impl_handle != NULL)
1163                                                 dataset = get_zfs_dataset(
1164                                                     impl_handle, path, B_FALSE);
1165                                         else
1166                                                 ret = SA_SYSTEM_ERR;
1167 
1168                                         sa_free_attr_string(path);
1169                                 }
1170                         } else {
1171                                 dataset = sa_get_group_attr(group, "name");
1172                         }
1173                         /* update only when there is an optstring found */
1174                         doupdate = 0;
1175                         if (proto != NULL && dataset != NULL) {
1176                                 optstring = sa_proto_legacy_format(proto,
1177                                     group, 1);
1178                                 zfsopts = get_zfs_property(dataset,
1179                                     ZFS_PROP_SHARENFS);
1180 
1181                                 if (optstring != NULL && zfsopts != NULL) {
1182                                         if (strcmp(optstring, zfsopts) != 0)
1183                                                 doupdate++;
1184                                 }
1185                                 if (doupdate) {
1186                                         if (optstring != NULL &&
1187                                             strlen(optstring) > 0) {
1188                                                 (void) snprintf(command,
1189                                                     ZFS_MAXPROPLEN * 2,
1190                                                     "%s set share%s=%s %s",
1191                                                     COMMAND, proto,
1192                                                     optstring, dataset);
1193                                         } else {
1194                                                 (void) snprintf(command,
1195                                                     ZFS_MAXPROPLEN * 2,
1196                                                     "%s set share%s=on %s",
1197                                                     COMMAND, proto,
1198                                                     dataset);
1199                                         }
1200                                         pfile = popen(command, "r");
1201                                         if (pfile != NULL)
1202                                                 ret = pclose(pfile);
1203                                         switch (ret) {
1204                                         default:
1205                                         case 1:
1206                                                 ret = SA_SYSTEM_ERR;
1207                                                 break;
1208                                         case 2:
1209                                                 ret = SA_SYNTAX_ERR;
1210                                                 break;
1211                                         case 0:
1212                                                 break;
1213                                         }
1214                                 }
1215                                 if (optstring != NULL)
1216                                         free(optstring);
1217                                 if (zfsopts != NULL)
1218                                         free(zfsopts);
1219                         }
1220                         if (proto != NULL)
1221                                 sa_free_attr_string(proto);
1222                         if (dataset != NULL)
1223                                 free(dataset);
1224                 }
1225                 free(command);
1226         }
1227         return (ret);
1228 }
1229 
1230 /*
1231  * sa_group_is_zfs(group)
1232  *
1233  * Given the group, determine if the zfs attribute is set.
1234  */
1235 
1236 int
1237 sa_group_is_zfs(sa_group_t group)
1238 {
1239         char *zfs;
1240         int ret = 0;
1241 
1242         zfs = sa_get_group_attr(group, "zfs");
1243         if (zfs != NULL) {
1244                 ret = 1;
1245                 sa_free_attr_string(zfs);
1246         }
1247         return (ret);
1248 }
1249 
1250 /*
1251  * sa_path_is_zfs(path)
1252  *
1253  * Check to see if the file system path represents is of type "zfs".
1254  */
1255 
1256 int
1257 sa_path_is_zfs(char *path)
1258 {
1259         char *fstype;
1260         int ret = 0;
1261 
1262         fstype = sa_fstype(path);
1263         if (fstype != NULL && strcmp(fstype, "zfs") == 0)
1264                 ret = 1;
1265         if (fstype != NULL)
1266                 sa_free_fstype(fstype);
1267         return (ret);
1268 }
1269 
1270 int
1271 sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto)
1272 {
1273         char *path;
1274 
1275         /* Make sure path is valid */
1276 
1277         path = sa_get_share_attr(share, "path");
1278         if (path != NULL) {
1279                 (void) memset(sh, 0, sizeof (sh));
1280                 (void) sa_fillshare(share, proto, sh);
1281                 sa_free_attr_string(path);
1282                 return (0);
1283         } else
1284                 return (1);
1285 }
1286 
1287 #define SMAX(i, j)      \
1288         if ((j) > (i)) { \
1289                 (i) = (j); \
1290         }
1291 
1292 int
1293 sa_share_zfs(sa_share_t share, sa_resource_t resource, char *path, share_t *sh,
1294     void *exportdata, zfs_share_op_t operation)
1295 {
1296         libzfs_handle_t *libhandle;
1297         sa_group_t group;
1298         sa_handle_t sahandle;
1299         char *dataset;
1300         int err = EINVAL;
1301         int i, j;
1302         char newpath[MAXPATHLEN];
1303         char *pathp;
1304 
1305         /*
1306          * First find the dataset name
1307          */
1308         if ((group = sa_get_parent_group(share)) == NULL)  {
1309                 return (EINVAL);
1310         }
1311         if ((sahandle = sa_find_group_handle(group)) == NULL) {
1312                 return (EINVAL);
1313         }
1314 
1315         /*
1316          * If get_zfs_dataset fails, see if it is a subdirectory
1317          */
1318 
1319         pathp = path;
1320         while ((dataset = get_zfs_dataset(sahandle, pathp, B_TRUE)) == NULL) {
1321                 char *p;
1322 
1323                 if (pathp == path) {
1324                         (void) strlcpy(newpath, path, sizeof (newpath));
1325                         pathp = newpath;
1326                 }
1327 
1328                 /*
1329                  * Make sure only one leading '/' This condition came
1330                  * about when using HAStoragePlus which insisted on
1331                  * putting an extra leading '/' in the ZFS path
1332                  * name. The problem is fixed in other areas, but this
1333                  * will catch any other ways that a double slash might
1334                  * get introduced.
1335                  */
1336                 while (*pathp == '/' && *(pathp + 1) == '/')
1337                         pathp++;
1338 
1339                 /*
1340                  * chop off part of path, but if we are at root then
1341                  * make sure path is a /
1342                  */
1343                 if ((strlen(pathp) > 1) && (p = strrchr(pathp, '/'))) {
1344                         if (pathp == p) {
1345                                 *(p + 1) = '\0';  /* skip over /, root case */
1346                         } else {
1347                                 *p = '\0';
1348                         }
1349                 } else {
1350                         return (EINVAL);
1351                 }
1352         }
1353 
1354         libhandle = libzfs_init();
1355         if (libhandle != NULL) {
1356                 char *resource_name;
1357 
1358                 i = (sh->sh_path ? strlen(sh->sh_path) : 0);
1359                 sh->sh_size = i;
1360 
1361                 j = (sh->sh_res ? strlen(sh->sh_res) : 0);
1362                 sh->sh_size += j;
1363                 SMAX(i, j);
1364 
1365                 j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0);
1366                 sh->sh_size += j;
1367                 SMAX(i, j);
1368 
1369                 j = (sh->sh_opts ? strlen(sh->sh_opts) : 0);
1370                 sh->sh_size += j;
1371                 SMAX(i, j);
1372 
1373                 j = (sh->sh_descr ? strlen(sh->sh_descr) : 0);
1374                 sh->sh_size += j;
1375                 SMAX(i, j);
1376 
1377                 resource_name = sa_get_resource_attr(resource, "name");
1378 
1379                 err = zfs_deleg_share_nfs(libhandle, dataset, path,
1380                     resource_name, exportdata, sh, i, operation);
1381                 if (err == SA_OK)
1382                         sa_update_sharetab_ts(sahandle);
1383                 else
1384                         err = errno;
1385                 if (resource_name)
1386                         sa_free_attr_string(resource_name);
1387 
1388                 libzfs_fini(libhandle);
1389         }
1390         free(dataset);
1391         return (err);
1392 }
1393 
1394 /*
1395  * sa_get_zfs_handle(handle)
1396  *
1397  * Given an sa_handle_t, return the libzfs_handle_t *. This is only
1398  * used internally by libzfs. Needed in order to avoid including
1399  * libshare_impl.h in libzfs.
1400  */
1401 
1402 libzfs_handle_t *
1403 sa_get_zfs_handle(sa_handle_t handle)
1404 {
1405         sa_handle_impl_t implhandle = (sa_handle_impl_t)handle;
1406 
1407         return (implhandle->zfs_libhandle);
1408 }
1409 
1410 /*
1411  * sa_get_zfs_info(libzfs, path, mountpoint, dataset)
1412  *
1413  * Find the ZFS dataset and mountpoint for a given path
1414  */
1415 int
1416 sa_zfs_get_info(libzfs_handle_t *libzfs, char *path, char *mountpointp,
1417     char *datasetp)
1418 {
1419         get_all_cbdata_t cb = { 0 };
1420         int i;
1421         char mountpoint[ZFS_MAXPROPLEN];
1422         char dataset[ZFS_MAXPROPLEN];
1423         char canmount[ZFS_MAXPROPLEN];
1424         char *dp;
1425         int count;
1426         int ret = 0;
1427 
1428         cb.cb_types = ZFS_TYPE_FILESYSTEM;
1429 
1430         if (libzfs == NULL)
1431                 return (0);
1432 
1433         (void) zfs_iter_root(libzfs, get_one_filesystem, &cb);
1434         count = cb.cb_used;
1435 
1436         qsort(cb.cb_handles, count, sizeof (void *), mountpoint_compare);
1437         for (i = 0; i < count; i++) {
1438                 /* must have a mountpoint */
1439                 if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_MOUNTPOINT,
1440                     mountpoint, sizeof (mountpoint),
1441                     NULL, NULL, 0, B_FALSE) != 0) {
1442                         /* no mountpoint */
1443                         continue;
1444                 }
1445 
1446                 /* mountpoint must be a path */
1447                 if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
1448                     strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1449                         /*
1450                          * Search mmttab for mountpoint
1451                          */
1452 
1453                         if (get_legacy_mountpoint(path, dataset,
1454                             ZFS_MAXPROPLEN, mountpoint,
1455                             ZFS_MAXPROPLEN) == 0) {
1456                                 ret = 1;
1457                                 break;
1458                         }
1459                         continue;
1460                 }
1461 
1462                 /* canmount must be set */
1463                 canmount[0] = '\0';
1464                 if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_CANMOUNT, canmount,
1465                     sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
1466                     strcmp(canmount, "off") == 0)
1467                         continue;
1468 
1469                 /*
1470                  * have a mountable handle but want to skip those marked none
1471                  * and legacy
1472                  */
1473                 if (strcmp(mountpoint, path) == 0) {
1474                         dp = (char *)zfs_get_name(cb.cb_handles[i]);
1475                         if (dp != NULL) {
1476                                 if (datasetp != NULL)
1477                                         (void) strcpy(datasetp, dp);
1478                                 if (mountpointp != NULL)
1479                                         (void) strcpy(mountpointp, mountpoint);
1480                                 ret = 1;
1481                         }
1482                         break;
1483                 }
1484 
1485         }
1486 
1487         return (ret);
1488 }
1489 
1490 /*
1491  * This method builds values for "sharesmb" property from the
1492  * nvlist argument. The values are returned in sharesmb_val variable.
1493  */
1494 static int
1495 sa_zfs_sprintf_new_prop(nvlist_t *nvl, char *sharesmb_val)
1496 {
1497         char cur_val[MAXPATHLEN];
1498         char *name, *val;
1499         nvpair_t *cur;
1500         int err = 0;
1501 
1502         cur = nvlist_next_nvpair(nvl, NULL);
1503         while (cur != NULL) {
1504                 name = nvpair_name(cur);
1505                 err = nvpair_value_string(cur, &val);
1506                 if ((err != 0) || (name == NULL) || (val == NULL))
1507                         return (-1);
1508 
1509                 (void) snprintf(cur_val, MAXPATHLEN, "%s=%s,", name, val);
1510                 (void) strlcat(sharesmb_val, cur_val, MAXPATHLEN);
1511 
1512                 cur = nvlist_next_nvpair(nvl, cur);
1513         }
1514 
1515         return (0);
1516 }
1517 
1518 /*
1519  * This method builds values for "sharesmb" property from values
1520  * already existing on the share. The properties set via sa_zfs_sprint_new_prop
1521  * method are passed in sharesmb_val. If a existing property is already
1522  * set via sa_zfs_sprint_new_prop method, then they are not appended
1523  * to the sharesmb_val string. The returned sharesmb_val string is a combination
1524  * of new and existing values for 'sharesmb' property.
1525  */
1526 static int
1527 sa_zfs_sprintf_existing_prop(zfs_handle_t *handle, char *sharesmb_val)
1528 {
1529         char shareopts[ZFS_MAXPROPLEN], cur_val[MAXPATHLEN];
1530         char *token, *last, *value;
1531 
1532         if (zfs_prop_get(handle, ZFS_PROP_SHARESMB, shareopts,
1533             sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0)
1534                 return (-1);
1535 
1536         if (strstr(shareopts, "=") == NULL)
1537                 return (0);
1538 
1539         for (token = strtok_r(shareopts, ",", &last); token != NULL;
1540             token = strtok_r(NULL, ",", &last)) {
1541                 value = strchr(token, '=');
1542                 if (value == NULL)
1543                         return (-1);
1544                 *value++ = '\0';
1545 
1546                 (void) snprintf(cur_val, MAXPATHLEN, "%s=", token);
1547                 if (strstr(sharesmb_val, cur_val) == NULL) {
1548                         (void) strlcat(cur_val, value, MAXPATHLEN);
1549                         (void) strlcat(cur_val, ",", MAXPATHLEN);
1550                         (void) strlcat(sharesmb_val, cur_val, MAXPATHLEN);
1551                 }
1552         }
1553 
1554         return (0);
1555 }
1556 
1557 /*
1558  * Sets the share properties on a ZFS share. For now, this method sets only
1559  * the "sharesmb" property.
1560  *
1561  * This method includes building a comma seperated name-value string to be
1562  * set on the "sharesmb" property of a ZFS share. This name-value string is
1563  * build in 2 steps:
1564  *    - New property values given as name-value pair are set first.
1565  *    - Existing optionset properties, which are not part of the new properties
1566  *      passed in step 1, are appended to the newly set properties.
1567  */
1568 int
1569 sa_zfs_setprop(sa_handle_t handle, char *path, nvlist_t *nvl)
1570 {
1571         zfs_handle_t *z_fs;
1572         libzfs_handle_t *z_lib;
1573         char sharesmb_val[MAXPATHLEN];
1574         char *dataset, *lastcomma;
1575 
1576         if (nvlist_empty(nvl))
1577                 return (0);
1578 
1579         if ((handle == NULL) || (path == NULL))
1580                 return (-1);
1581 
1582         if ((dataset = get_zfs_dataset(handle, path, B_FALSE)) == NULL)
1583                 return (-1);
1584 
1585         if ((z_lib = libzfs_init()) == NULL) {
1586                 free(dataset);
1587                 return (-1);
1588         }
1589 
1590         z_fs = zfs_open(z_lib, dataset, ZFS_TYPE_DATASET);
1591         if (z_fs == NULL) {
1592                 free(dataset);
1593                 libzfs_fini(z_lib);
1594                 return (-1);
1595         }
1596 
1597         bzero(sharesmb_val, MAXPATHLEN);
1598         if (sa_zfs_sprintf_new_prop(nvl, sharesmb_val) != 0) {
1599                 free(dataset);
1600                 zfs_close(z_fs);
1601                 libzfs_fini(z_lib);
1602                 return (-1);
1603         }
1604 
1605         if (sa_zfs_sprintf_existing_prop(z_fs, sharesmb_val) != 0) {
1606                 free(dataset);
1607                 zfs_close(z_fs);
1608                 libzfs_fini(z_lib);
1609                 return (-1);
1610         }
1611 
1612         lastcomma = strrchr(sharesmb_val, ',');
1613         if ((lastcomma != NULL) && (lastcomma[1] == '\0'))
1614                 *lastcomma = '\0';
1615 
1616         (void) zfs_prop_set(z_fs, zfs_prop_to_name(ZFS_PROP_SHARESMB),
1617             sharesmb_val);
1618         free(dataset);
1619         zfs_close(z_fs);
1620         libzfs_fini(z_lib);
1621 
1622         return (0);
1623 }