1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 213126Sahl 22789Sahrens /* 231371Seschrock * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28789Sahrens 29789Sahrens /* 30789Sahrens * Routines to manage ZFS mounts. We separate all the nasty routines that have 313126Sahl * to deal with the OS. The following functions are the main entry points -- 323126Sahl * they are used by mount and unmount and when changing a filesystem's 333126Sahl * mountpoint. 34789Sahrens * 35789Sahrens * zfs_is_mounted() 36789Sahrens * zfs_mount() 37789Sahrens * zfs_unmount() 38789Sahrens * zfs_unmountall() 39789Sahrens * 403126Sahl * This file also contains the functions used to manage sharing filesystems via 413126Sahl * NFS and iSCSI: 42789Sahrens * 43789Sahrens * zfs_is_shared() 44789Sahrens * zfs_share() 45789Sahrens * zfs_unshare() 463126Sahl * 473126Sahl * zfs_is_shared_nfs() 483126Sahl * zfs_share_nfs() 493126Sahl * zfs_unshare_nfs() 503126Sahl * zfs_unshareall_nfs() 513126Sahl * zfs_is_shared_iscsi() 523126Sahl * zfs_share_iscsi() 533126Sahl * zfs_unshare_iscsi() 542474Seschrock * 552474Seschrock * The following functions are available for pool consumers, and will 563126Sahl * mount/unmount and share/unshare all datasets within pool: 572474Seschrock * 583126Sahl * zpool_enable_datasets() 593126Sahl * zpool_disable_datasets() 60789Sahrens */ 61789Sahrens 62789Sahrens #include <dirent.h> 63*3134Sahl #include <dlfcn.h> 64789Sahrens #include <errno.h> 65789Sahrens #include <libgen.h> 66789Sahrens #include <libintl.h> 67789Sahrens #include <stdio.h> 68789Sahrens #include <stdlib.h> 69789Sahrens #include <strings.h> 70789Sahrens #include <unistd.h> 71789Sahrens #include <zone.h> 72789Sahrens #include <sys/mntent.h> 73789Sahrens #include <sys/mnttab.h> 74789Sahrens #include <sys/mount.h> 75789Sahrens #include <sys/stat.h> 76789Sahrens 77789Sahrens #include <libzfs.h> 78789Sahrens 79789Sahrens #include "libzfs_impl.h" 80789Sahrens 81*3134Sahl static int (*iscsitgt_zfs_share)(const char *); 82*3134Sahl static int (*iscsitgt_zfs_unshare)(const char *); 83*3134Sahl static int (*iscsitgt_zfs_is_shared)(const char *); 84*3134Sahl 85*3134Sahl #pragma init(zfs_iscsi_init) 86*3134Sahl static void 87*3134Sahl zfs_iscsi_init(void) 88*3134Sahl { 89*3134Sahl void *libiscsitgt; 90*3134Sahl 91*3134Sahl if ((libiscsitgt = dlopen("/lib/libiscsitgt.so.1", 92*3134Sahl RTLD_LAZY | RTLD_GLOBAL)) == NULL || 93*3134Sahl (iscsitgt_zfs_share = (int (*)(const char *))dlsym(libiscsitgt, 94*3134Sahl "iscsitgt_zfs_share")) == NULL || 95*3134Sahl (iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt, 96*3134Sahl "iscsitgt_zfs_unshare")) == NULL || 97*3134Sahl (iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt, 98*3134Sahl "iscsitgt_zfs_is_shared")) == NULL) { 99*3134Sahl iscsitgt_zfs_share = NULL; 100*3134Sahl iscsitgt_zfs_unshare = NULL; 101*3134Sahl iscsitgt_zfs_is_shared = NULL; 102*3134Sahl } 103*3134Sahl } 104*3134Sahl 105789Sahrens /* 1062082Seschrock * Search the sharetab for the given mountpoint, returning true if it is found. 107789Sahrens */ 1082082Seschrock static boolean_t 1092082Seschrock is_shared(libzfs_handle_t *hdl, const char *mountpoint) 110789Sahrens { 111789Sahrens char buf[MAXPATHLEN], *tab; 112789Sahrens 1132082Seschrock if (hdl->libzfs_sharetab == NULL) 114789Sahrens return (0); 115789Sahrens 1162082Seschrock (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 117789Sahrens 1182082Seschrock while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 119789Sahrens 120789Sahrens /* the mountpoint is the first entry on each line */ 121789Sahrens if ((tab = strchr(buf, '\t')) != NULL) { 122789Sahrens *tab = '\0'; 123789Sahrens if (strcmp(buf, mountpoint) == 0) 1242082Seschrock return (B_TRUE); 125789Sahrens } 126789Sahrens } 127789Sahrens 1282082Seschrock return (B_FALSE); 129789Sahrens } 130789Sahrens 131789Sahrens /* 1322082Seschrock * Returns true if the specified directory is empty. If we can't open the 1332082Seschrock * directory at all, return true so that the mount can fail with a more 134789Sahrens * informative error message. 135789Sahrens */ 1362082Seschrock static boolean_t 137789Sahrens dir_is_empty(const char *dirname) 138789Sahrens { 139789Sahrens DIR *dirp; 140789Sahrens struct dirent64 *dp; 141789Sahrens 142789Sahrens if ((dirp = opendir(dirname)) == NULL) 1432082Seschrock return (B_TRUE); 144789Sahrens 145789Sahrens while ((dp = readdir64(dirp)) != NULL) { 146789Sahrens 147789Sahrens if (strcmp(dp->d_name, ".") == 0 || 148789Sahrens strcmp(dp->d_name, "..") == 0) 149789Sahrens continue; 150789Sahrens 151789Sahrens (void) closedir(dirp); 1522082Seschrock return (B_FALSE); 153789Sahrens } 154789Sahrens 155789Sahrens (void) closedir(dirp); 1562082Seschrock return (B_TRUE); 157789Sahrens } 158789Sahrens 159789Sahrens /* 160789Sahrens * Checks to see if the mount is active. If the filesystem is mounted, we fill 161789Sahrens * in 'where' with the current mountpoint, and return 1. Otherwise, we return 162789Sahrens * 0. 163789Sahrens */ 1642082Seschrock boolean_t 165789Sahrens zfs_is_mounted(zfs_handle_t *zhp, char **where) 166789Sahrens { 167789Sahrens struct mnttab search = { 0 }, entry; 168789Sahrens 169789Sahrens /* 170789Sahrens * Search for the entry in /etc/mnttab. We don't bother getting the 171789Sahrens * mountpoint, as we can just search for the special device. This will 172789Sahrens * also let us find mounts when the mountpoint is 'legacy'. 173789Sahrens */ 174789Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 1751407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 176789Sahrens 1772082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 1782082Seschrock if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 1792082Seschrock return (B_FALSE); 180789Sahrens 181789Sahrens if (where != NULL) 1822082Seschrock *where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); 183789Sahrens 1842082Seschrock return (B_TRUE); 185789Sahrens } 186789Sahrens 187789Sahrens /* 1882676Seschrock * Returns true if the given dataset is mountable, false otherwise. Returns the 1892676Seschrock * mountpoint in 'buf'. 1902676Seschrock */ 1912676Seschrock static boolean_t 1922676Seschrock zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 1932676Seschrock zfs_source_t *source) 1942676Seschrock { 1952676Seschrock char sourceloc[ZFS_MAXNAMELEN]; 1962676Seschrock zfs_source_t sourcetype; 1972676Seschrock 1982676Seschrock if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 1992676Seschrock return (B_FALSE); 2002676Seschrock 2012676Seschrock verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 2022676Seschrock &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 2032676Seschrock 2042676Seschrock if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 2052676Seschrock strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 2062676Seschrock return (B_FALSE); 2072676Seschrock 2082676Seschrock if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) 2092676Seschrock return (B_FALSE); 2102676Seschrock 2112676Seschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 2122676Seschrock getzoneid() == GLOBAL_ZONEID) 2132676Seschrock return (B_FALSE); 2142676Seschrock 2152676Seschrock if (source) 2162676Seschrock *source = sourcetype; 2172676Seschrock 2182676Seschrock return (B_TRUE); 2192676Seschrock } 2202676Seschrock 2212676Seschrock /* 222789Sahrens * Mount the given filesystem. 223789Sahrens */ 224789Sahrens int 225789Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 226789Sahrens { 227789Sahrens struct stat buf; 228789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 229789Sahrens char mntopts[MNT_LINE_MAX]; 2302082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 231789Sahrens 232789Sahrens if (options == NULL) 233789Sahrens mntopts[0] = '\0'; 234789Sahrens else 235789Sahrens (void) strlcpy(mntopts, options, sizeof (mntopts)); 236789Sahrens 2372676Seschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 2382082Seschrock return (0); 239789Sahrens 240789Sahrens /* Create the directory if it doesn't already exist */ 241789Sahrens if (lstat(mountpoint, &buf) != 0) { 242789Sahrens if (mkdirp(mountpoint, 0755) != 0) { 2432082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2442082Seschrock "failed to create mountpoint")); 2452082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2462082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 2472082Seschrock mountpoint)); 248789Sahrens } 249789Sahrens } 250789Sahrens 251789Sahrens /* 252789Sahrens * Determine if the mountpoint is empty. If so, refuse to perform the 253789Sahrens * mount. We don't perform this check if MS_OVERLAY is specified, which 254789Sahrens * would defeat the point. We also avoid this check if 'remount' is 255789Sahrens * specified. 256789Sahrens */ 257789Sahrens if ((flags & MS_OVERLAY) == 0 && 258789Sahrens strstr(mntopts, MNTOPT_REMOUNT) == NULL && 259789Sahrens !dir_is_empty(mountpoint)) { 2602082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2612082Seschrock "directory is not empty")); 2622082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2632082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 264789Sahrens } 265789Sahrens 266789Sahrens /* perform the mount */ 267789Sahrens if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 268789Sahrens MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 269789Sahrens /* 270789Sahrens * Generic errors are nasty, but there are just way too many 271789Sahrens * from mount(), and they're well-understood. We pick a few 272789Sahrens * common ones to improve upon. 273789Sahrens */ 2742082Seschrock if (errno == EBUSY) 2752082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2762082Seschrock "mountpoint or dataset is busy")); 2772082Seschrock else 2782082Seschrock zfs_error_aux(hdl, strerror(errno)); 2792082Seschrock 2802082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2812082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 2822082Seschrock zhp->zfs_name)); 283789Sahrens } 284789Sahrens 285789Sahrens return (0); 286789Sahrens } 287789Sahrens 288789Sahrens /* 2892474Seschrock * Unmount a single filesystem. 2902474Seschrock */ 2912474Seschrock static int 2922474Seschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 2932474Seschrock { 2942474Seschrock if (umount2(mountpoint, flags) != 0) { 2952474Seschrock zfs_error_aux(hdl, strerror(errno)); 2962474Seschrock return (zfs_error(hdl, EZFS_UMOUNTFAILED, 2972474Seschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 2982474Seschrock mountpoint)); 2992474Seschrock } 3002474Seschrock 3012474Seschrock return (0); 3022474Seschrock } 3032474Seschrock 3042474Seschrock /* 305789Sahrens * Unmount the given filesystem. 306789Sahrens */ 307789Sahrens int 308789Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 309789Sahrens { 310789Sahrens struct mnttab search = { 0 }, entry; 311789Sahrens 312789Sahrens /* check to see if need to unmount the filesystem */ 3132474Seschrock search.mnt_special = zhp->zfs_name; 3141407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 3152082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 316789Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 3172082Seschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 318789Sahrens 319789Sahrens if (mountpoint == NULL) 320789Sahrens mountpoint = entry.mnt_mountp; 321789Sahrens 322789Sahrens /* 3232474Seschrock * Unshare and unmount the filesystem 324789Sahrens */ 3253126Sahl if (zfs_unshare_nfs(zhp, mountpoint) != 0 || 3262474Seschrock unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 327789Sahrens return (-1); 328789Sahrens } 329789Sahrens 330789Sahrens return (0); 331789Sahrens } 332789Sahrens 333789Sahrens /* 334789Sahrens * Unmount this filesystem and any children inheriting the mountpoint property. 335789Sahrens * To do this, just act like we're changing the mountpoint property, but don't 336789Sahrens * remount the filesystems afterwards. 337789Sahrens */ 338789Sahrens int 339789Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags) 340789Sahrens { 341789Sahrens prop_changelist_t *clp; 342789Sahrens int ret; 343789Sahrens 344789Sahrens clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 345789Sahrens if (clp == NULL) 346789Sahrens return (-1); 347789Sahrens 348789Sahrens ret = changelist_prefix(clp); 349789Sahrens changelist_free(clp); 350789Sahrens 351789Sahrens return (ret); 352789Sahrens } 353789Sahrens 3543126Sahl boolean_t 3553126Sahl zfs_is_shared(zfs_handle_t *zhp) 3563126Sahl { 3573126Sahl if (ZFS_IS_VOLUME(zhp)) 3583126Sahl return (zfs_is_shared_iscsi(zhp)); 3593126Sahl 3603126Sahl return (zfs_is_shared_nfs(zhp, NULL)); 3613126Sahl } 3623126Sahl 3633126Sahl int 3643126Sahl zfs_share(zfs_handle_t *zhp) 3653126Sahl { 3663126Sahl if (ZFS_IS_VOLUME(zhp)) 3673126Sahl return (zfs_share_iscsi(zhp)); 3683126Sahl 3693126Sahl return (zfs_share_nfs(zhp)); 3703126Sahl } 3713126Sahl 3723126Sahl int 3733126Sahl zfs_unshare(zfs_handle_t *zhp) 3743126Sahl { 3753126Sahl if (ZFS_IS_VOLUME(zhp)) 3763126Sahl return (zfs_unshare_iscsi(zhp)); 3773126Sahl 3783126Sahl return (zfs_unshare_nfs(zhp, NULL)); 3793126Sahl } 3803126Sahl 381789Sahrens /* 382789Sahrens * Check to see if the filesystem is currently shared. 383789Sahrens */ 3842082Seschrock boolean_t 3853126Sahl zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) 386789Sahrens { 387789Sahrens char *mountpoint; 388789Sahrens 389789Sahrens if (!zfs_is_mounted(zhp, &mountpoint)) 3902082Seschrock return (B_FALSE); 391789Sahrens 3922082Seschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 393789Sahrens if (where != NULL) 394789Sahrens *where = mountpoint; 395789Sahrens else 396789Sahrens free(mountpoint); 3972082Seschrock return (B_TRUE); 398789Sahrens } else { 399789Sahrens free(mountpoint); 4002082Seschrock return (B_FALSE); 401789Sahrens } 402789Sahrens } 403789Sahrens 404789Sahrens /* 405789Sahrens * Share the given filesystem according to the options in 'sharenfs'. We rely 406789Sahrens * on share(1M) to the dirty work for us. 407789Sahrens */ 408789Sahrens int 4093126Sahl zfs_share_nfs(zfs_handle_t *zhp) 410789Sahrens { 411789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 412789Sahrens char shareopts[ZFS_MAXPROPLEN]; 413789Sahrens char buf[MAXPATHLEN]; 414789Sahrens FILE *fp; 4152082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 416789Sahrens 4172676Seschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 418789Sahrens return (0); 419789Sahrens 4203126Sahl /* 4213126Sahl * Return success if there are no share options. 4223126Sahl */ 423789Sahrens if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 4242082Seschrock NULL, NULL, 0, B_FALSE) != 0 || 425789Sahrens strcmp(shareopts, "off") == 0) 426789Sahrens return (0); 427789Sahrens 428789Sahrens /* 4292676Seschrock * If the 'zoned' property is set, then zfs_is_mountable() will have 4302676Seschrock * already bailed out if we are in the global zone. But local 4312676Seschrock * zones cannot be NFS servers, so we ignore it for local zones as well. 432789Sahrens */ 4332676Seschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 434789Sahrens return (0); 435789Sahrens 436789Sahrens /* 437789Sahrens * Invoke the share(1M) command. We always do this, even if it's 438789Sahrens * currently shared, as the options may have changed. 439789Sahrens */ 440789Sahrens if (strcmp(shareopts, "on") == 0) 441789Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 442789Sahrens "-F nfs \"%s\" 2>&1", mountpoint); 443789Sahrens else 444789Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 445789Sahrens "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 446789Sahrens mountpoint); 447789Sahrens 4482082Seschrock if ((fp = popen(buf, "r")) == NULL) 4493126Sahl return (zfs_error(hdl, EZFS_SHARENFSFAILED, 4502082Seschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 4512082Seschrock zfs_get_name(zhp))); 452789Sahrens 453789Sahrens /* 454789Sahrens * share(1M) should only produce output if there is some kind 455789Sahrens * of error. All output begins with "share_nfs: ", so we trim 456789Sahrens * this off to get to the real error. 457789Sahrens */ 458789Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 459789Sahrens char *colon = strchr(buf, ':'); 460789Sahrens 461789Sahrens while (buf[strlen(buf) - 1] == '\n') 462789Sahrens buf[strlen(buf) - 1] = '\0'; 463789Sahrens 4642082Seschrock if (colon != NULL) 4652082Seschrock zfs_error_aux(hdl, colon + 2); 4662082Seschrock 4673126Sahl (void) zfs_error(hdl, EZFS_SHARENFSFAILED, 4682474Seschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 4692474Seschrock zfs_get_name(zhp)); 470789Sahrens 471789Sahrens verify(pclose(fp) != 0); 472789Sahrens return (-1); 473789Sahrens } 474789Sahrens 475789Sahrens verify(pclose(fp) == 0); 476789Sahrens 477789Sahrens return (0); 478789Sahrens } 479789Sahrens 480789Sahrens /* 4812474Seschrock * Unshare a filesystem by mountpoint. 4822474Seschrock */ 4832474Seschrock static int 4842474Seschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 4852474Seschrock { 4862474Seschrock char buf[MAXPATHLEN]; 4872474Seschrock FILE *fp; 4882474Seschrock 4892474Seschrock (void) snprintf(buf, sizeof (buf), 4902474Seschrock "/usr/sbin/unshare \"%s\" 2>&1", 4912474Seschrock mountpoint); 4922474Seschrock 4932474Seschrock if ((fp = popen(buf, "r")) == NULL) 4943126Sahl return (zfs_error(hdl, EZFS_UNSHARENFSFAILED, 4952474Seschrock dgettext(TEXT_DOMAIN, 4962474Seschrock "cannot unshare '%s'"), name)); 4972474Seschrock 4982474Seschrock /* 4992474Seschrock * unshare(1M) should only produce output if there is 5002474Seschrock * some kind of error. All output begins with "unshare 5012474Seschrock * nfs: ", so we trim this off to get to the real error. 5022474Seschrock */ 5032474Seschrock if (fgets(buf, sizeof (buf), fp) != NULL) { 5042474Seschrock char *colon = strchr(buf, ':'); 5052474Seschrock 5062474Seschrock while (buf[strlen(buf) - 1] == '\n') 5072474Seschrock buf[strlen(buf) - 1] = '\0'; 5082474Seschrock 5092474Seschrock if (colon != NULL) 5102474Seschrock zfs_error_aux(hdl, colon + 2); 5112474Seschrock 5122474Seschrock verify(pclose(fp) != 0); 5132474Seschrock 5143126Sahl return (zfs_error(hdl, EZFS_UNSHARENFSFAILED, 5152474Seschrock dgettext(TEXT_DOMAIN, 5162474Seschrock "cannot unshare '%s'"), name)); 5172474Seschrock } 5182474Seschrock 5192474Seschrock verify(pclose(fp) == 0); 5202474Seschrock 5212474Seschrock return (0); 5222474Seschrock } 5232474Seschrock 5242474Seschrock /* 525789Sahrens * Unshare the given filesystem. 526789Sahrens */ 527789Sahrens int 5283126Sahl zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) 529789Sahrens { 530789Sahrens struct mnttab search = { 0 }, entry; 531789Sahrens 532789Sahrens /* check to see if need to unmount the filesystem */ 533789Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 5341407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 5352082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 536789Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 5372082Seschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 538789Sahrens 539789Sahrens if (mountpoint == NULL) 540789Sahrens mountpoint = entry.mnt_mountp; 541789Sahrens 5422474Seschrock if (is_shared(zhp->zfs_hdl, mountpoint) && 5432474Seschrock unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 5442474Seschrock return (-1); 545789Sahrens } 546789Sahrens 547789Sahrens return (0); 548789Sahrens } 549789Sahrens 550789Sahrens /* 5513126Sahl * Same as zfs_unmountall(), but for NFS unshares. 552789Sahrens */ 553789Sahrens int 5543126Sahl zfs_unshareall_nfs(zfs_handle_t *zhp) 555789Sahrens { 556789Sahrens prop_changelist_t *clp; 557789Sahrens int ret; 558789Sahrens 559789Sahrens clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 560789Sahrens if (clp == NULL) 561789Sahrens return (-1); 562789Sahrens 563789Sahrens ret = changelist_unshare(clp); 564789Sahrens changelist_free(clp); 565789Sahrens 566789Sahrens return (ret); 567789Sahrens } 568789Sahrens 569789Sahrens /* 570789Sahrens * Remove the mountpoint associated with the current dataset, if necessary. 571789Sahrens * We only remove the underlying directory if: 572789Sahrens * 573789Sahrens * - The mountpoint is not 'none' or 'legacy' 574789Sahrens * - The mountpoint is non-empty 575789Sahrens * - The mountpoint is the default or inherited 576789Sahrens * - The 'zoned' property is set, or we're in a local zone 577789Sahrens * 578789Sahrens * Any other directories we leave alone. 579789Sahrens */ 580789Sahrens void 581789Sahrens remove_mountpoint(zfs_handle_t *zhp) 582789Sahrens { 583789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 5842676Seschrock zfs_source_t source; 585789Sahrens 5862676Seschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 5872676Seschrock &source)) 588789Sahrens return; 589789Sahrens 5902676Seschrock if (source == ZFS_SRC_DEFAULT || 5912676Seschrock source == ZFS_SRC_INHERITED) { 592789Sahrens /* 593789Sahrens * Try to remove the directory, silently ignoring any errors. 594789Sahrens * The filesystem may have since been removed or moved around, 5952676Seschrock * and this error isn't really useful to the administrator in 5962676Seschrock * any way. 597789Sahrens */ 598789Sahrens (void) rmdir(mountpoint); 599789Sahrens } 600789Sahrens } 6012474Seschrock 6023126Sahl boolean_t 6033126Sahl zfs_is_shared_iscsi(zfs_handle_t *zhp) 6043126Sahl { 605*3134Sahl return (iscsitgt_zfs_is_shared != NULL && 606*3134Sahl iscsitgt_zfs_is_shared(zhp->zfs_name) != 0); 6073126Sahl } 6083126Sahl 6093126Sahl int 6103126Sahl zfs_share_iscsi(zfs_handle_t *zhp) 6113126Sahl { 6123126Sahl char shareopts[ZFS_MAXPROPLEN]; 6133126Sahl const char *dataset = zhp->zfs_name; 6143126Sahl libzfs_handle_t *hdl = zhp->zfs_hdl; 6153126Sahl 6163126Sahl /* 6173126Sahl * Return success if there are no share options. 6183126Sahl */ 6193126Sahl if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 6203126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 || 6213126Sahl strcmp(shareopts, "off") == 0) 6223126Sahl return (0); 6233126Sahl 624*3134Sahl if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) 6253126Sahl return (zfs_error(hdl, EZFS_SHAREISCSIFAILED, 6263126Sahl dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset)); 6273126Sahl 6283126Sahl return (0); 6293126Sahl } 6303126Sahl 6313126Sahl int 6323126Sahl zfs_unshare_iscsi(zfs_handle_t *zhp) 6333126Sahl { 6343126Sahl const char *dataset = zfs_get_name(zhp); 6353126Sahl libzfs_handle_t *hdl = zhp->zfs_hdl; 6363126Sahl 6373126Sahl /* 6383126Sahl * If this fails with ENODEV it indicates that zvol wasn't shared so 6393126Sahl * we should return success in that case. 6403126Sahl */ 641*3134Sahl if (iscsitgt_zfs_unshare == NULL || 642*3134Sahl (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) 6433126Sahl return (zfs_error(hdl, EZFS_UNSHAREISCSIFAILED, 6443126Sahl dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset)); 6453126Sahl 6463126Sahl return (0); 6473126Sahl } 6483126Sahl 6492474Seschrock typedef struct mount_cbdata { 6502474Seschrock zfs_handle_t **cb_datasets; 6512474Seschrock int cb_used; 6522474Seschrock int cb_alloc; 6532474Seschrock } mount_cbdata_t; 6542474Seschrock 6552474Seschrock static int 6562474Seschrock mount_cb(zfs_handle_t *zhp, void *data) 6572474Seschrock { 6582474Seschrock mount_cbdata_t *cbp = data; 6592474Seschrock 6603126Sahl if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) { 6612474Seschrock zfs_close(zhp); 6622474Seschrock return (0); 6632474Seschrock } 6642474Seschrock 6652474Seschrock if (cbp->cb_alloc == cbp->cb_used) { 6662676Seschrock void *ptr; 6672474Seschrock 6682676Seschrock if ((ptr = zfs_realloc(zhp->zfs_hdl, 6692676Seschrock cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), 6702676Seschrock cbp->cb_alloc * 2 * sizeof (void *))) == NULL) 6712474Seschrock return (-1); 6722676Seschrock cbp->cb_datasets = ptr; 6732474Seschrock 6742676Seschrock cbp->cb_alloc *= 2; 6752474Seschrock } 6762474Seschrock 6772474Seschrock cbp->cb_datasets[cbp->cb_used++] = zhp; 6783126Sahl 6793126Sahl return (zfs_iter_children(zhp, mount_cb, cbp)); 6802474Seschrock } 6812474Seschrock 6822474Seschrock static int 6833126Sahl dataset_cmp(const void *a, const void *b) 6842474Seschrock { 6852474Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 6862474Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 6872474Seschrock char mounta[MAXPATHLEN]; 6882474Seschrock char mountb[MAXPATHLEN]; 6893126Sahl boolean_t gota, gotb; 6902474Seschrock 6913126Sahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 6923126Sahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 6933126Sahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 6943126Sahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 6953126Sahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 6963126Sahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 6972474Seschrock 6983126Sahl if (gota && gotb) 6993126Sahl return (strcmp(mounta, mountb)); 7003126Sahl 7013126Sahl if (gota) 7023126Sahl return (-1); 7033126Sahl if (gotb) 7043126Sahl return (1); 7053126Sahl 7063126Sahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 7072474Seschrock } 7082474Seschrock 7093126Sahl /* 7103126Sahl * Mount and share all datasets within the given pool. This assumes that no 7113126Sahl * datasets within the pool are currently mounted. Because users can create 7123126Sahl * complicated nested hierarchies of mountpoints, we first gather all the 7133126Sahl * datasets and mountpoints within the pool, and sort them by mountpoint. Once 7143126Sahl * we have the list of all filesystems, we iterate over them in order and mount 7153126Sahl * and/or share each one. 7163126Sahl */ 7173126Sahl #pragma weak zpool_mount_datasets = zpool_enable_datasets 7182474Seschrock int 7193126Sahl zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 7202474Seschrock { 7212474Seschrock mount_cbdata_t cb = { 0 }; 7222474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 7232474Seschrock zfs_handle_t *zfsp; 7242474Seschrock int i, ret = -1; 7252474Seschrock 7262474Seschrock /* 7272474Seschrock * Gather all datasets within the pool. 7282474Seschrock */ 7292474Seschrock if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 7302474Seschrock return (-1); 7312474Seschrock cb.cb_alloc = 4; 7322474Seschrock 7332474Seschrock if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 7342474Seschrock goto out; 7352474Seschrock 7362474Seschrock cb.cb_datasets[0] = zfsp; 7372474Seschrock cb.cb_used = 1; 7382474Seschrock 7392474Seschrock if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 7402474Seschrock goto out; 7412474Seschrock 7422474Seschrock /* 7432474Seschrock * Sort the datasets by mountpoint. 7442474Seschrock */ 7453126Sahl qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp); 7462474Seschrock 7472474Seschrock /* 7482474Seschrock * And mount all the datasets. 7492474Seschrock */ 7502474Seschrock ret = 0; 7512474Seschrock for (i = 0; i < cb.cb_used; i++) { 7522500Seschrock if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 7532474Seschrock zfs_share(cb.cb_datasets[i]) != 0) 7542474Seschrock ret = -1; 7552474Seschrock } 7562474Seschrock 7572474Seschrock out: 7582474Seschrock for (i = 0; i < cb.cb_used; i++) 7592474Seschrock zfs_close(cb.cb_datasets[i]); 7602474Seschrock free(cb.cb_datasets); 7612474Seschrock 7622474Seschrock return (ret); 7632474Seschrock } 7642474Seschrock 7653126Sahl 7663126Sahl static int 7673126Sahl zvol_cb(const char *dataset, void *data) 7683126Sahl { 7693126Sahl libzfs_handle_t *hdl = data; 7703126Sahl zfs_handle_t *zhp; 7713126Sahl 7723126Sahl /* 7733126Sahl * Ignore snapshots and ignore failures from non-existant datasets. 7743126Sahl */ 7753126Sahl if (strchr(dataset, '@') != NULL || 7763126Sahl (zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL) 7773126Sahl return (0); 7783126Sahl 7793126Sahl (void) zfs_unshare_iscsi(zhp); 7803126Sahl 7813126Sahl zfs_close(zhp); 7823126Sahl 7833126Sahl return (0); 7843126Sahl } 7853126Sahl 7862474Seschrock static int 7872474Seschrock mountpoint_compare(const void *a, const void *b) 7882474Seschrock { 7892474Seschrock const char *mounta = *((char **)a); 7902474Seschrock const char *mountb = *((char **)b); 7912474Seschrock 7922474Seschrock return (strcmp(mountb, mounta)); 7932474Seschrock } 7942474Seschrock 7953126Sahl /* 7963126Sahl * Unshare and unmount all datasets within the given pool. We don't want to 7973126Sahl * rely on traversing the DSL to discover the filesystems within the pool, 7983126Sahl * because this may be expensive (if not all of them are mounted), and can fail 7993126Sahl * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 8003126Sahl * gather all the filesystems that are currently mounted. 8013126Sahl */ 8023126Sahl #pragma weak zpool_unmount_datasets = zpool_disable_datasets 8032474Seschrock int 8043126Sahl zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) 8052474Seschrock { 8062474Seschrock int used, alloc; 8072474Seschrock struct mnttab entry; 8082474Seschrock size_t namelen; 8092474Seschrock char **mountpoints = NULL; 8102474Seschrock zfs_handle_t **datasets = NULL; 8112474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 8122474Seschrock int i; 8132474Seschrock int ret = -1; 8142474Seschrock int flags = (force ? MS_FORCE : 0); 8152474Seschrock 8163126Sahl /* 8173126Sahl * First unshare all zvols. 8183126Sahl */ 8193126Sahl if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0) 8203126Sahl return (-1); 8213126Sahl 8222474Seschrock namelen = strlen(zhp->zpool_name); 8232474Seschrock 8242474Seschrock rewind(hdl->libzfs_mnttab); 8252474Seschrock used = alloc = 0; 8262474Seschrock while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 8272474Seschrock /* 8282474Seschrock * Ignore non-ZFS entries. 8292474Seschrock */ 8302474Seschrock if (entry.mnt_fstype == NULL || 8312474Seschrock strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 8322474Seschrock continue; 8332474Seschrock 8342474Seschrock /* 8352474Seschrock * Ignore filesystems not within this pool. 8362474Seschrock */ 8372474Seschrock if (entry.mnt_mountp == NULL || 8382474Seschrock strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 8392474Seschrock (entry.mnt_special[namelen] != '/' && 8402474Seschrock entry.mnt_special[namelen] != '\0')) 8412474Seschrock continue; 8422474Seschrock 8432474Seschrock /* 8442474Seschrock * At this point we've found a filesystem within our pool. Add 8452474Seschrock * it to our growing list. 8462474Seschrock */ 8472474Seschrock if (used == alloc) { 8482474Seschrock if (alloc == 0) { 8492474Seschrock if ((mountpoints = zfs_alloc(hdl, 8502474Seschrock 8 * sizeof (void *))) == NULL) 8512474Seschrock goto out; 8522474Seschrock 8532474Seschrock if ((datasets = zfs_alloc(hdl, 8542474Seschrock 8 * sizeof (void *))) == NULL) 8552474Seschrock goto out; 8562474Seschrock 8572474Seschrock alloc = 8; 8582474Seschrock } else { 8592676Seschrock void *ptr; 8602474Seschrock 8612676Seschrock if ((ptr = zfs_realloc(hdl, mountpoints, 8622676Seschrock alloc * sizeof (void *), 8632474Seschrock alloc * 2 * sizeof (void *))) == NULL) 8642474Seschrock goto out; 8652676Seschrock mountpoints = ptr; 8662474Seschrock 8672676Seschrock if ((ptr = zfs_realloc(hdl, datasets, 8682676Seschrock alloc * sizeof (void *), 8692474Seschrock alloc * 2 * sizeof (void *))) == NULL) 8702474Seschrock goto out; 8712676Seschrock datasets = ptr; 8722474Seschrock 8732474Seschrock alloc *= 2; 8742474Seschrock } 8752474Seschrock } 8762474Seschrock 8772474Seschrock if ((mountpoints[used] = zfs_strdup(hdl, 8782474Seschrock entry.mnt_mountp)) == NULL) 8792474Seschrock goto out; 8802474Seschrock 8812474Seschrock /* 8822474Seschrock * This is allowed to fail, in case there is some I/O error. It 8832474Seschrock * is only used to determine if we need to remove the underlying 8842474Seschrock * mountpoint, so failure is not fatal. 8852474Seschrock */ 8862474Seschrock datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 8872474Seschrock 8882474Seschrock used++; 8892474Seschrock } 8902474Seschrock 8912474Seschrock /* 8922474Seschrock * At this point, we have the entire list of filesystems, so sort it by 8932474Seschrock * mountpoint. 8942474Seschrock */ 8952474Seschrock qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 8962474Seschrock 8972474Seschrock /* 8982474Seschrock * Walk through and first unshare everything. 8992474Seschrock */ 9002474Seschrock for (i = 0; i < used; i++) { 9012474Seschrock if (is_shared(hdl, mountpoints[i]) && 9022676Seschrock unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) 9032474Seschrock goto out; 9042474Seschrock } 9052474Seschrock 9062474Seschrock /* 9072474Seschrock * Now unmount everything, removing the underlying directories as 9082474Seschrock * appropriate. 9092474Seschrock */ 9102474Seschrock for (i = 0; i < used; i++) { 9112474Seschrock if (unmount_one(hdl, mountpoints[i], flags) != 0) 9122474Seschrock goto out; 9132676Seschrock } 9142474Seschrock 9152676Seschrock for (i = 0; i < used; i++) { 9162474Seschrock if (datasets[i]) 9172474Seschrock remove_mountpoint(datasets[i]); 9182474Seschrock } 9192474Seschrock 9202474Seschrock ret = 0; 9212474Seschrock out: 9222474Seschrock for (i = 0; i < used; i++) { 9232474Seschrock if (datasets[i]) 9242474Seschrock zfs_close(datasets[i]); 9252474Seschrock free(mountpoints[i]); 9262474Seschrock } 9272474Seschrock free(datasets); 9282474Seschrock free(mountpoints); 9292474Seschrock 9302474Seschrock return (ret); 9312474Seschrock } 932