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 /* 2311d2789dSgw * Copyright 2007 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> 63d8d59944Sahl #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 81d8d59944Sahl static int (*iscsitgt_zfs_share)(const char *); 82d8d59944Sahl static int (*iscsitgt_zfs_unshare)(const char *); 83d8d59944Sahl static int (*iscsitgt_zfs_is_shared)(const char *); 84d8d59944Sahl 85d8d59944Sahl #pragma init(zfs_iscsi_init) 86d8d59944Sahl static void 87d8d59944Sahl zfs_iscsi_init(void) 88d8d59944Sahl { 89d8d59944Sahl void *libiscsitgt; 90d8d59944Sahl 91d8d59944Sahl if ((libiscsitgt = dlopen("/lib/libiscsitgt.so.1", 92d8d59944Sahl RTLD_LAZY | RTLD_GLOBAL)) == NULL || 93d8d59944Sahl (iscsitgt_zfs_share = (int (*)(const char *))dlsym(libiscsitgt, 94d8d59944Sahl "iscsitgt_zfs_share")) == NULL || 95d8d59944Sahl (iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt, 96d8d59944Sahl "iscsitgt_zfs_unshare")) == NULL || 97d8d59944Sahl (iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt, 98d8d59944Sahl "iscsitgt_zfs_is_shared")) == NULL) { 99d8d59944Sahl iscsitgt_zfs_share = NULL; 100d8d59944Sahl iscsitgt_zfs_unshare = NULL; 101d8d59944Sahl iscsitgt_zfs_is_shared = NULL; 102d8d59944Sahl } 103d8d59944Sahl } 104d8d59944Sahl 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 165*55434c77Sek is_mounted(libzfs_handle_t *zfs_hdl, const char *special, 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 */ 174*55434c77Sek search.mnt_special = (char *)special; 17596dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 176fa9e4066Sahrens 177*55434c77Sek rewind(zfs_hdl->libzfs_mnttab); 178*55434c77Sek if (getmntany(zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 17999653d4eSeschrock return (B_FALSE); 180fa9e4066Sahrens 181fa9e4066Sahrens if (where != NULL) 182*55434c77Sek *where = zfs_strdup(zfs_hdl, entry.mnt_mountp); 183fa9e4066Sahrens 18499653d4eSeschrock return (B_TRUE); 185fa9e4066Sahrens } 186fa9e4066Sahrens 187*55434c77Sek boolean_t 188*55434c77Sek zfs_is_mounted(zfs_handle_t *zhp, char **where) 189*55434c77Sek { 190*55434c77Sek return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where)); 191*55434c77Sek } 192*55434c77Sek 193e9dbad6fSeschrock /* 194e9dbad6fSeschrock * Returns true if the given dataset is mountable, false otherwise. Returns the 195e9dbad6fSeschrock * mountpoint in 'buf'. 196e9dbad6fSeschrock */ 197e9dbad6fSeschrock static boolean_t 198e9dbad6fSeschrock zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 199e9dbad6fSeschrock zfs_source_t *source) 200e9dbad6fSeschrock { 201e9dbad6fSeschrock char sourceloc[ZFS_MAXNAMELEN]; 202e9dbad6fSeschrock zfs_source_t sourcetype; 203e9dbad6fSeschrock 204e9dbad6fSeschrock if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 205e9dbad6fSeschrock return (B_FALSE); 206e9dbad6fSeschrock 207e9dbad6fSeschrock verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 208e9dbad6fSeschrock &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 209e9dbad6fSeschrock 210e9dbad6fSeschrock if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 211e9dbad6fSeschrock strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 212e9dbad6fSeschrock return (B_FALSE); 213e9dbad6fSeschrock 214e9dbad6fSeschrock if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) 215e9dbad6fSeschrock return (B_FALSE); 216e9dbad6fSeschrock 217e9dbad6fSeschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 218e9dbad6fSeschrock getzoneid() == GLOBAL_ZONEID) 219e9dbad6fSeschrock return (B_FALSE); 220e9dbad6fSeschrock 221e9dbad6fSeschrock if (source) 222e9dbad6fSeschrock *source = sourcetype; 223e9dbad6fSeschrock 224e9dbad6fSeschrock return (B_TRUE); 225e9dbad6fSeschrock } 226e9dbad6fSeschrock 227fa9e4066Sahrens /* 228fa9e4066Sahrens * Mount the given filesystem. 229fa9e4066Sahrens */ 230fa9e4066Sahrens int 231fa9e4066Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 232fa9e4066Sahrens { 233fa9e4066Sahrens struct stat buf; 234fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 235fa9e4066Sahrens char mntopts[MNT_LINE_MAX]; 23699653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 237fa9e4066Sahrens 238fa9e4066Sahrens if (options == NULL) 239fa9e4066Sahrens mntopts[0] = '\0'; 240fa9e4066Sahrens else 241fa9e4066Sahrens (void) strlcpy(mntopts, options, sizeof (mntopts)); 242fa9e4066Sahrens 243e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 24499653d4eSeschrock return (0); 245fa9e4066Sahrens 246fa9e4066Sahrens /* Create the directory if it doesn't already exist */ 247fa9e4066Sahrens if (lstat(mountpoint, &buf) != 0) { 248fa9e4066Sahrens if (mkdirp(mountpoint, 0755) != 0) { 24999653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 25099653d4eSeschrock "failed to create mountpoint")); 251ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 25299653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 25399653d4eSeschrock mountpoint)); 254fa9e4066Sahrens } 255fa9e4066Sahrens } 256fa9e4066Sahrens 257fa9e4066Sahrens /* 258fa9e4066Sahrens * Determine if the mountpoint is empty. If so, refuse to perform the 259fa9e4066Sahrens * mount. We don't perform this check if MS_OVERLAY is specified, which 260fa9e4066Sahrens * would defeat the point. We also avoid this check if 'remount' is 261fa9e4066Sahrens * specified. 262fa9e4066Sahrens */ 263fa9e4066Sahrens if ((flags & MS_OVERLAY) == 0 && 264fa9e4066Sahrens strstr(mntopts, MNTOPT_REMOUNT) == NULL && 265fa9e4066Sahrens !dir_is_empty(mountpoint)) { 26699653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26799653d4eSeschrock "directory is not empty")); 268ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 26999653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 270fa9e4066Sahrens } 271fa9e4066Sahrens 272fa9e4066Sahrens /* perform the mount */ 273fa9e4066Sahrens if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 274fa9e4066Sahrens MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 275fa9e4066Sahrens /* 276fa9e4066Sahrens * Generic errors are nasty, but there are just way too many 277fa9e4066Sahrens * from mount(), and they're well-understood. We pick a few 278fa9e4066Sahrens * common ones to improve upon. 279fa9e4066Sahrens */ 28099653d4eSeschrock if (errno == EBUSY) 28199653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 28299653d4eSeschrock "mountpoint or dataset is busy")); 28399653d4eSeschrock else 28499653d4eSeschrock zfs_error_aux(hdl, strerror(errno)); 28599653d4eSeschrock 286ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 28799653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 28899653d4eSeschrock zhp->zfs_name)); 289fa9e4066Sahrens } 290fa9e4066Sahrens 291fa9e4066Sahrens return (0); 292fa9e4066Sahrens } 293fa9e4066Sahrens 2943bb79becSeschrock /* 2953bb79becSeschrock * Unmount a single filesystem. 2963bb79becSeschrock */ 2973bb79becSeschrock static int 2983bb79becSeschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 2993bb79becSeschrock { 3003bb79becSeschrock if (umount2(mountpoint, flags) != 0) { 3013bb79becSeschrock zfs_error_aux(hdl, strerror(errno)); 302ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, 3033bb79becSeschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 3043bb79becSeschrock mountpoint)); 3053bb79becSeschrock } 3063bb79becSeschrock 3073bb79becSeschrock return (0); 3083bb79becSeschrock } 3093bb79becSeschrock 310fa9e4066Sahrens /* 311fa9e4066Sahrens * Unmount the given filesystem. 312fa9e4066Sahrens */ 313fa9e4066Sahrens int 314fa9e4066Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 315fa9e4066Sahrens { 316fa9e4066Sahrens struct mnttab search = { 0 }, entry; 317fa9e4066Sahrens 318fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 3193bb79becSeschrock search.mnt_special = zhp->zfs_name; 32096dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 32199653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 322fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 32399653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 324fa9e4066Sahrens 325fa9e4066Sahrens if (mountpoint == NULL) 326fa9e4066Sahrens mountpoint = entry.mnt_mountp; 327fa9e4066Sahrens 328fa9e4066Sahrens /* 3293bb79becSeschrock * Unshare and unmount the filesystem 330fa9e4066Sahrens */ 331f3861e1aSahl if (zfs_unshare_nfs(zhp, mountpoint) != 0 || 3323bb79becSeschrock unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 333fa9e4066Sahrens return (-1); 334fa9e4066Sahrens } 335fa9e4066Sahrens 336fa9e4066Sahrens return (0); 337fa9e4066Sahrens } 338fa9e4066Sahrens 339fa9e4066Sahrens /* 340fa9e4066Sahrens * Unmount this filesystem and any children inheriting the mountpoint property. 341fa9e4066Sahrens * To do this, just act like we're changing the mountpoint property, but don't 342fa9e4066Sahrens * remount the filesystems afterwards. 343fa9e4066Sahrens */ 344fa9e4066Sahrens int 345fa9e4066Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags) 346fa9e4066Sahrens { 347fa9e4066Sahrens prop_changelist_t *clp; 348fa9e4066Sahrens int ret; 349fa9e4066Sahrens 350fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 351fa9e4066Sahrens if (clp == NULL) 352fa9e4066Sahrens return (-1); 353fa9e4066Sahrens 354fa9e4066Sahrens ret = changelist_prefix(clp); 355fa9e4066Sahrens changelist_free(clp); 356fa9e4066Sahrens 357fa9e4066Sahrens return (ret); 358fa9e4066Sahrens } 359fa9e4066Sahrens 360f3861e1aSahl boolean_t 361f3861e1aSahl zfs_is_shared(zfs_handle_t *zhp) 362f3861e1aSahl { 363f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 364f3861e1aSahl return (zfs_is_shared_iscsi(zhp)); 365f3861e1aSahl 366f3861e1aSahl return (zfs_is_shared_nfs(zhp, NULL)); 367f3861e1aSahl } 368f3861e1aSahl 369f3861e1aSahl int 370f3861e1aSahl zfs_share(zfs_handle_t *zhp) 371f3861e1aSahl { 372f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 373f3861e1aSahl return (zfs_share_iscsi(zhp)); 374f3861e1aSahl 375f3861e1aSahl return (zfs_share_nfs(zhp)); 376f3861e1aSahl } 377f3861e1aSahl 378f3861e1aSahl int 379f3861e1aSahl zfs_unshare(zfs_handle_t *zhp) 380f3861e1aSahl { 381f3861e1aSahl if (ZFS_IS_VOLUME(zhp)) 382f3861e1aSahl return (zfs_unshare_iscsi(zhp)); 383f3861e1aSahl 384f3861e1aSahl return (zfs_unshare_nfs(zhp, NULL)); 385f3861e1aSahl } 386f3861e1aSahl 387fa9e4066Sahrens /* 388fa9e4066Sahrens * Check to see if the filesystem is currently shared. 389fa9e4066Sahrens */ 39099653d4eSeschrock boolean_t 391f3861e1aSahl zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) 392fa9e4066Sahrens { 393fa9e4066Sahrens char *mountpoint; 394fa9e4066Sahrens 395fa9e4066Sahrens if (!zfs_is_mounted(zhp, &mountpoint)) 39699653d4eSeschrock return (B_FALSE); 397fa9e4066Sahrens 39899653d4eSeschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 399fa9e4066Sahrens if (where != NULL) 400fa9e4066Sahrens *where = mountpoint; 401fa9e4066Sahrens else 402fa9e4066Sahrens free(mountpoint); 40399653d4eSeschrock return (B_TRUE); 404fa9e4066Sahrens } else { 405fa9e4066Sahrens free(mountpoint); 40699653d4eSeschrock return (B_FALSE); 407fa9e4066Sahrens } 408fa9e4066Sahrens } 409fa9e4066Sahrens 410fa9e4066Sahrens /* 411fa9e4066Sahrens * Share the given filesystem according to the options in 'sharenfs'. We rely 412fa9e4066Sahrens * on share(1M) to the dirty work for us. 413fa9e4066Sahrens */ 414fa9e4066Sahrens int 415f3861e1aSahl zfs_share_nfs(zfs_handle_t *zhp) 416fa9e4066Sahrens { 417fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 418fa9e4066Sahrens char shareopts[ZFS_MAXPROPLEN]; 419fa9e4066Sahrens char buf[MAXPATHLEN]; 420fa9e4066Sahrens FILE *fp; 42199653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 422fa9e4066Sahrens 423e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 424fa9e4066Sahrens return (0); 425fa9e4066Sahrens 426f3861e1aSahl /* 427f3861e1aSahl * Return success if there are no share options. 428f3861e1aSahl */ 429fa9e4066Sahrens if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 43099653d4eSeschrock NULL, NULL, 0, B_FALSE) != 0 || 431fa9e4066Sahrens strcmp(shareopts, "off") == 0) 432fa9e4066Sahrens return (0); 433fa9e4066Sahrens 434fa9e4066Sahrens /* 435e9dbad6fSeschrock * If the 'zoned' property is set, then zfs_is_mountable() will have 436e9dbad6fSeschrock * already bailed out if we are in the global zone. But local 437e9dbad6fSeschrock * zones cannot be NFS servers, so we ignore it for local zones as well. 438fa9e4066Sahrens */ 439e9dbad6fSeschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 440fa9e4066Sahrens return (0); 441fa9e4066Sahrens 442fa9e4066Sahrens /* 443fa9e4066Sahrens * Invoke the share(1M) command. We always do this, even if it's 444fa9e4066Sahrens * currently shared, as the options may have changed. 445fa9e4066Sahrens */ 446fa9e4066Sahrens if (strcmp(shareopts, "on") == 0) 447fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 448fa9e4066Sahrens "-F nfs \"%s\" 2>&1", mountpoint); 449fa9e4066Sahrens else 450fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 451fa9e4066Sahrens "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 452fa9e4066Sahrens mountpoint); 453fa9e4066Sahrens 45499653d4eSeschrock if ((fp = popen(buf, "r")) == NULL) 455ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 45699653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 45799653d4eSeschrock zfs_get_name(zhp))); 458fa9e4066Sahrens 459fa9e4066Sahrens /* 460fa9e4066Sahrens * share(1M) should only produce output if there is some kind 461fa9e4066Sahrens * of error. All output begins with "share_nfs: ", so we trim 462fa9e4066Sahrens * this off to get to the real error. 463fa9e4066Sahrens */ 464fa9e4066Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 465fa9e4066Sahrens char *colon = strchr(buf, ':'); 466fa9e4066Sahrens 467fa9e4066Sahrens while (buf[strlen(buf) - 1] == '\n') 468fa9e4066Sahrens buf[strlen(buf) - 1] = '\0'; 469fa9e4066Sahrens 47099653d4eSeschrock if (colon != NULL) 47199653d4eSeschrock zfs_error_aux(hdl, colon + 2); 47299653d4eSeschrock 473ece3d9b3Slling (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 4743bb79becSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 4753bb79becSeschrock zfs_get_name(zhp)); 476fa9e4066Sahrens 477fa9e4066Sahrens verify(pclose(fp) != 0); 478fa9e4066Sahrens return (-1); 479fa9e4066Sahrens } 480fa9e4066Sahrens 481fa9e4066Sahrens verify(pclose(fp) == 0); 482fa9e4066Sahrens 483fa9e4066Sahrens return (0); 484fa9e4066Sahrens } 485fa9e4066Sahrens 4863bb79becSeschrock /* 4873bb79becSeschrock * Unshare a filesystem by mountpoint. 4883bb79becSeschrock */ 4893bb79becSeschrock static int 4903bb79becSeschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 4913bb79becSeschrock { 4923bb79becSeschrock char buf[MAXPATHLEN]; 4933bb79becSeschrock FILE *fp; 4943bb79becSeschrock 4953bb79becSeschrock (void) snprintf(buf, sizeof (buf), 4963bb79becSeschrock "/usr/sbin/unshare \"%s\" 2>&1", 4973bb79becSeschrock mountpoint); 4983bb79becSeschrock 4993bb79becSeschrock if ((fp = popen(buf, "r")) == NULL) 500ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 5013bb79becSeschrock dgettext(TEXT_DOMAIN, 5023bb79becSeschrock "cannot unshare '%s'"), name)); 5033bb79becSeschrock 5043bb79becSeschrock /* 5053bb79becSeschrock * unshare(1M) should only produce output if there is 5063bb79becSeschrock * some kind of error. All output begins with "unshare 5073bb79becSeschrock * nfs: ", so we trim this off to get to the real error. 5083bb79becSeschrock */ 5093bb79becSeschrock if (fgets(buf, sizeof (buf), fp) != NULL) { 5103bb79becSeschrock char *colon = strchr(buf, ':'); 5113bb79becSeschrock 5123bb79becSeschrock while (buf[strlen(buf) - 1] == '\n') 5133bb79becSeschrock buf[strlen(buf) - 1] = '\0'; 5143bb79becSeschrock 5153bb79becSeschrock if (colon != NULL) 5163bb79becSeschrock zfs_error_aux(hdl, colon + 2); 5173bb79becSeschrock 5183bb79becSeschrock verify(pclose(fp) != 0); 5193bb79becSeschrock 520ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 5213bb79becSeschrock dgettext(TEXT_DOMAIN, 5223bb79becSeschrock "cannot unshare '%s'"), name)); 5233bb79becSeschrock } 5243bb79becSeschrock 5253bb79becSeschrock verify(pclose(fp) == 0); 5263bb79becSeschrock 5273bb79becSeschrock return (0); 5283bb79becSeschrock } 5293bb79becSeschrock 530fa9e4066Sahrens /* 531fa9e4066Sahrens * Unshare the given filesystem. 532fa9e4066Sahrens */ 533fa9e4066Sahrens int 534f3861e1aSahl zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) 535fa9e4066Sahrens { 536fa9e4066Sahrens struct mnttab search = { 0 }, entry; 537fa9e4066Sahrens 538fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 539fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 54096dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 54199653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 542fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 54399653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 544fa9e4066Sahrens 545fa9e4066Sahrens if (mountpoint == NULL) 546fa9e4066Sahrens mountpoint = entry.mnt_mountp; 547fa9e4066Sahrens 5483bb79becSeschrock if (is_shared(zhp->zfs_hdl, mountpoint) && 5493bb79becSeschrock unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 5503bb79becSeschrock return (-1); 551fa9e4066Sahrens } 552fa9e4066Sahrens 553fa9e4066Sahrens return (0); 554fa9e4066Sahrens } 555fa9e4066Sahrens 556fa9e4066Sahrens /* 557f3861e1aSahl * Same as zfs_unmountall(), but for NFS unshares. 558fa9e4066Sahrens */ 559fa9e4066Sahrens int 560f3861e1aSahl zfs_unshareall_nfs(zfs_handle_t *zhp) 561fa9e4066Sahrens { 562fa9e4066Sahrens prop_changelist_t *clp; 563fa9e4066Sahrens int ret; 564fa9e4066Sahrens 565fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 566fa9e4066Sahrens if (clp == NULL) 567fa9e4066Sahrens return (-1); 568fa9e4066Sahrens 569fa9e4066Sahrens ret = changelist_unshare(clp); 570fa9e4066Sahrens changelist_free(clp); 571fa9e4066Sahrens 572fa9e4066Sahrens return (ret); 573fa9e4066Sahrens } 574fa9e4066Sahrens 575fa9e4066Sahrens /* 576fa9e4066Sahrens * Remove the mountpoint associated with the current dataset, if necessary. 577fa9e4066Sahrens * We only remove the underlying directory if: 578fa9e4066Sahrens * 579fa9e4066Sahrens * - The mountpoint is not 'none' or 'legacy' 580fa9e4066Sahrens * - The mountpoint is non-empty 581fa9e4066Sahrens * - The mountpoint is the default or inherited 582fa9e4066Sahrens * - The 'zoned' property is set, or we're in a local zone 583fa9e4066Sahrens * 584fa9e4066Sahrens * Any other directories we leave alone. 585fa9e4066Sahrens */ 586fa9e4066Sahrens void 587fa9e4066Sahrens remove_mountpoint(zfs_handle_t *zhp) 588fa9e4066Sahrens { 589fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 590e9dbad6fSeschrock zfs_source_t source; 591fa9e4066Sahrens 592e9dbad6fSeschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 593e9dbad6fSeschrock &source)) 594fa9e4066Sahrens return; 595fa9e4066Sahrens 596e9dbad6fSeschrock if (source == ZFS_SRC_DEFAULT || 597e9dbad6fSeschrock source == ZFS_SRC_INHERITED) { 598fa9e4066Sahrens /* 599fa9e4066Sahrens * Try to remove the directory, silently ignoring any errors. 600fa9e4066Sahrens * The filesystem may have since been removed or moved around, 601e9dbad6fSeschrock * and this error isn't really useful to the administrator in 602e9dbad6fSeschrock * any way. 603fa9e4066Sahrens */ 604fa9e4066Sahrens (void) rmdir(mountpoint); 605fa9e4066Sahrens } 606fa9e4066Sahrens } 6073bb79becSeschrock 608f3861e1aSahl boolean_t 609f3861e1aSahl zfs_is_shared_iscsi(zfs_handle_t *zhp) 610f3861e1aSahl { 611d8d59944Sahl return (iscsitgt_zfs_is_shared != NULL && 612d8d59944Sahl iscsitgt_zfs_is_shared(zhp->zfs_name) != 0); 613f3861e1aSahl } 614f3861e1aSahl 615f3861e1aSahl int 616f3861e1aSahl zfs_share_iscsi(zfs_handle_t *zhp) 617f3861e1aSahl { 618f3861e1aSahl char shareopts[ZFS_MAXPROPLEN]; 619f3861e1aSahl const char *dataset = zhp->zfs_name; 620f3861e1aSahl libzfs_handle_t *hdl = zhp->zfs_hdl; 621f3861e1aSahl 622f3861e1aSahl /* 623f3861e1aSahl * Return success if there are no share options. 624f3861e1aSahl */ 625f3861e1aSahl if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 626f3861e1aSahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 || 627f3861e1aSahl strcmp(shareopts, "off") == 0) 628f3861e1aSahl return (0); 629f3861e1aSahl 630d8d59944Sahl if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) 631ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_SHAREISCSIFAILED, 632f3861e1aSahl dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset)); 633f3861e1aSahl 634f3861e1aSahl return (0); 635f3861e1aSahl } 636f3861e1aSahl 637f3861e1aSahl int 638f3861e1aSahl zfs_unshare_iscsi(zfs_handle_t *zhp) 639f3861e1aSahl { 640f3861e1aSahl const char *dataset = zfs_get_name(zhp); 641f3861e1aSahl libzfs_handle_t *hdl = zhp->zfs_hdl; 642f3861e1aSahl 64311d2789dSgw /* 64411d2789dSgw * Return if the volume is not shared 64511d2789dSgw */ 64611d2789dSgw if (!zfs_is_shared_iscsi(zhp)) 64711d2789dSgw return (0); 64811d2789dSgw 649f3861e1aSahl /* 650f3861e1aSahl * If this fails with ENODEV it indicates that zvol wasn't shared so 651f3861e1aSahl * we should return success in that case. 652f3861e1aSahl */ 653d8d59944Sahl if (iscsitgt_zfs_unshare == NULL || 654d8d59944Sahl (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) 655ece3d9b3Slling return (zfs_error_fmt(hdl, EZFS_UNSHAREISCSIFAILED, 656f3861e1aSahl dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset)); 657f3861e1aSahl 658f3861e1aSahl return (0); 659f3861e1aSahl } 660f3861e1aSahl 6613bb79becSeschrock typedef struct mount_cbdata { 6623bb79becSeschrock zfs_handle_t **cb_datasets; 6633bb79becSeschrock int cb_used; 6643bb79becSeschrock int cb_alloc; 6653bb79becSeschrock } mount_cbdata_t; 6663bb79becSeschrock 6673bb79becSeschrock static int 6683bb79becSeschrock mount_cb(zfs_handle_t *zhp, void *data) 6693bb79becSeschrock { 6703bb79becSeschrock mount_cbdata_t *cbp = data; 6713bb79becSeschrock 672f3861e1aSahl if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) { 6733bb79becSeschrock zfs_close(zhp); 6743bb79becSeschrock return (0); 6753bb79becSeschrock } 6763bb79becSeschrock 6773bb79becSeschrock if (cbp->cb_alloc == cbp->cb_used) { 678e9dbad6fSeschrock void *ptr; 6793bb79becSeschrock 680e9dbad6fSeschrock if ((ptr = zfs_realloc(zhp->zfs_hdl, 681e9dbad6fSeschrock cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), 682e9dbad6fSeschrock cbp->cb_alloc * 2 * sizeof (void *))) == NULL) 6833bb79becSeschrock return (-1); 684e9dbad6fSeschrock cbp->cb_datasets = ptr; 6853bb79becSeschrock 686e9dbad6fSeschrock cbp->cb_alloc *= 2; 6873bb79becSeschrock } 6883bb79becSeschrock 6893bb79becSeschrock cbp->cb_datasets[cbp->cb_used++] = zhp; 690f3861e1aSahl 691f3861e1aSahl return (zfs_iter_children(zhp, mount_cb, cbp)); 6923bb79becSeschrock } 6933bb79becSeschrock 6943bb79becSeschrock static int 695f3861e1aSahl dataset_cmp(const void *a, const void *b) 6963bb79becSeschrock { 6973bb79becSeschrock zfs_handle_t **za = (zfs_handle_t **)a; 6983bb79becSeschrock zfs_handle_t **zb = (zfs_handle_t **)b; 6993bb79becSeschrock char mounta[MAXPATHLEN]; 7003bb79becSeschrock char mountb[MAXPATHLEN]; 701f3861e1aSahl boolean_t gota, gotb; 702f3861e1aSahl 703f3861e1aSahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 704f3861e1aSahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 705f3861e1aSahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 706f3861e1aSahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 707f3861e1aSahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 708f3861e1aSahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 709f3861e1aSahl 710f3861e1aSahl if (gota && gotb) 711f3861e1aSahl return (strcmp(mounta, mountb)); 7123bb79becSeschrock 713f3861e1aSahl if (gota) 714f3861e1aSahl return (-1); 715f3861e1aSahl if (gotb) 716f3861e1aSahl return (1); 7173bb79becSeschrock 718f3861e1aSahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 7193bb79becSeschrock } 7203bb79becSeschrock 721f3861e1aSahl /* 722f3861e1aSahl * Mount and share all datasets within the given pool. This assumes that no 723f3861e1aSahl * datasets within the pool are currently mounted. Because users can create 724f3861e1aSahl * complicated nested hierarchies of mountpoints, we first gather all the 725f3861e1aSahl * datasets and mountpoints within the pool, and sort them by mountpoint. Once 726f3861e1aSahl * we have the list of all filesystems, we iterate over them in order and mount 727f3861e1aSahl * and/or share each one. 728f3861e1aSahl */ 729f3861e1aSahl #pragma weak zpool_mount_datasets = zpool_enable_datasets 7303bb79becSeschrock int 731f3861e1aSahl zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 7323bb79becSeschrock { 7333bb79becSeschrock mount_cbdata_t cb = { 0 }; 7343bb79becSeschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 7353bb79becSeschrock zfs_handle_t *zfsp; 7363bb79becSeschrock int i, ret = -1; 7373bb79becSeschrock 7383bb79becSeschrock /* 7393bb79becSeschrock * Gather all datasets within the pool. 7403bb79becSeschrock */ 7413bb79becSeschrock if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 7423bb79becSeschrock return (-1); 7433bb79becSeschrock cb.cb_alloc = 4; 7443bb79becSeschrock 7453bb79becSeschrock if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 7463bb79becSeschrock goto out; 7473bb79becSeschrock 7483bb79becSeschrock cb.cb_datasets[0] = zfsp; 7493bb79becSeschrock cb.cb_used = 1; 7503bb79becSeschrock 7513bb79becSeschrock if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 7523bb79becSeschrock goto out; 7533bb79becSeschrock 7543bb79becSeschrock /* 7553bb79becSeschrock * Sort the datasets by mountpoint. 7563bb79becSeschrock */ 757f3861e1aSahl qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp); 7583bb79becSeschrock 7593bb79becSeschrock /* 7603bb79becSeschrock * And mount all the datasets. 7613bb79becSeschrock */ 7623bb79becSeschrock ret = 0; 7633bb79becSeschrock for (i = 0; i < cb.cb_used; i++) { 764c08432ebSeschrock if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 7653bb79becSeschrock zfs_share(cb.cb_datasets[i]) != 0) 7663bb79becSeschrock ret = -1; 7673bb79becSeschrock } 7683bb79becSeschrock 7693bb79becSeschrock out: 7703bb79becSeschrock for (i = 0; i < cb.cb_used; i++) 7713bb79becSeschrock zfs_close(cb.cb_datasets[i]); 7723bb79becSeschrock free(cb.cb_datasets); 7733bb79becSeschrock 7743bb79becSeschrock return (ret); 7753bb79becSeschrock } 7763bb79becSeschrock 777f3861e1aSahl 778f3861e1aSahl static int 779f3861e1aSahl zvol_cb(const char *dataset, void *data) 780f3861e1aSahl { 781f3861e1aSahl libzfs_handle_t *hdl = data; 782f3861e1aSahl zfs_handle_t *zhp; 783f3861e1aSahl 784f3861e1aSahl /* 785f3861e1aSahl * Ignore snapshots and ignore failures from non-existant datasets. 786f3861e1aSahl */ 787f3861e1aSahl if (strchr(dataset, '@') != NULL || 788f3861e1aSahl (zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL) 789f3861e1aSahl return (0); 790f3861e1aSahl 791f3861e1aSahl (void) zfs_unshare_iscsi(zhp); 792f3861e1aSahl 793f3861e1aSahl zfs_close(zhp); 794f3861e1aSahl 795f3861e1aSahl return (0); 796f3861e1aSahl } 797f3861e1aSahl 7983bb79becSeschrock static int 7993bb79becSeschrock mountpoint_compare(const void *a, const void *b) 8003bb79becSeschrock { 8013bb79becSeschrock const char *mounta = *((char **)a); 8023bb79becSeschrock const char *mountb = *((char **)b); 8033bb79becSeschrock 8043bb79becSeschrock return (strcmp(mountb, mounta)); 8053bb79becSeschrock } 8063bb79becSeschrock 807f3861e1aSahl /* 808f3861e1aSahl * Unshare and unmount all datasets within the given pool. We don't want to 809f3861e1aSahl * rely on traversing the DSL to discover the filesystems within the pool, 810f3861e1aSahl * because this may be expensive (if not all of them are mounted), and can fail 811f3861e1aSahl * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 812f3861e1aSahl * gather all the filesystems that are currently mounted. 813f3861e1aSahl */ 814f3861e1aSahl #pragma weak zpool_unmount_datasets = zpool_disable_datasets 8153bb79becSeschrock int 816f3861e1aSahl zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) 8173bb79becSeschrock { 8183bb79becSeschrock int used, alloc; 8193bb79becSeschrock struct mnttab entry; 8203bb79becSeschrock size_t namelen; 8213bb79becSeschrock char **mountpoints = NULL; 8223bb79becSeschrock zfs_handle_t **datasets = NULL; 8233bb79becSeschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 8243bb79becSeschrock int i; 8253bb79becSeschrock int ret = -1; 8263bb79becSeschrock int flags = (force ? MS_FORCE : 0); 8273bb79becSeschrock 828f3861e1aSahl /* 829f3861e1aSahl * First unshare all zvols. 830f3861e1aSahl */ 831f3861e1aSahl if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0) 832f3861e1aSahl return (-1); 833f3861e1aSahl 8343bb79becSeschrock namelen = strlen(zhp->zpool_name); 8353bb79becSeschrock 8363bb79becSeschrock rewind(hdl->libzfs_mnttab); 8373bb79becSeschrock used = alloc = 0; 8383bb79becSeschrock while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 8393bb79becSeschrock /* 8403bb79becSeschrock * Ignore non-ZFS entries. 8413bb79becSeschrock */ 8423bb79becSeschrock if (entry.mnt_fstype == NULL || 8433bb79becSeschrock strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 8443bb79becSeschrock continue; 8453bb79becSeschrock 8463bb79becSeschrock /* 8473bb79becSeschrock * Ignore filesystems not within this pool. 8483bb79becSeschrock */ 8493bb79becSeschrock if (entry.mnt_mountp == NULL || 8503bb79becSeschrock strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 8513bb79becSeschrock (entry.mnt_special[namelen] != '/' && 8523bb79becSeschrock entry.mnt_special[namelen] != '\0')) 8533bb79becSeschrock continue; 8543bb79becSeschrock 8553bb79becSeschrock /* 8563bb79becSeschrock * At this point we've found a filesystem within our pool. Add 8573bb79becSeschrock * it to our growing list. 8583bb79becSeschrock */ 8593bb79becSeschrock if (used == alloc) { 8603bb79becSeschrock if (alloc == 0) { 8613bb79becSeschrock if ((mountpoints = zfs_alloc(hdl, 8623bb79becSeschrock 8 * sizeof (void *))) == NULL) 8633bb79becSeschrock goto out; 8643bb79becSeschrock 8653bb79becSeschrock if ((datasets = zfs_alloc(hdl, 8663bb79becSeschrock 8 * sizeof (void *))) == NULL) 8673bb79becSeschrock goto out; 8683bb79becSeschrock 8693bb79becSeschrock alloc = 8; 8703bb79becSeschrock } else { 871e9dbad6fSeschrock void *ptr; 8723bb79becSeschrock 873e9dbad6fSeschrock if ((ptr = zfs_realloc(hdl, mountpoints, 874e9dbad6fSeschrock alloc * sizeof (void *), 8753bb79becSeschrock alloc * 2 * sizeof (void *))) == NULL) 8763bb79becSeschrock goto out; 877e9dbad6fSeschrock mountpoints = ptr; 8783bb79becSeschrock 879e9dbad6fSeschrock if ((ptr = zfs_realloc(hdl, datasets, 880e9dbad6fSeschrock alloc * sizeof (void *), 8813bb79becSeschrock alloc * 2 * sizeof (void *))) == NULL) 8823bb79becSeschrock goto out; 883e9dbad6fSeschrock datasets = ptr; 8843bb79becSeschrock 8853bb79becSeschrock alloc *= 2; 8863bb79becSeschrock } 8873bb79becSeschrock } 8883bb79becSeschrock 8893bb79becSeschrock if ((mountpoints[used] = zfs_strdup(hdl, 8903bb79becSeschrock entry.mnt_mountp)) == NULL) 8913bb79becSeschrock goto out; 8923bb79becSeschrock 8933bb79becSeschrock /* 8943bb79becSeschrock * This is allowed to fail, in case there is some I/O error. It 8953bb79becSeschrock * is only used to determine if we need to remove the underlying 8963bb79becSeschrock * mountpoint, so failure is not fatal. 8973bb79becSeschrock */ 8983bb79becSeschrock datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 8993bb79becSeschrock 9003bb79becSeschrock used++; 9013bb79becSeschrock } 9023bb79becSeschrock 9033bb79becSeschrock /* 9043bb79becSeschrock * At this point, we have the entire list of filesystems, so sort it by 9053bb79becSeschrock * mountpoint. 9063bb79becSeschrock */ 9073bb79becSeschrock qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 9083bb79becSeschrock 9093bb79becSeschrock /* 9103bb79becSeschrock * Walk through and first unshare everything. 9113bb79becSeschrock */ 9123bb79becSeschrock for (i = 0; i < used; i++) { 9133bb79becSeschrock if (is_shared(hdl, mountpoints[i]) && 914e9dbad6fSeschrock unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) 9153bb79becSeschrock goto out; 9163bb79becSeschrock } 9173bb79becSeschrock 9183bb79becSeschrock /* 9193bb79becSeschrock * Now unmount everything, removing the underlying directories as 9203bb79becSeschrock * appropriate. 9213bb79becSeschrock */ 9223bb79becSeschrock for (i = 0; i < used; i++) { 9233bb79becSeschrock if (unmount_one(hdl, mountpoints[i], flags) != 0) 9243bb79becSeschrock goto out; 925e9dbad6fSeschrock } 9263bb79becSeschrock 927e9dbad6fSeschrock for (i = 0; i < used; i++) { 9283bb79becSeschrock if (datasets[i]) 9293bb79becSeschrock remove_mountpoint(datasets[i]); 9303bb79becSeschrock } 9313bb79becSeschrock 9323bb79becSeschrock ret = 0; 9333bb79becSeschrock out: 9343bb79becSeschrock for (i = 0; i < used; i++) { 9353bb79becSeschrock if (datasets[i]) 9363bb79becSeschrock zfs_close(datasets[i]); 9373bb79becSeschrock free(mountpoints[i]); 9383bb79becSeschrock } 9393bb79becSeschrock free(datasets); 9403bb79becSeschrock free(mountpoints); 9413bb79becSeschrock 9423bb79becSeschrock return (ret); 9433bb79becSeschrock } 944