16185db85Sdougm /* 26185db85Sdougm * CDDL HEADER START 36185db85Sdougm * 46185db85Sdougm * The contents of this file are subject to the terms of the 56185db85Sdougm * Common Development and Distribution License (the "License"). 66185db85Sdougm * You may not use this file except in compliance with the License. 76185db85Sdougm * 86185db85Sdougm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96185db85Sdougm * or http://www.opensolaris.org/os/licensing. 106185db85Sdougm * See the License for the specific language governing permissions 116185db85Sdougm * and limitations under the License. 126185db85Sdougm * 136185db85Sdougm * When distributing Covered Code, include this CDDL HEADER in each 146185db85Sdougm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156185db85Sdougm * If applicable, add the following below this CDDL HEADER, with the 166185db85Sdougm * fields enclosed by brackets "[]" replaced with your own identifying 176185db85Sdougm * information: Portions Copyright [yyyy] [name of copyright owner] 186185db85Sdougm * 196185db85Sdougm * CDDL HEADER END 206185db85Sdougm */ 216185db85Sdougm 226185db85Sdougm /* 23148c5f43SAlan Wright * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 246185db85Sdougm */ 25c3f7431dSDan Kruchinin /* 26c3f7431dSDan Kruchinin * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 2795e79c0bSDaniel Hoffman * Copyright (c) 2012, 2016 by Delphix. All rights reserved. 28*ff524b23SAndrew Stormont * Copyright 2017 RackTop Systems. 29c3f7431dSDan Kruchinin */ 306185db85Sdougm 31a1ef5d63Smarks #include <stdio.h> 326185db85Sdougm #include <libzfs.h> 336185db85Sdougm #include <string.h> 34a3351425Sdougm #include <strings.h> 35a63214d6SBill Krier #include <errno.h> 366185db85Sdougm #include <libshare.h> 376185db85Sdougm #include "libshare_impl.h" 381cea05afSdougm #include <libintl.h> 39a1ef5d63Smarks #include <sys/mnttab.h> 40a1ef5d63Smarks #include <sys/mntent.h> 4195e79c0bSDaniel Hoffman #include <assert.h> 426185db85Sdougm 43da6c28aaSamw extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t); 446185db85Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *); 456185db85Sdougm extern char *sa_fstype(char *); 466185db85Sdougm extern void set_node_attr(void *, char *, char *); 476185db85Sdougm extern int sa_is_share(void *); 485b6e0c46Sdougm extern void sa_update_sharetab_ts(sa_handle_t); 491cea05afSdougm 501cea05afSdougm /* 511cea05afSdougm * File system specific code for ZFS. The original code was stolen 521cea05afSdougm * from the "zfs" command and modified to better suit this library's 531cea05afSdougm * usage. 541cea05afSdougm */ 551cea05afSdougm 561cea05afSdougm typedef struct get_all_cbdata { 571cea05afSdougm zfs_handle_t **cb_handles; 581cea05afSdougm size_t cb_alloc; 591cea05afSdougm size_t cb_used; 60a3351425Sdougm uint_t cb_types; 611cea05afSdougm } get_all_cbdata_t; 621cea05afSdougm 631cea05afSdougm /* 64549ec3ffSdougm * sa_zfs_init(impl_handle) 651cea05afSdougm * 66549ec3ffSdougm * Initialize an access handle into libzfs. The handle needs to stay 67549ec3ffSdougm * around until sa_zfs_fini() in order to maintain the cache of 68549ec3ffSdougm * mounts. 691cea05afSdougm */ 701cea05afSdougm 7157b448deSdougm int 72549ec3ffSdougm sa_zfs_init(sa_handle_impl_t impl_handle) 731cea05afSdougm { 74549ec3ffSdougm impl_handle->zfs_libhandle = libzfs_init(); 7557b448deSdougm if (impl_handle->zfs_libhandle != NULL) { 7657b448deSdougm libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE); 7757b448deSdougm return (B_TRUE); 7857b448deSdougm } 7957b448deSdougm return (B_FALSE); 801cea05afSdougm } 811cea05afSdougm 821cea05afSdougm /* 83549ec3ffSdougm * sa_zfs_fini(impl_handle) 841cea05afSdougm * 851cea05afSdougm * cleanup data structures and the libzfs handle used for accessing 861cea05afSdougm * zfs file share info. 871cea05afSdougm */ 881cea05afSdougm 891cea05afSdougm void 90549ec3ffSdougm sa_zfs_fini(sa_handle_impl_t impl_handle) 911cea05afSdougm { 92549ec3ffSdougm if (impl_handle->zfs_libhandle != NULL) { 9357b448deSdougm if (impl_handle->zfs_list != NULL) { 94a3351425Sdougm zfs_handle_t **zhp = impl_handle->zfs_list; 95a3351425Sdougm size_t i; 96a3351425Sdougm 9757b448deSdougm /* 98a3351425Sdougm * Contents of zfs_list need to be freed so we 99a3351425Sdougm * don't lose ZFS handles. 10057b448deSdougm */ 101a3351425Sdougm for (i = 0; i < impl_handle->zfs_list_count; i++) { 102a3351425Sdougm zfs_close(zhp[i]); 103a3351425Sdougm } 10457b448deSdougm free(impl_handle->zfs_list); 10557b448deSdougm impl_handle->zfs_list = NULL; 10657b448deSdougm impl_handle->zfs_list_count = 0; 10757b448deSdougm } 108a3351425Sdougm 109a3351425Sdougm libzfs_fini(impl_handle->zfs_libhandle); 110a3351425Sdougm impl_handle->zfs_libhandle = NULL; 1111cea05afSdougm } 1121cea05afSdougm } 1131cea05afSdougm 1141cea05afSdougm /* 1151cea05afSdougm * get_one_filesystem(zfs_handle_t, data) 1161cea05afSdougm * 117da6c28aaSamw * an iterator function called while iterating through the ZFS 1181cea05afSdougm * root. It accumulates into an array of file system handles that can 1191cea05afSdougm * be used to derive info about those file systems. 120a3351425Sdougm * 121a3351425Sdougm * Note that as this function is called, we close all zhp handles that 122a3351425Sdougm * are not going to be places into the cp_handles list. We don't want 123a3351425Sdougm * to close the ones we are keeping, but all others would be leaked if 124a3351425Sdougm * not closed here. 1251cea05afSdougm */ 1261cea05afSdougm 1271cea05afSdougm static int 1281cea05afSdougm get_one_filesystem(zfs_handle_t *zhp, void *data) 1291cea05afSdougm { 1301cea05afSdougm get_all_cbdata_t *cbp = data; 131a3351425Sdougm zfs_type_t type = zfs_get_type(zhp); 1321cea05afSdougm 1331cea05afSdougm /* 134a3351425Sdougm * Interate over any nested datasets. 1351cea05afSdougm */ 136a3351425Sdougm if (type == ZFS_TYPE_FILESYSTEM && 137a3351425Sdougm zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) { 138a3351425Sdougm zfs_close(zhp); 139a3351425Sdougm return (1); 140a3351425Sdougm } 141a3351425Sdougm 142a3351425Sdougm /* 143a3351425Sdougm * Skip any datasets whose type does not match. 144a3351425Sdougm */ 145a3351425Sdougm if ((type & cbp->cb_types) == 0) { 1461cea05afSdougm zfs_close(zhp); 1471cea05afSdougm return (0); 1481cea05afSdougm } 1491cea05afSdougm 1501cea05afSdougm if (cbp->cb_alloc == cbp->cb_used) { 1511cea05afSdougm zfs_handle_t **handles; 1521cea05afSdougm 1531cea05afSdougm if (cbp->cb_alloc == 0) 1541cea05afSdougm cbp->cb_alloc = 64; 1551cea05afSdougm else 1561cea05afSdougm cbp->cb_alloc *= 2; 1571cea05afSdougm 158a3351425Sdougm handles = (zfs_handle_t **)calloc(1, 159a3351425Sdougm cbp->cb_alloc * sizeof (void *)); 160a3351425Sdougm 1611cea05afSdougm if (handles == NULL) { 162a3351425Sdougm zfs_close(zhp); 16357b448deSdougm return (0); 1641cea05afSdougm } 1651cea05afSdougm if (cbp->cb_handles) { 166a3351425Sdougm bcopy(cbp->cb_handles, handles, 1671cea05afSdougm cbp->cb_used * sizeof (void *)); 1681cea05afSdougm free(cbp->cb_handles); 1691cea05afSdougm } 1701cea05afSdougm 1711cea05afSdougm cbp->cb_handles = handles; 1721cea05afSdougm } 1731cea05afSdougm 1741cea05afSdougm cbp->cb_handles[cbp->cb_used++] = zhp; 1751cea05afSdougm 176a3351425Sdougm return (0); 1771cea05afSdougm } 1781cea05afSdougm 1791cea05afSdougm /* 1801cea05afSdougm * get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 1811cea05afSdougm * 1821cea05afSdougm * iterate through all ZFS file systems starting at the root. Returns 1831cea05afSdougm * a count and an array of handle pointers. Allocating is only done 1841cea05afSdougm * once. The caller does not need to free since it will be done at 1851cea05afSdougm * sa_zfs_fini() time. 1861cea05afSdougm */ 1871cea05afSdougm 1881cea05afSdougm static void 189549ec3ffSdougm get_all_filesystems(sa_handle_impl_t impl_handle, 19095e79c0bSDaniel Hoffman zfs_handle_t ***fslist, size_t *count) 1911cea05afSdougm { 1921cea05afSdougm get_all_cbdata_t cb = { 0 }; 193a3351425Sdougm cb.cb_types = ZFS_TYPE_FILESYSTEM; 1941cea05afSdougm 195549ec3ffSdougm if (impl_handle->zfs_list != NULL) { 19657b448deSdougm *fslist = impl_handle->zfs_list; 19757b448deSdougm *count = impl_handle->zfs_list_count; 19857b448deSdougm return; 1991cea05afSdougm } 2001cea05afSdougm 201549ec3ffSdougm (void) zfs_iter_root(impl_handle->zfs_libhandle, 20257b448deSdougm get_one_filesystem, &cb); 2031cea05afSdougm 204549ec3ffSdougm impl_handle->zfs_list = *fslist = cb.cb_handles; 205549ec3ffSdougm impl_handle->zfs_list_count = *count = cb.cb_used; 2061cea05afSdougm } 2071cea05afSdougm 2086185db85Sdougm /* 2091cea05afSdougm * mountpoint_compare(a, b) 2101cea05afSdougm * 2111cea05afSdougm * compares the mountpoint on two zfs file systems handles. 2121cea05afSdougm * returns values following strcmp() model. 2136185db85Sdougm */ 2146185db85Sdougm 2151cea05afSdougm static int 2161cea05afSdougm mountpoint_compare(const void *a, const void *b) 2171cea05afSdougm { 2181cea05afSdougm zfs_handle_t **za = (zfs_handle_t **)a; 2191cea05afSdougm zfs_handle_t **zb = (zfs_handle_t **)b; 2201cea05afSdougm char mounta[MAXPATHLEN]; 2211cea05afSdougm char mountb[MAXPATHLEN]; 2221cea05afSdougm 2231cea05afSdougm verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 2241cea05afSdougm sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 2251cea05afSdougm verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 2261cea05afSdougm sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 2271cea05afSdougm 2281cea05afSdougm return (strcmp(mounta, mountb)); 2291cea05afSdougm } 2301cea05afSdougm 231a1ef5d63Smarks /* 232743a77edSAlan Wright * return legacy mountpoint. Caller provides space for mountpoint and 233743a77edSAlan Wright * dataset. 234a1ef5d63Smarks */ 235a1ef5d63Smarks int 23695e79c0bSDaniel Hoffman get_legacy_mountpoint(const char *path, char *dataset, size_t dlen, 237743a77edSAlan Wright char *mountpoint, size_t mlen) 238a1ef5d63Smarks { 239a1ef5d63Smarks FILE *fp; 240a1ef5d63Smarks struct mnttab entry; 241a1ef5d63Smarks 242a1ef5d63Smarks if ((fp = fopen(MNTTAB, "r")) == NULL) { 243a1ef5d63Smarks return (1); 244a1ef5d63Smarks } 245a1ef5d63Smarks 246a1ef5d63Smarks while (getmntent(fp, &entry) == 0) { 247a1ef5d63Smarks 248a1ef5d63Smarks if (entry.mnt_fstype == NULL || 249a1ef5d63Smarks strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 250a1ef5d63Smarks continue; 251a1ef5d63Smarks 252a1ef5d63Smarks if (strcmp(entry.mnt_mountp, path) == 0) { 253743a77edSAlan Wright if (mlen > 0) 254743a77edSAlan Wright (void) strlcpy(mountpoint, entry.mnt_mountp, 255743a77edSAlan Wright mlen); 256743a77edSAlan Wright if (dlen > 0) 257743a77edSAlan Wright (void) strlcpy(dataset, entry.mnt_special, 258743a77edSAlan Wright dlen); 259743a77edSAlan Wright break; 260a1ef5d63Smarks } 261a1ef5d63Smarks } 262a1ef5d63Smarks (void) fclose(fp); 263a1ef5d63Smarks return (1); 264a1ef5d63Smarks } 265a1ef5d63Smarks 26695e79c0bSDaniel Hoffman 26795e79c0bSDaniel Hoffman static char * 26895e79c0bSDaniel Hoffman verify_zfs_handle(zfs_handle_t *hdl, const char *path, boolean_t search_mnttab) 26995e79c0bSDaniel Hoffman { 27095e79c0bSDaniel Hoffman char mountpoint[ZFS_MAXPROPLEN]; 27195e79c0bSDaniel Hoffman char canmount[ZFS_MAXPROPLEN] = { 0 }; 27295e79c0bSDaniel Hoffman /* must have a mountpoint */ 27395e79c0bSDaniel Hoffman if (zfs_prop_get(hdl, ZFS_PROP_MOUNTPOINT, mountpoint, 27495e79c0bSDaniel Hoffman sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 27595e79c0bSDaniel Hoffman /* no mountpoint */ 27695e79c0bSDaniel Hoffman return (NULL); 27795e79c0bSDaniel Hoffman } 27895e79c0bSDaniel Hoffman 27995e79c0bSDaniel Hoffman /* mountpoint must be a path */ 28095e79c0bSDaniel Hoffman if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 28195e79c0bSDaniel Hoffman strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 28295e79c0bSDaniel Hoffman /* 28395e79c0bSDaniel Hoffman * Search mmttab for mountpoint and get dataset. 28495e79c0bSDaniel Hoffman */ 28595e79c0bSDaniel Hoffman 28695e79c0bSDaniel Hoffman if (search_mnttab == B_TRUE && 28795e79c0bSDaniel Hoffman get_legacy_mountpoint(path, mountpoint, 28895e79c0bSDaniel Hoffman sizeof (mountpoint), NULL, 0) == 0) { 28995e79c0bSDaniel Hoffman return (strdup(mountpoint)); 29095e79c0bSDaniel Hoffman } 29195e79c0bSDaniel Hoffman return (NULL); 29295e79c0bSDaniel Hoffman } 29395e79c0bSDaniel Hoffman 29495e79c0bSDaniel Hoffman /* canmount must be set */ 29595e79c0bSDaniel Hoffman if (zfs_prop_get(hdl, ZFS_PROP_CANMOUNT, canmount, 29695e79c0bSDaniel Hoffman sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 || 29795e79c0bSDaniel Hoffman strcmp(canmount, "off") == 0) 29895e79c0bSDaniel Hoffman return (NULL); 29995e79c0bSDaniel Hoffman 30095e79c0bSDaniel Hoffman /* 30195e79c0bSDaniel Hoffman * have a mountable handle but want to skip those marked none 30295e79c0bSDaniel Hoffman * and legacy 30395e79c0bSDaniel Hoffman */ 30495e79c0bSDaniel Hoffman if (strcmp(mountpoint, path) == 0) { 30595e79c0bSDaniel Hoffman return (strdup((char *)zfs_get_name(hdl))); 30695e79c0bSDaniel Hoffman } 30795e79c0bSDaniel Hoffman 30895e79c0bSDaniel Hoffman return (NULL); 30995e79c0bSDaniel Hoffman } 31095e79c0bSDaniel Hoffman 3116185db85Sdougm /* 312549ec3ffSdougm * get_zfs_dataset(impl_handle, path) 3136185db85Sdougm * 3146185db85Sdougm * get the name of the ZFS dataset the path is equivalent to. The 3156185db85Sdougm * dataset name is used for get/set of ZFS properties since libzfs 3166185db85Sdougm * requires a dataset to do a zfs_open(). 3176185db85Sdougm */ 3186185db85Sdougm 3196185db85Sdougm static char * 320a1ef5d63Smarks get_zfs_dataset(sa_handle_impl_t impl_handle, char *path, 321a1ef5d63Smarks boolean_t search_mnttab) 3226185db85Sdougm { 3231cea05afSdougm size_t i, count = 0; 3241cea05afSdougm zfs_handle_t **zlist; 32595e79c0bSDaniel Hoffman char *cutpath; 32695e79c0bSDaniel Hoffman zfs_handle_t *handle_from_path; 32795e79c0bSDaniel Hoffman char *ret = NULL; 3286185db85Sdougm 32995e79c0bSDaniel Hoffman /* 33095e79c0bSDaniel Hoffman * First we optimistically assume that the mount path for the filesystem 33195e79c0bSDaniel Hoffman * is the same as the name of the filesystem (minus some number of 33295e79c0bSDaniel Hoffman * leading slashes). If this is true, then zfs_open should properly open 33395e79c0bSDaniel Hoffman * the filesystem. We duplicate the error checking done later in the 33495e79c0bSDaniel Hoffman * function for consistency. If anything fails, we resort to the 33595e79c0bSDaniel Hoffman * (extremely slow) search of all the filesystems. 33695e79c0bSDaniel Hoffman */ 33795e79c0bSDaniel Hoffman cutpath = path + strspn(path, "/"); 33895e79c0bSDaniel Hoffman 33995e79c0bSDaniel Hoffman assert(impl_handle->zfs_libhandle != NULL); 340*ff524b23SAndrew Stormont libzfs_print_on_error(impl_handle->zfs_libhandle, B_FALSE); 34195e79c0bSDaniel Hoffman if ((handle_from_path = zfs_open(impl_handle->zfs_libhandle, cutpath, 342*ff524b23SAndrew Stormont ZFS_TYPE_FILESYSTEM)) != NULL) { 34395e79c0bSDaniel Hoffman if ((ret = verify_zfs_handle(handle_from_path, path, 344*ff524b23SAndrew Stormont search_mnttab)) != NULL) { 345*ff524b23SAndrew Stormont zfs_close(handle_from_path); 346*ff524b23SAndrew Stormont libzfs_print_on_error(impl_handle->zfs_libhandle, 347*ff524b23SAndrew Stormont B_TRUE); 34895e79c0bSDaniel Hoffman return (ret); 349*ff524b23SAndrew Stormont } 350*ff524b23SAndrew Stormont zfs_close(handle_from_path); 351*ff524b23SAndrew Stormont } 352*ff524b23SAndrew Stormont libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE); 353*ff524b23SAndrew Stormont 35495e79c0bSDaniel Hoffman /* 35595e79c0bSDaniel Hoffman * Couldn't find a filesystem optimistically, check all the handles we 35695e79c0bSDaniel Hoffman * can. 35795e79c0bSDaniel Hoffman */ 358549ec3ffSdougm get_all_filesystems(impl_handle, &zlist, &count); 3591cea05afSdougm for (i = 0; i < count; i++) { 36095e79c0bSDaniel Hoffman assert(zlist[i]); 36195e79c0bSDaniel Hoffman if ((ret = verify_zfs_handle(zlist[i], path, 36295e79c0bSDaniel Hoffman search_mnttab)) != NULL) 36395e79c0bSDaniel Hoffman return (ret); 3646185db85Sdougm } 3651cea05afSdougm 36695e79c0bSDaniel Hoffman /* Couldn't find a matching dataset */ 36795e79c0bSDaniel Hoffman return (NULL); 3686185db85Sdougm } 3696185db85Sdougm 3706185db85Sdougm /* 3716185db85Sdougm * get_zfs_property(dataset, property) 3726185db85Sdougm * 3736185db85Sdougm * Get the file system property specified from the ZFS dataset. 3746185db85Sdougm */ 3756185db85Sdougm 3766185db85Sdougm static char * 3776185db85Sdougm get_zfs_property(char *dataset, zfs_prop_t property) 3786185db85Sdougm { 3796185db85Sdougm zfs_handle_t *handle = NULL; 3806185db85Sdougm char shareopts[ZFS_MAXPROPLEN]; 3816185db85Sdougm libzfs_handle_t *libhandle; 3826185db85Sdougm 3836185db85Sdougm libhandle = libzfs_init(); 3846185db85Sdougm if (libhandle != NULL) { 38557b448deSdougm handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM); 38657b448deSdougm if (handle != NULL) { 38757b448deSdougm if (zfs_prop_get(handle, property, shareopts, 38857b448deSdougm sizeof (shareopts), NULL, NULL, 0, 38957b448deSdougm B_FALSE) == 0) { 39057b448deSdougm zfs_close(handle); 39157b448deSdougm libzfs_fini(libhandle); 39257b448deSdougm return (strdup(shareopts)); 39357b448deSdougm } 39457b448deSdougm zfs_close(handle); 3956185db85Sdougm } 39657b448deSdougm libzfs_fini(libhandle); 3976185db85Sdougm } 3986185db85Sdougm return (NULL); 3996185db85Sdougm } 4006185db85Sdougm 4016185db85Sdougm /* 402549ec3ffSdougm * sa_zfs_is_shared(handle, path) 4036185db85Sdougm * 4046185db85Sdougm * Check to see if the ZFS path provided has the sharenfs option set 4056185db85Sdougm * or not. 4066185db85Sdougm */ 4076185db85Sdougm 4086185db85Sdougm int 409549ec3ffSdougm sa_zfs_is_shared(sa_handle_t sahandle, char *path) 4106185db85Sdougm { 4116185db85Sdougm int ret = 0; 4126185db85Sdougm char *dataset; 4136185db85Sdougm zfs_handle_t *handle = NULL; 4146185db85Sdougm char shareopts[ZFS_MAXPROPLEN]; 4156185db85Sdougm libzfs_handle_t *libhandle; 4166185db85Sdougm 417a1ef5d63Smarks dataset = get_zfs_dataset((sa_handle_t)sahandle, path, B_FALSE); 4186185db85Sdougm if (dataset != NULL) { 41957b448deSdougm libhandle = libzfs_init(); 42057b448deSdougm if (libhandle != NULL) { 42157b448deSdougm handle = zfs_open(libhandle, dataset, 42257b448deSdougm ZFS_TYPE_FILESYSTEM); 42357b448deSdougm if (handle != NULL) { 42457b448deSdougm if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, 42557b448deSdougm shareopts, sizeof (shareopts), NULL, NULL, 42657b448deSdougm 0, B_FALSE) == 0 && 42757b448deSdougm strcmp(shareopts, "off") != 0) { 42857b448deSdougm ret = 1; /* it is shared */ 42957b448deSdougm } 43057b448deSdougm zfs_close(handle); 43157b448deSdougm } 43257b448deSdougm libzfs_fini(libhandle); 4336185db85Sdougm } 43457b448deSdougm free(dataset); 4356185db85Sdougm } 4366185db85Sdougm return (ret); 4376185db85Sdougm } 4386185db85Sdougm 4396185db85Sdougm /* 440da6c28aaSamw * find_or_create_group(handle, groupname, proto, *err) 4416185db85Sdougm * 4426185db85Sdougm * While walking the ZFS tree, we need to add shares to a defined 4436185db85Sdougm * group. If the group doesn't exist, create it first, making sure it 4446185db85Sdougm * is marked as a ZFS group. 4456185db85Sdougm * 44693a6f655Sdougm * Note that all ZFS shares are in a subgroup of the top level group 44793a6f655Sdougm * called "zfs". 4486185db85Sdougm */ 4496185db85Sdougm 4506185db85Sdougm static sa_group_t 451549ec3ffSdougm find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err) 4526185db85Sdougm { 4536185db85Sdougm sa_group_t group; 4546185db85Sdougm sa_optionset_t optionset; 4556185db85Sdougm int ret = SA_OK; 4566185db85Sdougm 4576185db85Sdougm /* 4586185db85Sdougm * we check to see if the "zfs" group exists. Since this 4596185db85Sdougm * should be the top level group, we don't want the 4606185db85Sdougm * parent. This is to make sure the zfs group has been created 4616185db85Sdougm * and to created if it hasn't been. 4626185db85Sdougm */ 463549ec3ffSdougm group = sa_get_group(handle, groupname); 4646185db85Sdougm if (group == NULL) { 46557b448deSdougm group = sa_create_group(handle, groupname, &ret); 46693a6f655Sdougm 46757b448deSdougm /* make sure this is flagged as a ZFS group */ 46857b448deSdougm if (group != NULL) 46957b448deSdougm ret = sa_set_group_attr(group, "zfs", "true"); 4706185db85Sdougm } 4716185db85Sdougm if (group != NULL) { 47257b448deSdougm if (proto != NULL) { 47357b448deSdougm optionset = sa_get_optionset(group, proto); 474da6c28aaSamw if (optionset == NULL) 47557b448deSdougm optionset = sa_create_optionset(group, proto); 4766185db85Sdougm } 4776185db85Sdougm } 4786185db85Sdougm if (err != NULL) 47957b448deSdougm *err = ret; 4806185db85Sdougm return (group); 4816185db85Sdougm } 4826185db85Sdougm 48393a6f655Sdougm /* 48493a6f655Sdougm * find_or_create_zfs_subgroup(groupname, optstring, *err) 48593a6f655Sdougm * 48693a6f655Sdougm * ZFS shares will be in a subgroup of the "zfs" master group. This 48793a6f655Sdougm * function looks to see if the groupname exists and returns it if it 48893a6f655Sdougm * does or else creates a new one with the specified name and returns 48993a6f655Sdougm * that. The "zfs" group will exist before we get here, but we make 49093a6f655Sdougm * sure just in case. 49193a6f655Sdougm * 49293a6f655Sdougm * err must be a valid pointer. 49393a6f655Sdougm */ 49493a6f655Sdougm 49593a6f655Sdougm static sa_group_t 496da6c28aaSamw find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, char *proto, 497da6c28aaSamw char *optstring, int *err) 49893a6f655Sdougm { 49993a6f655Sdougm sa_group_t group = NULL; 50093a6f655Sdougm sa_group_t zfs; 50193a6f655Sdougm char *name; 50293a6f655Sdougm char *options; 50393a6f655Sdougm 50493a6f655Sdougm /* start with the top-level "zfs" group */ 505549ec3ffSdougm zfs = sa_get_group(handle, "zfs"); 50693a6f655Sdougm *err = SA_OK; 50793a6f655Sdougm if (zfs != NULL) { 50857b448deSdougm for (group = sa_get_sub_group(zfs); group != NULL; 50957b448deSdougm group = sa_get_next_group(group)) { 51057b448deSdougm name = sa_get_group_attr(group, "name"); 51157b448deSdougm if (name != NULL && strcmp(name, groupname) == 0) { 51257b448deSdougm /* have the group so break out of here */ 51357b448deSdougm sa_free_attr_string(name); 51457b448deSdougm break; 51557b448deSdougm } 51657b448deSdougm if (name != NULL) 51757b448deSdougm sa_free_attr_string(name); 51893a6f655Sdougm } 51957b448deSdougm 52057b448deSdougm if (group == NULL) { 52157b448deSdougm /* 5223c484793Sdougm * Need to create the sub-group since it doesn't exist 52357b448deSdougm */ 52457b448deSdougm group = _sa_create_zfs_group(zfs, groupname); 5253c484793Sdougm if (group == NULL) { 5263c484793Sdougm *err = SA_NO_MEMORY; 5273c484793Sdougm return (NULL); 52857b448deSdougm } 5293c484793Sdougm set_node_attr(group, "zfs", "true"); 5303c484793Sdougm } 5313c484793Sdougm if (strcmp(optstring, "on") == 0) 5323c484793Sdougm optstring = "rw"; 5333c484793Sdougm options = strdup(optstring); 5343c484793Sdougm if (options != NULL) { 5353c484793Sdougm *err = sa_parse_legacy_options(group, options, 5363c484793Sdougm proto); 5373c484793Sdougm /* If no optionset, add one. */ 5383c484793Sdougm if (sa_get_optionset(group, proto) == NULL) 5393c484793Sdougm (void) sa_create_optionset(group, proto); 540c3f7431dSDan Kruchinin 541c3f7431dSDan Kruchinin /* 542c3f7431dSDan Kruchinin * Do not forget to update an optionset of 543c3f7431dSDan Kruchinin * the parent group so that it contains 544c3f7431dSDan Kruchinin * all protocols its subgroups have. 545c3f7431dSDan Kruchinin */ 546c3f7431dSDan Kruchinin if (sa_get_optionset(zfs, proto) == NULL) 547c3f7431dSDan Kruchinin (void) sa_create_optionset(zfs, proto); 548c3f7431dSDan Kruchinin 5493c484793Sdougm free(options); 5503c484793Sdougm } else { 5513c484793Sdougm *err = SA_NO_MEMORY; 55293a6f655Sdougm } 55393a6f655Sdougm } 55493a6f655Sdougm return (group); 55593a6f655Sdougm } 55693a6f655Sdougm 557da6c28aaSamw /* 558da6c28aaSamw * zfs_construct_resource(share, name, base, dataset) 559da6c28aaSamw * 560da6c28aaSamw * Add a resource to the share using name as a template. If name == 561da6c28aaSamw * NULL, then construct a name based on the dataset value. 562da6c28aaSamw * name. 563da6c28aaSamw */ 564da6c28aaSamw static void 565da6c28aaSamw zfs_construct_resource(sa_share_t share, char *dataset) 566da6c28aaSamw { 567da6c28aaSamw char buff[SA_MAX_RESOURCE_NAME + 1]; 568da6c28aaSamw int ret = SA_OK; 569da6c28aaSamw 570da6c28aaSamw (void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset); 571da6c28aaSamw sa_fix_resource_name(buff); 572da6c28aaSamw (void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret); 573da6c28aaSamw } 574da6c28aaSamw 57557b448deSdougm /* 57657b448deSdougm * zfs_inherited(handle, source, sourcestr) 57757b448deSdougm * 578da6c28aaSamw * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares 57957b448deSdougm * for readability. 58057b448deSdougm */ 58157b448deSdougm static int 58257b448deSdougm zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr, 583da6c28aaSamw char *shareopts, char *mountpoint, char *proto, char *dataset) 58457b448deSdougm { 58557b448deSdougm int doshopt = 0; 58657b448deSdougm int err = SA_OK; 58757b448deSdougm sa_group_t group; 588da6c28aaSamw sa_resource_t resource; 589da6c28aaSamw uint64_t features; 59057b448deSdougm 59157b448deSdougm /* 59257b448deSdougm * Need to find the "real" parent sub-group. It may not be 59357b448deSdougm * mounted, but it was identified in the "sourcestr" 59457b448deSdougm * variable. The real parent not mounted can occur if 59557b448deSdougm * "canmount=off and sharenfs=on". 59657b448deSdougm */ 597da6c28aaSamw group = find_or_create_zfs_subgroup(handle, sourcestr, proto, 598da6c28aaSamw shareopts, &doshopt); 59957b448deSdougm if (group != NULL) { 600da6c28aaSamw /* 601da6c28aaSamw * We may need the first share for resource 602da6c28aaSamw * prototype. We only care about it if it has a 603da6c28aaSamw * resource that sets a prefix value. 604da6c28aaSamw */ 605da6c28aaSamw if (share == NULL) 606da6c28aaSamw share = _sa_add_share(group, mountpoint, 607da6c28aaSamw SA_SHARE_TRANSIENT, &err, 608da6c28aaSamw (uint64_t)SA_FEATURE_NONE); 60957b448deSdougm /* 61057b448deSdougm * some options may only be on shares. If the opt 61157b448deSdougm * string contains one of those, we put it just on the 61257b448deSdougm * share. 61357b448deSdougm */ 61457b448deSdougm if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) { 61557b448deSdougm char *options; 61657b448deSdougm options = strdup(shareopts); 61757b448deSdougm if (options != NULL) { 618da6c28aaSamw set_node_attr(share, "dataset", dataset); 61957b448deSdougm err = sa_parse_legacy_options(share, options, 620da6c28aaSamw proto); 621da6c28aaSamw set_node_attr(share, "dataset", NULL); 62257b448deSdougm free(options); 62357b448deSdougm } 624da6c28aaSamw if (sa_get_optionset(group, proto) == NULL) 625da6c28aaSamw (void) sa_create_optionset(group, proto); 626da6c28aaSamw } 627da6c28aaSamw features = sa_proto_get_featureset(proto); 628da6c28aaSamw if (share != NULL && features & SA_FEATURE_RESOURCE) { 629da6c28aaSamw /* 630da6c28aaSamw * We have a share and the protocol requires 631da6c28aaSamw * that at least one resource exist (probably 632da6c28aaSamw * SMB). We need to make sure that there is at 633da6c28aaSamw * least one. 634da6c28aaSamw */ 635da6c28aaSamw resource = sa_get_share_resource(share, NULL); 636da6c28aaSamw if (resource == NULL) { 637da6c28aaSamw zfs_construct_resource(share, dataset); 638da6c28aaSamw } 63957b448deSdougm } 64057b448deSdougm } else { 64157b448deSdougm err = SA_NO_MEMORY; 64257b448deSdougm } 64357b448deSdougm return (err); 64457b448deSdougm } 64557b448deSdougm 64657b448deSdougm /* 64797df5ac9Sdougm * zfs_notinherited(group, share, mountpoint, shareopts, proto, dataset, 64897df5ac9Sdougm * grouperr) 64957b448deSdougm * 65057b448deSdougm * handle case where this is the top of a sub-group in ZFS. Pulled out 65197df5ac9Sdougm * of sa_get_zfs_shares for readability. We need the grouperr from the 65297df5ac9Sdougm * creation of the subgroup to know whether to add the public 65397df5ac9Sdougm * property, etc. to the specific share. 65457b448deSdougm */ 65557b448deSdougm static int 656da6c28aaSamw zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint, 65797df5ac9Sdougm char *shareopts, char *proto, char *dataset, int grouperr) 65857b448deSdougm { 65957b448deSdougm int err = SA_OK; 660da6c28aaSamw sa_resource_t resource; 661da6c28aaSamw uint64_t features; 66257b448deSdougm 66357b448deSdougm set_node_attr(group, "zfs", "true"); 664da6c28aaSamw if (share == NULL) 665da6c28aaSamw share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, 666da6c28aaSamw &err, (uint64_t)SA_FEATURE_NONE); 6673c484793Sdougm 6683c484793Sdougm if (err != SA_OK) 6693c484793Sdougm return (err); 6703c484793Sdougm 6713c484793Sdougm if (strcmp(shareopts, "on") == 0) 6723c484793Sdougm shareopts = ""; 6733c484793Sdougm if (shareopts != NULL) { 6743c484793Sdougm char *options; 6753c484793Sdougm if (grouperr == SA_PROP_SHARE_ONLY) { 676da6c28aaSamw /* 6773c484793Sdougm * Some properties may only be on shares, but 6783c484793Sdougm * due to the ZFS sub-groups being artificial, 6793c484793Sdougm * we sometimes get this and have to deal with 6803c484793Sdougm * it. We do it by attempting to put it on the 6813c484793Sdougm * share. 682da6c28aaSamw */ 6833c484793Sdougm options = strdup(shareopts); 6843c484793Sdougm if (options != NULL) { 6853c484793Sdougm err = sa_parse_legacy_options(share, 6863c484793Sdougm options, proto); 6873c484793Sdougm free(options); 688da6c28aaSamw } 68957b448deSdougm } 6903c484793Sdougm /* Unmark the share's changed state */ 6913c484793Sdougm set_node_attr(share, "changed", NULL); 6923c484793Sdougm } 6933c484793Sdougm features = sa_proto_get_featureset(proto); 6943c484793Sdougm if (share != NULL && features & SA_FEATURE_RESOURCE) { 6953c484793Sdougm /* 6963c484793Sdougm * We have a share and the protocol requires that at 6973c484793Sdougm * least one resource exist (probably SMB). We need to 6983c484793Sdougm * make sure that there is at least one. 6993c484793Sdougm */ 7003c484793Sdougm resource = sa_get_share_resource(share, NULL); 7013c484793Sdougm if (resource == NULL) { 7023c484793Sdougm zfs_construct_resource(share, dataset); 7033c484793Sdougm } 70457b448deSdougm } 70557b448deSdougm return (err); 70657b448deSdougm } 70757b448deSdougm 70857b448deSdougm /* 70957b448deSdougm * zfs_grp_error(err) 71057b448deSdougm * 71157b448deSdougm * Print group create error, but only once. If err is 0 do the 71257b448deSdougm * print else don't. 71357b448deSdougm */ 71457b448deSdougm 71557b448deSdougm static void 71657b448deSdougm zfs_grp_error(int err) 71757b448deSdougm { 71857b448deSdougm if (err == 0) { 71957b448deSdougm /* only print error once */ 72057b448deSdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 72157b448deSdougm "Cannot create ZFS subgroup during initialization:" 72257b448deSdougm " %s\n"), sa_errorstr(SA_SYSTEM_ERR)); 72357b448deSdougm } 72457b448deSdougm } 72557b448deSdougm 726da6c28aaSamw /* 727da6c28aaSamw * zfs_process_share(handle, share, mountpoint, proto, source, 728da6c28aaSamw * shareopts, sourcestr) 729da6c28aaSamw * 7305b6e0c46Sdougm * Creates the subgroup, if necessary and adds shares, resources 731da6c28aaSamw * and properties. 732da6c28aaSamw */ 7335b6e0c46Sdougm int 7345b6e0c46Sdougm sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share, 735da6c28aaSamw char *mountpoint, char *proto, zprop_source_t source, char *shareopts, 736da6c28aaSamw char *sourcestr, char *dataset) 737da6c28aaSamw { 738da6c28aaSamw int err = SA_OK; 739da6c28aaSamw 740da6c28aaSamw if (source & ZPROP_SRC_INHERITED) { 741da6c28aaSamw err = zfs_inherited(handle, share, sourcestr, shareopts, 742da6c28aaSamw mountpoint, proto, dataset); 743da6c28aaSamw } else { 744da6c28aaSamw group = find_or_create_zfs_subgroup(handle, dataset, proto, 745da6c28aaSamw shareopts, &err); 746da6c28aaSamw if (group == NULL) { 74797df5ac9Sdougm static boolean_t reported_error = B_FALSE; 748da6c28aaSamw /* 74997df5ac9Sdougm * There is a problem, but we can't do 750da6c28aaSamw * anything about it at this point so we issue 75197df5ac9Sdougm * a warning and move on. 752da6c28aaSamw */ 75397df5ac9Sdougm zfs_grp_error(reported_error); 75497df5ac9Sdougm reported_error = B_TRUE; 755da6c28aaSamw } 756da6c28aaSamw set_node_attr(group, "zfs", "true"); 757da6c28aaSamw /* 758da6c28aaSamw * Add share with local opts via zfs_notinherited. 759da6c28aaSamw */ 760da6c28aaSamw err = zfs_notinherited(group, share, mountpoint, shareopts, 76197df5ac9Sdougm proto, dataset, err); 762da6c28aaSamw } 763da6c28aaSamw return (err); 764da6c28aaSamw } 765da6c28aaSamw 7666185db85Sdougm /* 767549ec3ffSdougm * sa_get_zfs_shares(handle, groupname) 7686185db85Sdougm * 7696185db85Sdougm * Walk the mnttab for all zfs mounts and determine which are 7706185db85Sdougm * shared. Find or create the appropriate group/sub-group to contain 7716185db85Sdougm * the shares. 7726185db85Sdougm * 7736185db85Sdougm * All shares are in a sub-group that will hold the properties. This 7746185db85Sdougm * allows representing the inherited property model. 7753c484793Sdougm * 7763c484793Sdougm * One area of complication is if "sharenfs" is set at one level of 7773c484793Sdougm * the directory tree and "sharesmb" is set at a different level, the 7783c484793Sdougm * a sub-group must be formed at the lower level for both 7793c484793Sdougm * protocols. That is the nature of the problem in CR 6667349. 7806185db85Sdougm */ 7816185db85Sdougm 7826185db85Sdougm int 783549ec3ffSdougm sa_get_zfs_shares(sa_handle_t handle, char *groupname) 7846185db85Sdougm { 7856185db85Sdougm sa_group_t zfsgroup; 7863c484793Sdougm boolean_t nfs; 7873c484793Sdougm boolean_t nfs_inherited; 7883c484793Sdougm boolean_t smb; 7893c484793Sdougm boolean_t smb_inherited; 7901cea05afSdougm zfs_handle_t **zlist; 7913c484793Sdougm char nfsshareopts[ZFS_MAXPROPLEN]; 7923c484793Sdougm char smbshareopts[ZFS_MAXPROPLEN]; 7936185db85Sdougm sa_share_t share; 794990b4856Slling zprop_source_t source; 7953c484793Sdougm char nfssourcestr[ZFS_MAXPROPLEN]; 7963c484793Sdougm char smbsourcestr[ZFS_MAXPROPLEN]; 7971cea05afSdougm char mountpoint[ZFS_MAXPROPLEN]; 7981cea05afSdougm size_t count = 0, i; 799549ec3ffSdougm libzfs_handle_t *zfs_libhandle; 8003c484793Sdougm int err = SA_OK; 8016185db85Sdougm 8026185db85Sdougm /* 803549ec3ffSdougm * If we can't access libzfs, don't bother doing anything. 8046185db85Sdougm */ 805549ec3ffSdougm zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle; 8061cea05afSdougm if (zfs_libhandle == NULL) 80757b448deSdougm return (SA_SYSTEM_ERR); 8086185db85Sdougm 809da6c28aaSamw zfsgroup = find_or_create_group(handle, groupname, NULL, &err); 8103c484793Sdougm /* Not an error, this could be a legacy condition */ 811546405c3Sdougm if (zfsgroup == NULL) 8123c484793Sdougm return (SA_OK); 813546405c3Sdougm 814546405c3Sdougm /* 815546405c3Sdougm * need to walk the mounted ZFS pools and datasets to 816546405c3Sdougm * find shares that are possible. 817546405c3Sdougm */ 818546405c3Sdougm get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count); 819546405c3Sdougm qsort(zlist, count, sizeof (void *), mountpoint_compare); 820546405c3Sdougm 821546405c3Sdougm for (i = 0; i < count; i++) { 822546405c3Sdougm char *dataset; 823546405c3Sdougm 824990b4856Slling source = ZPROP_SRC_ALL; 825546405c3Sdougm /* If no mountpoint, skip. */ 826546405c3Sdougm if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, 827546405c3Sdougm mountpoint, sizeof (mountpoint), NULL, NULL, 0, 828546405c3Sdougm B_FALSE) != 0) 829546405c3Sdougm continue; 830546405c3Sdougm 8316185db85Sdougm /* 832546405c3Sdougm * zfs_get_name value must not be freed. It is just a 833546405c3Sdougm * pointer to a value in the handle. 8346185db85Sdougm */ 835546405c3Sdougm if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL) 836546405c3Sdougm continue; 8371cea05afSdougm 838546405c3Sdougm /* 839546405c3Sdougm * only deal with "mounted" file systems since 840546405c3Sdougm * unmounted file systems can't actually be shared. 841546405c3Sdougm */ 84257b448deSdougm 843546405c3Sdougm if (!zfs_is_mounted(zlist[i], NULL)) 844546405c3Sdougm continue; 84557b448deSdougm 8463c484793Sdougm nfs = nfs_inherited = B_FALSE; 8473c484793Sdougm 8483c484793Sdougm if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, nfsshareopts, 8493c484793Sdougm sizeof (nfsshareopts), &source, nfssourcestr, 850546405c3Sdougm ZFS_MAXPROPLEN, B_FALSE) == 0 && 8513c484793Sdougm strcmp(nfsshareopts, "off") != 0) { 8523c484793Sdougm if (source & ZPROP_SRC_INHERITED) 8533c484793Sdougm nfs_inherited = B_TRUE; 8543c484793Sdougm else 8553c484793Sdougm nfs = B_TRUE; 856da6c28aaSamw } 8573c484793Sdougm 8583c484793Sdougm smb = smb_inherited = B_FALSE; 8593c484793Sdougm if (zfs_prop_get(zlist[i], ZFS_PROP_SHARESMB, smbshareopts, 8603c484793Sdougm sizeof (smbshareopts), &source, smbsourcestr, 861da6c28aaSamw ZFS_MAXPROPLEN, B_FALSE) == 0 && 8623c484793Sdougm strcmp(smbshareopts, "off") != 0) { 8633c484793Sdougm if (source & ZPROP_SRC_INHERITED) 8643c484793Sdougm smb_inherited = B_TRUE; 8653c484793Sdougm else 8663c484793Sdougm smb = B_TRUE; 8673c484793Sdougm } 8683c484793Sdougm 8693c484793Sdougm /* 8703c484793Sdougm * If the mountpoint is already shared, it must be a 8713c484793Sdougm * non-ZFS share. We want to remove the share from its 8723c484793Sdougm * parent group and reshare it under ZFS. 8733c484793Sdougm */ 8743c484793Sdougm share = sa_find_share(handle, mountpoint); 8753c484793Sdougm if (share != NULL && 8763c484793Sdougm (nfs || smb || nfs_inherited || smb_inherited)) { 8773c484793Sdougm err = sa_remove_share(share); 8783c484793Sdougm share = NULL; 8793c484793Sdougm } 8803c484793Sdougm 8813c484793Sdougm /* 8823c484793Sdougm * At this point, we have the information needed to 8833c484793Sdougm * determine what to do with the share. 8843c484793Sdougm * 8853c484793Sdougm * If smb or nfs is set, we have a new sub-group. 8863c484793Sdougm * If smb_inherit and/or nfs_inherit is set, then 8873c484793Sdougm * place on an existing sub-group. If both are set, 8883c484793Sdougm * the existing sub-group is the closest up the tree. 8893c484793Sdougm */ 8903c484793Sdougm if (nfs || smb) { 8913c484793Sdougm /* 8923c484793Sdougm * Non-inherited is the straightforward 8933c484793Sdougm * case. sa_zfs_process_share handles it 8943c484793Sdougm * directly. Make sure that if the "other" 8953c484793Sdougm * protocol is inherited, that we treat it as 8963c484793Sdougm * non-inherited as well. 8973c484793Sdougm */ 8983c484793Sdougm if (nfs || nfs_inherited) { 8993c484793Sdougm err = sa_zfs_process_share(handle, zfsgroup, 9003c484793Sdougm share, mountpoint, "nfs", 9013c484793Sdougm 0, nfsshareopts, 9023c484793Sdougm nfssourcestr, dataset); 9033c484793Sdougm share = sa_find_share(handle, mountpoint); 904da6c28aaSamw } 9053c484793Sdougm if (smb || smb_inherited) { 9063c484793Sdougm err = sa_zfs_process_share(handle, zfsgroup, 9073c484793Sdougm share, mountpoint, "smb", 9083c484793Sdougm 0, smbshareopts, 9093c484793Sdougm smbsourcestr, dataset); 9103c484793Sdougm } 9113c484793Sdougm } else if (nfs_inherited || smb_inherited) { 9123c484793Sdougm char *grpdataset; 9133c484793Sdougm /* 9143c484793Sdougm * If we only have inherited groups, it is 9153c484793Sdougm * important to find the closer of the two if 9163c484793Sdougm * the protocols are set at different 9173c484793Sdougm * levels. The closest sub-group is the one we 9183c484793Sdougm * want to work with. 9193c484793Sdougm */ 9203c484793Sdougm if (nfs_inherited && smb_inherited) { 9213c484793Sdougm if (strcmp(nfssourcestr, smbsourcestr) <= 0) 9223c484793Sdougm grpdataset = nfssourcestr; 9233c484793Sdougm else 9243c484793Sdougm grpdataset = smbsourcestr; 9253c484793Sdougm } else if (nfs_inherited) { 9263c484793Sdougm grpdataset = nfssourcestr; 9273c484793Sdougm } else if (smb_inherited) { 9283c484793Sdougm grpdataset = smbsourcestr; 9293c484793Sdougm } 9303c484793Sdougm if (nfs_inherited) { 9313c484793Sdougm err = sa_zfs_process_share(handle, zfsgroup, 9323c484793Sdougm share, mountpoint, "nfs", 9333c484793Sdougm ZPROP_SRC_INHERITED, nfsshareopts, 9343c484793Sdougm grpdataset, dataset); 9353c484793Sdougm share = sa_find_share(handle, mountpoint); 9363c484793Sdougm } 9373c484793Sdougm if (smb_inherited) { 9383c484793Sdougm err = sa_zfs_process_share(handle, zfsgroup, 9393c484793Sdougm share, mountpoint, "smb", 9403c484793Sdougm ZPROP_SRC_INHERITED, smbshareopts, 9413c484793Sdougm grpdataset, dataset); 9426185db85Sdougm } 9436185db85Sdougm } 9446185db85Sdougm } 9451cea05afSdougm /* 9461cea05afSdougm * Don't need to free the "zlist" variable since it is only a 9471cea05afSdougm * pointer to a cached value that will be freed when 9481cea05afSdougm * sa_fini() is called. 9491cea05afSdougm */ 9503c484793Sdougm return (err); 9516185db85Sdougm } 9526185db85Sdougm 9536185db85Sdougm #define COMMAND "/usr/sbin/zfs" 9546185db85Sdougm 9556185db85Sdougm /* 9566185db85Sdougm * sa_zfs_set_sharenfs(group, path, on) 9576185db85Sdougm * 9586185db85Sdougm * Update the "sharenfs" property on the path. If on is true, then set 9596185db85Sdougm * to the properties on the group or "on" if no properties are 9606185db85Sdougm * defined. Set to "off" if on is false. 9616185db85Sdougm */ 9626185db85Sdougm 9636185db85Sdougm int 9646185db85Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on) 9656185db85Sdougm { 9666185db85Sdougm int ret = SA_NOT_IMPLEMENTED; 9676185db85Sdougm char *command; 9686185db85Sdougm 9696185db85Sdougm command = malloc(ZFS_MAXPROPLEN * 2); 9706185db85Sdougm if (command != NULL) { 97157b448deSdougm char *opts = NULL; 97257b448deSdougm char *dataset = NULL; 97357b448deSdougm FILE *pfile; 97457b448deSdougm sa_handle_impl_t impl_handle; 97557b448deSdougm /* for now, NFS is always available for "zfs" */ 97657b448deSdougm if (on) { 97757b448deSdougm opts = sa_proto_legacy_format("nfs", group, 1); 97857b448deSdougm if (opts != NULL && strlen(opts) == 0) { 97957b448deSdougm free(opts); 98057b448deSdougm opts = strdup("on"); 98157b448deSdougm } 9826185db85Sdougm } 98357b448deSdougm 98457b448deSdougm impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 98557b448deSdougm assert(impl_handle != NULL); 98657b448deSdougm if (impl_handle != NULL) 987a1ef5d63Smarks dataset = get_zfs_dataset(impl_handle, path, B_FALSE); 98857b448deSdougm else 9896185db85Sdougm ret = SA_SYSTEM_ERR; 99057b448deSdougm 99157b448deSdougm if (dataset != NULL) { 99257b448deSdougm (void) snprintf(command, ZFS_MAXPROPLEN * 2, 99357b448deSdougm "%s set sharenfs=\"%s\" %s", COMMAND, 99457b448deSdougm opts != NULL ? opts : "off", dataset); 99557b448deSdougm pfile = popen(command, "r"); 99657b448deSdougm if (pfile != NULL) { 99757b448deSdougm ret = pclose(pfile); 99857b448deSdougm if (ret != 0) 99957b448deSdougm ret = SA_SYSTEM_ERR; 100057b448deSdougm } 10016185db85Sdougm } 100257b448deSdougm if (opts != NULL) 100357b448deSdougm free(opts); 100457b448deSdougm if (dataset != NULL) 100557b448deSdougm free(dataset); 100657b448deSdougm free(command); 10076185db85Sdougm } 10086185db85Sdougm return (ret); 10096185db85Sdougm } 10106185db85Sdougm 1011da6c28aaSamw /* 1012da6c28aaSamw * add_resources(share, opt) 1013da6c28aaSamw * 1014da6c28aaSamw * Add resource properties to those in "opt". Resources are prefixed 1015da6c28aaSamw * with name=resourcename. 1016da6c28aaSamw */ 1017da6c28aaSamw static char * 1018da6c28aaSamw add_resources(sa_share_t share, char *opt) 1019da6c28aaSamw { 1020da6c28aaSamw char *newopt = NULL; 1021da6c28aaSamw char *propstr; 1022da6c28aaSamw sa_resource_t resource; 1023da6c28aaSamw 1024da6c28aaSamw newopt = strdup(opt); 1025da6c28aaSamw if (newopt == NULL) 1026da6c28aaSamw return (newopt); 1027da6c28aaSamw 1028da6c28aaSamw for (resource = sa_get_share_resource(share, NULL); 1029da6c28aaSamw resource != NULL; 1030da6c28aaSamw resource = sa_get_next_resource(resource)) { 1031da6c28aaSamw char *name; 1032da6c28aaSamw size_t size; 1033da6c28aaSamw 1034da6c28aaSamw name = sa_get_resource_attr(resource, "name"); 1035da6c28aaSamw if (name == NULL) { 1036da6c28aaSamw free(newopt); 1037da6c28aaSamw return (NULL); 1038da6c28aaSamw } 1039da6c28aaSamw size = strlen(name) + strlen(opt) + sizeof ("name=") + 1; 1040da6c28aaSamw newopt = calloc(1, size); 1041da6c28aaSamw if (newopt != NULL) 1042da6c28aaSamw (void) snprintf(newopt, size, "%s,name=%s", opt, name); 1043fe1c642dSBill Krier sa_free_attr_string(name); 1044da6c28aaSamw free(opt); 1045da6c28aaSamw opt = newopt; 1046da6c28aaSamw propstr = sa_proto_legacy_format("smb", resource, 0); 1047da6c28aaSamw if (propstr == NULL) { 1048da6c28aaSamw free(opt); 1049da6c28aaSamw return (NULL); 1050da6c28aaSamw } 1051da6c28aaSamw size = strlen(propstr) + strlen(opt) + 2; 1052da6c28aaSamw newopt = calloc(1, size); 1053da6c28aaSamw if (newopt != NULL) 1054da6c28aaSamw (void) snprintf(newopt, size, "%s,%s", opt, propstr); 1055da6c28aaSamw free(opt); 1056da6c28aaSamw opt = newopt; 1057da6c28aaSamw } 1058da6c28aaSamw return (opt); 1059da6c28aaSamw } 1060da6c28aaSamw 1061da6c28aaSamw /* 1062da6c28aaSamw * sa_zfs_set_sharesmb(group, path, on) 1063da6c28aaSamw * 1064da6c28aaSamw * Update the "sharesmb" property on the path. If on is true, then set 1065da6c28aaSamw * to the properties on the group or "on" if no properties are 1066da6c28aaSamw * defined. Set to "off" if on is false. 1067da6c28aaSamw */ 1068da6c28aaSamw 1069da6c28aaSamw int 1070da6c28aaSamw sa_zfs_set_sharesmb(sa_group_t group, char *path, int on) 1071da6c28aaSamw { 1072da6c28aaSamw int ret = SA_NOT_IMPLEMENTED; 1073da6c28aaSamw char *command; 1074da6c28aaSamw sa_share_t share; 1075da6c28aaSamw 1076da6c28aaSamw /* In case SMB not enabled */ 1077da6c28aaSamw if (sa_get_optionset(group, "smb") == NULL) 1078da6c28aaSamw return (SA_NOT_SUPPORTED); 1079da6c28aaSamw 1080da6c28aaSamw command = malloc(ZFS_MAXPROPLEN * 2); 1081da6c28aaSamw if (command != NULL) { 1082da6c28aaSamw char *opts = NULL; 1083da6c28aaSamw char *dataset = NULL; 1084da6c28aaSamw FILE *pfile; 1085da6c28aaSamw sa_handle_impl_t impl_handle; 1086da6c28aaSamw 1087da6c28aaSamw if (on) { 1088da6c28aaSamw char *newopt; 1089da6c28aaSamw 1090da6c28aaSamw share = sa_get_share(group, NULL); 1091da6c28aaSamw opts = sa_proto_legacy_format("smb", share, 1); 1092da6c28aaSamw if (opts != NULL && strlen(opts) == 0) { 1093da6c28aaSamw free(opts); 1094da6c28aaSamw opts = strdup("on"); 1095da6c28aaSamw } 1096da6c28aaSamw newopt = add_resources(opts, share); 1097da6c28aaSamw free(opts); 1098da6c28aaSamw opts = newopt; 1099da6c28aaSamw } 1100da6c28aaSamw 1101da6c28aaSamw impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1102da6c28aaSamw assert(impl_handle != NULL); 1103da6c28aaSamw if (impl_handle != NULL) 1104da6c28aaSamw dataset = get_zfs_dataset(impl_handle, path, B_FALSE); 1105da6c28aaSamw else 1106da6c28aaSamw ret = SA_SYSTEM_ERR; 1107da6c28aaSamw 1108da6c28aaSamw if (dataset != NULL) { 1109da6c28aaSamw (void) snprintf(command, ZFS_MAXPROPLEN * 2, 1110da6c28aaSamw "echo %s set sharesmb=\"%s\" %s", COMMAND, 1111da6c28aaSamw opts != NULL ? opts : "off", dataset); 1112da6c28aaSamw pfile = popen(command, "r"); 1113da6c28aaSamw if (pfile != NULL) { 1114da6c28aaSamw ret = pclose(pfile); 1115da6c28aaSamw if (ret != 0) 1116da6c28aaSamw ret = SA_SYSTEM_ERR; 1117da6c28aaSamw } 1118da6c28aaSamw } 1119da6c28aaSamw if (opts != NULL) 1120da6c28aaSamw free(opts); 1121da6c28aaSamw if (dataset != NULL) 1122da6c28aaSamw free(dataset); 1123da6c28aaSamw free(command); 1124da6c28aaSamw } 1125da6c28aaSamw return (ret); 1126da6c28aaSamw } 1127da6c28aaSamw 11286185db85Sdougm /* 11296185db85Sdougm * sa_zfs_update(group) 11306185db85Sdougm * 11316185db85Sdougm * call back to ZFS to update the share if necessary. 11326185db85Sdougm * Don't do it if it isn't a real change. 11336185db85Sdougm */ 11346185db85Sdougm int 11356185db85Sdougm sa_zfs_update(sa_group_t group) 11366185db85Sdougm { 11376185db85Sdougm sa_optionset_t protopt; 11386185db85Sdougm sa_group_t parent; 11396185db85Sdougm char *command; 11406185db85Sdougm char *optstring; 11416185db85Sdougm int ret = SA_OK; 11426185db85Sdougm int doupdate = 0; 11436185db85Sdougm FILE *pfile; 11446185db85Sdougm 11456185db85Sdougm if (sa_is_share(group)) 114657b448deSdougm parent = sa_get_parent_group(group); 11476185db85Sdougm else 114857b448deSdougm parent = group; 11496185db85Sdougm 11506185db85Sdougm if (parent != NULL) { 115157b448deSdougm command = malloc(ZFS_MAXPROPLEN * 2); 115257b448deSdougm if (command == NULL) 115357b448deSdougm return (SA_NO_MEMORY); 115457b448deSdougm 115557b448deSdougm *command = '\0'; 115657b448deSdougm for (protopt = sa_get_optionset(parent, NULL); protopt != NULL; 115757b448deSdougm protopt = sa_get_next_optionset(protopt)) { 115857b448deSdougm 115957b448deSdougm char *proto = sa_get_optionset_attr(protopt, "type"); 116057b448deSdougm char *path; 116157b448deSdougm char *dataset = NULL; 116257b448deSdougm char *zfsopts = NULL; 116357b448deSdougm 116457b448deSdougm if (sa_is_share(group)) { 116557b448deSdougm path = sa_get_share_attr((sa_share_t)group, 116657b448deSdougm "path"); 116757b448deSdougm if (path != NULL) { 116857b448deSdougm sa_handle_impl_t impl_handle; 116957b448deSdougm 117057b448deSdougm impl_handle = sa_find_group_handle( 117157b448deSdougm group); 117257b448deSdougm if (impl_handle != NULL) 117357b448deSdougm dataset = get_zfs_dataset( 1174a1ef5d63Smarks impl_handle, path, B_FALSE); 117557b448deSdougm else 117657b448deSdougm ret = SA_SYSTEM_ERR; 117757b448deSdougm 117857b448deSdougm sa_free_attr_string(path); 117957b448deSdougm } 11806185db85Sdougm } else { 118157b448deSdougm dataset = sa_get_group_attr(group, "name"); 11826185db85Sdougm } 118357b448deSdougm /* update only when there is an optstring found */ 118457b448deSdougm doupdate = 0; 118557b448deSdougm if (proto != NULL && dataset != NULL) { 118657b448deSdougm optstring = sa_proto_legacy_format(proto, 118757b448deSdougm group, 1); 118857b448deSdougm zfsopts = get_zfs_property(dataset, 118957b448deSdougm ZFS_PROP_SHARENFS); 119057b448deSdougm 119157b448deSdougm if (optstring != NULL && zfsopts != NULL) { 119257b448deSdougm if (strcmp(optstring, zfsopts) != 0) 119357b448deSdougm doupdate++; 119457b448deSdougm } 119557b448deSdougm if (doupdate) { 119657b448deSdougm if (optstring != NULL && 119757b448deSdougm strlen(optstring) > 0) { 119857b448deSdougm (void) snprintf(command, 119957b448deSdougm ZFS_MAXPROPLEN * 2, 12001f713840SDoug McCallum "%s set share%s=%s %s", 12011f713840SDoug McCallum COMMAND, proto, 120257b448deSdougm optstring, dataset); 120357b448deSdougm } else { 120457b448deSdougm (void) snprintf(command, 120557b448deSdougm ZFS_MAXPROPLEN * 2, 12061f713840SDoug McCallum "%s set share%s=on %s", 12071f713840SDoug McCallum COMMAND, proto, 120857b448deSdougm dataset); 120957b448deSdougm } 121057b448deSdougm pfile = popen(command, "r"); 121157b448deSdougm if (pfile != NULL) 121257b448deSdougm ret = pclose(pfile); 121357b448deSdougm switch (ret) { 121457b448deSdougm default: 121557b448deSdougm case 1: 121657b448deSdougm ret = SA_SYSTEM_ERR; 121757b448deSdougm break; 121857b448deSdougm case 2: 121957b448deSdougm ret = SA_SYNTAX_ERR; 122057b448deSdougm break; 122157b448deSdougm case 0: 122257b448deSdougm break; 122357b448deSdougm } 122457b448deSdougm } 122557b448deSdougm if (optstring != NULL) 122657b448deSdougm free(optstring); 122757b448deSdougm if (zfsopts != NULL) 122857b448deSdougm free(zfsopts); 12296185db85Sdougm } 123057b448deSdougm if (proto != NULL) 123157b448deSdougm sa_free_attr_string(proto); 123257b448deSdougm if (dataset != NULL) 123357b448deSdougm free(dataset); 12346185db85Sdougm } 123557b448deSdougm free(command); 12366185db85Sdougm } 12376185db85Sdougm return (ret); 12386185db85Sdougm } 12396185db85Sdougm 12406185db85Sdougm /* 12416185db85Sdougm * sa_group_is_zfs(group) 12426185db85Sdougm * 12436185db85Sdougm * Given the group, determine if the zfs attribute is set. 12446185db85Sdougm */ 12456185db85Sdougm 12466185db85Sdougm int 12476185db85Sdougm sa_group_is_zfs(sa_group_t group) 12486185db85Sdougm { 12496185db85Sdougm char *zfs; 12506185db85Sdougm int ret = 0; 12516185db85Sdougm 12526185db85Sdougm zfs = sa_get_group_attr(group, "zfs"); 12536185db85Sdougm if (zfs != NULL) { 125457b448deSdougm ret = 1; 125557b448deSdougm sa_free_attr_string(zfs); 12566185db85Sdougm } 12576185db85Sdougm return (ret); 12586185db85Sdougm } 12596185db85Sdougm 12606185db85Sdougm /* 12616185db85Sdougm * sa_path_is_zfs(path) 12626185db85Sdougm * 12636185db85Sdougm * Check to see if the file system path represents is of type "zfs". 12646185db85Sdougm */ 12656185db85Sdougm 12666185db85Sdougm int 12676185db85Sdougm sa_path_is_zfs(char *path) 12686185db85Sdougm { 12696185db85Sdougm char *fstype; 12706185db85Sdougm int ret = 0; 12716185db85Sdougm 12726185db85Sdougm fstype = sa_fstype(path); 127357b448deSdougm if (fstype != NULL && strcmp(fstype, "zfs") == 0) 127457b448deSdougm ret = 1; 12756185db85Sdougm if (fstype != NULL) 127657b448deSdougm sa_free_fstype(fstype); 12776185db85Sdougm return (ret); 12786185db85Sdougm } 1279ecd6cf80Smarks 1280ecd6cf80Smarks int 1281ecd6cf80Smarks sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto) 1282ecd6cf80Smarks { 1283ecd6cf80Smarks char *path; 1284ecd6cf80Smarks 1285ed78bdc4Smarks /* Make sure path is valid */ 1286ed78bdc4Smarks 1287ecd6cf80Smarks path = sa_get_share_attr(share, "path"); 1288ecd6cf80Smarks if (path != NULL) { 1289ecd6cf80Smarks (void) memset(sh, 0, sizeof (sh)); 1290ecd6cf80Smarks (void) sa_fillshare(share, proto, sh); 1291ed78bdc4Smarks sa_free_attr_string(path); 1292ecd6cf80Smarks return (0); 1293ecd6cf80Smarks } else 1294ecd6cf80Smarks return (1); 1295ecd6cf80Smarks } 1296ecd6cf80Smarks 1297ecd6cf80Smarks #define SMAX(i, j) \ 1298ecd6cf80Smarks if ((j) > (i)) { \ 1299ecd6cf80Smarks (i) = (j); \ 1300ecd6cf80Smarks } 1301ecd6cf80Smarks 1302ecd6cf80Smarks int 1303743a77edSAlan Wright sa_share_zfs(sa_share_t share, sa_resource_t resource, char *path, share_t *sh, 1304da6c28aaSamw void *exportdata, zfs_share_op_t operation) 1305ecd6cf80Smarks { 1306ecd6cf80Smarks libzfs_handle_t *libhandle; 1307ecd6cf80Smarks sa_group_t group; 1308ecd6cf80Smarks sa_handle_t sahandle; 1309ecd6cf80Smarks char *dataset; 1310ecd6cf80Smarks int err = EINVAL; 1311ecd6cf80Smarks int i, j; 13124d79fe3bSmarks char newpath[MAXPATHLEN]; 1313a1ef5d63Smarks char *pathp; 1314ecd6cf80Smarks 1315ecd6cf80Smarks /* 1316ecd6cf80Smarks * First find the dataset name 1317ecd6cf80Smarks */ 1318ecd6cf80Smarks if ((group = sa_get_parent_group(share)) == NULL) { 1319a63214d6SBill Krier return (EINVAL); 1320ecd6cf80Smarks } 1321ecd6cf80Smarks if ((sahandle = sa_find_group_handle(group)) == NULL) { 1322a63214d6SBill Krier return (EINVAL); 1323ecd6cf80Smarks } 1324ecd6cf80Smarks 13254d79fe3bSmarks /* 13264d79fe3bSmarks * If get_zfs_dataset fails, see if it is a subdirectory 13274d79fe3bSmarks */ 1328a1ef5d63Smarks 1329a1ef5d63Smarks pathp = path; 1330a1ef5d63Smarks while ((dataset = get_zfs_dataset(sahandle, pathp, B_TRUE)) == NULL) { 13314d79fe3bSmarks char *p; 13324d79fe3bSmarks 1333a1ef5d63Smarks if (pathp == path) { 1334a1ef5d63Smarks (void) strlcpy(newpath, path, sizeof (newpath)); 1335a1ef5d63Smarks pathp = newpath; 1336a1ef5d63Smarks } 1337a1ef5d63Smarks 1338d34e4517SDoug McCallum /* 1339d34e4517SDoug McCallum * Make sure only one leading '/' This condition came 1340d34e4517SDoug McCallum * about when using HAStoragePlus which insisted on 1341d34e4517SDoug McCallum * putting an extra leading '/' in the ZFS path 1342d34e4517SDoug McCallum * name. The problem is fixed in other areas, but this 1343d34e4517SDoug McCallum * will catch any other ways that a double slash might 1344d34e4517SDoug McCallum * get introduced. 1345d34e4517SDoug McCallum */ 1346d34e4517SDoug McCallum while (*pathp == '/' && *(pathp + 1) == '/') 1347d34e4517SDoug McCallum pathp++; 1348d34e4517SDoug McCallum 1349a1ef5d63Smarks /* 1350a1ef5d63Smarks * chop off part of path, but if we are at root then 1351a1ef5d63Smarks * make sure path is a / 1352a1ef5d63Smarks */ 1353a1ef5d63Smarks if ((strlen(pathp) > 1) && (p = strrchr(pathp, '/'))) { 1354a1ef5d63Smarks if (pathp == p) { 1355a1ef5d63Smarks *(p + 1) = '\0'; /* skip over /, root case */ 1356a1ef5d63Smarks } else { 1357a1ef5d63Smarks *p = '\0'; 1358a1ef5d63Smarks } 1359a1ef5d63Smarks } else { 1360a63214d6SBill Krier return (EINVAL); 1361a1ef5d63Smarks } 1362ecd6cf80Smarks } 1363ecd6cf80Smarks 1364ecd6cf80Smarks libhandle = libzfs_init(); 1365ecd6cf80Smarks if (libhandle != NULL) { 1366743a77edSAlan Wright char *resource_name; 1367ecd6cf80Smarks 1368ecd6cf80Smarks i = (sh->sh_path ? strlen(sh->sh_path) : 0); 1369ecd6cf80Smarks sh->sh_size = i; 1370ecd6cf80Smarks 1371ecd6cf80Smarks j = (sh->sh_res ? strlen(sh->sh_res) : 0); 1372ecd6cf80Smarks sh->sh_size += j; 1373ecd6cf80Smarks SMAX(i, j); 1374ecd6cf80Smarks 1375ecd6cf80Smarks j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0); 1376ecd6cf80Smarks sh->sh_size += j; 1377ecd6cf80Smarks SMAX(i, j); 1378ecd6cf80Smarks 1379ecd6cf80Smarks j = (sh->sh_opts ? strlen(sh->sh_opts) : 0); 1380ecd6cf80Smarks sh->sh_size += j; 1381ecd6cf80Smarks SMAX(i, j); 1382ecd6cf80Smarks 1383ecd6cf80Smarks j = (sh->sh_descr ? strlen(sh->sh_descr) : 0); 1384ecd6cf80Smarks sh->sh_size += j; 1385ecd6cf80Smarks SMAX(i, j); 1386743a77edSAlan Wright 1387743a77edSAlan Wright resource_name = sa_get_resource_attr(resource, "name"); 1388743a77edSAlan Wright 1389ecd6cf80Smarks err = zfs_deleg_share_nfs(libhandle, dataset, path, 1390743a77edSAlan Wright resource_name, exportdata, sh, i, operation); 13915b6e0c46Sdougm if (err == SA_OK) 13925b6e0c46Sdougm sa_update_sharetab_ts(sahandle); 1393a63214d6SBill Krier else 1394a63214d6SBill Krier err = errno; 1395743a77edSAlan Wright if (resource_name) 1396743a77edSAlan Wright sa_free_attr_string(resource_name); 1397743a77edSAlan Wright 1398ecd6cf80Smarks libzfs_fini(libhandle); 1399ecd6cf80Smarks } 1400ecd6cf80Smarks free(dataset); 1401ecd6cf80Smarks return (err); 1402ecd6cf80Smarks } 14035b6e0c46Sdougm 14045b6e0c46Sdougm /* 14055b6e0c46Sdougm * sa_get_zfs_handle(handle) 14065b6e0c46Sdougm * 14075b6e0c46Sdougm * Given an sa_handle_t, return the libzfs_handle_t *. This is only 14085b6e0c46Sdougm * used internally by libzfs. Needed in order to avoid including 14095b6e0c46Sdougm * libshare_impl.h in libzfs. 14105b6e0c46Sdougm */ 14115b6e0c46Sdougm 14125b6e0c46Sdougm libzfs_handle_t * 14135b6e0c46Sdougm sa_get_zfs_handle(sa_handle_t handle) 14145b6e0c46Sdougm { 14155b6e0c46Sdougm sa_handle_impl_t implhandle = (sa_handle_impl_t)handle; 14165b6e0c46Sdougm 14175b6e0c46Sdougm return (implhandle->zfs_libhandle); 14185b6e0c46Sdougm } 1419743a77edSAlan Wright 1420743a77edSAlan Wright /* 1421743a77edSAlan Wright * sa_get_zfs_info(libzfs, path, mountpoint, dataset) 1422743a77edSAlan Wright * 1423743a77edSAlan Wright * Find the ZFS dataset and mountpoint for a given path 1424743a77edSAlan Wright */ 1425743a77edSAlan Wright int 1426743a77edSAlan Wright sa_zfs_get_info(libzfs_handle_t *libzfs, char *path, char *mountpointp, 1427743a77edSAlan Wright char *datasetp) 1428743a77edSAlan Wright { 1429743a77edSAlan Wright get_all_cbdata_t cb = { 0 }; 1430743a77edSAlan Wright int i; 1431743a77edSAlan Wright char mountpoint[ZFS_MAXPROPLEN]; 1432743a77edSAlan Wright char dataset[ZFS_MAXPROPLEN]; 1433743a77edSAlan Wright char canmount[ZFS_MAXPROPLEN]; 1434743a77edSAlan Wright char *dp; 1435743a77edSAlan Wright int count; 1436743a77edSAlan Wright int ret = 0; 1437743a77edSAlan Wright 1438743a77edSAlan Wright cb.cb_types = ZFS_TYPE_FILESYSTEM; 1439743a77edSAlan Wright 1440743a77edSAlan Wright if (libzfs == NULL) 1441743a77edSAlan Wright return (0); 1442743a77edSAlan Wright 1443743a77edSAlan Wright (void) zfs_iter_root(libzfs, get_one_filesystem, &cb); 1444743a77edSAlan Wright count = cb.cb_used; 1445743a77edSAlan Wright 1446743a77edSAlan Wright qsort(cb.cb_handles, count, sizeof (void *), mountpoint_compare); 1447743a77edSAlan Wright for (i = 0; i < count; i++) { 1448743a77edSAlan Wright /* must have a mountpoint */ 1449743a77edSAlan Wright if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_MOUNTPOINT, 1450743a77edSAlan Wright mountpoint, sizeof (mountpoint), 1451743a77edSAlan Wright NULL, NULL, 0, B_FALSE) != 0) { 1452743a77edSAlan Wright /* no mountpoint */ 1453743a77edSAlan Wright continue; 1454743a77edSAlan Wright } 1455743a77edSAlan Wright 1456743a77edSAlan Wright /* mountpoint must be a path */ 1457743a77edSAlan Wright if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 1458743a77edSAlan Wright strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 1459743a77edSAlan Wright /* 1460743a77edSAlan Wright * Search mmttab for mountpoint 1461743a77edSAlan Wright */ 1462743a77edSAlan Wright 1463743a77edSAlan Wright if (get_legacy_mountpoint(path, dataset, 1464743a77edSAlan Wright ZFS_MAXPROPLEN, mountpoint, 1465743a77edSAlan Wright ZFS_MAXPROPLEN) == 0) { 1466743a77edSAlan Wright ret = 1; 1467743a77edSAlan Wright break; 1468743a77edSAlan Wright } 1469743a77edSAlan Wright continue; 1470743a77edSAlan Wright } 1471743a77edSAlan Wright 1472743a77edSAlan Wright /* canmount must be set */ 1473743a77edSAlan Wright canmount[0] = '\0'; 1474743a77edSAlan Wright if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_CANMOUNT, canmount, 1475743a77edSAlan Wright sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 || 1476743a77edSAlan Wright strcmp(canmount, "off") == 0) 1477743a77edSAlan Wright continue; 1478743a77edSAlan Wright 1479743a77edSAlan Wright /* 1480743a77edSAlan Wright * have a mountable handle but want to skip those marked none 1481743a77edSAlan Wright * and legacy 1482743a77edSAlan Wright */ 1483743a77edSAlan Wright if (strcmp(mountpoint, path) == 0) { 1484743a77edSAlan Wright dp = (char *)zfs_get_name(cb.cb_handles[i]); 1485743a77edSAlan Wright if (dp != NULL) { 1486743a77edSAlan Wright if (datasetp != NULL) 1487743a77edSAlan Wright (void) strcpy(datasetp, dp); 1488743a77edSAlan Wright if (mountpointp != NULL) 1489743a77edSAlan Wright (void) strcpy(mountpointp, mountpoint); 1490743a77edSAlan Wright ret = 1; 1491743a77edSAlan Wright } 1492743a77edSAlan Wright break; 1493743a77edSAlan Wright } 1494743a77edSAlan Wright 1495743a77edSAlan Wright } 1496743a77edSAlan Wright 1497743a77edSAlan Wright return (ret); 1498743a77edSAlan Wright } 1499148c5f43SAlan Wright 1500148c5f43SAlan Wright /* 1501148c5f43SAlan Wright * This method builds values for "sharesmb" property from the 1502148c5f43SAlan Wright * nvlist argument. The values are returned in sharesmb_val variable. 1503148c5f43SAlan Wright */ 1504148c5f43SAlan Wright static int 1505148c5f43SAlan Wright sa_zfs_sprintf_new_prop(nvlist_t *nvl, char *sharesmb_val) 1506148c5f43SAlan Wright { 1507148c5f43SAlan Wright char cur_val[MAXPATHLEN]; 1508148c5f43SAlan Wright char *name, *val; 1509148c5f43SAlan Wright nvpair_t *cur; 1510148c5f43SAlan Wright int err = 0; 1511148c5f43SAlan Wright 1512148c5f43SAlan Wright cur = nvlist_next_nvpair(nvl, NULL); 1513148c5f43SAlan Wright while (cur != NULL) { 1514148c5f43SAlan Wright name = nvpair_name(cur); 1515148c5f43SAlan Wright err = nvpair_value_string(cur, &val); 1516148c5f43SAlan Wright if ((err != 0) || (name == NULL) || (val == NULL)) 1517148c5f43SAlan Wright return (-1); 1518148c5f43SAlan Wright 1519148c5f43SAlan Wright (void) snprintf(cur_val, MAXPATHLEN, "%s=%s,", name, val); 1520148c5f43SAlan Wright (void) strlcat(sharesmb_val, cur_val, MAXPATHLEN); 1521148c5f43SAlan Wright 1522148c5f43SAlan Wright cur = nvlist_next_nvpair(nvl, cur); 1523148c5f43SAlan Wright } 1524148c5f43SAlan Wright 1525148c5f43SAlan Wright return (0); 1526148c5f43SAlan Wright } 1527148c5f43SAlan Wright 1528148c5f43SAlan Wright /* 1529148c5f43SAlan Wright * This method builds values for "sharesmb" property from values 1530148c5f43SAlan Wright * already existing on the share. The properties set via sa_zfs_sprint_new_prop 1531148c5f43SAlan Wright * method are passed in sharesmb_val. If a existing property is already 1532148c5f43SAlan Wright * set via sa_zfs_sprint_new_prop method, then they are not appended 1533148c5f43SAlan Wright * to the sharesmb_val string. The returned sharesmb_val string is a combination 1534148c5f43SAlan Wright * of new and existing values for 'sharesmb' property. 1535148c5f43SAlan Wright */ 1536148c5f43SAlan Wright static int 1537148c5f43SAlan Wright sa_zfs_sprintf_existing_prop(zfs_handle_t *handle, char *sharesmb_val) 1538148c5f43SAlan Wright { 1539148c5f43SAlan Wright char shareopts[ZFS_MAXPROPLEN], cur_val[MAXPATHLEN]; 1540148c5f43SAlan Wright char *token, *last, *value; 1541148c5f43SAlan Wright 1542148c5f43SAlan Wright if (zfs_prop_get(handle, ZFS_PROP_SHARESMB, shareopts, 1543148c5f43SAlan Wright sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0) 1544148c5f43SAlan Wright return (-1); 1545148c5f43SAlan Wright 1546148c5f43SAlan Wright if (strstr(shareopts, "=") == NULL) 1547148c5f43SAlan Wright return (0); 1548148c5f43SAlan Wright 1549148c5f43SAlan Wright for (token = strtok_r(shareopts, ",", &last); token != NULL; 1550148c5f43SAlan Wright token = strtok_r(NULL, ",", &last)) { 1551148c5f43SAlan Wright value = strchr(token, '='); 1552148c5f43SAlan Wright if (value == NULL) 1553148c5f43SAlan Wright return (-1); 1554148c5f43SAlan Wright *value++ = '\0'; 1555148c5f43SAlan Wright 1556148c5f43SAlan Wright (void) snprintf(cur_val, MAXPATHLEN, "%s=", token); 1557148c5f43SAlan Wright if (strstr(sharesmb_val, cur_val) == NULL) { 1558148c5f43SAlan Wright (void) strlcat(cur_val, value, MAXPATHLEN); 1559148c5f43SAlan Wright (void) strlcat(cur_val, ",", MAXPATHLEN); 1560148c5f43SAlan Wright (void) strlcat(sharesmb_val, cur_val, MAXPATHLEN); 1561148c5f43SAlan Wright } 1562148c5f43SAlan Wright } 1563148c5f43SAlan Wright 1564148c5f43SAlan Wright return (0); 1565148c5f43SAlan Wright } 1566148c5f43SAlan Wright 1567148c5f43SAlan Wright /* 1568148c5f43SAlan Wright * Sets the share properties on a ZFS share. For now, this method sets only 1569148c5f43SAlan Wright * the "sharesmb" property. 1570148c5f43SAlan Wright * 1571148c5f43SAlan Wright * This method includes building a comma seperated name-value string to be 1572148c5f43SAlan Wright * set on the "sharesmb" property of a ZFS share. This name-value string is 1573148c5f43SAlan Wright * build in 2 steps: 1574148c5f43SAlan Wright * - New property values given as name-value pair are set first. 1575148c5f43SAlan Wright * - Existing optionset properties, which are not part of the new properties 1576148c5f43SAlan Wright * passed in step 1, are appended to the newly set properties. 1577148c5f43SAlan Wright */ 1578148c5f43SAlan Wright int 1579148c5f43SAlan Wright sa_zfs_setprop(sa_handle_t handle, char *path, nvlist_t *nvl) 1580148c5f43SAlan Wright { 1581148c5f43SAlan Wright zfs_handle_t *z_fs; 1582148c5f43SAlan Wright libzfs_handle_t *z_lib; 1583148c5f43SAlan Wright char sharesmb_val[MAXPATHLEN]; 1584148c5f43SAlan Wright char *dataset, *lastcomma; 1585148c5f43SAlan Wright 1586148c5f43SAlan Wright if (nvlist_empty(nvl)) 1587148c5f43SAlan Wright return (0); 1588148c5f43SAlan Wright 1589148c5f43SAlan Wright if ((handle == NULL) || (path == NULL)) 1590148c5f43SAlan Wright return (-1); 1591148c5f43SAlan Wright 1592148c5f43SAlan Wright if ((dataset = get_zfs_dataset(handle, path, B_FALSE)) == NULL) 1593148c5f43SAlan Wright return (-1); 1594148c5f43SAlan Wright 1595148c5f43SAlan Wright if ((z_lib = libzfs_init()) == NULL) { 1596148c5f43SAlan Wright free(dataset); 1597148c5f43SAlan Wright return (-1); 1598148c5f43SAlan Wright } 1599148c5f43SAlan Wright 1600148c5f43SAlan Wright z_fs = zfs_open(z_lib, dataset, ZFS_TYPE_DATASET); 1601148c5f43SAlan Wright if (z_fs == NULL) { 1602148c5f43SAlan Wright free(dataset); 1603148c5f43SAlan Wright libzfs_fini(z_lib); 1604148c5f43SAlan Wright return (-1); 1605148c5f43SAlan Wright } 1606148c5f43SAlan Wright 1607148c5f43SAlan Wright bzero(sharesmb_val, MAXPATHLEN); 1608148c5f43SAlan Wright if (sa_zfs_sprintf_new_prop(nvl, sharesmb_val) != 0) { 1609148c5f43SAlan Wright free(dataset); 1610148c5f43SAlan Wright zfs_close(z_fs); 1611148c5f43SAlan Wright libzfs_fini(z_lib); 1612148c5f43SAlan Wright return (-1); 1613148c5f43SAlan Wright } 1614148c5f43SAlan Wright 1615148c5f43SAlan Wright if (sa_zfs_sprintf_existing_prop(z_fs, sharesmb_val) != 0) { 1616148c5f43SAlan Wright free(dataset); 1617148c5f43SAlan Wright zfs_close(z_fs); 1618148c5f43SAlan Wright libzfs_fini(z_lib); 1619148c5f43SAlan Wright return (-1); 1620148c5f43SAlan Wright } 1621148c5f43SAlan Wright 1622148c5f43SAlan Wright lastcomma = strrchr(sharesmb_val, ','); 1623148c5f43SAlan Wright if ((lastcomma != NULL) && (lastcomma[1] == '\0')) 1624148c5f43SAlan Wright *lastcomma = '\0'; 1625148c5f43SAlan Wright 1626148c5f43SAlan Wright (void) zfs_prop_set(z_fs, zfs_prop_to_name(ZFS_PROP_SHARESMB), 1627148c5f43SAlan Wright sharesmb_val); 1628148c5f43SAlan Wright free(dataset); 1629148c5f43SAlan Wright zfs_close(z_fs); 1630148c5f43SAlan Wright libzfs_fini(z_lib); 1631148c5f43SAlan Wright 1632148c5f43SAlan Wright return (0); 1633148c5f43SAlan Wright } 1634