13034Sdougm /* 23034Sdougm * CDDL HEADER START 33034Sdougm * 43034Sdougm * The contents of this file are subject to the terms of the 53034Sdougm * Common Development and Distribution License (the "License"). 63034Sdougm * You may not use this file except in compliance with the License. 73034Sdougm * 83034Sdougm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93034Sdougm * or http://www.opensolaris.org/os/licensing. 103034Sdougm * See the License for the specific language governing permissions 113034Sdougm * and limitations under the License. 123034Sdougm * 133034Sdougm * When distributing Covered Code, include this CDDL HEADER in each 143034Sdougm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153034Sdougm * If applicable, add the following below this CDDL HEADER, with the 163034Sdougm * fields enclosed by brackets "[]" replaced with your own identifying 173034Sdougm * information: Portions Copyright [yyyy] [name of copyright owner] 183034Sdougm * 193034Sdougm * CDDL HEADER END 203034Sdougm */ 213034Sdougm 223034Sdougm /* 233407Sdougm * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 243034Sdougm * Use is subject to license terms. 253034Sdougm */ 263034Sdougm 273034Sdougm #pragma ident "%Z%%M% %I% %E% SMI" 283034Sdougm 294592Smarks #include <stdio.h> 303034Sdougm #include <libzfs.h> 313034Sdougm #include <string.h> 324345Sdougm #include <strings.h> 333034Sdougm #include <libshare.h> 343034Sdougm #include "libshare_impl.h" 353218Sdougm #include <libintl.h> 364592Smarks #include <sys/mnttab.h> 374592Smarks #include <sys/mntent.h> 383034Sdougm 393034Sdougm extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *); 403034Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *); 413034Sdougm extern char *sa_fstype(char *); 423034Sdougm extern void set_node_attr(void *, char *, char *); 433034Sdougm extern int sa_is_share(void *); 443218Sdougm 453034Sdougm /* 463218Sdougm * File system specific code for ZFS. The original code was stolen 473218Sdougm * from the "zfs" command and modified to better suit this library's 483218Sdougm * usage. 493218Sdougm */ 503218Sdougm 513218Sdougm typedef struct get_all_cbdata { 523218Sdougm zfs_handle_t **cb_handles; 533218Sdougm size_t cb_alloc; 543218Sdougm size_t cb_used; 554345Sdougm uint_t cb_types; 563218Sdougm } get_all_cbdata_t; 573218Sdougm 583218Sdougm /* 593910Sdougm * sa_zfs_init(impl_handle) 603218Sdougm * 613910Sdougm * Initialize an access handle into libzfs. The handle needs to stay 623910Sdougm * around until sa_zfs_fini() in order to maintain the cache of 633910Sdougm * mounts. 643218Sdougm */ 653218Sdougm 664327Sdougm int 673910Sdougm sa_zfs_init(sa_handle_impl_t impl_handle) 683218Sdougm { 693910Sdougm impl_handle->zfs_libhandle = libzfs_init(); 704327Sdougm if (impl_handle->zfs_libhandle != NULL) { 714327Sdougm libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE); 724327Sdougm return (B_TRUE); 734327Sdougm } 744327Sdougm return (B_FALSE); 753218Sdougm } 763218Sdougm 773218Sdougm /* 783910Sdougm * sa_zfs_fini(impl_handle) 793218Sdougm * 803218Sdougm * cleanup data structures and the libzfs handle used for accessing 813218Sdougm * zfs file share info. 823218Sdougm */ 833218Sdougm 843218Sdougm void 853910Sdougm sa_zfs_fini(sa_handle_impl_t impl_handle) 863218Sdougm { 873910Sdougm if (impl_handle->zfs_libhandle != NULL) { 884327Sdougm if (impl_handle->zfs_list != NULL) { 894345Sdougm zfs_handle_t **zhp = impl_handle->zfs_list; 904345Sdougm size_t i; 914345Sdougm 924327Sdougm /* 934345Sdougm * Contents of zfs_list need to be freed so we 944345Sdougm * don't lose ZFS handles. 954327Sdougm */ 964345Sdougm for (i = 0; i < impl_handle->zfs_list_count; i++) { 974345Sdougm zfs_close(zhp[i]); 984345Sdougm } 994327Sdougm free(impl_handle->zfs_list); 1004327Sdougm impl_handle->zfs_list = NULL; 1014327Sdougm impl_handle->zfs_list_count = 0; 1024327Sdougm } 1034345Sdougm 1044345Sdougm libzfs_fini(impl_handle->zfs_libhandle); 1054345Sdougm impl_handle->zfs_libhandle = NULL; 1063218Sdougm } 1073218Sdougm } 1083218Sdougm 1093218Sdougm /* 1103218Sdougm * get_one_filesystem(zfs_handle_t, data) 1113218Sdougm * 1123218Sdougm * an interator function called while iterating through the ZFS 1133218Sdougm * root. It accumulates into an array of file system handles that can 1143218Sdougm * be used to derive info about those file systems. 1154345Sdougm * 1164345Sdougm * Note that as this function is called, we close all zhp handles that 1174345Sdougm * are not going to be places into the cp_handles list. We don't want 1184345Sdougm * to close the ones we are keeping, but all others would be leaked if 1194345Sdougm * not closed here. 1203034Sdougm */ 1213034Sdougm 1223218Sdougm static int 1233218Sdougm get_one_filesystem(zfs_handle_t *zhp, void *data) 1243218Sdougm { 1253218Sdougm get_all_cbdata_t *cbp = data; 1264345Sdougm zfs_type_t type = zfs_get_type(zhp); 1273218Sdougm 1283218Sdougm /* 1294345Sdougm * Interate over any nested datasets. 1303218Sdougm */ 1314345Sdougm if (type == ZFS_TYPE_FILESYSTEM && 1324345Sdougm zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) { 1334345Sdougm zfs_close(zhp); 1344345Sdougm return (1); 1354345Sdougm } 1364345Sdougm 1374345Sdougm /* 1384345Sdougm * Skip any datasets whose type does not match. 1394345Sdougm */ 1404345Sdougm if ((type & cbp->cb_types) == 0) { 1413218Sdougm zfs_close(zhp); 1423218Sdougm return (0); 1433218Sdougm } 1443218Sdougm 1453218Sdougm if (cbp->cb_alloc == cbp->cb_used) { 1463218Sdougm zfs_handle_t **handles; 1473218Sdougm 1483218Sdougm if (cbp->cb_alloc == 0) 1493218Sdougm cbp->cb_alloc = 64; 1503218Sdougm else 1513218Sdougm cbp->cb_alloc *= 2; 1523218Sdougm 1534345Sdougm handles = (zfs_handle_t **)calloc(1, 1544345Sdougm cbp->cb_alloc * sizeof (void *)); 1554345Sdougm 1563218Sdougm if (handles == NULL) { 1574345Sdougm zfs_close(zhp); 1584327Sdougm return (0); 1593218Sdougm } 1603218Sdougm if (cbp->cb_handles) { 1614345Sdougm bcopy(cbp->cb_handles, handles, 1623218Sdougm cbp->cb_used * sizeof (void *)); 1633218Sdougm free(cbp->cb_handles); 1643218Sdougm } 1653218Sdougm 1663218Sdougm cbp->cb_handles = handles; 1673218Sdougm } 1683218Sdougm 1693218Sdougm cbp->cb_handles[cbp->cb_used++] = zhp; 1703218Sdougm 1714345Sdougm return (0); 1723218Sdougm } 1733218Sdougm 1743218Sdougm /* 1753218Sdougm * get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 1763218Sdougm * 1773218Sdougm * iterate through all ZFS file systems starting at the root. Returns 1783218Sdougm * a count and an array of handle pointers. Allocating is only done 1793218Sdougm * once. The caller does not need to free since it will be done at 1803218Sdougm * sa_zfs_fini() time. 1813218Sdougm */ 1823218Sdougm 1833218Sdougm static void 1843910Sdougm get_all_filesystems(sa_handle_impl_t impl_handle, 1853910Sdougm zfs_handle_t ***fslist, size_t *count) 1863218Sdougm { 1873218Sdougm get_all_cbdata_t cb = { 0 }; 1884345Sdougm cb.cb_types = ZFS_TYPE_FILESYSTEM; 1893218Sdougm 1903910Sdougm if (impl_handle->zfs_list != NULL) { 1914327Sdougm *fslist = impl_handle->zfs_list; 1924327Sdougm *count = impl_handle->zfs_list_count; 1934327Sdougm return; 1943218Sdougm } 1953218Sdougm 1963910Sdougm (void) zfs_iter_root(impl_handle->zfs_libhandle, 1974327Sdougm get_one_filesystem, &cb); 1983218Sdougm 1993910Sdougm impl_handle->zfs_list = *fslist = cb.cb_handles; 2003910Sdougm impl_handle->zfs_list_count = *count = cb.cb_used; 2013218Sdougm } 2023218Sdougm 2033218Sdougm /* 2043218Sdougm * mountpoint_compare(a, b) 2053218Sdougm * 2063218Sdougm * compares the mountpoint on two zfs file systems handles. 2073218Sdougm * returns values following strcmp() model. 2083218Sdougm */ 2093218Sdougm 2103218Sdougm static int 2113218Sdougm mountpoint_compare(const void *a, const void *b) 2123218Sdougm { 2133218Sdougm zfs_handle_t **za = (zfs_handle_t **)a; 2143218Sdougm zfs_handle_t **zb = (zfs_handle_t **)b; 2153218Sdougm char mounta[MAXPATHLEN]; 2163218Sdougm char mountb[MAXPATHLEN]; 2173218Sdougm 2183218Sdougm verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 2193218Sdougm sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 2203218Sdougm verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 2213218Sdougm sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 2223218Sdougm 2233218Sdougm return (strcmp(mounta, mountb)); 2243218Sdougm } 2253218Sdougm 2263034Sdougm /* 2274592Smarks * return legacy mountpoint. Caller provides space for mountpoint. 2284592Smarks */ 2294592Smarks int 2304592Smarks get_legacy_mountpoint(char *path, char *mountpoint, size_t len) 2314592Smarks { 2324592Smarks FILE *fp; 2334592Smarks struct mnttab entry; 2344592Smarks 2354592Smarks if ((fp = fopen(MNTTAB, "r")) == NULL) { 2364592Smarks return (1); 2374592Smarks } 2384592Smarks 2394592Smarks while (getmntent(fp, &entry) == 0) { 2404592Smarks 2414592Smarks if (entry.mnt_fstype == NULL || 2424592Smarks strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2434592Smarks continue; 2444592Smarks 2454592Smarks if (strcmp(entry.mnt_mountp, path) == 0) { 2464592Smarks (void) strlcpy(mountpoint, entry.mnt_special, len); 2474592Smarks (void) fclose(fp); 2484592Smarks return (0); 2494592Smarks } 2504592Smarks } 2514592Smarks (void) fclose(fp); 2524592Smarks return (1); 2534592Smarks } 2544592Smarks 2554592Smarks /* 2563910Sdougm * get_zfs_dataset(impl_handle, path) 2573034Sdougm * 2583034Sdougm * get the name of the ZFS dataset the path is equivalent to. The 2593034Sdougm * dataset name is used for get/set of ZFS properties since libzfs 2603034Sdougm * requires a dataset to do a zfs_open(). 2613034Sdougm */ 2623034Sdougm 2633034Sdougm static char * 2644592Smarks get_zfs_dataset(sa_handle_impl_t impl_handle, char *path, 2654592Smarks boolean_t search_mnttab) 2663034Sdougm { 2673218Sdougm size_t i, count = 0; 2683034Sdougm char *dataset = NULL; 2693218Sdougm zfs_handle_t **zlist; 2703218Sdougm char mountpoint[ZFS_MAXPROPLEN]; 2714180Sdougm char canmount[ZFS_MAXPROPLEN]; 2723218Sdougm 2733910Sdougm get_all_filesystems(impl_handle, &zlist, &count); 2743218Sdougm qsort(zlist, count, sizeof (void *), mountpoint_compare); 2753218Sdougm for (i = 0; i < count; i++) { 2764327Sdougm /* must have a mountpoint */ 2774327Sdougm if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint, 2784327Sdougm sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 2794327Sdougm /* no mountpoint */ 2804327Sdougm continue; 2814327Sdougm } 2823034Sdougm 2834327Sdougm /* mountpoint must be a path */ 2844327Sdougm if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 2854592Smarks strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2864592Smarks /* 2874592Smarks * Search mmttab for mountpoint 2884592Smarks */ 2894592Smarks 2904592Smarks if (search_mnttab == B_TRUE && 2914592Smarks get_legacy_mountpoint(path, mountpoint, 2924592Smarks sizeof (mountpoint)) == 0) { 2934592Smarks dataset = mountpoint; 2944592Smarks break; 2954592Smarks } 2964327Sdougm continue; 2974592Smarks } 2983218Sdougm 2994327Sdougm /* canmount must be set */ 3004327Sdougm canmount[0] = '\0'; 3014543Smarks if (zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount, 3024180Sdougm sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 || 3034327Sdougm strcmp(canmount, "off") == 0) 3044327Sdougm continue; 3053218Sdougm 3064327Sdougm /* 3074327Sdougm * have a mountable handle but want to skip those marked none 3084327Sdougm * and legacy 3094327Sdougm */ 3104327Sdougm if (strcmp(mountpoint, path) == 0) { 3114327Sdougm dataset = (char *)zfs_get_name(zlist[i]); 3124327Sdougm break; 3134327Sdougm } 3143218Sdougm 3153034Sdougm } 3163218Sdougm 3174327Sdougm if (dataset != NULL) 3184327Sdougm dataset = strdup(dataset); 3194327Sdougm 3203034Sdougm return (dataset); 3213034Sdougm } 3223034Sdougm 3233034Sdougm /* 3243034Sdougm * get_zfs_property(dataset, property) 3253034Sdougm * 3263034Sdougm * Get the file system property specified from the ZFS dataset. 3273034Sdougm */ 3283034Sdougm 3293034Sdougm static char * 3303034Sdougm get_zfs_property(char *dataset, zfs_prop_t property) 3313034Sdougm { 3323034Sdougm zfs_handle_t *handle = NULL; 3333034Sdougm char shareopts[ZFS_MAXPROPLEN]; 3343034Sdougm libzfs_handle_t *libhandle; 3353034Sdougm 3363034Sdougm libhandle = libzfs_init(); 3373034Sdougm if (libhandle != NULL) { 3384327Sdougm handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM); 3394327Sdougm if (handle != NULL) { 3404327Sdougm if (zfs_prop_get(handle, property, shareopts, 3414327Sdougm sizeof (shareopts), NULL, NULL, 0, 3424327Sdougm B_FALSE) == 0) { 3434327Sdougm zfs_close(handle); 3444327Sdougm libzfs_fini(libhandle); 3454327Sdougm return (strdup(shareopts)); 3464327Sdougm } 3474327Sdougm zfs_close(handle); 3483034Sdougm } 3494327Sdougm libzfs_fini(libhandle); 3503034Sdougm } 3513034Sdougm return (NULL); 3523034Sdougm } 3533034Sdougm 3543034Sdougm /* 3553910Sdougm * sa_zfs_is_shared(handle, path) 3563034Sdougm * 3573034Sdougm * Check to see if the ZFS path provided has the sharenfs option set 3583034Sdougm * or not. 3593034Sdougm */ 3603034Sdougm 3613034Sdougm int 3623910Sdougm sa_zfs_is_shared(sa_handle_t sahandle, char *path) 3633034Sdougm { 3643034Sdougm int ret = 0; 3653034Sdougm char *dataset; 3663034Sdougm zfs_handle_t *handle = NULL; 3673034Sdougm char shareopts[ZFS_MAXPROPLEN]; 3683034Sdougm libzfs_handle_t *libhandle; 3693034Sdougm 3704592Smarks dataset = get_zfs_dataset((sa_handle_t)sahandle, path, B_FALSE); 3713034Sdougm if (dataset != NULL) { 3724327Sdougm libhandle = libzfs_init(); 3734327Sdougm if (libhandle != NULL) { 3744327Sdougm handle = zfs_open(libhandle, dataset, 3754327Sdougm ZFS_TYPE_FILESYSTEM); 3764327Sdougm if (handle != NULL) { 3774327Sdougm if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, 3784327Sdougm shareopts, sizeof (shareopts), NULL, NULL, 3794327Sdougm 0, B_FALSE) == 0 && 3804327Sdougm strcmp(shareopts, "off") != 0) { 3814327Sdougm ret = 1; /* it is shared */ 3824327Sdougm } 3834327Sdougm zfs_close(handle); 3844327Sdougm } 3854327Sdougm libzfs_fini(libhandle); 3863034Sdougm } 3874327Sdougm free(dataset); 3883034Sdougm } 3893034Sdougm return (ret); 3903034Sdougm } 3913034Sdougm 3923034Sdougm /* 3933034Sdougm * find_or_create_group(groupname, proto, *err) 3943034Sdougm * 3953034Sdougm * While walking the ZFS tree, we need to add shares to a defined 3963034Sdougm * group. If the group doesn't exist, create it first, making sure it 3973034Sdougm * is marked as a ZFS group. 3983034Sdougm * 3993108Sdougm * Note that all ZFS shares are in a subgroup of the top level group 4003108Sdougm * called "zfs". 4013034Sdougm */ 4023034Sdougm 4033034Sdougm static sa_group_t 4043910Sdougm find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err) 4053034Sdougm { 4063034Sdougm sa_group_t group; 4073034Sdougm sa_optionset_t optionset; 4083034Sdougm int ret = SA_OK; 4093034Sdougm 4103034Sdougm /* 4113034Sdougm * we check to see if the "zfs" group exists. Since this 4123034Sdougm * should be the top level group, we don't want the 4133034Sdougm * parent. This is to make sure the zfs group has been created 4143034Sdougm * and to created if it hasn't been. 4153034Sdougm */ 4163910Sdougm group = sa_get_group(handle, groupname); 4173034Sdougm if (group == NULL) { 4184327Sdougm group = sa_create_group(handle, groupname, &ret); 4193108Sdougm 4204327Sdougm /* make sure this is flagged as a ZFS group */ 4214327Sdougm if (group != NULL) 4224327Sdougm ret = sa_set_group_attr(group, "zfs", "true"); 4233034Sdougm } 4243034Sdougm if (group != NULL) { 4254327Sdougm if (proto != NULL) { 4264327Sdougm optionset = sa_get_optionset(group, proto); 4274327Sdougm if (optionset == NULL) { 4284327Sdougm optionset = sa_create_optionset(group, proto); 4294327Sdougm } else { 4304327Sdougm char **protolist; 4314327Sdougm int numprotos, i; 4324327Sdougm numprotos = sa_get_protocols(&protolist); 4334327Sdougm for (i = 0; i < numprotos; i++) { 4344327Sdougm optionset = sa_create_optionset(group, 4354327Sdougm protolist[i]); 4364327Sdougm } 4374327Sdougm if (protolist != NULL) 4384327Sdougm free(protolist); 4394327Sdougm } 4403034Sdougm } 4413034Sdougm } 4423034Sdougm if (err != NULL) 4434327Sdougm *err = ret; 4443034Sdougm return (group); 4453034Sdougm } 4463034Sdougm 4473034Sdougm /* 4483108Sdougm * find_or_create_zfs_subgroup(groupname, optstring, *err) 4493108Sdougm * 4503108Sdougm * ZFS shares will be in a subgroup of the "zfs" master group. This 4513108Sdougm * function looks to see if the groupname exists and returns it if it 4523108Sdougm * does or else creates a new one with the specified name and returns 4533108Sdougm * that. The "zfs" group will exist before we get here, but we make 4543108Sdougm * sure just in case. 4553108Sdougm * 4563108Sdougm * err must be a valid pointer. 4573108Sdougm */ 4583108Sdougm 4593108Sdougm static sa_group_t 4603910Sdougm find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, 4613910Sdougm char *optstring, int *err) 4623108Sdougm { 4633108Sdougm sa_group_t group = NULL; 4643108Sdougm sa_group_t zfs; 4653108Sdougm char *name; 4663108Sdougm char *options; 4673108Sdougm 4683108Sdougm /* start with the top-level "zfs" group */ 4693910Sdougm zfs = sa_get_group(handle, "zfs"); 4703108Sdougm *err = SA_OK; 4713108Sdougm if (zfs != NULL) { 4724327Sdougm for (group = sa_get_sub_group(zfs); group != NULL; 4734327Sdougm group = sa_get_next_group(group)) { 4744327Sdougm name = sa_get_group_attr(group, "name"); 4754327Sdougm if (name != NULL && strcmp(name, groupname) == 0) { 4764327Sdougm /* have the group so break out of here */ 4774327Sdougm sa_free_attr_string(name); 4784327Sdougm break; 4794327Sdougm } 4804327Sdougm if (name != NULL) 4814327Sdougm sa_free_attr_string(name); 4823108Sdougm } 4833108Sdougm 4844327Sdougm if (group == NULL) { 4854327Sdougm /* 4864327Sdougm * need to create the sub-group since it doesn't exist 4874327Sdougm */ 4884327Sdougm group = _sa_create_zfs_group(zfs, groupname); 4894327Sdougm if (group != NULL) 4904327Sdougm set_node_attr(group, "zfs", "true"); 4914327Sdougm if (strcmp(optstring, "on") == 0) 4924327Sdougm optstring = "rw"; 4934327Sdougm if (group != NULL) { 4944327Sdougm options = strdup(optstring); 4954327Sdougm if (options != NULL) { 4964327Sdougm *err = sa_parse_legacy_options(group, 4974327Sdougm options, "nfs"); 4984327Sdougm free(options); 4994327Sdougm } else { 5004327Sdougm *err = SA_NO_MEMORY; 5014327Sdougm } 5024327Sdougm } 5033108Sdougm } 5043108Sdougm } 5053108Sdougm return (group); 5063108Sdougm } 5073108Sdougm 5083108Sdougm /* 5094327Sdougm * zfs_inherited(handle, source, sourcestr) 5104327Sdougm * 5114327Sdougm * handle case of inherited sharenfs. Pulled out of sa_get_zfs_shares 5124327Sdougm * for readability. 5134327Sdougm */ 5144327Sdougm static int 5154327Sdougm zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr, 5164327Sdougm char *shareopts, char *mountpoint) 5174327Sdougm { 5184327Sdougm int doshopt = 0; 5194327Sdougm int err = SA_OK; 5204327Sdougm sa_group_t group; 5214327Sdougm 5224327Sdougm /* 5234327Sdougm * Need to find the "real" parent sub-group. It may not be 5244327Sdougm * mounted, but it was identified in the "sourcestr" 5254327Sdougm * variable. The real parent not mounted can occur if 5264327Sdougm * "canmount=off and sharenfs=on". 5274327Sdougm */ 5284327Sdougm group = find_or_create_zfs_subgroup(handle, sourcestr, shareopts, 5294327Sdougm &doshopt); 5304327Sdougm if (group != NULL) { 5314327Sdougm share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, 5324327Sdougm &err); 5334327Sdougm /* 5344327Sdougm * some options may only be on shares. If the opt 5354327Sdougm * string contains one of those, we put it just on the 5364327Sdougm * share. 5374327Sdougm */ 5384327Sdougm if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) { 5394327Sdougm char *options; 5404327Sdougm options = strdup(shareopts); 5414327Sdougm if (options != NULL) { 5424327Sdougm err = sa_parse_legacy_options(share, options, 5434327Sdougm "nfs"); 5444327Sdougm free(options); 5454327Sdougm } 5464327Sdougm } 5474327Sdougm } else { 5484327Sdougm err = SA_NO_MEMORY; 5494327Sdougm } 5504327Sdougm return (err); 5514327Sdougm } 5524327Sdougm 5534327Sdougm /* 5544327Sdougm * zfs_notinherited() 5554327Sdougm * 5564327Sdougm * handle case where this is the top of a sub-group in ZFS. Pulled out 5574327Sdougm * of sa_get_zfs_shares for readability. 5584327Sdougm */ 5594327Sdougm static int 5604327Sdougm zfs_notinherited(sa_group_t group, char *mountpoint, char *shareopts) 5614327Sdougm { 5624327Sdougm int err = SA_OK; 5634327Sdougm sa_share_t share; 5644327Sdougm 5654327Sdougm set_node_attr(group, "zfs", "true"); 5664327Sdougm share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, &err); 5674327Sdougm if (err == SA_OK) { 5684327Sdougm if (strcmp(shareopts, "on") != 0) { 5694327Sdougm char *options; 5704327Sdougm options = strdup(shareopts); 5714327Sdougm if (options != NULL) { 5724327Sdougm err = sa_parse_legacy_options(group, options, 5734327Sdougm "nfs"); 5744327Sdougm free(options); 5754327Sdougm } 5764327Sdougm if (err == SA_PROP_SHARE_ONLY) { 5774327Sdougm /* 5784327Sdougm * Same as above, some properties may 5794327Sdougm * only be on shares, but due to the 5804327Sdougm * ZFS sub-groups being artificial, we 5814327Sdougm * sometimes get this and have to deal 5824327Sdougm * with it. We do it by attempting to 5834327Sdougm * put it on the share. 5844327Sdougm */ 5854327Sdougm options = strdup(shareopts); 5864345Sdougm if (options != NULL) { 5874327Sdougm err = sa_parse_legacy_options(share, 5884327Sdougm options, "nfs"); 5894327Sdougm free(options); 5904345Sdougm } 5914327Sdougm } 5924327Sdougm /* unmark the share's changed state */ 5934327Sdougm set_node_attr(share, "changed", NULL); 5944327Sdougm } 5954327Sdougm } 5964327Sdougm return (err); 5974327Sdougm } 5984327Sdougm 5994327Sdougm /* 6004327Sdougm * zfs_grp_error(err) 6014327Sdougm * 6024327Sdougm * Print group create error, but only once. If err is 0 do the 6034327Sdougm * print else don't. 6044327Sdougm */ 6054327Sdougm 6064327Sdougm static void 6074327Sdougm zfs_grp_error(int err) 6084327Sdougm { 6094327Sdougm if (err == 0) { 6104327Sdougm /* only print error once */ 6114327Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 6124327Sdougm "Cannot create ZFS subgroup during initialization:" 6134327Sdougm " %s\n"), sa_errorstr(SA_SYSTEM_ERR)); 6144327Sdougm } 6154327Sdougm } 6164327Sdougm 6174327Sdougm /* 6183910Sdougm * sa_get_zfs_shares(handle, groupname) 6193034Sdougm * 6203034Sdougm * Walk the mnttab for all zfs mounts and determine which are 6213034Sdougm * shared. Find or create the appropriate group/sub-group to contain 6223034Sdougm * the shares. 6233034Sdougm * 6243034Sdougm * All shares are in a sub-group that will hold the properties. This 6253034Sdougm * allows representing the inherited property model. 6263034Sdougm */ 6273034Sdougm 6283034Sdougm int 6293910Sdougm sa_get_zfs_shares(sa_handle_t handle, char *groupname) 6303034Sdougm { 6313034Sdougm sa_group_t group; 6323034Sdougm sa_group_t zfsgroup; 6333034Sdougm int legacy = 0; 6343034Sdougm int err; 6353218Sdougm zfs_handle_t **zlist; 6363034Sdougm char shareopts[ZFS_MAXPROPLEN]; 6373034Sdougm sa_share_t share; 6383034Sdougm zfs_source_t source; 6393034Sdougm char sourcestr[ZFS_MAXPROPLEN]; 6403218Sdougm char mountpoint[ZFS_MAXPROPLEN]; 6413218Sdougm size_t count = 0, i; 6423910Sdougm libzfs_handle_t *zfs_libhandle; 6433034Sdougm 6443034Sdougm /* 6453910Sdougm * If we can't access libzfs, don't bother doing anything. 6463034Sdougm */ 6473910Sdougm zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle; 6483218Sdougm if (zfs_libhandle == NULL) 6494327Sdougm return (SA_SYSTEM_ERR); 6503034Sdougm 6513910Sdougm zfsgroup = find_or_create_group(handle, groupname, "nfs", &err); 6524524Sdougm if (zfsgroup == NULL) 6534524Sdougm return (legacy); 6543218Sdougm 6554524Sdougm /* 6564524Sdougm * need to walk the mounted ZFS pools and datasets to 6574524Sdougm * find shares that are possible. 6584524Sdougm */ 6594524Sdougm get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count); 6604524Sdougm qsort(zlist, count, sizeof (void *), mountpoint_compare); 6614524Sdougm 6624524Sdougm group = zfsgroup; 6634524Sdougm for (i = 0; i < count; i++) { 6644524Sdougm char *dataset; 6653218Sdougm 6664524Sdougm source = ZFS_SRC_ALL; 6674524Sdougm /* If no mountpoint, skip. */ 6684524Sdougm if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, 6694524Sdougm mountpoint, sizeof (mountpoint), NULL, NULL, 0, 6704524Sdougm B_FALSE) != 0) 6714524Sdougm continue; 6724327Sdougm 6734524Sdougm /* 6744524Sdougm * zfs_get_name value must not be freed. It is just a 6754524Sdougm * pointer to a value in the handle. 6764524Sdougm */ 6774524Sdougm if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL) 6784524Sdougm continue; 6794327Sdougm 6804524Sdougm /* 6814524Sdougm * only deal with "mounted" file systems since 6824524Sdougm * unmounted file systems can't actually be shared. 6834524Sdougm */ 6844327Sdougm 6854524Sdougm if (!zfs_is_mounted(zlist[i], NULL)) 6864524Sdougm continue; 6874327Sdougm 6884524Sdougm if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts, 6894524Sdougm sizeof (shareopts), &source, sourcestr, 6904524Sdougm ZFS_MAXPROPLEN, B_FALSE) == 0 && 6914524Sdougm strcmp(shareopts, "off") != 0) { 6924524Sdougm /* it is shared so add to list */ 6934524Sdougm share = sa_find_share(handle, mountpoint); 6944524Sdougm err = SA_OK; 6954524Sdougm if (share != NULL) { 6964524Sdougm /* 6974524Sdougm * A zfs file system had been shared 6984524Sdougm * through traditional methods 6994524Sdougm * (share/dfstab or added to a non-zfs 7004524Sdougm * group. Now it has been added to a 7014524Sdougm * ZFS group via the zfs 7024524Sdougm * command. Remove from previous 7034524Sdougm * config and setup with current 7044524Sdougm * options. 7054524Sdougm */ 7064524Sdougm err = sa_remove_share(share); 7074524Sdougm share = NULL; 7084524Sdougm } 7094524Sdougm if (err == SA_OK) { 7104524Sdougm if (source & ZFS_SRC_INHERITED) { 7114524Sdougm err = zfs_inherited(handle, 7124524Sdougm share, sourcestr, 7134524Sdougm shareopts, mountpoint); 7144524Sdougm } else { 7154524Sdougm group = _sa_create_zfs_group( 7164524Sdougm zfsgroup, dataset); 7174524Sdougm if (group == NULL) { 7184524Sdougm static int err = 0; 7194345Sdougm /* 7204345Sdougm * there is a problem, 7214345Sdougm * but we can't do 7224345Sdougm * anything about it 7234345Sdougm * at this point so we 7244345Sdougm * issue a warning an 7254345Sdougm * move on. 7264345Sdougm */ 7274524Sdougm zfs_grp_error(err); 7284524Sdougm err = 1; 7294524Sdougm continue; 7304327Sdougm } 7314524Sdougm set_node_attr(group, "zfs", 7324524Sdougm "true"); 7334524Sdougm /* 7344524Sdougm * Add share with local opts via 7354524Sdougm * zfs_notinherited. 7364524Sdougm */ 7374524Sdougm err = zfs_notinherited(group, 7384524Sdougm mountpoint, shareopts); 7393218Sdougm } 7403034Sdougm } 7413034Sdougm } 7423034Sdougm } 7433218Sdougm /* 7443218Sdougm * Don't need to free the "zlist" variable since it is only a 7453218Sdougm * pointer to a cached value that will be freed when 7463218Sdougm * sa_fini() is called. 7473218Sdougm */ 7483034Sdougm return (legacy); 7493034Sdougm } 7503034Sdougm 7513034Sdougm #define COMMAND "/usr/sbin/zfs" 7523034Sdougm 7533034Sdougm /* 7543034Sdougm * sa_zfs_set_sharenfs(group, path, on) 7553034Sdougm * 7563034Sdougm * Update the "sharenfs" property on the path. If on is true, then set 7573034Sdougm * to the properties on the group or "on" if no properties are 7583034Sdougm * defined. Set to "off" if on is false. 7593034Sdougm */ 7603034Sdougm 7613034Sdougm int 7623034Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on) 7633034Sdougm { 7643034Sdougm int ret = SA_NOT_IMPLEMENTED; 7653034Sdougm char *command; 7663034Sdougm 7673034Sdougm command = malloc(ZFS_MAXPROPLEN * 2); 7683034Sdougm if (command != NULL) { 7694327Sdougm char *opts = NULL; 7704327Sdougm char *dataset = NULL; 7714327Sdougm FILE *pfile; 7724327Sdougm sa_handle_impl_t impl_handle; 7734327Sdougm /* for now, NFS is always available for "zfs" */ 7744327Sdougm if (on) { 7754327Sdougm opts = sa_proto_legacy_format("nfs", group, 1); 7764327Sdougm if (opts != NULL && strlen(opts) == 0) { 7774327Sdougm free(opts); 7784327Sdougm opts = strdup("on"); 7794327Sdougm } 7803034Sdougm } 7813910Sdougm 7824327Sdougm impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 7834327Sdougm assert(impl_handle != NULL); 7844327Sdougm if (impl_handle != NULL) 7854592Smarks dataset = get_zfs_dataset(impl_handle, path, B_FALSE); 7864327Sdougm else 7874327Sdougm ret = SA_SYSTEM_ERR; 7883910Sdougm 7894327Sdougm if (dataset != NULL) { 7904327Sdougm (void) snprintf(command, ZFS_MAXPROPLEN * 2, 7914327Sdougm "%s set sharenfs=\"%s\" %s", COMMAND, 7924327Sdougm opts != NULL ? opts : "off", dataset); 7934327Sdougm pfile = popen(command, "r"); 7944327Sdougm if (pfile != NULL) { 7954327Sdougm ret = pclose(pfile); 7964327Sdougm if (ret != 0) 7974327Sdougm ret = SA_SYSTEM_ERR; 7984327Sdougm } 7993034Sdougm } 8004327Sdougm if (opts != NULL) 8014327Sdougm free(opts); 8024327Sdougm if (dataset != NULL) 8034327Sdougm free(dataset); 8044327Sdougm free(command); 8053034Sdougm } 8063034Sdougm return (ret); 8073034Sdougm } 8083034Sdougm 8093034Sdougm /* 8103034Sdougm * sa_zfs_update(group) 8113034Sdougm * 8123034Sdougm * call back to ZFS to update the share if necessary. 8133034Sdougm * Don't do it if it isn't a real change. 8143034Sdougm */ 8153034Sdougm int 8163034Sdougm sa_zfs_update(sa_group_t group) 8173034Sdougm { 8183034Sdougm sa_optionset_t protopt; 8193034Sdougm sa_group_t parent; 8203034Sdougm char *command; 8213034Sdougm char *optstring; 8223034Sdougm int ret = SA_OK; 8233034Sdougm int doupdate = 0; 8243034Sdougm FILE *pfile; 8253034Sdougm 8263034Sdougm if (sa_is_share(group)) 8274327Sdougm parent = sa_get_parent_group(group); 8283034Sdougm else 8294327Sdougm parent = group; 8303034Sdougm 8313034Sdougm if (parent != NULL) { 8324327Sdougm command = malloc(ZFS_MAXPROPLEN * 2); 8334327Sdougm if (command == NULL) 8344327Sdougm return (SA_NO_MEMORY); 8354327Sdougm 8364327Sdougm *command = '\0'; 8374327Sdougm for (protopt = sa_get_optionset(parent, NULL); protopt != NULL; 8384327Sdougm protopt = sa_get_next_optionset(protopt)) { 8393034Sdougm 8404327Sdougm char *proto = sa_get_optionset_attr(protopt, "type"); 8414327Sdougm char *path; 8424327Sdougm char *dataset = NULL; 8434327Sdougm char *zfsopts = NULL; 8443034Sdougm 8454327Sdougm if (sa_is_share(group)) { 8464327Sdougm path = sa_get_share_attr((sa_share_t)group, 8474327Sdougm "path"); 8484327Sdougm if (path != NULL) { 8494327Sdougm sa_handle_impl_t impl_handle; 8503034Sdougm 8514327Sdougm impl_handle = sa_find_group_handle( 8524327Sdougm group); 8534327Sdougm if (impl_handle != NULL) 8544327Sdougm dataset = get_zfs_dataset( 8554592Smarks impl_handle, path, B_FALSE); 8564327Sdougm else 8574327Sdougm ret = SA_SYSTEM_ERR; 8583910Sdougm 8594327Sdougm sa_free_attr_string(path); 8604327Sdougm } 8614327Sdougm } else { 8624327Sdougm dataset = sa_get_group_attr(group, "name"); 8634327Sdougm } 8644327Sdougm /* update only when there is an optstring found */ 8654327Sdougm doupdate = 0; 8664327Sdougm if (proto != NULL && dataset != NULL) { 8674327Sdougm optstring = sa_proto_legacy_format(proto, 8684327Sdougm group, 1); 8694327Sdougm zfsopts = get_zfs_property(dataset, 8704327Sdougm ZFS_PROP_SHARENFS); 8713034Sdougm 8724327Sdougm if (optstring != NULL && zfsopts != NULL) { 8734327Sdougm if (strcmp(optstring, zfsopts) != 0) 8744327Sdougm doupdate++; 8754327Sdougm } 8763034Sdougm 8774327Sdougm if (doupdate) { 8784327Sdougm if (optstring != NULL && 8794327Sdougm strlen(optstring) > 0) { 8804327Sdougm (void) snprintf(command, 8814327Sdougm ZFS_MAXPROPLEN * 2, 882*4942Sdougm "%s set sharenfs=%s %s", 8834327Sdougm COMMAND, 8844327Sdougm optstring, dataset); 8854327Sdougm } else { 8864327Sdougm (void) snprintf(command, 8874327Sdougm ZFS_MAXPROPLEN * 2, 8884327Sdougm "%s set sharenfs=on %s", 8894327Sdougm COMMAND, 8904327Sdougm dataset); 8914327Sdougm } 8924327Sdougm pfile = popen(command, "r"); 8934327Sdougm if (pfile != NULL) 8944327Sdougm ret = pclose(pfile); 8954327Sdougm switch (ret) { 8964327Sdougm default: 8974327Sdougm case 1: 8984327Sdougm ret = SA_SYSTEM_ERR; 8994327Sdougm break; 9004327Sdougm case 2: 9014327Sdougm ret = SA_SYNTAX_ERR; 9024327Sdougm break; 9034327Sdougm case 0: 9044327Sdougm break; 9054327Sdougm } 9064327Sdougm } 9074327Sdougm if (optstring != NULL) 9084327Sdougm free(optstring); 9094327Sdougm if (zfsopts != NULL) 9104327Sdougm free(zfsopts); 9113034Sdougm } 9124327Sdougm if (proto != NULL) 9134327Sdougm sa_free_attr_string(proto); 9144327Sdougm if (dataset != NULL) 9154327Sdougm free(dataset); 9163034Sdougm } 9174327Sdougm free(command); 9183034Sdougm } 9193034Sdougm return (ret); 9203034Sdougm } 9213034Sdougm 9223034Sdougm /* 9233034Sdougm * sa_group_is_zfs(group) 9243034Sdougm * 9253034Sdougm * Given the group, determine if the zfs attribute is set. 9263034Sdougm */ 9273034Sdougm 9283034Sdougm int 9293034Sdougm sa_group_is_zfs(sa_group_t group) 9303034Sdougm { 9313034Sdougm char *zfs; 9323034Sdougm int ret = 0; 9333034Sdougm 9343034Sdougm zfs = sa_get_group_attr(group, "zfs"); 9353034Sdougm if (zfs != NULL) { 9364327Sdougm ret = 1; 9374327Sdougm sa_free_attr_string(zfs); 9383034Sdougm } 9393034Sdougm return (ret); 9403034Sdougm } 9413034Sdougm 9423034Sdougm /* 9433034Sdougm * sa_path_is_zfs(path) 9443034Sdougm * 9453034Sdougm * Check to see if the file system path represents is of type "zfs". 9463034Sdougm */ 9473034Sdougm 9483034Sdougm int 9493034Sdougm sa_path_is_zfs(char *path) 9503034Sdougm { 9513034Sdougm char *fstype; 9523034Sdougm int ret = 0; 9533034Sdougm 9543034Sdougm fstype = sa_fstype(path); 9554327Sdougm if (fstype != NULL && strcmp(fstype, "zfs") == 0) 9564327Sdougm ret = 1; 9573034Sdougm if (fstype != NULL) 9584327Sdougm sa_free_fstype(fstype); 9593034Sdougm return (ret); 9603034Sdougm } 9614543Smarks 9624543Smarks int 9634543Smarks sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto) 9644543Smarks { 9654543Smarks char *path; 9664543Smarks 9674713Smarks /* Make sure path is valid */ 9684713Smarks 9694543Smarks path = sa_get_share_attr(share, "path"); 9704543Smarks if (path != NULL) { 9714543Smarks (void) memset(sh, 0, sizeof (sh)); 9724543Smarks (void) sa_fillshare(share, proto, sh); 9734713Smarks sa_free_attr_string(path); 9744543Smarks return (0); 9754543Smarks } else 9764543Smarks return (1); 9774543Smarks } 9784543Smarks 9794543Smarks #define SMAX(i, j) \ 9804543Smarks if ((j) > (i)) { \ 9814543Smarks (i) = (j); \ 9824543Smarks } 9834543Smarks 9844543Smarks int 9854543Smarks sa_share_zfs(sa_share_t share, char *path, share_t *sh, 9864543Smarks void *exportdata, boolean_t on) 9874543Smarks { 9884543Smarks libzfs_handle_t *libhandle; 9894543Smarks sa_group_t group; 9904543Smarks sa_handle_t sahandle; 9914543Smarks char *dataset; 9924543Smarks int err = EINVAL; 9934543Smarks int i, j; 9944566Smarks char newpath[MAXPATHLEN]; 9954592Smarks char *pathp; 9964543Smarks 9974543Smarks /* 9984543Smarks * First find the dataset name 9994543Smarks */ 10004543Smarks if ((group = sa_get_parent_group(share)) == NULL) { 10014543Smarks return (SA_SYSTEM_ERR); 10024543Smarks } 10034543Smarks if ((sahandle = sa_find_group_handle(group)) == NULL) { 10044543Smarks return (SA_SYSTEM_ERR); 10054543Smarks } 10064543Smarks 10074566Smarks /* 10084566Smarks * If get_zfs_dataset fails, see if it is a subdirectory 10094566Smarks */ 10104592Smarks 10114592Smarks pathp = path; 10124592Smarks while ((dataset = get_zfs_dataset(sahandle, pathp, B_TRUE)) == NULL) { 10134566Smarks char *p; 10144566Smarks 10154592Smarks if (pathp == path) { 10164592Smarks (void) strlcpy(newpath, path, sizeof (newpath)); 10174592Smarks pathp = newpath; 10184592Smarks } 10194592Smarks 10204592Smarks /* 10214592Smarks * chop off part of path, but if we are at root then 10224592Smarks * make sure path is a / 10234592Smarks */ 10244592Smarks if ((strlen(pathp) > 1) && (p = strrchr(pathp, '/'))) { 10254592Smarks if (pathp == p) { 10264592Smarks *(p + 1) = '\0'; /* skip over /, root case */ 10274592Smarks } else { 10284592Smarks *p = '\0'; 10294592Smarks } 10304592Smarks } else { 10314566Smarks return (SA_SYSTEM_ERR); 10324592Smarks } 10334543Smarks } 10344543Smarks 10354543Smarks libhandle = libzfs_init(); 10364543Smarks if (libhandle != NULL) { 10374543Smarks 10384543Smarks i = (sh->sh_path ? strlen(sh->sh_path) : 0); 10394543Smarks sh->sh_size = i; 10404543Smarks 10414543Smarks j = (sh->sh_res ? strlen(sh->sh_res) : 0); 10424543Smarks sh->sh_size += j; 10434543Smarks SMAX(i, j); 10444543Smarks 10454543Smarks j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0); 10464543Smarks sh->sh_size += j; 10474543Smarks SMAX(i, j); 10484543Smarks 10494543Smarks j = (sh->sh_opts ? strlen(sh->sh_opts) : 0); 10504543Smarks sh->sh_size += j; 10514543Smarks SMAX(i, j); 10524543Smarks 10534543Smarks j = (sh->sh_descr ? strlen(sh->sh_descr) : 0); 10544543Smarks sh->sh_size += j; 10554543Smarks SMAX(i, j); 10564543Smarks err = zfs_deleg_share_nfs(libhandle, dataset, path, 10574543Smarks exportdata, sh, i, on); 10584543Smarks libzfs_fini(libhandle); 10594543Smarks } 10604543Smarks free(dataset); 10614543Smarks return (err); 10624543Smarks } 1063