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 /* 2324424a35Sdougm * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 246185db85Sdougm * Use is subject to license terms. 256185db85Sdougm */ 266185db85Sdougm 276185db85Sdougm #pragma ident "%Z%%M% %I% %E% SMI" 286185db85Sdougm 296185db85Sdougm #include <libzfs.h> 306185db85Sdougm #include <string.h> 31*a3351425Sdougm #include <strings.h> 326185db85Sdougm #include <libshare.h> 336185db85Sdougm #include "libshare_impl.h" 341cea05afSdougm #include <libintl.h> 356185db85Sdougm 366185db85Sdougm extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *); 376185db85Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *); 386185db85Sdougm extern char *sa_fstype(char *); 396185db85Sdougm extern void set_node_attr(void *, char *, char *); 406185db85Sdougm extern int sa_is_share(void *); 411cea05afSdougm 421cea05afSdougm /* 431cea05afSdougm * File system specific code for ZFS. The original code was stolen 441cea05afSdougm * from the "zfs" command and modified to better suit this library's 451cea05afSdougm * usage. 461cea05afSdougm */ 471cea05afSdougm 481cea05afSdougm typedef struct get_all_cbdata { 491cea05afSdougm zfs_handle_t **cb_handles; 501cea05afSdougm size_t cb_alloc; 511cea05afSdougm size_t cb_used; 52*a3351425Sdougm uint_t cb_types; 531cea05afSdougm } get_all_cbdata_t; 541cea05afSdougm 551cea05afSdougm /* 56549ec3ffSdougm * sa_zfs_init(impl_handle) 571cea05afSdougm * 58549ec3ffSdougm * Initialize an access handle into libzfs. The handle needs to stay 59549ec3ffSdougm * around until sa_zfs_fini() in order to maintain the cache of 60549ec3ffSdougm * mounts. 611cea05afSdougm */ 621cea05afSdougm 6357b448deSdougm int 64549ec3ffSdougm sa_zfs_init(sa_handle_impl_t impl_handle) 651cea05afSdougm { 66549ec3ffSdougm impl_handle->zfs_libhandle = libzfs_init(); 6757b448deSdougm if (impl_handle->zfs_libhandle != NULL) { 6857b448deSdougm libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE); 6957b448deSdougm return (B_TRUE); 7057b448deSdougm } 7157b448deSdougm return (B_FALSE); 721cea05afSdougm } 731cea05afSdougm 741cea05afSdougm /* 75549ec3ffSdougm * sa_zfs_fini(impl_handle) 761cea05afSdougm * 771cea05afSdougm * cleanup data structures and the libzfs handle used for accessing 781cea05afSdougm * zfs file share info. 791cea05afSdougm */ 801cea05afSdougm 811cea05afSdougm void 82549ec3ffSdougm sa_zfs_fini(sa_handle_impl_t impl_handle) 831cea05afSdougm { 84549ec3ffSdougm if (impl_handle->zfs_libhandle != NULL) { 8557b448deSdougm if (impl_handle->zfs_list != NULL) { 86*a3351425Sdougm zfs_handle_t **zhp = impl_handle->zfs_list; 87*a3351425Sdougm size_t i; 88*a3351425Sdougm 8957b448deSdougm /* 90*a3351425Sdougm * Contents of zfs_list need to be freed so we 91*a3351425Sdougm * don't lose ZFS handles. 9257b448deSdougm */ 93*a3351425Sdougm for (i = 0; i < impl_handle->zfs_list_count; i++) { 94*a3351425Sdougm zfs_close(zhp[i]); 95*a3351425Sdougm } 9657b448deSdougm free(impl_handle->zfs_list); 9757b448deSdougm impl_handle->zfs_list = NULL; 9857b448deSdougm impl_handle->zfs_list_count = 0; 9957b448deSdougm } 100*a3351425Sdougm 101*a3351425Sdougm libzfs_fini(impl_handle->zfs_libhandle); 102*a3351425Sdougm impl_handle->zfs_libhandle = NULL; 1031cea05afSdougm } 1041cea05afSdougm } 1051cea05afSdougm 1061cea05afSdougm /* 1071cea05afSdougm * get_one_filesystem(zfs_handle_t, data) 1081cea05afSdougm * 1091cea05afSdougm * an interator function called while iterating through the ZFS 1101cea05afSdougm * root. It accumulates into an array of file system handles that can 1111cea05afSdougm * be used to derive info about those file systems. 112*a3351425Sdougm * 113*a3351425Sdougm * Note that as this function is called, we close all zhp handles that 114*a3351425Sdougm * are not going to be places into the cp_handles list. We don't want 115*a3351425Sdougm * to close the ones we are keeping, but all others would be leaked if 116*a3351425Sdougm * not closed here. 1171cea05afSdougm */ 1181cea05afSdougm 1191cea05afSdougm static int 1201cea05afSdougm get_one_filesystem(zfs_handle_t *zhp, void *data) 1211cea05afSdougm { 1221cea05afSdougm get_all_cbdata_t *cbp = data; 123*a3351425Sdougm zfs_type_t type = zfs_get_type(zhp); 1241cea05afSdougm 1251cea05afSdougm /* 126*a3351425Sdougm * Interate over any nested datasets. 1271cea05afSdougm */ 128*a3351425Sdougm if (type == ZFS_TYPE_FILESYSTEM && 129*a3351425Sdougm zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) { 130*a3351425Sdougm zfs_close(zhp); 131*a3351425Sdougm return (1); 132*a3351425Sdougm } 133*a3351425Sdougm 134*a3351425Sdougm /* 135*a3351425Sdougm * Skip any datasets whose type does not match. 136*a3351425Sdougm */ 137*a3351425Sdougm if ((type & cbp->cb_types) == 0) { 1381cea05afSdougm zfs_close(zhp); 1391cea05afSdougm return (0); 1401cea05afSdougm } 1411cea05afSdougm 1421cea05afSdougm if (cbp->cb_alloc == cbp->cb_used) { 1431cea05afSdougm zfs_handle_t **handles; 1441cea05afSdougm 1451cea05afSdougm if (cbp->cb_alloc == 0) 1461cea05afSdougm cbp->cb_alloc = 64; 1471cea05afSdougm else 1481cea05afSdougm cbp->cb_alloc *= 2; 1491cea05afSdougm 150*a3351425Sdougm handles = (zfs_handle_t **)calloc(1, 151*a3351425Sdougm cbp->cb_alloc * sizeof (void *)); 152*a3351425Sdougm 1531cea05afSdougm if (handles == NULL) { 154*a3351425Sdougm zfs_close(zhp); 15557b448deSdougm return (0); 1561cea05afSdougm } 1571cea05afSdougm if (cbp->cb_handles) { 158*a3351425Sdougm bcopy(cbp->cb_handles, handles, 1591cea05afSdougm cbp->cb_used * sizeof (void *)); 1601cea05afSdougm free(cbp->cb_handles); 1611cea05afSdougm } 1621cea05afSdougm 1631cea05afSdougm cbp->cb_handles = handles; 1641cea05afSdougm } 1651cea05afSdougm 1661cea05afSdougm cbp->cb_handles[cbp->cb_used++] = zhp; 1671cea05afSdougm 168*a3351425Sdougm return (0); 1691cea05afSdougm } 1701cea05afSdougm 1711cea05afSdougm /* 1721cea05afSdougm * get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 1731cea05afSdougm * 1741cea05afSdougm * iterate through all ZFS file systems starting at the root. Returns 1751cea05afSdougm * a count and an array of handle pointers. Allocating is only done 1761cea05afSdougm * once. The caller does not need to free since it will be done at 1771cea05afSdougm * sa_zfs_fini() time. 1781cea05afSdougm */ 1791cea05afSdougm 1801cea05afSdougm static void 181549ec3ffSdougm get_all_filesystems(sa_handle_impl_t impl_handle, 182549ec3ffSdougm zfs_handle_t ***fslist, size_t *count) 1831cea05afSdougm { 1841cea05afSdougm get_all_cbdata_t cb = { 0 }; 185*a3351425Sdougm cb.cb_types = ZFS_TYPE_FILESYSTEM; 1861cea05afSdougm 187549ec3ffSdougm if (impl_handle->zfs_list != NULL) { 18857b448deSdougm *fslist = impl_handle->zfs_list; 18957b448deSdougm *count = impl_handle->zfs_list_count; 19057b448deSdougm return; 1911cea05afSdougm } 1921cea05afSdougm 193549ec3ffSdougm (void) zfs_iter_root(impl_handle->zfs_libhandle, 19457b448deSdougm get_one_filesystem, &cb); 1951cea05afSdougm 196549ec3ffSdougm impl_handle->zfs_list = *fslist = cb.cb_handles; 197549ec3ffSdougm impl_handle->zfs_list_count = *count = cb.cb_used; 1981cea05afSdougm } 1991cea05afSdougm 2006185db85Sdougm /* 2011cea05afSdougm * mountpoint_compare(a, b) 2021cea05afSdougm * 2031cea05afSdougm * compares the mountpoint on two zfs file systems handles. 2041cea05afSdougm * returns values following strcmp() model. 2056185db85Sdougm */ 2066185db85Sdougm 2071cea05afSdougm static int 2081cea05afSdougm mountpoint_compare(const void *a, const void *b) 2091cea05afSdougm { 2101cea05afSdougm zfs_handle_t **za = (zfs_handle_t **)a; 2111cea05afSdougm zfs_handle_t **zb = (zfs_handle_t **)b; 2121cea05afSdougm char mounta[MAXPATHLEN]; 2131cea05afSdougm char mountb[MAXPATHLEN]; 2141cea05afSdougm 2151cea05afSdougm verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 2161cea05afSdougm sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 2171cea05afSdougm verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 2181cea05afSdougm sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 2191cea05afSdougm 2201cea05afSdougm return (strcmp(mounta, mountb)); 2211cea05afSdougm } 2221cea05afSdougm 2236185db85Sdougm /* 224549ec3ffSdougm * get_zfs_dataset(impl_handle, path) 2256185db85Sdougm * 2266185db85Sdougm * get the name of the ZFS dataset the path is equivalent to. The 2276185db85Sdougm * dataset name is used for get/set of ZFS properties since libzfs 2286185db85Sdougm * requires a dataset to do a zfs_open(). 2296185db85Sdougm */ 2306185db85Sdougm 2316185db85Sdougm static char * 232549ec3ffSdougm get_zfs_dataset(sa_handle_impl_t impl_handle, char *path) 2336185db85Sdougm { 2341cea05afSdougm size_t i, count = 0; 2356185db85Sdougm char *dataset = NULL; 2361cea05afSdougm zfs_handle_t **zlist; 2371cea05afSdougm char mountpoint[ZFS_MAXPROPLEN]; 23867331909Sdougm char canmount[ZFS_MAXPROPLEN]; 2396185db85Sdougm 240549ec3ffSdougm get_all_filesystems(impl_handle, &zlist, &count); 2411cea05afSdougm qsort(zlist, count, sizeof (void *), mountpoint_compare); 2421cea05afSdougm for (i = 0; i < count; i++) { 24357b448deSdougm /* must have a mountpoint */ 24457b448deSdougm if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint, 24557b448deSdougm sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 24657b448deSdougm /* no mountpoint */ 24757b448deSdougm continue; 24857b448deSdougm } 24957b448deSdougm 25057b448deSdougm /* mountpoint must be a path */ 25157b448deSdougm if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 25257b448deSdougm strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 25357b448deSdougm continue; 25457b448deSdougm 25557b448deSdougm /* canmount must be set */ 25657b448deSdougm canmount[0] = '\0'; 25757b448deSdougm if (!zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount, 25867331909Sdougm sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 || 25957b448deSdougm strcmp(canmount, "off") == 0) 26057b448deSdougm continue; 2611cea05afSdougm 26257b448deSdougm /* 26357b448deSdougm * have a mountable handle but want to skip those marked none 26457b448deSdougm * and legacy 26557b448deSdougm */ 26657b448deSdougm if (strcmp(mountpoint, path) == 0) { 26757b448deSdougm dataset = (char *)zfs_get_name(zlist[i]); 26857b448deSdougm break; 26957b448deSdougm } 2701cea05afSdougm 2716185db85Sdougm } 2721cea05afSdougm 27357b448deSdougm if (dataset != NULL) 27457b448deSdougm dataset = strdup(dataset); 27557b448deSdougm 2766185db85Sdougm return (dataset); 2776185db85Sdougm } 2786185db85Sdougm 2796185db85Sdougm /* 2806185db85Sdougm * get_zfs_property(dataset, property) 2816185db85Sdougm * 2826185db85Sdougm * Get the file system property specified from the ZFS dataset. 2836185db85Sdougm */ 2846185db85Sdougm 2856185db85Sdougm static char * 2866185db85Sdougm get_zfs_property(char *dataset, zfs_prop_t property) 2876185db85Sdougm { 2886185db85Sdougm zfs_handle_t *handle = NULL; 2896185db85Sdougm char shareopts[ZFS_MAXPROPLEN]; 2906185db85Sdougm libzfs_handle_t *libhandle; 2916185db85Sdougm 2926185db85Sdougm libhandle = libzfs_init(); 2936185db85Sdougm if (libhandle != NULL) { 29457b448deSdougm handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM); 29557b448deSdougm if (handle != NULL) { 29657b448deSdougm if (zfs_prop_get(handle, property, shareopts, 29757b448deSdougm sizeof (shareopts), NULL, NULL, 0, 29857b448deSdougm B_FALSE) == 0) { 29957b448deSdougm zfs_close(handle); 30057b448deSdougm libzfs_fini(libhandle); 30157b448deSdougm return (strdup(shareopts)); 30257b448deSdougm } 30357b448deSdougm zfs_close(handle); 3046185db85Sdougm } 30557b448deSdougm libzfs_fini(libhandle); 3066185db85Sdougm } 3076185db85Sdougm return (NULL); 3086185db85Sdougm } 3096185db85Sdougm 3106185db85Sdougm /* 311549ec3ffSdougm * sa_zfs_is_shared(handle, path) 3126185db85Sdougm * 3136185db85Sdougm * Check to see if the ZFS path provided has the sharenfs option set 3146185db85Sdougm * or not. 3156185db85Sdougm */ 3166185db85Sdougm 3176185db85Sdougm int 318549ec3ffSdougm sa_zfs_is_shared(sa_handle_t sahandle, char *path) 3196185db85Sdougm { 3206185db85Sdougm int ret = 0; 3216185db85Sdougm char *dataset; 3226185db85Sdougm zfs_handle_t *handle = NULL; 3236185db85Sdougm char shareopts[ZFS_MAXPROPLEN]; 3246185db85Sdougm libzfs_handle_t *libhandle; 3256185db85Sdougm 326549ec3ffSdougm dataset = get_zfs_dataset((sa_handle_t)sahandle, path); 3276185db85Sdougm if (dataset != NULL) { 32857b448deSdougm libhandle = libzfs_init(); 32957b448deSdougm if (libhandle != NULL) { 33057b448deSdougm handle = zfs_open(libhandle, dataset, 33157b448deSdougm ZFS_TYPE_FILESYSTEM); 33257b448deSdougm if (handle != NULL) { 33357b448deSdougm if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, 33457b448deSdougm shareopts, sizeof (shareopts), NULL, NULL, 33557b448deSdougm 0, B_FALSE) == 0 && 33657b448deSdougm strcmp(shareopts, "off") != 0) { 33757b448deSdougm ret = 1; /* it is shared */ 33857b448deSdougm } 33957b448deSdougm zfs_close(handle); 34057b448deSdougm } 34157b448deSdougm libzfs_fini(libhandle); 3426185db85Sdougm } 34357b448deSdougm free(dataset); 3446185db85Sdougm } 3456185db85Sdougm return (ret); 3466185db85Sdougm } 3476185db85Sdougm 3486185db85Sdougm /* 3496185db85Sdougm * find_or_create_group(groupname, proto, *err) 3506185db85Sdougm * 3516185db85Sdougm * While walking the ZFS tree, we need to add shares to a defined 3526185db85Sdougm * group. If the group doesn't exist, create it first, making sure it 3536185db85Sdougm * is marked as a ZFS group. 3546185db85Sdougm * 35593a6f655Sdougm * Note that all ZFS shares are in a subgroup of the top level group 35693a6f655Sdougm * called "zfs". 3576185db85Sdougm */ 3586185db85Sdougm 3596185db85Sdougm static sa_group_t 360549ec3ffSdougm find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err) 3616185db85Sdougm { 3626185db85Sdougm sa_group_t group; 3636185db85Sdougm sa_optionset_t optionset; 3646185db85Sdougm int ret = SA_OK; 3656185db85Sdougm 3666185db85Sdougm /* 3676185db85Sdougm * we check to see if the "zfs" group exists. Since this 3686185db85Sdougm * should be the top level group, we don't want the 3696185db85Sdougm * parent. This is to make sure the zfs group has been created 3706185db85Sdougm * and to created if it hasn't been. 3716185db85Sdougm */ 372549ec3ffSdougm group = sa_get_group(handle, groupname); 3736185db85Sdougm if (group == NULL) { 37457b448deSdougm group = sa_create_group(handle, groupname, &ret); 37593a6f655Sdougm 37657b448deSdougm /* make sure this is flagged as a ZFS group */ 37757b448deSdougm if (group != NULL) 37857b448deSdougm ret = sa_set_group_attr(group, "zfs", "true"); 3796185db85Sdougm } 3806185db85Sdougm if (group != NULL) { 38157b448deSdougm if (proto != NULL) { 38257b448deSdougm optionset = sa_get_optionset(group, proto); 38357b448deSdougm if (optionset == NULL) { 38457b448deSdougm optionset = sa_create_optionset(group, proto); 38557b448deSdougm } else { 38657b448deSdougm char **protolist; 38757b448deSdougm int numprotos, i; 38857b448deSdougm numprotos = sa_get_protocols(&protolist); 38957b448deSdougm for (i = 0; i < numprotos; i++) { 39057b448deSdougm optionset = sa_create_optionset(group, 39157b448deSdougm protolist[i]); 39257b448deSdougm } 39357b448deSdougm if (protolist != NULL) 39457b448deSdougm free(protolist); 39557b448deSdougm } 3966185db85Sdougm } 3976185db85Sdougm } 3986185db85Sdougm if (err != NULL) 39957b448deSdougm *err = ret; 4006185db85Sdougm return (group); 4016185db85Sdougm } 4026185db85Sdougm 40393a6f655Sdougm /* 40493a6f655Sdougm * find_or_create_zfs_subgroup(groupname, optstring, *err) 40593a6f655Sdougm * 40693a6f655Sdougm * ZFS shares will be in a subgroup of the "zfs" master group. This 40793a6f655Sdougm * function looks to see if the groupname exists and returns it if it 40893a6f655Sdougm * does or else creates a new one with the specified name and returns 40993a6f655Sdougm * that. The "zfs" group will exist before we get here, but we make 41093a6f655Sdougm * sure just in case. 41193a6f655Sdougm * 41293a6f655Sdougm * err must be a valid pointer. 41393a6f655Sdougm */ 41493a6f655Sdougm 41593a6f655Sdougm static sa_group_t 416549ec3ffSdougm find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, 417549ec3ffSdougm char *optstring, int *err) 41893a6f655Sdougm { 41993a6f655Sdougm sa_group_t group = NULL; 42093a6f655Sdougm sa_group_t zfs; 42193a6f655Sdougm char *name; 42293a6f655Sdougm char *options; 42393a6f655Sdougm 42493a6f655Sdougm /* start with the top-level "zfs" group */ 425549ec3ffSdougm zfs = sa_get_group(handle, "zfs"); 42693a6f655Sdougm *err = SA_OK; 42793a6f655Sdougm if (zfs != NULL) { 42857b448deSdougm for (group = sa_get_sub_group(zfs); group != NULL; 42957b448deSdougm group = sa_get_next_group(group)) { 43057b448deSdougm name = sa_get_group_attr(group, "name"); 43157b448deSdougm if (name != NULL && strcmp(name, groupname) == 0) { 43257b448deSdougm /* have the group so break out of here */ 43357b448deSdougm sa_free_attr_string(name); 43457b448deSdougm break; 43557b448deSdougm } 43657b448deSdougm if (name != NULL) 43757b448deSdougm sa_free_attr_string(name); 43893a6f655Sdougm } 43957b448deSdougm 44057b448deSdougm if (group == NULL) { 44157b448deSdougm /* 44257b448deSdougm * need to create the sub-group since it doesn't exist 44357b448deSdougm */ 44457b448deSdougm group = _sa_create_zfs_group(zfs, groupname); 44557b448deSdougm if (group != NULL) 44657b448deSdougm set_node_attr(group, "zfs", "true"); 44757b448deSdougm if (strcmp(optstring, "on") == 0) 44857b448deSdougm optstring = "rw"; 44957b448deSdougm if (group != NULL) { 45057b448deSdougm options = strdup(optstring); 45157b448deSdougm if (options != NULL) { 45257b448deSdougm *err = sa_parse_legacy_options(group, 45357b448deSdougm options, "nfs"); 45457b448deSdougm free(options); 45557b448deSdougm } else { 45657b448deSdougm *err = SA_NO_MEMORY; 45757b448deSdougm } 45857b448deSdougm } 45993a6f655Sdougm } 46093a6f655Sdougm } 46193a6f655Sdougm return (group); 46293a6f655Sdougm } 46393a6f655Sdougm 46457b448deSdougm /* 46557b448deSdougm * zfs_inherited(handle, source, sourcestr) 46657b448deSdougm * 46757b448deSdougm * handle case of inherited sharenfs. Pulled out of sa_get_zfs_shares 46857b448deSdougm * for readability. 46957b448deSdougm */ 47057b448deSdougm static int 47157b448deSdougm zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr, 47257b448deSdougm char *shareopts, char *mountpoint) 47357b448deSdougm { 47457b448deSdougm int doshopt = 0; 47557b448deSdougm int err = SA_OK; 47657b448deSdougm sa_group_t group; 47757b448deSdougm 47857b448deSdougm /* 47957b448deSdougm * Need to find the "real" parent sub-group. It may not be 48057b448deSdougm * mounted, but it was identified in the "sourcestr" 48157b448deSdougm * variable. The real parent not mounted can occur if 48257b448deSdougm * "canmount=off and sharenfs=on". 48357b448deSdougm */ 48457b448deSdougm group = find_or_create_zfs_subgroup(handle, sourcestr, shareopts, 48557b448deSdougm &doshopt); 48657b448deSdougm if (group != NULL) { 48757b448deSdougm share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, 48857b448deSdougm &err); 48957b448deSdougm /* 49057b448deSdougm * some options may only be on shares. If the opt 49157b448deSdougm * string contains one of those, we put it just on the 49257b448deSdougm * share. 49357b448deSdougm */ 49457b448deSdougm if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) { 49557b448deSdougm char *options; 49657b448deSdougm options = strdup(shareopts); 49757b448deSdougm if (options != NULL) { 49857b448deSdougm err = sa_parse_legacy_options(share, options, 49957b448deSdougm "nfs"); 50057b448deSdougm free(options); 50157b448deSdougm } 50257b448deSdougm } 50357b448deSdougm } else { 50457b448deSdougm err = SA_NO_MEMORY; 50557b448deSdougm } 50657b448deSdougm return (err); 50757b448deSdougm } 50857b448deSdougm 50957b448deSdougm /* 51057b448deSdougm * zfs_notinherited() 51157b448deSdougm * 51257b448deSdougm * handle case where this is the top of a sub-group in ZFS. Pulled out 51357b448deSdougm * of sa_get_zfs_shares for readability. 51457b448deSdougm */ 51557b448deSdougm static int 51657b448deSdougm zfs_notinherited(sa_group_t group, char *mountpoint, char *shareopts) 51757b448deSdougm { 51857b448deSdougm int err = SA_OK; 51957b448deSdougm sa_share_t share; 52057b448deSdougm 52157b448deSdougm set_node_attr(group, "zfs", "true"); 52257b448deSdougm share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, &err); 52357b448deSdougm if (err == SA_OK) { 52457b448deSdougm if (strcmp(shareopts, "on") != 0) { 52557b448deSdougm char *options; 52657b448deSdougm options = strdup(shareopts); 52757b448deSdougm if (options != NULL) { 52857b448deSdougm err = sa_parse_legacy_options(group, options, 52957b448deSdougm "nfs"); 53057b448deSdougm free(options); 53157b448deSdougm } 53257b448deSdougm if (err == SA_PROP_SHARE_ONLY) { 53357b448deSdougm /* 53457b448deSdougm * Same as above, some properties may 53557b448deSdougm * only be on shares, but due to the 53657b448deSdougm * ZFS sub-groups being artificial, we 53757b448deSdougm * sometimes get this and have to deal 53857b448deSdougm * with it. We do it by attempting to 53957b448deSdougm * put it on the share. 54057b448deSdougm */ 54157b448deSdougm options = strdup(shareopts); 542*a3351425Sdougm if (options != NULL) { 54357b448deSdougm err = sa_parse_legacy_options(share, 54457b448deSdougm options, "nfs"); 54557b448deSdougm free(options); 546*a3351425Sdougm } 54757b448deSdougm } 54857b448deSdougm /* unmark the share's changed state */ 54957b448deSdougm set_node_attr(share, "changed", NULL); 55057b448deSdougm } 55157b448deSdougm } 55257b448deSdougm return (err); 55357b448deSdougm } 55457b448deSdougm 55557b448deSdougm /* 55657b448deSdougm * zfs_grp_error(err) 55757b448deSdougm * 55857b448deSdougm * Print group create error, but only once. If err is 0 do the 55957b448deSdougm * print else don't. 56057b448deSdougm */ 56157b448deSdougm 56257b448deSdougm static void 56357b448deSdougm zfs_grp_error(int err) 56457b448deSdougm { 56557b448deSdougm if (err == 0) { 56657b448deSdougm /* only print error once */ 56757b448deSdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 56857b448deSdougm "Cannot create ZFS subgroup during initialization:" 56957b448deSdougm " %s\n"), sa_errorstr(SA_SYSTEM_ERR)); 57057b448deSdougm } 57157b448deSdougm } 57257b448deSdougm 5736185db85Sdougm /* 574549ec3ffSdougm * sa_get_zfs_shares(handle, groupname) 5756185db85Sdougm * 5766185db85Sdougm * Walk the mnttab for all zfs mounts and determine which are 5776185db85Sdougm * shared. Find or create the appropriate group/sub-group to contain 5786185db85Sdougm * the shares. 5796185db85Sdougm * 5806185db85Sdougm * All shares are in a sub-group that will hold the properties. This 5816185db85Sdougm * allows representing the inherited property model. 5826185db85Sdougm */ 5836185db85Sdougm 5846185db85Sdougm int 585549ec3ffSdougm sa_get_zfs_shares(sa_handle_t handle, char *groupname) 5866185db85Sdougm { 5876185db85Sdougm sa_group_t group; 5886185db85Sdougm sa_group_t zfsgroup; 5896185db85Sdougm int legacy = 0; 5906185db85Sdougm int err; 5911cea05afSdougm zfs_handle_t **zlist; 5926185db85Sdougm char shareopts[ZFS_MAXPROPLEN]; 5936185db85Sdougm sa_share_t share; 5946185db85Sdougm zfs_source_t source; 5956185db85Sdougm char sourcestr[ZFS_MAXPROPLEN]; 5961cea05afSdougm char mountpoint[ZFS_MAXPROPLEN]; 5971cea05afSdougm size_t count = 0, i; 598549ec3ffSdougm libzfs_handle_t *zfs_libhandle; 5996185db85Sdougm 6006185db85Sdougm /* 601549ec3ffSdougm * If we can't access libzfs, don't bother doing anything. 6026185db85Sdougm */ 603549ec3ffSdougm zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle; 6041cea05afSdougm if (zfs_libhandle == NULL) 60557b448deSdougm return (SA_SYSTEM_ERR); 6066185db85Sdougm 607549ec3ffSdougm zfsgroup = find_or_create_group(handle, groupname, "nfs", &err); 6086185db85Sdougm if (zfsgroup != NULL) { 6096185db85Sdougm /* 6106185db85Sdougm * need to walk the mounted ZFS pools and datasets to 6116185db85Sdougm * find shares that are possible. 6126185db85Sdougm */ 61357b448deSdougm get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count); 61457b448deSdougm qsort(zlist, count, sizeof (void *), mountpoint_compare); 61557b448deSdougm 61657b448deSdougm group = zfsgroup; 61757b448deSdougm for (i = 0; i < count; i++) { 61857b448deSdougm char *dataset; 61957b448deSdougm 62057b448deSdougm source = ZFS_SRC_ALL; 62157b448deSdougm if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, 62257b448deSdougm mountpoint, sizeof (mountpoint), NULL, NULL, 0, 62357b448deSdougm B_FALSE) != 0) { 62457b448deSdougm /* no mountpoint */ 62557b448deSdougm continue; 62657b448deSdougm } 6271cea05afSdougm 6281cea05afSdougm /* 62957b448deSdougm * zfs_get_name value must not be freed. It is just a 63057b448deSdougm * pointer to a value in the handle. 6311cea05afSdougm */ 63257b448deSdougm if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL) 63357b448deSdougm continue; 63457b448deSdougm 6351cea05afSdougm /* 63657b448deSdougm * only deal with "mounted" file systems since 63757b448deSdougm * unmounted file systems can't actually be shared. 6381cea05afSdougm */ 63957b448deSdougm 64057b448deSdougm if (!zfs_is_mounted(zlist[i], NULL)) 6411cea05afSdougm continue; 64257b448deSdougm 64357b448deSdougm if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts, 64457b448deSdougm sizeof (shareopts), &source, sourcestr, 64557b448deSdougm ZFS_MAXPROPLEN, B_FALSE) == 0 && 64657b448deSdougm strcmp(shareopts, "off") != 0) { 64757b448deSdougm /* it is shared so add to list */ 64857b448deSdougm share = sa_find_share(handle, mountpoint); 64957b448deSdougm err = SA_OK; 65057b448deSdougm if (share != NULL) { 65193a6f655Sdougm /* 65257b448deSdougm * A zfs file system had been shared 65357b448deSdougm * through traditional methods 65457b448deSdougm * (share/dfstab or added to a non-zfs 65557b448deSdougm * group. Now it has been added to a 65657b448deSdougm * ZFS group via the zfs 65757b448deSdougm * command. Remove from previous 65857b448deSdougm * config and setup with current 65957b448deSdougm * options. 66093a6f655Sdougm */ 66157b448deSdougm err = sa_remove_share(share); 66257b448deSdougm share = NULL; 66357b448deSdougm } 66457b448deSdougm if (err == SA_OK) { 66557b448deSdougm if (source & ZFS_SRC_INHERITED) { 66657b448deSdougm err = zfs_inherited(handle, 66757b448deSdougm share, sourcestr, 66857b448deSdougm shareopts, mountpoint); 66957b448deSdougm } else { 67057b448deSdougm group = _sa_create_zfs_group( 67157b448deSdougm zfsgroup, dataset); 67257b448deSdougm if (group == NULL) { 67357b448deSdougm static int err = 0; 674*a3351425Sdougm /* 675*a3351425Sdougm * there is a problem, 676*a3351425Sdougm * but we can't do 677*a3351425Sdougm * anything about it 678*a3351425Sdougm * at this point so we 679*a3351425Sdougm * issue a warning an 680*a3351425Sdougm * move on. 681*a3351425Sdougm */ 68257b448deSdougm zfs_grp_error(err); 68357b448deSdougm err = 1; 68457b448deSdougm continue; 68557b448deSdougm } 68657b448deSdougm set_node_attr(group, "zfs", 68757b448deSdougm "true"); 68857b448deSdougm share = _sa_add_share(group, 68957b448deSdougm mountpoint, 69057b448deSdougm SA_SHARE_TRANSIENT, &err); 69157b448deSdougm err = zfs_notinherited(group, 69257b448deSdougm mountpoint, shareopts); 69393a6f655Sdougm } 6946185db85Sdougm } 6956185db85Sdougm } 6966185db85Sdougm } 6976185db85Sdougm } 6981cea05afSdougm /* 6991cea05afSdougm * Don't need to free the "zlist" variable since it is only a 7001cea05afSdougm * pointer to a cached value that will be freed when 7011cea05afSdougm * sa_fini() is called. 7021cea05afSdougm */ 7036185db85Sdougm return (legacy); 7046185db85Sdougm } 7056185db85Sdougm 7066185db85Sdougm #define COMMAND "/usr/sbin/zfs" 7076185db85Sdougm 7086185db85Sdougm /* 7096185db85Sdougm * sa_zfs_set_sharenfs(group, path, on) 7106185db85Sdougm * 7116185db85Sdougm * Update the "sharenfs" property on the path. If on is true, then set 7126185db85Sdougm * to the properties on the group or "on" if no properties are 7136185db85Sdougm * defined. Set to "off" if on is false. 7146185db85Sdougm */ 7156185db85Sdougm 7166185db85Sdougm int 7176185db85Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on) 7186185db85Sdougm { 7196185db85Sdougm int ret = SA_NOT_IMPLEMENTED; 7206185db85Sdougm char *command; 7216185db85Sdougm 7226185db85Sdougm command = malloc(ZFS_MAXPROPLEN * 2); 7236185db85Sdougm if (command != NULL) { 72457b448deSdougm char *opts = NULL; 72557b448deSdougm char *dataset = NULL; 72657b448deSdougm FILE *pfile; 72757b448deSdougm sa_handle_impl_t impl_handle; 72857b448deSdougm /* for now, NFS is always available for "zfs" */ 72957b448deSdougm if (on) { 73057b448deSdougm opts = sa_proto_legacy_format("nfs", group, 1); 73157b448deSdougm if (opts != NULL && strlen(opts) == 0) { 73257b448deSdougm free(opts); 73357b448deSdougm opts = strdup("on"); 73457b448deSdougm } 7356185db85Sdougm } 73657b448deSdougm 73757b448deSdougm impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 73857b448deSdougm assert(impl_handle != NULL); 73957b448deSdougm if (impl_handle != NULL) 74057b448deSdougm dataset = get_zfs_dataset(impl_handle, path); 74157b448deSdougm else 7426185db85Sdougm ret = SA_SYSTEM_ERR; 74357b448deSdougm 74457b448deSdougm if (dataset != NULL) { 74557b448deSdougm (void) snprintf(command, ZFS_MAXPROPLEN * 2, 74657b448deSdougm "%s set sharenfs=\"%s\" %s", COMMAND, 74757b448deSdougm opts != NULL ? opts : "off", dataset); 74857b448deSdougm pfile = popen(command, "r"); 74957b448deSdougm if (pfile != NULL) { 75057b448deSdougm ret = pclose(pfile); 75157b448deSdougm if (ret != 0) 75257b448deSdougm ret = SA_SYSTEM_ERR; 75357b448deSdougm } 7546185db85Sdougm } 75557b448deSdougm if (opts != NULL) 75657b448deSdougm free(opts); 75757b448deSdougm if (dataset != NULL) 75857b448deSdougm free(dataset); 75957b448deSdougm free(command); 7606185db85Sdougm } 7616185db85Sdougm return (ret); 7626185db85Sdougm } 7636185db85Sdougm 7646185db85Sdougm /* 7656185db85Sdougm * sa_zfs_update(group) 7666185db85Sdougm * 7676185db85Sdougm * call back to ZFS to update the share if necessary. 7686185db85Sdougm * Don't do it if it isn't a real change. 7696185db85Sdougm */ 7706185db85Sdougm int 7716185db85Sdougm sa_zfs_update(sa_group_t group) 7726185db85Sdougm { 7736185db85Sdougm sa_optionset_t protopt; 7746185db85Sdougm sa_group_t parent; 7756185db85Sdougm char *command; 7766185db85Sdougm char *optstring; 7776185db85Sdougm int ret = SA_OK; 7786185db85Sdougm int doupdate = 0; 7796185db85Sdougm FILE *pfile; 7806185db85Sdougm 7816185db85Sdougm if (sa_is_share(group)) 78257b448deSdougm parent = sa_get_parent_group(group); 7836185db85Sdougm else 78457b448deSdougm parent = group; 7856185db85Sdougm 7866185db85Sdougm if (parent != NULL) { 78757b448deSdougm command = malloc(ZFS_MAXPROPLEN * 2); 78857b448deSdougm if (command == NULL) 78957b448deSdougm return (SA_NO_MEMORY); 79057b448deSdougm 79157b448deSdougm *command = '\0'; 79257b448deSdougm for (protopt = sa_get_optionset(parent, NULL); protopt != NULL; 79357b448deSdougm protopt = sa_get_next_optionset(protopt)) { 79457b448deSdougm 79557b448deSdougm char *proto = sa_get_optionset_attr(protopt, "type"); 79657b448deSdougm char *path; 79757b448deSdougm char *dataset = NULL; 79857b448deSdougm char *zfsopts = NULL; 79957b448deSdougm 80057b448deSdougm if (sa_is_share(group)) { 80157b448deSdougm path = sa_get_share_attr((sa_share_t)group, 80257b448deSdougm "path"); 80357b448deSdougm if (path != NULL) { 80457b448deSdougm sa_handle_impl_t impl_handle; 80557b448deSdougm 80657b448deSdougm impl_handle = sa_find_group_handle( 80757b448deSdougm group); 80857b448deSdougm if (impl_handle != NULL) 80957b448deSdougm dataset = get_zfs_dataset( 81057b448deSdougm impl_handle, path); 81157b448deSdougm else 81257b448deSdougm ret = SA_SYSTEM_ERR; 81357b448deSdougm 81457b448deSdougm sa_free_attr_string(path); 81557b448deSdougm } 8166185db85Sdougm } else { 81757b448deSdougm dataset = sa_get_group_attr(group, "name"); 8186185db85Sdougm } 81957b448deSdougm /* update only when there is an optstring found */ 82057b448deSdougm doupdate = 0; 82157b448deSdougm if (proto != NULL && dataset != NULL) { 82257b448deSdougm optstring = sa_proto_legacy_format(proto, 82357b448deSdougm group, 1); 82457b448deSdougm zfsopts = get_zfs_property(dataset, 82557b448deSdougm ZFS_PROP_SHARENFS); 82657b448deSdougm 82757b448deSdougm if (optstring != NULL && zfsopts != NULL) { 82857b448deSdougm if (strcmp(optstring, zfsopts) != 0) 82957b448deSdougm doupdate++; 83057b448deSdougm } 83157b448deSdougm 83257b448deSdougm if (doupdate) { 83357b448deSdougm if (optstring != NULL && 83457b448deSdougm strlen(optstring) > 0) { 83557b448deSdougm (void) snprintf(command, 83657b448deSdougm ZFS_MAXPROPLEN * 2, 83757b448deSdougm "%s set sharenfs=%s %s" 83857b448deSdougm COMMAND, 83957b448deSdougm optstring, dataset); 84057b448deSdougm } else { 84157b448deSdougm (void) snprintf(command, 84257b448deSdougm ZFS_MAXPROPLEN * 2, 84357b448deSdougm "%s set sharenfs=on %s", 84457b448deSdougm COMMAND, 84557b448deSdougm dataset); 84657b448deSdougm } 84757b448deSdougm pfile = popen(command, "r"); 84857b448deSdougm if (pfile != NULL) 84957b448deSdougm ret = pclose(pfile); 85057b448deSdougm switch (ret) { 85157b448deSdougm default: 85257b448deSdougm case 1: 85357b448deSdougm ret = SA_SYSTEM_ERR; 85457b448deSdougm break; 85557b448deSdougm case 2: 85657b448deSdougm ret = SA_SYNTAX_ERR; 85757b448deSdougm break; 85857b448deSdougm case 0: 85957b448deSdougm break; 86057b448deSdougm } 86157b448deSdougm } 86257b448deSdougm if (optstring != NULL) 86357b448deSdougm free(optstring); 86457b448deSdougm if (zfsopts != NULL) 86557b448deSdougm free(zfsopts); 8666185db85Sdougm } 86757b448deSdougm if (proto != NULL) 86857b448deSdougm sa_free_attr_string(proto); 86957b448deSdougm if (dataset != NULL) 87057b448deSdougm free(dataset); 8716185db85Sdougm } 87257b448deSdougm free(command); 8736185db85Sdougm } 8746185db85Sdougm return (ret); 8756185db85Sdougm } 8766185db85Sdougm 8776185db85Sdougm /* 8786185db85Sdougm * sa_group_is_zfs(group) 8796185db85Sdougm * 8806185db85Sdougm * Given the group, determine if the zfs attribute is set. 8816185db85Sdougm */ 8826185db85Sdougm 8836185db85Sdougm int 8846185db85Sdougm sa_group_is_zfs(sa_group_t group) 8856185db85Sdougm { 8866185db85Sdougm char *zfs; 8876185db85Sdougm int ret = 0; 8886185db85Sdougm 8896185db85Sdougm zfs = sa_get_group_attr(group, "zfs"); 8906185db85Sdougm if (zfs != NULL) { 89157b448deSdougm ret = 1; 89257b448deSdougm sa_free_attr_string(zfs); 8936185db85Sdougm } 8946185db85Sdougm return (ret); 8956185db85Sdougm } 8966185db85Sdougm 8976185db85Sdougm /* 8986185db85Sdougm * sa_path_is_zfs(path) 8996185db85Sdougm * 9006185db85Sdougm * Check to see if the file system path represents is of type "zfs". 9016185db85Sdougm */ 9026185db85Sdougm 9036185db85Sdougm int 9046185db85Sdougm sa_path_is_zfs(char *path) 9056185db85Sdougm { 9066185db85Sdougm char *fstype; 9076185db85Sdougm int ret = 0; 9086185db85Sdougm 9096185db85Sdougm fstype = sa_fstype(path); 91057b448deSdougm if (fstype != NULL && strcmp(fstype, "zfs") == 0) 91157b448deSdougm ret = 1; 9126185db85Sdougm if (fstype != NULL) 91357b448deSdougm sa_free_fstype(fstype); 9146185db85Sdougm return (ret); 9156185db85Sdougm } 916