1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 221371Seschrock * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens /* 29789Sahrens * Routines to manage ZFS mounts. We separate all the nasty routines that have 30789Sahrens * to deal with the OS. The main entry points are: 31789Sahrens * 32789Sahrens * zfs_is_mounted() 33789Sahrens * zfs_mount() 34789Sahrens * zfs_unmount() 35789Sahrens * zfs_unmountall() 36789Sahrens * 37789Sahrens * These functions are used by mount and unmount, and when changing a 38789Sahrens * filesystem's mountpoint. This file also contains the functions used to 39789Sahrens * manage sharing filesystems via NFS: 40789Sahrens * 41789Sahrens * zfs_is_shared() 42789Sahrens * zfs_share() 43789Sahrens * zfs_unshare() 44789Sahrens * zfs_unshareall() 452474Seschrock * 462474Seschrock * The following functions are available for pool consumers, and will 472474Seschrock * mount/unmount (and share/unshare) all datasets within pool: 482474Seschrock * 492474Seschrock * zpool_mount_datasets() 502474Seschrock * zpool_unmount_datasets() 51789Sahrens */ 52789Sahrens 53789Sahrens #include <dirent.h> 54789Sahrens #include <errno.h> 55789Sahrens #include <libgen.h> 56789Sahrens #include <libintl.h> 57789Sahrens #include <stdio.h> 58789Sahrens #include <stdlib.h> 59789Sahrens #include <strings.h> 60789Sahrens #include <unistd.h> 61789Sahrens #include <zone.h> 62789Sahrens #include <sys/mntent.h> 63789Sahrens #include <sys/mnttab.h> 64789Sahrens #include <sys/mount.h> 65789Sahrens #include <sys/stat.h> 66789Sahrens 67789Sahrens #include <libzfs.h> 68789Sahrens 69789Sahrens #include "libzfs_impl.h" 70789Sahrens 71789Sahrens /* 722082Seschrock * Search the sharetab for the given mountpoint, returning true if it is found. 73789Sahrens */ 742082Seschrock static boolean_t 752082Seschrock is_shared(libzfs_handle_t *hdl, const char *mountpoint) 76789Sahrens { 77789Sahrens char buf[MAXPATHLEN], *tab; 78789Sahrens 792082Seschrock if (hdl->libzfs_sharetab == NULL) 80789Sahrens return (0); 81789Sahrens 822082Seschrock (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 83789Sahrens 842082Seschrock while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 85789Sahrens 86789Sahrens /* the mountpoint is the first entry on each line */ 87789Sahrens if ((tab = strchr(buf, '\t')) != NULL) { 88789Sahrens *tab = '\0'; 89789Sahrens if (strcmp(buf, mountpoint) == 0) 902082Seschrock return (B_TRUE); 91789Sahrens } 92789Sahrens } 93789Sahrens 942082Seschrock return (B_FALSE); 95789Sahrens } 96789Sahrens 97789Sahrens /* 982082Seschrock * Returns true if the specified directory is empty. If we can't open the 992082Seschrock * directory at all, return true so that the mount can fail with a more 100789Sahrens * informative error message. 101789Sahrens */ 1022082Seschrock static boolean_t 103789Sahrens dir_is_empty(const char *dirname) 104789Sahrens { 105789Sahrens DIR *dirp; 106789Sahrens struct dirent64 *dp; 107789Sahrens 108789Sahrens if ((dirp = opendir(dirname)) == NULL) 1092082Seschrock return (B_TRUE); 110789Sahrens 111789Sahrens while ((dp = readdir64(dirp)) != NULL) { 112789Sahrens 113789Sahrens if (strcmp(dp->d_name, ".") == 0 || 114789Sahrens strcmp(dp->d_name, "..") == 0) 115789Sahrens continue; 116789Sahrens 117789Sahrens (void) closedir(dirp); 1182082Seschrock return (B_FALSE); 119789Sahrens } 120789Sahrens 121789Sahrens (void) closedir(dirp); 1222082Seschrock return (B_TRUE); 123789Sahrens } 124789Sahrens 125789Sahrens /* 126789Sahrens * Checks to see if the mount is active. If the filesystem is mounted, we fill 127789Sahrens * in 'where' with the current mountpoint, and return 1. Otherwise, we return 128789Sahrens * 0. 129789Sahrens */ 1302082Seschrock boolean_t 131789Sahrens zfs_is_mounted(zfs_handle_t *zhp, char **where) 132789Sahrens { 133789Sahrens struct mnttab search = { 0 }, entry; 134789Sahrens 135789Sahrens /* 136789Sahrens * Search for the entry in /etc/mnttab. We don't bother getting the 137789Sahrens * mountpoint, as we can just search for the special device. This will 138789Sahrens * also let us find mounts when the mountpoint is 'legacy'. 139789Sahrens */ 140789Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 1411407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 142789Sahrens 1432082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 1442082Seschrock if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 1452082Seschrock return (B_FALSE); 146789Sahrens 147789Sahrens if (where != NULL) 1482082Seschrock *where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); 149789Sahrens 1502082Seschrock return (B_TRUE); 151789Sahrens } 152789Sahrens 153789Sahrens /* 154*2676Seschrock * Returns true if the given dataset is mountable, false otherwise. Returns the 155*2676Seschrock * mountpoint in 'buf'. 156*2676Seschrock */ 157*2676Seschrock static boolean_t 158*2676Seschrock zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 159*2676Seschrock zfs_source_t *source) 160*2676Seschrock { 161*2676Seschrock char sourceloc[ZFS_MAXNAMELEN]; 162*2676Seschrock zfs_source_t sourcetype; 163*2676Seschrock 164*2676Seschrock if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 165*2676Seschrock return (B_FALSE); 166*2676Seschrock 167*2676Seschrock verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 168*2676Seschrock &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 169*2676Seschrock 170*2676Seschrock if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 171*2676Seschrock strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 172*2676Seschrock return (B_FALSE); 173*2676Seschrock 174*2676Seschrock if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) 175*2676Seschrock return (B_FALSE); 176*2676Seschrock 177*2676Seschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 178*2676Seschrock getzoneid() == GLOBAL_ZONEID) 179*2676Seschrock return (B_FALSE); 180*2676Seschrock 181*2676Seschrock if (source) 182*2676Seschrock *source = sourcetype; 183*2676Seschrock 184*2676Seschrock return (B_TRUE); 185*2676Seschrock } 186*2676Seschrock 187*2676Seschrock /* 188789Sahrens * Mount the given filesystem. 189789Sahrens */ 190789Sahrens int 191789Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 192789Sahrens { 193789Sahrens struct stat buf; 194789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 195789Sahrens char mntopts[MNT_LINE_MAX]; 1962082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 197789Sahrens 198789Sahrens if (options == NULL) 199789Sahrens mntopts[0] = '\0'; 200789Sahrens else 201789Sahrens (void) strlcpy(mntopts, options, sizeof (mntopts)); 202789Sahrens 203*2676Seschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 2042082Seschrock return (0); 205789Sahrens 206789Sahrens /* Create the directory if it doesn't already exist */ 207789Sahrens if (lstat(mountpoint, &buf) != 0) { 208789Sahrens if (mkdirp(mountpoint, 0755) != 0) { 2092082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2102082Seschrock "failed to create mountpoint")); 2112082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2122082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 2132082Seschrock mountpoint)); 214789Sahrens } 215789Sahrens } 216789Sahrens 217789Sahrens /* 218789Sahrens * Determine if the mountpoint is empty. If so, refuse to perform the 219789Sahrens * mount. We don't perform this check if MS_OVERLAY is specified, which 220789Sahrens * would defeat the point. We also avoid this check if 'remount' is 221789Sahrens * specified. 222789Sahrens */ 223789Sahrens if ((flags & MS_OVERLAY) == 0 && 224789Sahrens strstr(mntopts, MNTOPT_REMOUNT) == NULL && 225789Sahrens !dir_is_empty(mountpoint)) { 2262082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2272082Seschrock "directory is not empty")); 2282082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2292082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 230789Sahrens } 231789Sahrens 232789Sahrens /* perform the mount */ 233789Sahrens if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 234789Sahrens MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 235789Sahrens /* 236789Sahrens * Generic errors are nasty, but there are just way too many 237789Sahrens * from mount(), and they're well-understood. We pick a few 238789Sahrens * common ones to improve upon. 239789Sahrens */ 2402082Seschrock if (errno == EBUSY) 2412082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2422082Seschrock "mountpoint or dataset is busy")); 2432082Seschrock else 2442082Seschrock zfs_error_aux(hdl, strerror(errno)); 2452082Seschrock 2462082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2472082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 2482082Seschrock zhp->zfs_name)); 249789Sahrens } 250789Sahrens 251789Sahrens return (0); 252789Sahrens } 253789Sahrens 254789Sahrens /* 2552474Seschrock * Unmount a single filesystem. 2562474Seschrock */ 2572474Seschrock static int 2582474Seschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 2592474Seschrock { 2602474Seschrock if (umount2(mountpoint, flags) != 0) { 2612474Seschrock zfs_error_aux(hdl, strerror(errno)); 2622474Seschrock return (zfs_error(hdl, EZFS_UMOUNTFAILED, 2632474Seschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 2642474Seschrock mountpoint)); 2652474Seschrock } 2662474Seschrock 2672474Seschrock return (0); 2682474Seschrock } 2692474Seschrock 2702474Seschrock /* 271789Sahrens * Unmount the given filesystem. 272789Sahrens */ 273789Sahrens int 274789Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 275789Sahrens { 276789Sahrens struct mnttab search = { 0 }, entry; 277789Sahrens 278789Sahrens /* check to see if need to unmount the filesystem */ 2792474Seschrock search.mnt_special = zhp->zfs_name; 2801407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 2812082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 282789Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 2832082Seschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 284789Sahrens 285789Sahrens if (mountpoint == NULL) 286789Sahrens mountpoint = entry.mnt_mountp; 287789Sahrens 288789Sahrens /* 2892474Seschrock * Unshare and unmount the filesystem 290789Sahrens */ 2912474Seschrock if (zfs_unshare(zhp, mountpoint) != 0 || 2922474Seschrock unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 293789Sahrens return (-1); 294789Sahrens } 295789Sahrens 296789Sahrens return (0); 297789Sahrens } 298789Sahrens 299789Sahrens /* 300789Sahrens * Unmount this filesystem and any children inheriting the mountpoint property. 301789Sahrens * To do this, just act like we're changing the mountpoint property, but don't 302789Sahrens * remount the filesystems afterwards. 303789Sahrens */ 304789Sahrens int 305789Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags) 306789Sahrens { 307789Sahrens prop_changelist_t *clp; 308789Sahrens int ret; 309789Sahrens 310789Sahrens clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 311789Sahrens if (clp == NULL) 312789Sahrens return (-1); 313789Sahrens 314789Sahrens ret = changelist_prefix(clp); 315789Sahrens changelist_free(clp); 316789Sahrens 317789Sahrens return (ret); 318789Sahrens } 319789Sahrens 320789Sahrens /* 321789Sahrens * Check to see if the filesystem is currently shared. 322789Sahrens */ 3232082Seschrock boolean_t 324789Sahrens zfs_is_shared(zfs_handle_t *zhp, char **where) 325789Sahrens { 326789Sahrens char *mountpoint; 327789Sahrens 328789Sahrens if (!zfs_is_mounted(zhp, &mountpoint)) 3292082Seschrock return (B_FALSE); 330789Sahrens 3312082Seschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 332789Sahrens if (where != NULL) 333789Sahrens *where = mountpoint; 334789Sahrens else 335789Sahrens free(mountpoint); 3362082Seschrock return (B_TRUE); 337789Sahrens } else { 338789Sahrens free(mountpoint); 3392082Seschrock return (B_FALSE); 340789Sahrens } 341789Sahrens } 342789Sahrens 343789Sahrens /* 344789Sahrens * Share the given filesystem according to the options in 'sharenfs'. We rely 345789Sahrens * on share(1M) to the dirty work for us. 346789Sahrens */ 347789Sahrens int 348789Sahrens zfs_share(zfs_handle_t *zhp) 349789Sahrens { 350789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 351789Sahrens char shareopts[ZFS_MAXPROPLEN]; 352789Sahrens char buf[MAXPATHLEN]; 353789Sahrens FILE *fp; 3542082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 355789Sahrens 356*2676Seschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 357789Sahrens return (0); 358789Sahrens 359789Sahrens /* return success if there are no share options */ 360789Sahrens if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 3612082Seschrock NULL, NULL, 0, B_FALSE) != 0 || 362789Sahrens strcmp(shareopts, "off") == 0) 363789Sahrens return (0); 364789Sahrens 365789Sahrens /* 366*2676Seschrock * If the 'zoned' property is set, then zfs_is_mountable() will have 367*2676Seschrock * already bailed out if we are in the global zone. But local 368*2676Seschrock * zones cannot be NFS servers, so we ignore it for local zones as well. 369789Sahrens */ 370*2676Seschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 371789Sahrens return (0); 372789Sahrens 373789Sahrens /* 374789Sahrens * Invoke the share(1M) command. We always do this, even if it's 375789Sahrens * currently shared, as the options may have changed. 376789Sahrens */ 377789Sahrens if (strcmp(shareopts, "on") == 0) 378789Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 379789Sahrens "-F nfs \"%s\" 2>&1", mountpoint); 380789Sahrens else 381789Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 382789Sahrens "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 383789Sahrens mountpoint); 384789Sahrens 3852082Seschrock if ((fp = popen(buf, "r")) == NULL) 3862082Seschrock return (zfs_error(hdl, EZFS_SHAREFAILED, 3872082Seschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 3882082Seschrock zfs_get_name(zhp))); 389789Sahrens 390789Sahrens /* 391789Sahrens * share(1M) should only produce output if there is some kind 392789Sahrens * of error. All output begins with "share_nfs: ", so we trim 393789Sahrens * this off to get to the real error. 394789Sahrens */ 395789Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 396789Sahrens char *colon = strchr(buf, ':'); 397789Sahrens 398789Sahrens while (buf[strlen(buf) - 1] == '\n') 399789Sahrens buf[strlen(buf) - 1] = '\0'; 400789Sahrens 4012082Seschrock if (colon != NULL) 4022082Seschrock zfs_error_aux(hdl, colon + 2); 4032082Seschrock 4042082Seschrock (void) zfs_error(hdl, EZFS_SHAREFAILED, 4052474Seschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 4062474Seschrock zfs_get_name(zhp)); 407789Sahrens 408789Sahrens verify(pclose(fp) != 0); 409789Sahrens return (-1); 410789Sahrens } 411789Sahrens 412789Sahrens verify(pclose(fp) == 0); 413789Sahrens 414789Sahrens return (0); 415789Sahrens } 416789Sahrens 417789Sahrens /* 4182474Seschrock * Unshare a filesystem by mountpoint. 4192474Seschrock */ 4202474Seschrock static int 4212474Seschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 4222474Seschrock { 4232474Seschrock char buf[MAXPATHLEN]; 4242474Seschrock FILE *fp; 4252474Seschrock 4262474Seschrock (void) snprintf(buf, sizeof (buf), 4272474Seschrock "/usr/sbin/unshare \"%s\" 2>&1", 4282474Seschrock mountpoint); 4292474Seschrock 4302474Seschrock if ((fp = popen(buf, "r")) == NULL) 4312474Seschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 4322474Seschrock dgettext(TEXT_DOMAIN, 4332474Seschrock "cannot unshare '%s'"), name)); 4342474Seschrock 4352474Seschrock /* 4362474Seschrock * unshare(1M) should only produce output if there is 4372474Seschrock * some kind of error. All output begins with "unshare 4382474Seschrock * nfs: ", so we trim this off to get to the real error. 4392474Seschrock */ 4402474Seschrock if (fgets(buf, sizeof (buf), fp) != NULL) { 4412474Seschrock char *colon = strchr(buf, ':'); 4422474Seschrock 4432474Seschrock while (buf[strlen(buf) - 1] == '\n') 4442474Seschrock buf[strlen(buf) - 1] = '\0'; 4452474Seschrock 4462474Seschrock if (colon != NULL) 4472474Seschrock zfs_error_aux(hdl, colon + 2); 4482474Seschrock 4492474Seschrock verify(pclose(fp) != 0); 4502474Seschrock 4512474Seschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 4522474Seschrock dgettext(TEXT_DOMAIN, 4532474Seschrock "cannot unshare '%s'"), name)); 4542474Seschrock } 4552474Seschrock 4562474Seschrock verify(pclose(fp) == 0); 4572474Seschrock 4582474Seschrock return (0); 4592474Seschrock } 4602474Seschrock 4612474Seschrock /* 462789Sahrens * Unshare the given filesystem. 463789Sahrens */ 464789Sahrens int 465789Sahrens zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) 466789Sahrens { 467789Sahrens struct mnttab search = { 0 }, entry; 468789Sahrens 469789Sahrens /* check to see if need to unmount the filesystem */ 470789Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 4711407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 4722082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 473789Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 4742082Seschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 475789Sahrens 476789Sahrens if (mountpoint == NULL) 477789Sahrens mountpoint = entry.mnt_mountp; 478789Sahrens 4792474Seschrock if (is_shared(zhp->zfs_hdl, mountpoint) && 4802474Seschrock unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 4812474Seschrock return (-1); 482789Sahrens } 483789Sahrens 484789Sahrens return (0); 485789Sahrens } 486789Sahrens 487789Sahrens /* 488789Sahrens * Same as zfs_unmountall(), but for unshares. 489789Sahrens */ 490789Sahrens int 491789Sahrens zfs_unshareall(zfs_handle_t *zhp) 492789Sahrens { 493789Sahrens prop_changelist_t *clp; 494789Sahrens int ret; 495789Sahrens 496789Sahrens clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 497789Sahrens if (clp == NULL) 498789Sahrens return (-1); 499789Sahrens 500789Sahrens ret = changelist_unshare(clp); 501789Sahrens changelist_free(clp); 502789Sahrens 503789Sahrens return (ret); 504789Sahrens } 505789Sahrens 506789Sahrens /* 507789Sahrens * Remove the mountpoint associated with the current dataset, if necessary. 508789Sahrens * We only remove the underlying directory if: 509789Sahrens * 510789Sahrens * - The mountpoint is not 'none' or 'legacy' 511789Sahrens * - The mountpoint is non-empty 512789Sahrens * - The mountpoint is the default or inherited 513789Sahrens * - The 'zoned' property is set, or we're in a local zone 514789Sahrens * 515789Sahrens * Any other directories we leave alone. 516789Sahrens */ 517789Sahrens void 518789Sahrens remove_mountpoint(zfs_handle_t *zhp) 519789Sahrens { 520789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 521*2676Seschrock zfs_source_t source; 522789Sahrens 523*2676Seschrock if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 524*2676Seschrock &source)) 525789Sahrens return; 526789Sahrens 527*2676Seschrock if (source == ZFS_SRC_DEFAULT || 528*2676Seschrock source == ZFS_SRC_INHERITED) { 529789Sahrens /* 530789Sahrens * Try to remove the directory, silently ignoring any errors. 531789Sahrens * The filesystem may have since been removed or moved around, 532*2676Seschrock * and this error isn't really useful to the administrator in 533*2676Seschrock * any way. 534789Sahrens */ 535789Sahrens (void) rmdir(mountpoint); 536789Sahrens } 537789Sahrens } 5382474Seschrock 5392474Seschrock /* 5402474Seschrock * Mount and share all datasets within the given pool. This assumes that no 5412474Seschrock * datasets within the pool are currently mounted. Because users can create 5422474Seschrock * complicated nested hierarchies of mountpoints, we first gather all the 5432474Seschrock * datasets and mountpoints within the pool, and sort them by mountpoint. Once 5442474Seschrock * we have the list of all filesystems, we iterate over them in order and mount 5452474Seschrock * and/or share each one. 5462474Seschrock */ 5472474Seschrock typedef struct mount_cbdata { 5482474Seschrock zfs_handle_t **cb_datasets; 5492474Seschrock int cb_used; 5502474Seschrock int cb_alloc; 5512474Seschrock } mount_cbdata_t; 5522474Seschrock 5532474Seschrock static int 5542474Seschrock mount_cb(zfs_handle_t *zhp, void *data) 5552474Seschrock { 5562474Seschrock mount_cbdata_t *cbp = data; 5572474Seschrock 5582474Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 5592474Seschrock zfs_close(zhp); 5602474Seschrock return (0); 5612474Seschrock } 5622474Seschrock 5632474Seschrock if (cbp->cb_alloc == cbp->cb_used) { 564*2676Seschrock void *ptr; 5652474Seschrock 566*2676Seschrock if ((ptr = zfs_realloc(zhp->zfs_hdl, 567*2676Seschrock cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), 568*2676Seschrock cbp->cb_alloc * 2 * sizeof (void *))) == NULL) 5692474Seschrock return (-1); 570*2676Seschrock cbp->cb_datasets = ptr; 5712474Seschrock 572*2676Seschrock cbp->cb_alloc *= 2; 5732474Seschrock } 5742474Seschrock 5752474Seschrock cbp->cb_datasets[cbp->cb_used++] = zhp; 5762474Seschrock return (0); 5772474Seschrock } 5782474Seschrock 5792474Seschrock static int 5802474Seschrock dataset_compare(const void *a, const void *b) 5812474Seschrock { 5822474Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 5832474Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 5842474Seschrock char mounta[MAXPATHLEN]; 5852474Seschrock char mountb[MAXPATHLEN]; 5862474Seschrock 5872474Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 5882474Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 5892474Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 5902474Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 5912474Seschrock 5922474Seschrock return (strcmp(mounta, mountb)); 5932474Seschrock } 5942474Seschrock 5952474Seschrock int 5962500Seschrock zpool_mount_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 5972474Seschrock { 5982474Seschrock mount_cbdata_t cb = { 0 }; 5992474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 6002474Seschrock zfs_handle_t *zfsp; 6012474Seschrock int i, ret = -1; 6022474Seschrock 6032474Seschrock /* 6042474Seschrock * Gather all datasets within the pool. 6052474Seschrock */ 6062474Seschrock if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 6072474Seschrock return (-1); 6082474Seschrock cb.cb_alloc = 4; 6092474Seschrock 6102474Seschrock if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 6112474Seschrock goto out; 6122474Seschrock 6132474Seschrock cb.cb_datasets[0] = zfsp; 6142474Seschrock cb.cb_used = 1; 6152474Seschrock 6162474Seschrock if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 6172474Seschrock goto out; 6182474Seschrock 6192474Seschrock /* 6202474Seschrock * Sort the datasets by mountpoint. 6212474Seschrock */ 6222474Seschrock qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_compare); 6232474Seschrock 6242474Seschrock /* 6252474Seschrock * And mount all the datasets. 6262474Seschrock */ 6272474Seschrock ret = 0; 6282474Seschrock for (i = 0; i < cb.cb_used; i++) { 6292500Seschrock if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 6302474Seschrock zfs_share(cb.cb_datasets[i]) != 0) 6312474Seschrock ret = -1; 6322474Seschrock } 6332474Seschrock 6342474Seschrock out: 6352474Seschrock for (i = 0; i < cb.cb_used; i++) 6362474Seschrock zfs_close(cb.cb_datasets[i]); 6372474Seschrock free(cb.cb_datasets); 6382474Seschrock 6392474Seschrock return (ret); 6402474Seschrock } 6412474Seschrock 6422474Seschrock /* 6432474Seschrock * Unshare and unmount all datasets within the given pool. We don't want to 6442474Seschrock * rely on traversing the DSL to discover the filesystems within the pool, 6452474Seschrock * because this may be expensive (if not all of them are mounted), and can fail 6462474Seschrock * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 6472474Seschrock * gather all the filesystems that are currently mounted. 6482474Seschrock */ 6492474Seschrock static int 6502474Seschrock mountpoint_compare(const void *a, const void *b) 6512474Seschrock { 6522474Seschrock const char *mounta = *((char **)a); 6532474Seschrock const char *mountb = *((char **)b); 6542474Seschrock 6552474Seschrock return (strcmp(mountb, mounta)); 6562474Seschrock } 6572474Seschrock 6582474Seschrock int 6592474Seschrock zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force) 6602474Seschrock { 6612474Seschrock int used, alloc; 6622474Seschrock struct mnttab entry; 6632474Seschrock size_t namelen; 6642474Seschrock char **mountpoints = NULL; 6652474Seschrock zfs_handle_t **datasets = NULL; 6662474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 6672474Seschrock int i; 6682474Seschrock int ret = -1; 6692474Seschrock int flags = (force ? MS_FORCE : 0); 6702474Seschrock 6712474Seschrock namelen = strlen(zhp->zpool_name); 6722474Seschrock 6732474Seschrock rewind(hdl->libzfs_mnttab); 6742474Seschrock used = alloc = 0; 6752474Seschrock while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 6762474Seschrock /* 6772474Seschrock * Ignore non-ZFS entries. 6782474Seschrock */ 6792474Seschrock if (entry.mnt_fstype == NULL || 6802474Seschrock strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6812474Seschrock continue; 6822474Seschrock 6832474Seschrock /* 6842474Seschrock * Ignore filesystems not within this pool. 6852474Seschrock */ 6862474Seschrock if (entry.mnt_mountp == NULL || 6872474Seschrock strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 6882474Seschrock (entry.mnt_special[namelen] != '/' && 6892474Seschrock entry.mnt_special[namelen] != '\0')) 6902474Seschrock continue; 6912474Seschrock 6922474Seschrock /* 6932474Seschrock * At this point we've found a filesystem within our pool. Add 6942474Seschrock * it to our growing list. 6952474Seschrock */ 6962474Seschrock if (used == alloc) { 6972474Seschrock if (alloc == 0) { 6982474Seschrock if ((mountpoints = zfs_alloc(hdl, 6992474Seschrock 8 * sizeof (void *))) == NULL) 7002474Seschrock goto out; 7012474Seschrock 7022474Seschrock if ((datasets = zfs_alloc(hdl, 7032474Seschrock 8 * sizeof (void *))) == NULL) 7042474Seschrock goto out; 7052474Seschrock 7062474Seschrock alloc = 8; 7072474Seschrock } else { 708*2676Seschrock void *ptr; 7092474Seschrock 710*2676Seschrock if ((ptr = zfs_realloc(hdl, mountpoints, 711*2676Seschrock alloc * sizeof (void *), 7122474Seschrock alloc * 2 * sizeof (void *))) == NULL) 7132474Seschrock goto out; 714*2676Seschrock mountpoints = ptr; 7152474Seschrock 716*2676Seschrock if ((ptr = zfs_realloc(hdl, datasets, 717*2676Seschrock alloc * sizeof (void *), 7182474Seschrock alloc * 2 * sizeof (void *))) == NULL) 7192474Seschrock goto out; 720*2676Seschrock datasets = ptr; 7212474Seschrock 7222474Seschrock alloc *= 2; 7232474Seschrock } 7242474Seschrock } 7252474Seschrock 7262474Seschrock if ((mountpoints[used] = zfs_strdup(hdl, 7272474Seschrock entry.mnt_mountp)) == NULL) 7282474Seschrock goto out; 7292474Seschrock 7302474Seschrock /* 7312474Seschrock * This is allowed to fail, in case there is some I/O error. It 7322474Seschrock * is only used to determine if we need to remove the underlying 7332474Seschrock * mountpoint, so failure is not fatal. 7342474Seschrock */ 7352474Seschrock datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 7362474Seschrock 7372474Seschrock used++; 7382474Seschrock } 7392474Seschrock 7402474Seschrock /* 7412474Seschrock * At this point, we have the entire list of filesystems, so sort it by 7422474Seschrock * mountpoint. 7432474Seschrock */ 7442474Seschrock qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 7452474Seschrock 7462474Seschrock /* 7472474Seschrock * Walk through and first unshare everything. 7482474Seschrock */ 7492474Seschrock for (i = 0; i < used; i++) { 7502474Seschrock if (is_shared(hdl, mountpoints[i]) && 751*2676Seschrock unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) 7522474Seschrock goto out; 7532474Seschrock } 7542474Seschrock 7552474Seschrock /* 7562474Seschrock * Now unmount everything, removing the underlying directories as 7572474Seschrock * appropriate. 7582474Seschrock */ 7592474Seschrock for (i = 0; i < used; i++) { 7602474Seschrock if (unmount_one(hdl, mountpoints[i], flags) != 0) 7612474Seschrock goto out; 762*2676Seschrock } 7632474Seschrock 764*2676Seschrock for (i = 0; i < used; i++) { 7652474Seschrock if (datasets[i]) 7662474Seschrock remove_mountpoint(datasets[i]); 7672474Seschrock } 7682474Seschrock 7692474Seschrock ret = 0; 7702474Seschrock out: 7712474Seschrock for (i = 0; i < used; i++) { 7722474Seschrock if (datasets[i]) 7732474Seschrock zfs_close(datasets[i]); 7742474Seschrock free(mountpoints[i]); 7752474Seschrock } 7762474Seschrock free(datasets); 7772474Seschrock free(mountpoints); 7782474Seschrock 7792474Seschrock return (ret); 7802474Seschrock } 781