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 */ 21f3861e1aSahl 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 31f3861e1aSahl * to deal with the OS. The following functions are the main entry points -- 32f3861e1aSahl * they are used by mount and unmount and when changing a filesystem's 33f3861e1aSahl * mountpoint. 34fa9e4066Sahrens * 35fa9e4066Sahrens * zfs_is_mounted() 36fa9e4066Sahrens * zfs_mount() 37fa9e4066Sahrens * zfs_unmount() 38fa9e4066Sahrens * zfs_unmountall() 39fa9e4066Sahrens * 40f3861e1aSahl * This file also contains the functions used to manage sharing filesystems via 41f3861e1aSahl * NFS and iSCSI: 42fa9e4066Sahrens * 43fa9e4066Sahrens * zfs_is_shared() 44fa9e4066Sahrens * zfs_share() 45fa9e4066Sahrens * zfs_unshare() 46f3861e1aSahl * 47f3861e1aSahl * zfs_is_shared_nfs() 48f3861e1aSahl * zfs_share_nfs() 49f3861e1aSahl * zfs_unshare_nfs() 50f3861e1aSahl * zfs_unshareall_nfs() 51f3861e1aSahl * zfs_is_shared_iscsi() 52f3861e1aSahl * zfs_share_iscsi() 53f3861e1aSahl * zfs_unshare_iscsi() 543bb79becSeschrock * 553bb79becSeschrock * The following functions are available for pool consumers, and will 56f3861e1aSahl * mount/unmount and share/unshare all datasets within pool: 573bb79becSeschrock * 58f3861e1aSahl * zpool_enable_datasets() 59f3861e1aSahl * zpool_disable_datasets() 60fa9e4066Sahrens */ 61fa9e4066Sahrens 62fa9e4066Sahrens #include <dirent.h> 63*d8d59944Sahl #include <dlfcn.h> 64fa9e4066Sahrens #include <errno.h> 65fa9e4066Sahrens #include <libgen.h> 66fa9e4066Sahrens #include <libintl.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 81*d8d59944Sahl static int (*iscsitgt_zfs_share)(const char *); 82*d8d59944Sahl static int (*iscsitgt_zfs_unshare)(const char *); 83*d8d59944Sahl static int (*iscsitgt_zfs_is_shared)(const char *); 84*d8d59944Sahl 85*d8d59944Sahl #pragma init(zfs_iscsi_init) 86*d8d59944Sahl static void 87*d8d59944Sahl zfs_iscsi_init(void) 88*d8d59944Sahl { 89*d8d59944Sahl void *libiscsitgt; 90*d8d59944Sahl 91*d8d59944Sahl if ((libiscsitgt = dlopen("/lib/libiscsitgt.so.1", 92*d8d59944Sahl RTLD_LAZY | RTLD_GLOBAL)) == NULL || 93*d8d59944Sahl (iscsitgt_zfs_share = (int (*)(const char *))dlsym(libiscsitgt, 94*d8d59944Sahl "iscsitgt_zfs_share")) == NULL || 95*d8d59944Sahl (iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt, 96*d8d59944Sahl "iscsitgt_zfs_unshare")) == NULL || 97*d8d59944Sahl (iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt, 98*d8d59944Sahl "iscsitgt_zfs_is_shared")) == NULL) { 99*d8d59944Sahl iscsitgt_zfs_share = NULL; 100*d8d59944Sahl iscsitgt_zfs_unshare = NULL; 101*d8d59944Sahl iscsitgt_zfs_is_shared = NULL; 102*d8d59944Sahl } 103*d8d59944Sahl } 104*d8d59944Sahl 105fa9e4066Sahrens /* 10699653d4eSeschrock * Search the sharetab for the given mountpoint, returning true if it is found. 107fa9e4066Sahrens */ 10899653d4eSeschrock static boolean_t 10999653d4eSeschrock is_shared(libzfs_handle_t *hdl, const char *mountpoint) 110fa9e4066Sahrens { 111fa9e4066Sahrens char buf[MAXPATHLEN], *tab; 112fa9e4066Sahrens 11399653d4eSeschrock if (hdl->libzfs_sharetab == NULL) 114fa9e4066Sahrens return (0); 115fa9e4066Sahrens 11699653d4eSeschrock (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 117fa9e4066Sahrens 11899653d4eSeschrock while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 119fa9e4066Sahrens 120fa9e4066Sahrens /* the mountpoint is the first entry on each line */ 121fa9e4066Sahrens if ((tab = strchr(buf, '\t')) != NULL) { 122fa9e4066Sahrens *tab = '\0'; 123fa9e4066Sahrens if (strcmp(buf, mountpoint) == 0) 12499653d4eSeschrock return (B_TRUE); 125fa9e4066Sahrens } 126fa9e4066Sahrens } 127fa9e4066Sahrens 12899653d4eSeschrock return (B_FALSE); 129fa9e4066Sahrens } 130fa9e4066Sahrens 131fa9e4066Sahrens /* 13299653d4eSeschrock * Returns true if the specified directory is empty. If we can't open the 13399653d4eSeschrock * directory at all, return true so that the mount can fail with a more 134fa9e4066Sahrens * informative error message. 135fa9e4066Sahrens */ 13699653d4eSeschrock static boolean_t 137fa9e4066Sahrens dir_is_empty(const char *dirname) 138fa9e4066Sahrens { 139fa9e4066Sahrens DIR *dirp; 140fa9e4066Sahrens struct dirent64 *dp; 141fa9e4066Sahrens 142fa9e4066Sahrens if ((dirp = opendir(dirname)) == NULL) 14399653d4eSeschrock return (B_TRUE); 144fa9e4066Sahrens 145fa9e4066Sahrens while ((dp = readdir64(dirp)) != NULL) { 146fa9e4066Sahrens 147fa9e4066Sahrens if (strcmp(dp->d_name, ".") == 0 || 148fa9e4066Sahrens strcmp(dp->d_name, "..") == 0) 149fa9e4066Sahrens continue; 150fa9e4066Sahrens 151fa9e4066Sahrens (void) closedir(dirp); 15299653d4eSeschrock return (B_FALSE); 153fa9e4066Sahrens } 154fa9e4066Sahrens 155fa9e4066Sahrens (void) closedir(dirp); 15699653d4eSeschrock return (B_TRUE); 157fa9e4066Sahrens } 158fa9e4066Sahrens 159fa9e4066Sahrens /* 160fa9e4066Sahrens * Checks to see if the mount is active. If the filesystem is mounted, we fill 161fa9e4066Sahrens * in 'where' with the current mountpoint, and return 1. Otherwise, we return 162fa9e4066Sahrens * 0. 163fa9e4066Sahrens */ 16499653d4eSeschrock boolean_t 165fa9e4066Sahrens zfs_is_mounted(zfs_handle_t *zhp, char **where) 166fa9e4066Sahrens { 167fa9e4066Sahrens struct mnttab search = { 0 }, entry; 168fa9e4066Sahrens 169fa9e4066Sahrens /* 170fa9e4066Sahrens * Search for the entry in /etc/mnttab. We don't bother getting the 171fa9e4066Sahrens * mountpoint, as we can just search for the special device. This will 172fa9e4066Sahrens * also let us find mounts when the mountpoint is 'legacy'. 173fa9e4066Sahrens */ 174fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 17596dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 176fa9e4066Sahrens 17799653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 17899653d4eSeschrock if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 17999653d4eSeschrock return (B_FALSE); 180fa9e4066Sahrens 181fa9e4066Sahrens if (where != NULL) 18299653d4eSeschrock *where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); 183fa9e4066Sahrens 18499653d4eSeschrock return (B_TRUE); 185fa9e4066Sahrens } 186fa9e4066Sahrens 187e9dbad6fSeschrock /* 188e9dbad6fSeschrock * Returns true if the given dataset is mountable, false otherwise. Returns the 189e9dbad6fSeschrock * mountpoint in 'buf'. 190e9dbad6fSeschrock */ 191e9dbad6fSeschrock static boolean_t 192e9dbad6fSeschrock zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 193e9dbad6fSeschrock zfs_source_t *source) 194e9dbad6fSeschrock { 195e9dbad6fSeschrock char sourceloc[ZFS_MAXNAMELEN]; 196e9dbad6fSeschrock zfs_source_t sourcetype; 197e9dbad6fSeschrock 198e9dbad6fSeschrock if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 199e9dbad6fSeschrock return (B_FALSE); 200e9dbad6fSeschrock 201e9dbad6fSeschrock verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 202e9dbad6fSeschrock &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 203e9dbad6fSeschrock 204e9dbad6fSeschrock if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 205e9dbad6fSeschrock strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 206e9dbad6fSeschrock return (B_FALSE); 207e9dbad6fSeschrock 208e9dbad6fSeschrock if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) 209e9dbad6fSeschrock return (B_FALSE); 210e9dbad6fSeschrock 211e9dbad6fSeschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 212e9dbad6fSeschrock getzoneid() == GLOBAL_ZONEID) 213e9dbad6fSeschrock return (B_FALSE); 214e9dbad6fSeschrock 215e9dbad6fSeschrock if (source) 216e9dbad6fSeschrock *source = sourcetype; 217e9dbad6fSeschrock 218e9dbad6fSeschrock return (B_TRUE); 219e9dbad6fSeschrock } 220e9dbad6fSeschrock 221fa9e4066Sahrens /* 222fa9e4066Sahrens * Mount the given filesystem. 223fa9e4066Sahrens */ 224fa9e4066Sahrens int 225fa9e4066Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 226fa9e4066Sahrens { 227fa9e4066Sahrens struct stat buf; 228fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 229fa9e4066Sahrens char mntopts[MNT_LINE_MAX]; 23099653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 231fa9e4066Sahrens 232fa9e4066Sahrens if (options == NULL) 233fa9e4066Sahrens mntopts[0] = '\0'; 234fa9e4066Sahrens else 235fa9e4066Sahrens (void) strlcpy(mntopts, options, sizeof (mntopts)); 236fa9e4066Sahrens 237e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 23899653d4eSeschrock return (0); 239fa9e4066Sahrens 240fa9e4066Sahrens /* Create the directory if it doesn't already exist */ 241fa9e4066Sahrens if (lstat(mountpoint, &buf) != 0) { 242fa9e4066Sahrens if (mkdirp(mountpoint, 0755) != 0) { 24399653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 24499653d4eSeschrock "failed to create mountpoint")); 24599653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 24699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 24799653d4eSeschrock mountpoint)); 248fa9e4066Sahrens } 249fa9e4066Sahrens } 250fa9e4066Sahrens 251fa9e4066Sahrens /* 252fa9e4066Sahrens * Determine if the mountpoint is empty. If so, refuse to perform the 253fa9e4066Sahrens * mount. We don't perform this check if MS_OVERLAY is specified, which 254fa9e4066Sahrens * would defeat the point. We also avoid this check if 'remount' is 255fa9e4066Sahrens * specified. 256fa9e4066Sahrens */ 257fa9e4066Sahrens if ((flags & MS_OVERLAY) == 0 && 258fa9e4066Sahrens strstr(mntopts, MNTOPT_REMOUNT) == NULL && 259fa9e4066Sahrens !dir_is_empty(mountpoint)) { 26099653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26199653d4eSeschrock "directory is not empty")); 26299653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 26399653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 264fa9e4066Sahrens } 265fa9e4066Sahrens 266fa9e4066Sahrens /* perform the mount */ 267fa9e4066Sahrens if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 268fa9e4066Sahrens MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 269fa9e4066Sahrens /* 270fa9e4066Sahrens * Generic errors are nasty, but there are just way too many 271fa9e4066Sahrens * from mount(), and they're well-understood. We pick a few 272fa9e4066Sahrens * common ones to improve upon. 273fa9e4066Sahrens */ 27499653d4eSeschrock if (errno == EBUSY) 27599653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 27699653d4eSeschrock "mountpoint or dataset is busy")); 27799653d4eSeschrock else 27899653d4eSeschrock zfs_error_aux(hdl, strerror(errno)); 27999653d4eSeschrock 28099653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 28199653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 28299653d4eSeschrock zhp->zfs_name)); 283fa9e4066Sahrens } 284fa9e4066Sahrens 285fa9e4066Sahrens return (0); 286fa9e4066Sahrens } 287fa9e4066Sahrens 2883bb79becSeschrock /* 2893bb79becSeschrock * Unmount a single filesystem. 2903bb79becSeschrock */ 2913bb79becSeschrock static int 2923bb79becSeschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 2933bb79becSeschrock { 2943bb79becSeschrock if (umount2(mountpoint, flags) != 0) { 2953bb79becSeschrock zfs_error_aux(hdl, strerror(errno)); 2963bb79becSeschrock return (zfs_error(hdl, EZFS_UMOUNTFAILED, 2973bb79becSeschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 2983bb79becSeschrock mountpoint)); 2993bb79becSeschrock } 3003bb79becSeschrock 3013bb79becSeschrock return (0); 3023bb79becSeschrock } 3033bb79becSeschrock 304fa9e4066Sahrens /* 305fa9e4066Sahrens * Unmount the given filesystem. 306fa9e4066Sahrens */ 307fa9e4066Sahrens int 308fa9e4066Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 309fa9e4066Sahrens { 310fa9e4066Sahrens struct mnttab search = { 0 }, entry; 311fa9e4066Sahrens 312fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 3133bb79becSeschrock search.mnt_special = zhp->zfs_name; 31496dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 31599653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 316fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 31799653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 318fa9e4066Sahrens 319fa9e4066Sahrens if (mountpoint == NULL) 320fa9e4066Sahrens mountpoint = entry.mnt_mountp; 321fa9e4066Sahrens 322fa9e4066Sahrens /* 3233bb79becSeschrock * Unshare and unmount the filesystem 324fa9e4066Sahrens */ 325f3861e1aSahl if (zfs_unshare_nfs(zhp, mountpoint) != 0 || 3263bb79becSeschrock unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 327fa9e4066Sahrens return (-1); 328fa9e4066Sahrens } 329fa9e4066Sahrens 330fa9e4066Sahrens return (0); 331fa9e4066Sahrens } 332fa9e4066Sahrens 333fa9e4066Sahrens /* 334fa9e4066Sahrens * Unmount this filesystem and any children inheriting the mountpoint property. 335fa9e4066Sahrens * To do this, just act like we're changing the mountpoint property, but don't 336fa9e4066Sahrens * remount the filesystems afterwards. 337fa9e4066Sahrens */ 338fa9e4066Sahrens int 339fa9e4066Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags) 340fa9e4066Sahrens { 341fa9e4066Sahrens prop_changelist_t *clp; 342fa9e4066Sahrens int ret; 343fa9e4066Sahrens 344fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 345fa9e4066Sahrens if (clp == NULL) 346fa9e4066Sahrens return (-1); 347fa9e4066Sahrens 348fa9e4066Sahrens ret = changelist_prefix(clp); 349fa9e4066Sahrens changelist_free(clp); 350fa9e4066Sahrens 351fa9e4066Sahrens return (ret); 352fa9e4066Sahrens } 353fa9e4066Sahrens 354f3861e1aSahl boolean_t 355f3861e1aSahl zfs_is_shared(zfs_handle_t *zhp) 356f3861e1aSahl { 357f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 358f3861e1aSahl return (zfs_is_shared_iscsi(zhp)); 359f3861e1aSahl 360f3861e1aSahl return (zfs_is_shared_nfs(zhp, NULL)); 361f3861e1aSahl } 362f3861e1aSahl 363f3861e1aSahl int 364f3861e1aSahl zfs_share(zfs_handle_t *zhp) 365f3861e1aSahl { 366f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 367f3861e1aSahl return (zfs_share_iscsi(zhp)); 368f3861e1aSahl 369f3861e1aSahl return (zfs_share_nfs(zhp)); 370f3861e1aSahl } 371f3861e1aSahl 372f3861e1aSahl int 373f3861e1aSahl zfs_unshare(zfs_handle_t *zhp) 374f3861e1aSahl { 375f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 376f3861e1aSahl return (zfs_unshare_iscsi(zhp)); 377f3861e1aSahl 378f3861e1aSahl return (zfs_unshare_nfs(zhp, NULL)); 379f3861e1aSahl } 380f3861e1aSahl 381fa9e4066Sahrens /* 382fa9e4066Sahrens * Check to see if the filesystem is currently shared. 383fa9e4066Sahrens */ 38499653d4eSeschrock boolean_t 385f3861e1aSahl zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) 386fa9e4066Sahrens { 387fa9e4066Sahrens char *mountpoint; 388fa9e4066Sahrens 389fa9e4066Sahrens if (!zfs_is_mounted(zhp, &mountpoint)) 39099653d4eSeschrock return (B_FALSE); 391fa9e4066Sahrens 39299653d4eSeschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 393fa9e4066Sahrens if (where != NULL) 394fa9e4066Sahrens *where = mountpoint; 395fa9e4066Sahrens else 396fa9e4066Sahrens free(mountpoint); 39799653d4eSeschrock return (B_TRUE); 398fa9e4066Sahrens } else { 399fa9e4066Sahrens free(mountpoint); 40099653d4eSeschrock return (B_FALSE); 401fa9e4066Sahrens } 402fa9e4066Sahrens } 403fa9e4066Sahrens 404fa9e4066Sahrens /* 405fa9e4066Sahrens * Share the given filesystem according to the options in 'sharenfs'. We rely 406fa9e4066Sahrens * on share(1M) to the dirty work for us. 407fa9e4066Sahrens */ 408fa9e4066Sahrens int 409f3861e1aSahl zfs_share_nfs(zfs_handle_t *zhp) 410fa9e4066Sahrens { 411fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 412fa9e4066Sahrens char shareopts[ZFS_MAXPROPLEN]; 413fa9e4066Sahrens char buf[MAXPATHLEN]; 414fa9e4066Sahrens FILE *fp; 41599653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 416fa9e4066Sahrens 417e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 418fa9e4066Sahrens return (0); 419fa9e4066Sahrens 420f3861e1aSahl /* 421f3861e1aSahl * Return success if there are no share options. 422f3861e1aSahl */ 423fa9e4066Sahrens if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 42499653d4eSeschrock NULL, NULL, 0, B_FALSE) != 0 || 425fa9e4066Sahrens strcmp(shareopts, "off") == 0) 426fa9e4066Sahrens return (0); 427fa9e4066Sahrens 428fa9e4066Sahrens /* 429e9dbad6fSeschrock * If the 'zoned' property is set, then zfs_is_mountable() will have 430e9dbad6fSeschrock * already bailed out if we are in the global zone. But local 431e9dbad6fSeschrock * zones cannot be NFS servers, so we ignore it for local zones as well. 432fa9e4066Sahrens */ 433e9dbad6fSeschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 434fa9e4066Sahrens return (0); 435fa9e4066Sahrens 436fa9e4066Sahrens /* 437fa9e4066Sahrens * Invoke the share(1M) command. We always do this, even if it's 438fa9e4066Sahrens * currently shared, as the options may have changed. 439fa9e4066Sahrens */ 440fa9e4066Sahrens if (strcmp(shareopts, "on") == 0) 441fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 442fa9e4066Sahrens "-F nfs \"%s\" 2>&1", mountpoint); 443fa9e4066Sahrens else 444fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 445fa9e4066Sahrens "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 446fa9e4066Sahrens mountpoint); 447fa9e4066Sahrens 44899653d4eSeschrock if ((fp = popen(buf, "r")) == NULL) 449f3861e1aSahl return (zfs_error(hdl, EZFS_SHARENFSFAILED, 45099653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 45199653d4eSeschrock zfs_get_name(zhp))); 452fa9e4066Sahrens 453fa9e4066Sahrens /* 454fa9e4066Sahrens * share(1M) should only produce output if there is some kind 455fa9e4066Sahrens * of error. All output begins with "share_nfs: ", so we trim 456fa9e4066Sahrens * this off to get to the real error. 457fa9e4066Sahrens */ 458fa9e4066Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 459fa9e4066Sahrens char *colon = strchr(buf, ':'); 460fa9e4066Sahrens 461fa9e4066Sahrens while (buf[strlen(buf) - 1] == '\n') 462fa9e4066Sahrens buf[strlen(buf) - 1] = '\0'; 463fa9e4066Sahrens 46499653d4eSeschrock if (colon != NULL) 46599653d4eSeschrock zfs_error_aux(hdl, colon + 2); 46699653d4eSeschrock 467f3861e1aSahl (void) zfs_error(hdl, EZFS_SHARENFSFAILED, 4683bb79becSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 4693bb79becSeschrock zfs_get_name(zhp)); 470fa9e4066Sahrens 471fa9e4066Sahrens verify(pclose(fp) != 0); 472fa9e4066Sahrens return (-1); 473fa9e4066Sahrens } 474fa9e4066Sahrens 475fa9e4066Sahrens verify(pclose(fp) == 0); 476fa9e4066Sahrens 477fa9e4066Sahrens return (0); 478fa9e4066Sahrens } 479fa9e4066Sahrens 4803bb79becSeschrock /* 4813bb79becSeschrock * Unshare a filesystem by mountpoint. 4823bb79becSeschrock */ 4833bb79becSeschrock static int 4843bb79becSeschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 4853bb79becSeschrock { 4863bb79becSeschrock char buf[MAXPATHLEN]; 4873bb79becSeschrock FILE *fp; 4883bb79becSeschrock 4893bb79becSeschrock (void) snprintf(buf, sizeof (buf), 4903bb79becSeschrock "/usr/sbin/unshare \"%s\" 2>&1", 4913bb79becSeschrock mountpoint); 4923bb79becSeschrock 4933bb79becSeschrock if ((fp = popen(buf, "r")) == NULL) 494f3861e1aSahl return (zfs_error(hdl, EZFS_UNSHARENFSFAILED, 4953bb79becSeschrock dgettext(TEXT_DOMAIN, 4963bb79becSeschrock "cannot unshare '%s'"), name)); 4973bb79becSeschrock 4983bb79becSeschrock /* 4993bb79becSeschrock * unshare(1M) should only produce output if there is 5003bb79becSeschrock * some kind of error. All output begins with "unshare 5013bb79becSeschrock * nfs: ", so we trim this off to get to the real error. 5023bb79becSeschrock */ 5033bb79becSeschrock if (fgets(buf, sizeof (buf), fp) != NULL) { 5043bb79becSeschrock char *colon = strchr(buf, ':'); 5053bb79becSeschrock 5063bb79becSeschrock while (buf[strlen(buf) - 1] == '\n') 5073bb79becSeschrock buf[strlen(buf) - 1] = '\0'; 5083bb79becSeschrock 5093bb79becSeschrock if (colon != NULL) 5103bb79becSeschrock zfs_error_aux(hdl, colon + 2); 5113bb79becSeschrock 5123bb79becSeschrock verify(pclose(fp) != 0); 5133bb79becSeschrock 514f3861e1aSahl return (zfs_error(hdl, EZFS_UNSHARENFSFAILED, 5153bb79becSeschrock dgettext(TEXT_DOMAIN, 5163bb79becSeschrock "cannot unshare '%s'"), name)); 5173bb79becSeschrock } 5183bb79becSeschrock 5193bb79becSeschrock verify(pclose(fp) == 0); 5203bb79becSeschrock 5213bb79becSeschrock return (0); 5223bb79becSeschrock } 5233bb79becSeschrock 524fa9e4066Sahrens /* 525fa9e4066Sahrens * Unshare the given filesystem. 526fa9e4066Sahrens */ 527fa9e4066Sahrens int 528f3861e1aSahl zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) 529fa9e4066Sahrens { 530fa9e4066Sahrens struct mnttab search = { 0 }, entry; 531fa9e4066Sahrens 532fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 533fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 53496dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 53599653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 536fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 53799653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 538fa9e4066Sahrens 539fa9e4066Sahrens if (mountpoint == NULL) 540fa9e4066Sahrens mountpoint = entry.mnt_mountp; 541fa9e4066Sahrens 5423bb79becSeschrock if (is_shared(zhp->zfs_hdl, mountpoint) && 5433bb79becSeschrock unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 5443bb79becSeschrock return (-1); 545fa9e4066Sahrens } 546fa9e4066Sahrens 547fa9e4066Sahrens return (0); 548fa9e4066Sahrens } 549fa9e4066Sahrens 550fa9e4066Sahrens /* 551f3861e1aSahl * Same as zfs_unmountall(), but for NFS unshares. 552fa9e4066Sahrens */ 553fa9e4066Sahrens int 554f3861e1aSahl zfs_unshareall_nfs(zfs_handle_t *zhp) 555fa9e4066Sahrens { 556fa9e4066Sahrens prop_changelist_t *clp; 557fa9e4066Sahrens int ret; 558fa9e4066Sahrens 559fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 560fa9e4066Sahrens if (clp == NULL) 561fa9e4066Sahrens return (-1); 562fa9e4066Sahrens 563fa9e4066Sahrens ret = changelist_unshare(clp); 564fa9e4066Sahrens changelist_free(clp); 565fa9e4066Sahrens 566fa9e4066Sahrens return (ret); 567fa9e4066Sahrens } 568fa9e4066Sahrens 569fa9e4066Sahrens /* 570fa9e4066Sahrens * Remove the mountpoint associated with the current dataset, if necessary. 571fa9e4066Sahrens * We only remove the underlying directory if: 572fa9e4066Sahrens * 573fa9e4066Sahrens * - The mountpoint is not 'none' or 'legacy' 574fa9e4066Sahrens * - The mountpoint is non-empty 575fa9e4066Sahrens * - The mountpoint is the default or inherited 576fa9e4066Sahrens * - The 'zoned' property is set, or we're in a local zone 577fa9e4066Sahrens * 578fa9e4066Sahrens * Any other directories we leave alone. 579fa9e4066Sahrens */ 580fa9e4066Sahrens void 581fa9e4066Sahrens remove_mountpoint(zfs_handle_t *zhp) 582fa9e4066Sahrens { 583fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 584e9dbad6fSeschrock zfs_source_t source; 585fa9e4066Sahrens 586e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 587e9dbad6fSeschrock &source)) 588fa9e4066Sahrens return; 589fa9e4066Sahrens 590e9dbad6fSeschrock if (source == ZFS_SRC_DEFAULT || 591e9dbad6fSeschrock source == ZFS_SRC_INHERITED) { 592fa9e4066Sahrens /* 593fa9e4066Sahrens * Try to remove the directory, silently ignoring any errors. 594fa9e4066Sahrens * The filesystem may have since been removed or moved around, 595e9dbad6fSeschrock * and this error isn't really useful to the administrator in 596e9dbad6fSeschrock * any way. 597fa9e4066Sahrens */ 598fa9e4066Sahrens (void) rmdir(mountpoint); 599fa9e4066Sahrens } 600fa9e4066Sahrens } 6013bb79becSeschrock 602f3861e1aSahl boolean_t 603f3861e1aSahl zfs_is_shared_iscsi(zfs_handle_t *zhp) 604f3861e1aSahl { 605*d8d59944Sahl return (iscsitgt_zfs_is_shared != NULL && 606*d8d59944Sahl iscsitgt_zfs_is_shared(zhp->zfs_name) != 0); 607f3861e1aSahl } 608f3861e1aSahl 609f3861e1aSahl int 610f3861e1aSahl zfs_share_iscsi(zfs_handle_t *zhp) 611f3861e1aSahl { 612f3861e1aSahl char shareopts[ZFS_MAXPROPLEN]; 613f3861e1aSahl const char *dataset = zhp->zfs_name; 614f3861e1aSahl libzfs_handle_t *hdl = zhp->zfs_hdl; 615f3861e1aSahl 616f3861e1aSahl /* 617f3861e1aSahl * Return success if there are no share options. 618f3861e1aSahl */ 619f3861e1aSahl if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 620f3861e1aSahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 || 621f3861e1aSahl strcmp(shareopts, "off") == 0) 622f3861e1aSahl return (0); 623f3861e1aSahl 624*d8d59944Sahl if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) 625f3861e1aSahl return (zfs_error(hdl, EZFS_SHAREISCSIFAILED, 626f3861e1aSahl dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset)); 627f3861e1aSahl 628f3861e1aSahl return (0); 629f3861e1aSahl } 630f3861e1aSahl 631f3861e1aSahl int 632f3861e1aSahl zfs_unshare_iscsi(zfs_handle_t *zhp) 633f3861e1aSahl { 634f3861e1aSahl const char *dataset = zfs_get_name(zhp); 635f3861e1aSahl libzfs_handle_t *hdl = zhp->zfs_hdl; 636f3861e1aSahl 637f3861e1aSahl /* 638f3861e1aSahl * If this fails with ENODEV it indicates that zvol wasn't shared so 639f3861e1aSahl * we should return success in that case. 640f3861e1aSahl */ 641*d8d59944Sahl if (iscsitgt_zfs_unshare == NULL || 642*d8d59944Sahl (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) 643f3861e1aSahl return (zfs_error(hdl, EZFS_UNSHAREISCSIFAILED, 644f3861e1aSahl dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset)); 645f3861e1aSahl 646f3861e1aSahl return (0); 647f3861e1aSahl } 648f3861e1aSahl 6493bb79becSeschrock typedef struct mount_cbdata { 6503bb79becSeschrock zfs_handle_t **cb_datasets; 6513bb79becSeschrock int cb_used; 6523bb79becSeschrock int cb_alloc; 6533bb79becSeschrock } mount_cbdata_t; 6543bb79becSeschrock 6553bb79becSeschrock static int 6563bb79becSeschrock mount_cb(zfs_handle_t *zhp, void *data) 6573bb79becSeschrock { 6583bb79becSeschrock mount_cbdata_t *cbp = data; 6593bb79becSeschrock 660f3861e1aSahl if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) { 6613bb79becSeschrock zfs_close(zhp); 6623bb79becSeschrock return (0); 6633bb79becSeschrock } 6643bb79becSeschrock 6653bb79becSeschrock if (cbp->cb_alloc == cbp->cb_used) { 666e9dbad6fSeschrock void *ptr; 6673bb79becSeschrock 668e9dbad6fSeschrock if ((ptr = zfs_realloc(zhp->zfs_hdl, 669e9dbad6fSeschrock cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), 670e9dbad6fSeschrock cbp->cb_alloc * 2 * sizeof (void *))) == NULL) 6713bb79becSeschrock return (-1); 672e9dbad6fSeschrock cbp->cb_datasets = ptr; 6733bb79becSeschrock 674e9dbad6fSeschrock cbp->cb_alloc *= 2; 6753bb79becSeschrock } 6763bb79becSeschrock 6773bb79becSeschrock cbp->cb_datasets[cbp->cb_used++] = zhp; 678f3861e1aSahl 679f3861e1aSahl return (zfs_iter_children(zhp, mount_cb, cbp)); 6803bb79becSeschrock } 6813bb79becSeschrock 6823bb79becSeschrock static int 683f3861e1aSahl dataset_cmp(const void *a, const void *b) 6843bb79becSeschrock { 6853bb79becSeschrock zfs_handle_t **za = (zfs_handle_t **)a; 6863bb79becSeschrock zfs_handle_t **zb = (zfs_handle_t **)b; 6873bb79becSeschrock char mounta[MAXPATHLEN]; 6883bb79becSeschrock char mountb[MAXPATHLEN]; 689f3861e1aSahl boolean_t gota, gotb; 690f3861e1aSahl 691f3861e1aSahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 692f3861e1aSahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 693f3861e1aSahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 694f3861e1aSahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 695f3861e1aSahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 696f3861e1aSahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 697f3861e1aSahl 698f3861e1aSahl if (gota && gotb) 699f3861e1aSahl return (strcmp(mounta, mountb)); 7003bb79becSeschrock 701f3861e1aSahl if (gota) 702f3861e1aSahl return (-1); 703f3861e1aSahl if (gotb) 704f3861e1aSahl return (1); 7053bb79becSeschrock 706f3861e1aSahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 7073bb79becSeschrock } 7083bb79becSeschrock 709f3861e1aSahl /* 710f3861e1aSahl * Mount and share all datasets within the given pool. This assumes that no 711f3861e1aSahl * datasets within the pool are currently mounted. Because users can create 712f3861e1aSahl * complicated nested hierarchies of mountpoints, we first gather all the 713f3861e1aSahl * datasets and mountpoints within the pool, and sort them by mountpoint. Once 714f3861e1aSahl * we have the list of all filesystems, we iterate over them in order and mount 715f3861e1aSahl * and/or share each one. 716f3861e1aSahl */ 717f3861e1aSahl #pragma weak zpool_mount_datasets = zpool_enable_datasets 7183bb79becSeschrock int 719f3861e1aSahl zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 7203bb79becSeschrock { 7213bb79becSeschrock mount_cbdata_t cb = { 0 }; 7223bb79becSeschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 7233bb79becSeschrock zfs_handle_t *zfsp; 7243bb79becSeschrock int i, ret = -1; 7253bb79becSeschrock 7263bb79becSeschrock /* 7273bb79becSeschrock * Gather all datasets within the pool. 7283bb79becSeschrock */ 7293bb79becSeschrock if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 7303bb79becSeschrock return (-1); 7313bb79becSeschrock cb.cb_alloc = 4; 7323bb79becSeschrock 7333bb79becSeschrock if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 7343bb79becSeschrock goto out; 7353bb79becSeschrock 7363bb79becSeschrock cb.cb_datasets[0] = zfsp; 7373bb79becSeschrock cb.cb_used = 1; 7383bb79becSeschrock 7393bb79becSeschrock if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 7403bb79becSeschrock goto out; 7413bb79becSeschrock 7423bb79becSeschrock /* 7433bb79becSeschrock * Sort the datasets by mountpoint. 7443bb79becSeschrock */ 745f3861e1aSahl qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp); 7463bb79becSeschrock 7473bb79becSeschrock /* 7483bb79becSeschrock * And mount all the datasets. 7493bb79becSeschrock */ 7503bb79becSeschrock ret = 0; 7513bb79becSeschrock for (i = 0; i < cb.cb_used; i++) { 752c08432ebSeschrock if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 7533bb79becSeschrock zfs_share(cb.cb_datasets[i]) != 0) 7543bb79becSeschrock ret = -1; 7553bb79becSeschrock } 7563bb79becSeschrock 7573bb79becSeschrock out: 7583bb79becSeschrock for (i = 0; i < cb.cb_used; i++) 7593bb79becSeschrock zfs_close(cb.cb_datasets[i]); 7603bb79becSeschrock free(cb.cb_datasets); 7613bb79becSeschrock 7623bb79becSeschrock return (ret); 7633bb79becSeschrock } 7643bb79becSeschrock 765f3861e1aSahl 766f3861e1aSahl static int 767f3861e1aSahl zvol_cb(const char *dataset, void *data) 768f3861e1aSahl { 769f3861e1aSahl libzfs_handle_t *hdl = data; 770f3861e1aSahl zfs_handle_t *zhp; 771f3861e1aSahl 772f3861e1aSahl /* 773f3861e1aSahl * Ignore snapshots and ignore failures from non-existant datasets. 774f3861e1aSahl */ 775f3861e1aSahl if (strchr(dataset, '@') != NULL || 776f3861e1aSahl (zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL) 777f3861e1aSahl return (0); 778f3861e1aSahl 779f3861e1aSahl (void) zfs_unshare_iscsi(zhp); 780f3861e1aSahl 781f3861e1aSahl zfs_close(zhp); 782f3861e1aSahl 783f3861e1aSahl return (0); 784f3861e1aSahl } 785f3861e1aSahl 7863bb79becSeschrock static int 7873bb79becSeschrock mountpoint_compare(const void *a, const void *b) 7883bb79becSeschrock { 7893bb79becSeschrock const char *mounta = *((char **)a); 7903bb79becSeschrock const char *mountb = *((char **)b); 7913bb79becSeschrock 7923bb79becSeschrock return (strcmp(mountb, mounta)); 7933bb79becSeschrock } 7943bb79becSeschrock 795f3861e1aSahl /* 796f3861e1aSahl * Unshare and unmount all datasets within the given pool. We don't want to 797f3861e1aSahl * rely on traversing the DSL to discover the filesystems within the pool, 798f3861e1aSahl * because this may be expensive (if not all of them are mounted), and can fail 799f3861e1aSahl * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 800f3861e1aSahl * gather all the filesystems that are currently mounted. 801f3861e1aSahl */ 802f3861e1aSahl #pragma weak zpool_unmount_datasets = zpool_disable_datasets 8033bb79becSeschrock int 804f3861e1aSahl zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) 8053bb79becSeschrock { 8063bb79becSeschrock int used, alloc; 8073bb79becSeschrock struct mnttab entry; 8083bb79becSeschrock size_t namelen; 8093bb79becSeschrock char **mountpoints = NULL; 8103bb79becSeschrock zfs_handle_t **datasets = NULL; 8113bb79becSeschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 8123bb79becSeschrock int i; 8133bb79becSeschrock int ret = -1; 8143bb79becSeschrock int flags = (force ? MS_FORCE : 0); 8153bb79becSeschrock 816f3861e1aSahl /* 817f3861e1aSahl * First unshare all zvols. 818f3861e1aSahl */ 819f3861e1aSahl if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0) 820f3861e1aSahl return (-1); 821f3861e1aSahl 8223bb79becSeschrock namelen = strlen(zhp->zpool_name); 8233bb79becSeschrock 8243bb79becSeschrock rewind(hdl->libzfs_mnttab); 8253bb79becSeschrock used = alloc = 0; 8263bb79becSeschrock while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 8273bb79becSeschrock /* 8283bb79becSeschrock * Ignore non-ZFS entries. 8293bb79becSeschrock */ 8303bb79becSeschrock if (entry.mnt_fstype == NULL || 8313bb79becSeschrock strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 8323bb79becSeschrock continue; 8333bb79becSeschrock 8343bb79becSeschrock /* 8353bb79becSeschrock * Ignore filesystems not within this pool. 8363bb79becSeschrock */ 8373bb79becSeschrock if (entry.mnt_mountp == NULL || 8383bb79becSeschrock strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 8393bb79becSeschrock (entry.mnt_special[namelen] != '/' && 8403bb79becSeschrock entry.mnt_special[namelen] != '\0')) 8413bb79becSeschrock continue; 8423bb79becSeschrock 8433bb79becSeschrock /* 8443bb79becSeschrock * At this point we've found a filesystem within our pool. Add 8453bb79becSeschrock * it to our growing list. 8463bb79becSeschrock */ 8473bb79becSeschrock if (used == alloc) { 8483bb79becSeschrock if (alloc == 0) { 8493bb79becSeschrock if ((mountpoints = zfs_alloc(hdl, 8503bb79becSeschrock 8 * sizeof (void *))) == NULL) 8513bb79becSeschrock goto out; 8523bb79becSeschrock 8533bb79becSeschrock if ((datasets = zfs_alloc(hdl, 8543bb79becSeschrock 8 * sizeof (void *))) == NULL) 8553bb79becSeschrock goto out; 8563bb79becSeschrock 8573bb79becSeschrock alloc = 8; 8583bb79becSeschrock } else { 859e9dbad6fSeschrock void *ptr; 8603bb79becSeschrock 861e9dbad6fSeschrock if ((ptr = zfs_realloc(hdl, mountpoints, 862e9dbad6fSeschrock alloc * sizeof (void *), 8633bb79becSeschrock alloc * 2 * sizeof (void *))) == NULL) 8643bb79becSeschrock goto out; 865e9dbad6fSeschrock mountpoints = ptr; 8663bb79becSeschrock 867e9dbad6fSeschrock if ((ptr = zfs_realloc(hdl, datasets, 868e9dbad6fSeschrock alloc * sizeof (void *), 8693bb79becSeschrock alloc * 2 * sizeof (void *))) == NULL) 8703bb79becSeschrock goto out; 871e9dbad6fSeschrock datasets = ptr; 8723bb79becSeschrock 8733bb79becSeschrock alloc *= 2; 8743bb79becSeschrock } 8753bb79becSeschrock } 8763bb79becSeschrock 8773bb79becSeschrock if ((mountpoints[used] = zfs_strdup(hdl, 8783bb79becSeschrock entry.mnt_mountp)) == NULL) 8793bb79becSeschrock goto out; 8803bb79becSeschrock 8813bb79becSeschrock /* 8823bb79becSeschrock * This is allowed to fail, in case there is some I/O error. It 8833bb79becSeschrock * is only used to determine if we need to remove the underlying 8843bb79becSeschrock * mountpoint, so failure is not fatal. 8853bb79becSeschrock */ 8863bb79becSeschrock datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 8873bb79becSeschrock 8883bb79becSeschrock used++; 8893bb79becSeschrock } 8903bb79becSeschrock 8913bb79becSeschrock /* 8923bb79becSeschrock * At this point, we have the entire list of filesystems, so sort it by 8933bb79becSeschrock * mountpoint. 8943bb79becSeschrock */ 8953bb79becSeschrock qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 8963bb79becSeschrock 8973bb79becSeschrock /* 8983bb79becSeschrock * Walk through and first unshare everything. 8993bb79becSeschrock */ 9003bb79becSeschrock for (i = 0; i < used; i++) { 9013bb79becSeschrock if (is_shared(hdl, mountpoints[i]) && 902e9dbad6fSeschrock unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) 9033bb79becSeschrock goto out; 9043bb79becSeschrock } 9053bb79becSeschrock 9063bb79becSeschrock /* 9073bb79becSeschrock * Now unmount everything, removing the underlying directories as 9083bb79becSeschrock * appropriate. 9093bb79becSeschrock */ 9103bb79becSeschrock for (i = 0; i < used; i++) { 9113bb79becSeschrock if (unmount_one(hdl, mountpoints[i], flags) != 0) 9123bb79becSeschrock goto out; 913e9dbad6fSeschrock } 9143bb79becSeschrock 915e9dbad6fSeschrock for (i = 0; i < used; i++) { 9163bb79becSeschrock if (datasets[i]) 9173bb79becSeschrock remove_mountpoint(datasets[i]); 9183bb79becSeschrock } 9193bb79becSeschrock 9203bb79becSeschrock ret = 0; 9213bb79becSeschrock out: 9223bb79becSeschrock for (i = 0; i < used; i++) { 9233bb79becSeschrock if (datasets[i]) 9243bb79becSeschrock zfs_close(datasets[i]); 9253bb79becSeschrock free(mountpoints[i]); 9263bb79becSeschrock } 9273bb79becSeschrock free(datasets); 9283bb79becSeschrock free(mountpoints); 9293bb79becSeschrock 9303bb79becSeschrock return (ret); 9313bb79becSeschrock } 932