1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5ea8dc4b6Seschrock * Common Development and Distribution License (the "License"). 6ea8dc4b6Seschrock * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21*f3861e1aSahl 22fa9e4066Sahrens /* 23260921a4Seschrock * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24fa9e4066Sahrens * Use is subject to license terms. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28fa9e4066Sahrens 29fa9e4066Sahrens /* 30fa9e4066Sahrens * Routines to manage ZFS mounts. We separate all the nasty routines that have 31*f3861e1aSahl * to deal with the OS. The following functions are the main entry points -- 32*f3861e1aSahl * they are used by mount and unmount and when changing a filesystem's 33*f3861e1aSahl * mountpoint. 34fa9e4066Sahrens * 35fa9e4066Sahrens * zfs_is_mounted() 36fa9e4066Sahrens * zfs_mount() 37fa9e4066Sahrens * zfs_unmount() 38fa9e4066Sahrens * zfs_unmountall() 39fa9e4066Sahrens * 40*f3861e1aSahl * This file also contains the functions used to manage sharing filesystems via 41*f3861e1aSahl * NFS and iSCSI: 42fa9e4066Sahrens * 43fa9e4066Sahrens * zfs_is_shared() 44fa9e4066Sahrens * zfs_share() 45fa9e4066Sahrens * zfs_unshare() 46*f3861e1aSahl * 47*f3861e1aSahl * zfs_is_shared_nfs() 48*f3861e1aSahl * zfs_share_nfs() 49*f3861e1aSahl * zfs_unshare_nfs() 50*f3861e1aSahl * zfs_unshareall_nfs() 51*f3861e1aSahl * zfs_is_shared_iscsi() 52*f3861e1aSahl * zfs_share_iscsi() 53*f3861e1aSahl * zfs_unshare_iscsi() 543bb79becSeschrock * 553bb79becSeschrock * The following functions are available for pool consumers, and will 56*f3861e1aSahl * mount/unmount and share/unshare all datasets within pool: 573bb79becSeschrock * 58*f3861e1aSahl * zpool_enable_datasets() 59*f3861e1aSahl * zpool_disable_datasets() 60fa9e4066Sahrens */ 61fa9e4066Sahrens 62fa9e4066Sahrens #include <dirent.h> 63fa9e4066Sahrens #include <errno.h> 64fa9e4066Sahrens #include <libgen.h> 65fa9e4066Sahrens #include <libintl.h> 66*f3861e1aSahl #include <libiscsitgt.h> 67fa9e4066Sahrens #include <stdio.h> 68fa9e4066Sahrens #include <stdlib.h> 69fa9e4066Sahrens #include <strings.h> 70fa9e4066Sahrens #include <unistd.h> 71fa9e4066Sahrens #include <zone.h> 72fa9e4066Sahrens #include <sys/mntent.h> 73fa9e4066Sahrens #include <sys/mnttab.h> 74fa9e4066Sahrens #include <sys/mount.h> 75fa9e4066Sahrens #include <sys/stat.h> 76fa9e4066Sahrens 77fa9e4066Sahrens #include <libzfs.h> 78fa9e4066Sahrens 79fa9e4066Sahrens #include "libzfs_impl.h" 80fa9e4066Sahrens 81fa9e4066Sahrens /* 8299653d4eSeschrock * Search the sharetab for the given mountpoint, returning true if it is found. 83fa9e4066Sahrens */ 8499653d4eSeschrock static boolean_t 8599653d4eSeschrock is_shared(libzfs_handle_t *hdl, const char *mountpoint) 86fa9e4066Sahrens { 87fa9e4066Sahrens char buf[MAXPATHLEN], *tab; 88fa9e4066Sahrens 8999653d4eSeschrock if (hdl->libzfs_sharetab == NULL) 90fa9e4066Sahrens return (0); 91fa9e4066Sahrens 9299653d4eSeschrock (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 93fa9e4066Sahrens 9499653d4eSeschrock while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 95fa9e4066Sahrens 96fa9e4066Sahrens /* the mountpoint is the first entry on each line */ 97fa9e4066Sahrens if ((tab = strchr(buf, '\t')) != NULL) { 98fa9e4066Sahrens *tab = '\0'; 99fa9e4066Sahrens if (strcmp(buf, mountpoint) == 0) 10099653d4eSeschrock return (B_TRUE); 101fa9e4066Sahrens } 102fa9e4066Sahrens } 103fa9e4066Sahrens 10499653d4eSeschrock return (B_FALSE); 105fa9e4066Sahrens } 106fa9e4066Sahrens 107fa9e4066Sahrens /* 10899653d4eSeschrock * Returns true if the specified directory is empty. If we can't open the 10999653d4eSeschrock * directory at all, return true so that the mount can fail with a more 110fa9e4066Sahrens * informative error message. 111fa9e4066Sahrens */ 11299653d4eSeschrock static boolean_t 113fa9e4066Sahrens dir_is_empty(const char *dirname) 114fa9e4066Sahrens { 115fa9e4066Sahrens DIR *dirp; 116fa9e4066Sahrens struct dirent64 *dp; 117fa9e4066Sahrens 118fa9e4066Sahrens if ((dirp = opendir(dirname)) == NULL) 11999653d4eSeschrock return (B_TRUE); 120fa9e4066Sahrens 121fa9e4066Sahrens while ((dp = readdir64(dirp)) != NULL) { 122fa9e4066Sahrens 123fa9e4066Sahrens if (strcmp(dp->d_name, ".") == 0 || 124fa9e4066Sahrens strcmp(dp->d_name, "..") == 0) 125fa9e4066Sahrens continue; 126fa9e4066Sahrens 127fa9e4066Sahrens (void) closedir(dirp); 12899653d4eSeschrock return (B_FALSE); 129fa9e4066Sahrens } 130fa9e4066Sahrens 131fa9e4066Sahrens (void) closedir(dirp); 13299653d4eSeschrock return (B_TRUE); 133fa9e4066Sahrens } 134fa9e4066Sahrens 135fa9e4066Sahrens /* 136fa9e4066Sahrens * Checks to see if the mount is active. If the filesystem is mounted, we fill 137fa9e4066Sahrens * in 'where' with the current mountpoint, and return 1. Otherwise, we return 138fa9e4066Sahrens * 0. 139fa9e4066Sahrens */ 14099653d4eSeschrock boolean_t 141fa9e4066Sahrens zfs_is_mounted(zfs_handle_t *zhp, char **where) 142fa9e4066Sahrens { 143fa9e4066Sahrens struct mnttab search = { 0 }, entry; 144fa9e4066Sahrens 145fa9e4066Sahrens /* 146fa9e4066Sahrens * Search for the entry in /etc/mnttab. We don't bother getting the 147fa9e4066Sahrens * mountpoint, as we can just search for the special device. This will 148fa9e4066Sahrens * also let us find mounts when the mountpoint is 'legacy'. 149fa9e4066Sahrens */ 150fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 15196dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 152fa9e4066Sahrens 15399653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 15499653d4eSeschrock if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 15599653d4eSeschrock return (B_FALSE); 156fa9e4066Sahrens 157fa9e4066Sahrens if (where != NULL) 15899653d4eSeschrock *where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); 159fa9e4066Sahrens 16099653d4eSeschrock return (B_TRUE); 161fa9e4066Sahrens } 162fa9e4066Sahrens 163e9dbad6fSeschrock /* 164e9dbad6fSeschrock * Returns true if the given dataset is mountable, false otherwise. Returns the 165e9dbad6fSeschrock * mountpoint in 'buf'. 166e9dbad6fSeschrock */ 167e9dbad6fSeschrock static boolean_t 168e9dbad6fSeschrock zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 169e9dbad6fSeschrock zfs_source_t *source) 170e9dbad6fSeschrock { 171e9dbad6fSeschrock char sourceloc[ZFS_MAXNAMELEN]; 172e9dbad6fSeschrock zfs_source_t sourcetype; 173e9dbad6fSeschrock 174e9dbad6fSeschrock if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 175e9dbad6fSeschrock return (B_FALSE); 176e9dbad6fSeschrock 177e9dbad6fSeschrock verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 178e9dbad6fSeschrock &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 179e9dbad6fSeschrock 180e9dbad6fSeschrock if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 181e9dbad6fSeschrock strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 182e9dbad6fSeschrock return (B_FALSE); 183e9dbad6fSeschrock 184e9dbad6fSeschrock if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) 185e9dbad6fSeschrock return (B_FALSE); 186e9dbad6fSeschrock 187e9dbad6fSeschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 188e9dbad6fSeschrock getzoneid() == GLOBAL_ZONEID) 189e9dbad6fSeschrock return (B_FALSE); 190e9dbad6fSeschrock 191e9dbad6fSeschrock if (source) 192e9dbad6fSeschrock *source = sourcetype; 193e9dbad6fSeschrock 194e9dbad6fSeschrock return (B_TRUE); 195e9dbad6fSeschrock } 196e9dbad6fSeschrock 197fa9e4066Sahrens /* 198fa9e4066Sahrens * Mount the given filesystem. 199fa9e4066Sahrens */ 200fa9e4066Sahrens int 201fa9e4066Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 202fa9e4066Sahrens { 203fa9e4066Sahrens struct stat buf; 204fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 205fa9e4066Sahrens char mntopts[MNT_LINE_MAX]; 20699653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 207fa9e4066Sahrens 208fa9e4066Sahrens if (options == NULL) 209fa9e4066Sahrens mntopts[0] = '\0'; 210fa9e4066Sahrens else 211fa9e4066Sahrens (void) strlcpy(mntopts, options, sizeof (mntopts)); 212fa9e4066Sahrens 213e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 21499653d4eSeschrock return (0); 215fa9e4066Sahrens 216fa9e4066Sahrens /* Create the directory if it doesn't already exist */ 217fa9e4066Sahrens if (lstat(mountpoint, &buf) != 0) { 218fa9e4066Sahrens if (mkdirp(mountpoint, 0755) != 0) { 21999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 22099653d4eSeschrock "failed to create mountpoint")); 22199653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 22299653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 22399653d4eSeschrock mountpoint)); 224fa9e4066Sahrens } 225fa9e4066Sahrens } 226fa9e4066Sahrens 227fa9e4066Sahrens /* 228fa9e4066Sahrens * Determine if the mountpoint is empty. If so, refuse to perform the 229fa9e4066Sahrens * mount. We don't perform this check if MS_OVERLAY is specified, which 230fa9e4066Sahrens * would defeat the point. We also avoid this check if 'remount' is 231fa9e4066Sahrens * specified. 232fa9e4066Sahrens */ 233fa9e4066Sahrens if ((flags & MS_OVERLAY) == 0 && 234fa9e4066Sahrens strstr(mntopts, MNTOPT_REMOUNT) == NULL && 235fa9e4066Sahrens !dir_is_empty(mountpoint)) { 23699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 23799653d4eSeschrock "directory is not empty")); 23899653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 23999653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 240fa9e4066Sahrens } 241fa9e4066Sahrens 242fa9e4066Sahrens /* perform the mount */ 243fa9e4066Sahrens if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 244fa9e4066Sahrens MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 245fa9e4066Sahrens /* 246fa9e4066Sahrens * Generic errors are nasty, but there are just way too many 247fa9e4066Sahrens * from mount(), and they're well-understood. We pick a few 248fa9e4066Sahrens * common ones to improve upon. 249fa9e4066Sahrens */ 25099653d4eSeschrock if (errno == EBUSY) 25199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25299653d4eSeschrock "mountpoint or dataset is busy")); 25399653d4eSeschrock else 25499653d4eSeschrock zfs_error_aux(hdl, strerror(errno)); 25599653d4eSeschrock 25699653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 25799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 25899653d4eSeschrock zhp->zfs_name)); 259fa9e4066Sahrens } 260fa9e4066Sahrens 261fa9e4066Sahrens return (0); 262fa9e4066Sahrens } 263fa9e4066Sahrens 2643bb79becSeschrock /* 2653bb79becSeschrock * Unmount a single filesystem. 2663bb79becSeschrock */ 2673bb79becSeschrock static int 2683bb79becSeschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 2693bb79becSeschrock { 2703bb79becSeschrock if (umount2(mountpoint, flags) != 0) { 2713bb79becSeschrock zfs_error_aux(hdl, strerror(errno)); 2723bb79becSeschrock return (zfs_error(hdl, EZFS_UMOUNTFAILED, 2733bb79becSeschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 2743bb79becSeschrock mountpoint)); 2753bb79becSeschrock } 2763bb79becSeschrock 2773bb79becSeschrock return (0); 2783bb79becSeschrock } 2793bb79becSeschrock 280fa9e4066Sahrens /* 281fa9e4066Sahrens * Unmount the given filesystem. 282fa9e4066Sahrens */ 283fa9e4066Sahrens int 284fa9e4066Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 285fa9e4066Sahrens { 286fa9e4066Sahrens struct mnttab search = { 0 }, entry; 287fa9e4066Sahrens 288fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 2893bb79becSeschrock search.mnt_special = zhp->zfs_name; 29096dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 29199653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 292fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 29399653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 294fa9e4066Sahrens 295fa9e4066Sahrens if (mountpoint == NULL) 296fa9e4066Sahrens mountpoint = entry.mnt_mountp; 297fa9e4066Sahrens 298fa9e4066Sahrens /* 2993bb79becSeschrock * Unshare and unmount the filesystem 300fa9e4066Sahrens */ 301*f3861e1aSahl if (zfs_unshare_nfs(zhp, mountpoint) != 0 || 3023bb79becSeschrock unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 303fa9e4066Sahrens return (-1); 304fa9e4066Sahrens } 305fa9e4066Sahrens 306fa9e4066Sahrens return (0); 307fa9e4066Sahrens } 308fa9e4066Sahrens 309fa9e4066Sahrens /* 310fa9e4066Sahrens * Unmount this filesystem and any children inheriting the mountpoint property. 311fa9e4066Sahrens * To do this, just act like we're changing the mountpoint property, but don't 312fa9e4066Sahrens * remount the filesystems afterwards. 313fa9e4066Sahrens */ 314fa9e4066Sahrens int 315fa9e4066Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags) 316fa9e4066Sahrens { 317fa9e4066Sahrens prop_changelist_t *clp; 318fa9e4066Sahrens int ret; 319fa9e4066Sahrens 320fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 321fa9e4066Sahrens if (clp == NULL) 322fa9e4066Sahrens return (-1); 323fa9e4066Sahrens 324fa9e4066Sahrens ret = changelist_prefix(clp); 325fa9e4066Sahrens changelist_free(clp); 326fa9e4066Sahrens 327fa9e4066Sahrens return (ret); 328fa9e4066Sahrens } 329fa9e4066Sahrens 330*f3861e1aSahl boolean_t 331*f3861e1aSahl zfs_is_shared(zfs_handle_t *zhp) 332*f3861e1aSahl { 333*f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 334*f3861e1aSahl return (zfs_is_shared_iscsi(zhp)); 335*f3861e1aSahl 336*f3861e1aSahl return (zfs_is_shared_nfs(zhp, NULL)); 337*f3861e1aSahl } 338*f3861e1aSahl 339*f3861e1aSahl int 340*f3861e1aSahl zfs_share(zfs_handle_t *zhp) 341*f3861e1aSahl { 342*f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 343*f3861e1aSahl return (zfs_share_iscsi(zhp)); 344*f3861e1aSahl 345*f3861e1aSahl return (zfs_share_nfs(zhp)); 346*f3861e1aSahl } 347*f3861e1aSahl 348*f3861e1aSahl int 349*f3861e1aSahl zfs_unshare(zfs_handle_t *zhp) 350*f3861e1aSahl { 351*f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 352*f3861e1aSahl return (zfs_unshare_iscsi(zhp)); 353*f3861e1aSahl 354*f3861e1aSahl return (zfs_unshare_nfs(zhp, NULL)); 355*f3861e1aSahl } 356*f3861e1aSahl 357fa9e4066Sahrens /* 358fa9e4066Sahrens * Check to see if the filesystem is currently shared. 359fa9e4066Sahrens */ 36099653d4eSeschrock boolean_t 361*f3861e1aSahl zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) 362fa9e4066Sahrens { 363fa9e4066Sahrens char *mountpoint; 364fa9e4066Sahrens 365fa9e4066Sahrens if (!zfs_is_mounted(zhp, &mountpoint)) 36699653d4eSeschrock return (B_FALSE); 367fa9e4066Sahrens 36899653d4eSeschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 369fa9e4066Sahrens if (where != NULL) 370fa9e4066Sahrens *where = mountpoint; 371fa9e4066Sahrens else 372fa9e4066Sahrens free(mountpoint); 37399653d4eSeschrock return (B_TRUE); 374fa9e4066Sahrens } else { 375fa9e4066Sahrens free(mountpoint); 37699653d4eSeschrock return (B_FALSE); 377fa9e4066Sahrens } 378fa9e4066Sahrens } 379fa9e4066Sahrens 380fa9e4066Sahrens /* 381fa9e4066Sahrens * Share the given filesystem according to the options in 'sharenfs'. We rely 382fa9e4066Sahrens * on share(1M) to the dirty work for us. 383fa9e4066Sahrens */ 384fa9e4066Sahrens int 385*f3861e1aSahl zfs_share_nfs(zfs_handle_t *zhp) 386fa9e4066Sahrens { 387fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 388fa9e4066Sahrens char shareopts[ZFS_MAXPROPLEN]; 389fa9e4066Sahrens char buf[MAXPATHLEN]; 390fa9e4066Sahrens FILE *fp; 39199653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 392fa9e4066Sahrens 393e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 394fa9e4066Sahrens return (0); 395fa9e4066Sahrens 396*f3861e1aSahl /* 397*f3861e1aSahl * Return success if there are no share options. 398*f3861e1aSahl */ 399fa9e4066Sahrens if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 40099653d4eSeschrock NULL, NULL, 0, B_FALSE) != 0 || 401fa9e4066Sahrens strcmp(shareopts, "off") == 0) 402fa9e4066Sahrens return (0); 403fa9e4066Sahrens 404fa9e4066Sahrens /* 405e9dbad6fSeschrock * If the 'zoned' property is set, then zfs_is_mountable() will have 406e9dbad6fSeschrock * already bailed out if we are in the global zone. But local 407e9dbad6fSeschrock * zones cannot be NFS servers, so we ignore it for local zones as well. 408fa9e4066Sahrens */ 409e9dbad6fSeschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 410fa9e4066Sahrens return (0); 411fa9e4066Sahrens 412fa9e4066Sahrens /* 413fa9e4066Sahrens * Invoke the share(1M) command. We always do this, even if it's 414fa9e4066Sahrens * currently shared, as the options may have changed. 415fa9e4066Sahrens */ 416fa9e4066Sahrens if (strcmp(shareopts, "on") == 0) 417fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 418fa9e4066Sahrens "-F nfs \"%s\" 2>&1", mountpoint); 419fa9e4066Sahrens else 420fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 421fa9e4066Sahrens "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 422fa9e4066Sahrens mountpoint); 423fa9e4066Sahrens 42499653d4eSeschrock if ((fp = popen(buf, "r")) == NULL) 425*f3861e1aSahl return (zfs_error(hdl, EZFS_SHARENFSFAILED, 42699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 42799653d4eSeschrock zfs_get_name(zhp))); 428fa9e4066Sahrens 429fa9e4066Sahrens /* 430fa9e4066Sahrens * share(1M) should only produce output if there is some kind 431fa9e4066Sahrens * of error. All output begins with "share_nfs: ", so we trim 432fa9e4066Sahrens * this off to get to the real error. 433fa9e4066Sahrens */ 434fa9e4066Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 435fa9e4066Sahrens char *colon = strchr(buf, ':'); 436fa9e4066Sahrens 437fa9e4066Sahrens while (buf[strlen(buf) - 1] == '\n') 438fa9e4066Sahrens buf[strlen(buf) - 1] = '\0'; 439fa9e4066Sahrens 44099653d4eSeschrock if (colon != NULL) 44199653d4eSeschrock zfs_error_aux(hdl, colon + 2); 44299653d4eSeschrock 443*f3861e1aSahl (void) zfs_error(hdl, EZFS_SHARENFSFAILED, 4443bb79becSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 4453bb79becSeschrock zfs_get_name(zhp)); 446fa9e4066Sahrens 447fa9e4066Sahrens verify(pclose(fp) != 0); 448fa9e4066Sahrens return (-1); 449fa9e4066Sahrens } 450fa9e4066Sahrens 451fa9e4066Sahrens verify(pclose(fp) == 0); 452fa9e4066Sahrens 453fa9e4066Sahrens return (0); 454fa9e4066Sahrens } 455fa9e4066Sahrens 4563bb79becSeschrock /* 4573bb79becSeschrock * Unshare a filesystem by mountpoint. 4583bb79becSeschrock */ 4593bb79becSeschrock static int 4603bb79becSeschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 4613bb79becSeschrock { 4623bb79becSeschrock char buf[MAXPATHLEN]; 4633bb79becSeschrock FILE *fp; 4643bb79becSeschrock 4653bb79becSeschrock (void) snprintf(buf, sizeof (buf), 4663bb79becSeschrock "/usr/sbin/unshare \"%s\" 2>&1", 4673bb79becSeschrock mountpoint); 4683bb79becSeschrock 4693bb79becSeschrock if ((fp = popen(buf, "r")) == NULL) 470*f3861e1aSahl return (zfs_error(hdl, EZFS_UNSHARENFSFAILED, 4713bb79becSeschrock dgettext(TEXT_DOMAIN, 4723bb79becSeschrock "cannot unshare '%s'"), name)); 4733bb79becSeschrock 4743bb79becSeschrock /* 4753bb79becSeschrock * unshare(1M) should only produce output if there is 4763bb79becSeschrock * some kind of error. All output begins with "unshare 4773bb79becSeschrock * nfs: ", so we trim this off to get to the real error. 4783bb79becSeschrock */ 4793bb79becSeschrock if (fgets(buf, sizeof (buf), fp) != NULL) { 4803bb79becSeschrock char *colon = strchr(buf, ':'); 4813bb79becSeschrock 4823bb79becSeschrock while (buf[strlen(buf) - 1] == '\n') 4833bb79becSeschrock buf[strlen(buf) - 1] = '\0'; 4843bb79becSeschrock 4853bb79becSeschrock if (colon != NULL) 4863bb79becSeschrock zfs_error_aux(hdl, colon + 2); 4873bb79becSeschrock 4883bb79becSeschrock verify(pclose(fp) != 0); 4893bb79becSeschrock 490*f3861e1aSahl return (zfs_error(hdl, EZFS_UNSHARENFSFAILED, 4913bb79becSeschrock dgettext(TEXT_DOMAIN, 4923bb79becSeschrock "cannot unshare '%s'"), name)); 4933bb79becSeschrock } 4943bb79becSeschrock 4953bb79becSeschrock verify(pclose(fp) == 0); 4963bb79becSeschrock 4973bb79becSeschrock return (0); 4983bb79becSeschrock } 4993bb79becSeschrock 500fa9e4066Sahrens /* 501fa9e4066Sahrens * Unshare the given filesystem. 502fa9e4066Sahrens */ 503fa9e4066Sahrens int 504*f3861e1aSahl zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) 505fa9e4066Sahrens { 506fa9e4066Sahrens struct mnttab search = { 0 }, entry; 507fa9e4066Sahrens 508fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 509fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 51096dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 51199653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 512fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 51399653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 514fa9e4066Sahrens 515fa9e4066Sahrens if (mountpoint == NULL) 516fa9e4066Sahrens mountpoint = entry.mnt_mountp; 517fa9e4066Sahrens 5183bb79becSeschrock if (is_shared(zhp->zfs_hdl, mountpoint) && 5193bb79becSeschrock unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 5203bb79becSeschrock return (-1); 521fa9e4066Sahrens } 522fa9e4066Sahrens 523fa9e4066Sahrens return (0); 524fa9e4066Sahrens } 525fa9e4066Sahrens 526fa9e4066Sahrens /* 527*f3861e1aSahl * Same as zfs_unmountall(), but for NFS unshares. 528fa9e4066Sahrens */ 529fa9e4066Sahrens int 530*f3861e1aSahl zfs_unshareall_nfs(zfs_handle_t *zhp) 531fa9e4066Sahrens { 532fa9e4066Sahrens prop_changelist_t *clp; 533fa9e4066Sahrens int ret; 534fa9e4066Sahrens 535fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 536fa9e4066Sahrens if (clp == NULL) 537fa9e4066Sahrens return (-1); 538fa9e4066Sahrens 539fa9e4066Sahrens ret = changelist_unshare(clp); 540fa9e4066Sahrens changelist_free(clp); 541fa9e4066Sahrens 542fa9e4066Sahrens return (ret); 543fa9e4066Sahrens } 544fa9e4066Sahrens 545fa9e4066Sahrens /* 546fa9e4066Sahrens * Remove the mountpoint associated with the current dataset, if necessary. 547fa9e4066Sahrens * We only remove the underlying directory if: 548fa9e4066Sahrens * 549fa9e4066Sahrens * - The mountpoint is not 'none' or 'legacy' 550fa9e4066Sahrens * - The mountpoint is non-empty 551fa9e4066Sahrens * - The mountpoint is the default or inherited 552fa9e4066Sahrens * - The 'zoned' property is set, or we're in a local zone 553fa9e4066Sahrens * 554fa9e4066Sahrens * Any other directories we leave alone. 555fa9e4066Sahrens */ 556fa9e4066Sahrens void 557fa9e4066Sahrens remove_mountpoint(zfs_handle_t *zhp) 558fa9e4066Sahrens { 559fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 560e9dbad6fSeschrock zfs_source_t source; 561fa9e4066Sahrens 562e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 563e9dbad6fSeschrock &source)) 564fa9e4066Sahrens return; 565fa9e4066Sahrens 566e9dbad6fSeschrock if (source == ZFS_SRC_DEFAULT || 567e9dbad6fSeschrock source == ZFS_SRC_INHERITED) { 568fa9e4066Sahrens /* 569fa9e4066Sahrens * Try to remove the directory, silently ignoring any errors. 570fa9e4066Sahrens * The filesystem may have since been removed or moved around, 571e9dbad6fSeschrock * and this error isn't really useful to the administrator in 572e9dbad6fSeschrock * any way. 573fa9e4066Sahrens */ 574fa9e4066Sahrens (void) rmdir(mountpoint); 575fa9e4066Sahrens } 576fa9e4066Sahrens } 5773bb79becSeschrock 578*f3861e1aSahl boolean_t 579*f3861e1aSahl zfs_is_shared_iscsi(zfs_handle_t *zhp) 580*f3861e1aSahl { 581*f3861e1aSahl return (iscsitgt_zfs_is_shared(zhp->zfs_name) != 0); 582*f3861e1aSahl } 583*f3861e1aSahl 584*f3861e1aSahl int 585*f3861e1aSahl zfs_share_iscsi(zfs_handle_t *zhp) 586*f3861e1aSahl { 587*f3861e1aSahl char shareopts[ZFS_MAXPROPLEN]; 588*f3861e1aSahl const char *dataset = zhp->zfs_name; 589*f3861e1aSahl libzfs_handle_t *hdl = zhp->zfs_hdl; 590*f3861e1aSahl 591*f3861e1aSahl /* 592*f3861e1aSahl * Return success if there are no share options. 593*f3861e1aSahl */ 594*f3861e1aSahl if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 595*f3861e1aSahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 || 596*f3861e1aSahl strcmp(shareopts, "off") == 0) 597*f3861e1aSahl return (0); 598*f3861e1aSahl 599*f3861e1aSahl if (iscsitgt_zfs_share(dataset) != 0) 600*f3861e1aSahl return (zfs_error(hdl, EZFS_SHAREISCSIFAILED, 601*f3861e1aSahl dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset)); 602*f3861e1aSahl 603*f3861e1aSahl return (0); 604*f3861e1aSahl } 605*f3861e1aSahl 606*f3861e1aSahl int 607*f3861e1aSahl zfs_unshare_iscsi(zfs_handle_t *zhp) 608*f3861e1aSahl { 609*f3861e1aSahl const char *dataset = zfs_get_name(zhp); 610*f3861e1aSahl libzfs_handle_t *hdl = zhp->zfs_hdl; 611*f3861e1aSahl 612*f3861e1aSahl /* 613*f3861e1aSahl * If this fails with ENODEV it indicates that zvol wasn't shared so 614*f3861e1aSahl * we should return success in that case. 615*f3861e1aSahl */ 616*f3861e1aSahl if (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV) 617*f3861e1aSahl return (zfs_error(hdl, EZFS_UNSHAREISCSIFAILED, 618*f3861e1aSahl dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset)); 619*f3861e1aSahl 620*f3861e1aSahl return (0); 621*f3861e1aSahl } 622*f3861e1aSahl 6233bb79becSeschrock typedef struct mount_cbdata { 6243bb79becSeschrock zfs_handle_t **cb_datasets; 6253bb79becSeschrock int cb_used; 6263bb79becSeschrock int cb_alloc; 6273bb79becSeschrock } mount_cbdata_t; 6283bb79becSeschrock 6293bb79becSeschrock static int 6303bb79becSeschrock mount_cb(zfs_handle_t *zhp, void *data) 6313bb79becSeschrock { 6323bb79becSeschrock mount_cbdata_t *cbp = data; 6333bb79becSeschrock 634*f3861e1aSahl if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) { 6353bb79becSeschrock zfs_close(zhp); 6363bb79becSeschrock return (0); 6373bb79becSeschrock } 6383bb79becSeschrock 6393bb79becSeschrock if (cbp->cb_alloc == cbp->cb_used) { 640e9dbad6fSeschrock void *ptr; 6413bb79becSeschrock 642e9dbad6fSeschrock if ((ptr = zfs_realloc(zhp->zfs_hdl, 643e9dbad6fSeschrock cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), 644e9dbad6fSeschrock cbp->cb_alloc * 2 * sizeof (void *))) == NULL) 6453bb79becSeschrock return (-1); 646e9dbad6fSeschrock cbp->cb_datasets = ptr; 6473bb79becSeschrock 648e9dbad6fSeschrock cbp->cb_alloc *= 2; 6493bb79becSeschrock } 6503bb79becSeschrock 6513bb79becSeschrock cbp->cb_datasets[cbp->cb_used++] = zhp; 652*f3861e1aSahl 653*f3861e1aSahl return (zfs_iter_children(zhp, mount_cb, cbp)); 6543bb79becSeschrock } 6553bb79becSeschrock 6563bb79becSeschrock static int 657*f3861e1aSahl dataset_cmp(const void *a, const void *b) 6583bb79becSeschrock { 6593bb79becSeschrock zfs_handle_t **za = (zfs_handle_t **)a; 6603bb79becSeschrock zfs_handle_t **zb = (zfs_handle_t **)b; 6613bb79becSeschrock char mounta[MAXPATHLEN]; 6623bb79becSeschrock char mountb[MAXPATHLEN]; 663*f3861e1aSahl boolean_t gota, gotb; 664*f3861e1aSahl 665*f3861e1aSahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 666*f3861e1aSahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 667*f3861e1aSahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 668*f3861e1aSahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 669*f3861e1aSahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 670*f3861e1aSahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 671*f3861e1aSahl 672*f3861e1aSahl if (gota && gotb) 673*f3861e1aSahl return (strcmp(mounta, mountb)); 6743bb79becSeschrock 675*f3861e1aSahl if (gota) 676*f3861e1aSahl return (-1); 677*f3861e1aSahl if (gotb) 678*f3861e1aSahl return (1); 6793bb79becSeschrock 680*f3861e1aSahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 6813bb79becSeschrock } 6823bb79becSeschrock 683*f3861e1aSahl /* 684*f3861e1aSahl * Mount and share all datasets within the given pool. This assumes that no 685*f3861e1aSahl * datasets within the pool are currently mounted. Because users can create 686*f3861e1aSahl * complicated nested hierarchies of mountpoints, we first gather all the 687*f3861e1aSahl * datasets and mountpoints within the pool, and sort them by mountpoint. Once 688*f3861e1aSahl * we have the list of all filesystems, we iterate over them in order and mount 689*f3861e1aSahl * and/or share each one. 690*f3861e1aSahl */ 691*f3861e1aSahl #pragma weak zpool_mount_datasets = zpool_enable_datasets 6923bb79becSeschrock int 693*f3861e1aSahl zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 6943bb79becSeschrock { 6953bb79becSeschrock mount_cbdata_t cb = { 0 }; 6963bb79becSeschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 6973bb79becSeschrock zfs_handle_t *zfsp; 6983bb79becSeschrock int i, ret = -1; 6993bb79becSeschrock 7003bb79becSeschrock /* 7013bb79becSeschrock * Gather all datasets within the pool. 7023bb79becSeschrock */ 7033bb79becSeschrock if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 7043bb79becSeschrock return (-1); 7053bb79becSeschrock cb.cb_alloc = 4; 7063bb79becSeschrock 7073bb79becSeschrock if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 7083bb79becSeschrock goto out; 7093bb79becSeschrock 7103bb79becSeschrock cb.cb_datasets[0] = zfsp; 7113bb79becSeschrock cb.cb_used = 1; 7123bb79becSeschrock 7133bb79becSeschrock if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 7143bb79becSeschrock goto out; 7153bb79becSeschrock 7163bb79becSeschrock /* 7173bb79becSeschrock * Sort the datasets by mountpoint. 7183bb79becSeschrock */ 719*f3861e1aSahl qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp); 7203bb79becSeschrock 7213bb79becSeschrock /* 7223bb79becSeschrock * And mount all the datasets. 7233bb79becSeschrock */ 7243bb79becSeschrock ret = 0; 7253bb79becSeschrock for (i = 0; i < cb.cb_used; i++) { 726c08432ebSeschrock if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 7273bb79becSeschrock zfs_share(cb.cb_datasets[i]) != 0) 7283bb79becSeschrock ret = -1; 7293bb79becSeschrock } 7303bb79becSeschrock 7313bb79becSeschrock out: 7323bb79becSeschrock for (i = 0; i < cb.cb_used; i++) 7333bb79becSeschrock zfs_close(cb.cb_datasets[i]); 7343bb79becSeschrock free(cb.cb_datasets); 7353bb79becSeschrock 7363bb79becSeschrock return (ret); 7373bb79becSeschrock } 7383bb79becSeschrock 739*f3861e1aSahl 740*f3861e1aSahl static int 741*f3861e1aSahl zvol_cb(const char *dataset, void *data) 742*f3861e1aSahl { 743*f3861e1aSahl libzfs_handle_t *hdl = data; 744*f3861e1aSahl zfs_handle_t *zhp; 745*f3861e1aSahl 746*f3861e1aSahl /* 747*f3861e1aSahl * Ignore snapshots and ignore failures from non-existant datasets. 748*f3861e1aSahl */ 749*f3861e1aSahl if (strchr(dataset, '@') != NULL || 750*f3861e1aSahl (zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL) 751*f3861e1aSahl return (0); 752*f3861e1aSahl 753*f3861e1aSahl (void) zfs_unshare_iscsi(zhp); 754*f3861e1aSahl 755*f3861e1aSahl zfs_close(zhp); 756*f3861e1aSahl 757*f3861e1aSahl return (0); 758*f3861e1aSahl } 759*f3861e1aSahl 7603bb79becSeschrock static int 7613bb79becSeschrock mountpoint_compare(const void *a, const void *b) 7623bb79becSeschrock { 7633bb79becSeschrock const char *mounta = *((char **)a); 7643bb79becSeschrock const char *mountb = *((char **)b); 7653bb79becSeschrock 7663bb79becSeschrock return (strcmp(mountb, mounta)); 7673bb79becSeschrock } 7683bb79becSeschrock 769*f3861e1aSahl /* 770*f3861e1aSahl * Unshare and unmount all datasets within the given pool. We don't want to 771*f3861e1aSahl * rely on traversing the DSL to discover the filesystems within the pool, 772*f3861e1aSahl * because this may be expensive (if not all of them are mounted), and can fail 773*f3861e1aSahl * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 774*f3861e1aSahl * gather all the filesystems that are currently mounted. 775*f3861e1aSahl */ 776*f3861e1aSahl #pragma weak zpool_unmount_datasets = zpool_disable_datasets 7773bb79becSeschrock int 778*f3861e1aSahl zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) 7793bb79becSeschrock { 7803bb79becSeschrock int used, alloc; 7813bb79becSeschrock struct mnttab entry; 7823bb79becSeschrock size_t namelen; 7833bb79becSeschrock char **mountpoints = NULL; 7843bb79becSeschrock zfs_handle_t **datasets = NULL; 7853bb79becSeschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 7863bb79becSeschrock int i; 7873bb79becSeschrock int ret = -1; 7883bb79becSeschrock int flags = (force ? MS_FORCE : 0); 7893bb79becSeschrock 790*f3861e1aSahl /* 791*f3861e1aSahl * First unshare all zvols. 792*f3861e1aSahl */ 793*f3861e1aSahl if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0) 794*f3861e1aSahl return (-1); 795*f3861e1aSahl 7963bb79becSeschrock namelen = strlen(zhp->zpool_name); 7973bb79becSeschrock 7983bb79becSeschrock rewind(hdl->libzfs_mnttab); 7993bb79becSeschrock used = alloc = 0; 8003bb79becSeschrock while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 8013bb79becSeschrock /* 8023bb79becSeschrock * Ignore non-ZFS entries. 8033bb79becSeschrock */ 8043bb79becSeschrock if (entry.mnt_fstype == NULL || 8053bb79becSeschrock strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 8063bb79becSeschrock continue; 8073bb79becSeschrock 8083bb79becSeschrock /* 8093bb79becSeschrock * Ignore filesystems not within this pool. 8103bb79becSeschrock */ 8113bb79becSeschrock if (entry.mnt_mountp == NULL || 8123bb79becSeschrock strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 8133bb79becSeschrock (entry.mnt_special[namelen] != '/' && 8143bb79becSeschrock entry.mnt_special[namelen] != '\0')) 8153bb79becSeschrock continue; 8163bb79becSeschrock 8173bb79becSeschrock /* 8183bb79becSeschrock * At this point we've found a filesystem within our pool. Add 8193bb79becSeschrock * it to our growing list. 8203bb79becSeschrock */ 8213bb79becSeschrock if (used == alloc) { 8223bb79becSeschrock if (alloc == 0) { 8233bb79becSeschrock if ((mountpoints = zfs_alloc(hdl, 8243bb79becSeschrock 8 * sizeof (void *))) == NULL) 8253bb79becSeschrock goto out; 8263bb79becSeschrock 8273bb79becSeschrock if ((datasets = zfs_alloc(hdl, 8283bb79becSeschrock 8 * sizeof (void *))) == NULL) 8293bb79becSeschrock goto out; 8303bb79becSeschrock 8313bb79becSeschrock alloc = 8; 8323bb79becSeschrock } else { 833e9dbad6fSeschrock void *ptr; 8343bb79becSeschrock 835e9dbad6fSeschrock if ((ptr = zfs_realloc(hdl, mountpoints, 836e9dbad6fSeschrock alloc * sizeof (void *), 8373bb79becSeschrock alloc * 2 * sizeof (void *))) == NULL) 8383bb79becSeschrock goto out; 839e9dbad6fSeschrock mountpoints = ptr; 8403bb79becSeschrock 841e9dbad6fSeschrock if ((ptr = zfs_realloc(hdl, datasets, 842e9dbad6fSeschrock alloc * sizeof (void *), 8433bb79becSeschrock alloc * 2 * sizeof (void *))) == NULL) 8443bb79becSeschrock goto out; 845e9dbad6fSeschrock datasets = ptr; 8463bb79becSeschrock 8473bb79becSeschrock alloc *= 2; 8483bb79becSeschrock } 8493bb79becSeschrock } 8503bb79becSeschrock 8513bb79becSeschrock if ((mountpoints[used] = zfs_strdup(hdl, 8523bb79becSeschrock entry.mnt_mountp)) == NULL) 8533bb79becSeschrock goto out; 8543bb79becSeschrock 8553bb79becSeschrock /* 8563bb79becSeschrock * This is allowed to fail, in case there is some I/O error. It 8573bb79becSeschrock * is only used to determine if we need to remove the underlying 8583bb79becSeschrock * mountpoint, so failure is not fatal. 8593bb79becSeschrock */ 8603bb79becSeschrock datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 8613bb79becSeschrock 8623bb79becSeschrock used++; 8633bb79becSeschrock } 8643bb79becSeschrock 8653bb79becSeschrock /* 8663bb79becSeschrock * At this point, we have the entire list of filesystems, so sort it by 8673bb79becSeschrock * mountpoint. 8683bb79becSeschrock */ 8693bb79becSeschrock qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 8703bb79becSeschrock 8713bb79becSeschrock /* 8723bb79becSeschrock * Walk through and first unshare everything. 8733bb79becSeschrock */ 8743bb79becSeschrock for (i = 0; i < used; i++) { 8753bb79becSeschrock if (is_shared(hdl, mountpoints[i]) && 876e9dbad6fSeschrock unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) 8773bb79becSeschrock goto out; 8783bb79becSeschrock } 8793bb79becSeschrock 8803bb79becSeschrock /* 8813bb79becSeschrock * Now unmount everything, removing the underlying directories as 8823bb79becSeschrock * appropriate. 8833bb79becSeschrock */ 8843bb79becSeschrock for (i = 0; i < used; i++) { 8853bb79becSeschrock if (unmount_one(hdl, mountpoints[i], flags) != 0) 8863bb79becSeschrock goto out; 887e9dbad6fSeschrock } 8883bb79becSeschrock 889e9dbad6fSeschrock for (i = 0; i < used; i++) { 8903bb79becSeschrock if (datasets[i]) 8913bb79becSeschrock remove_mountpoint(datasets[i]); 8923bb79becSeschrock } 8933bb79becSeschrock 8943bb79becSeschrock ret = 0; 8953bb79becSeschrock out: 8963bb79becSeschrock for (i = 0; i < used; i++) { 8973bb79becSeschrock if (datasets[i]) 8983bb79becSeschrock zfs_close(datasets[i]); 8993bb79becSeschrock free(mountpoints[i]); 9003bb79becSeschrock } 9013bb79becSeschrock free(datasets); 9023bb79becSeschrock free(mountpoints); 9033bb79becSeschrock 9043bb79becSeschrock return (ret); 9053bb79becSeschrock } 906