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 /* 154789Sahrens * Mount the given filesystem. 155789Sahrens */ 156789Sahrens int 157789Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 158789Sahrens { 159789Sahrens struct stat buf; 160789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 161789Sahrens char mntopts[MNT_LINE_MAX]; 1622082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 163789Sahrens 164789Sahrens if (options == NULL) 165789Sahrens mntopts[0] = '\0'; 166789Sahrens else 167789Sahrens (void) strlcpy(mntopts, options, sizeof (mntopts)); 168789Sahrens 169789Sahrens /* ignore non-filesystems */ 170789Sahrens if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 1712082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) 172789Sahrens return (0); 173789Sahrens 174789Sahrens /* return success if there is no mountpoint set */ 175789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 176789Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 177789Sahrens return (0); 178789Sahrens 179789Sahrens /* 180789Sahrens * If the 'zoned' property is set, and we're in the global zone, simply 181789Sahrens * return success. 182789Sahrens */ 1832082Seschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 1842082Seschrock getzoneid() == GLOBAL_ZONEID) 1852082Seschrock return (0); 186789Sahrens 187789Sahrens /* Create the directory if it doesn't already exist */ 188789Sahrens if (lstat(mountpoint, &buf) != 0) { 189789Sahrens if (mkdirp(mountpoint, 0755) != 0) { 1902082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1912082Seschrock "failed to create mountpoint")); 1922082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 1932082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 1942082Seschrock mountpoint)); 195789Sahrens } 196789Sahrens } 197789Sahrens 198789Sahrens /* 199789Sahrens * Determine if the mountpoint is empty. If so, refuse to perform the 200789Sahrens * mount. We don't perform this check if MS_OVERLAY is specified, which 201789Sahrens * would defeat the point. We also avoid this check if 'remount' is 202789Sahrens * specified. 203789Sahrens */ 204789Sahrens if ((flags & MS_OVERLAY) == 0 && 205789Sahrens strstr(mntopts, MNTOPT_REMOUNT) == NULL && 206789Sahrens !dir_is_empty(mountpoint)) { 2072082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2082082Seschrock "directory is not empty")); 2092082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2102082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 211789Sahrens } 212789Sahrens 213789Sahrens /* perform the mount */ 214789Sahrens if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 215789Sahrens MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 216789Sahrens /* 217789Sahrens * Generic errors are nasty, but there are just way too many 218789Sahrens * from mount(), and they're well-understood. We pick a few 219789Sahrens * common ones to improve upon. 220789Sahrens */ 2212082Seschrock if (errno == EBUSY) 2222082Seschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2232082Seschrock "mountpoint or dataset is busy")); 2242082Seschrock else 2252082Seschrock zfs_error_aux(hdl, strerror(errno)); 2262082Seschrock 2272082Seschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 2282082Seschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 2292082Seschrock zhp->zfs_name)); 230789Sahrens } 231789Sahrens 232789Sahrens return (0); 233789Sahrens } 234789Sahrens 235789Sahrens /* 2362474Seschrock * Unmount a single filesystem. 2372474Seschrock */ 2382474Seschrock static int 2392474Seschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 2402474Seschrock { 2412474Seschrock if (umount2(mountpoint, flags) != 0) { 2422474Seschrock zfs_error_aux(hdl, strerror(errno)); 2432474Seschrock return (zfs_error(hdl, EZFS_UMOUNTFAILED, 2442474Seschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 2452474Seschrock mountpoint)); 2462474Seschrock } 2472474Seschrock 2482474Seschrock return (0); 2492474Seschrock } 2502474Seschrock 2512474Seschrock /* 252789Sahrens * Unmount the given filesystem. 253789Sahrens */ 254789Sahrens int 255789Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 256789Sahrens { 257789Sahrens struct mnttab search = { 0 }, entry; 258789Sahrens 259789Sahrens /* check to see if need to unmount the filesystem */ 2602474Seschrock search.mnt_special = zhp->zfs_name; 2611407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 2622082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 263789Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 2642082Seschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 265789Sahrens 266789Sahrens if (mountpoint == NULL) 267789Sahrens mountpoint = entry.mnt_mountp; 268789Sahrens 269789Sahrens /* 2702474Seschrock * Unshare and unmount the filesystem 271789Sahrens */ 2722474Seschrock if (zfs_unshare(zhp, mountpoint) != 0 || 2732474Seschrock unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 274789Sahrens return (-1); 275789Sahrens } 276789Sahrens 277789Sahrens return (0); 278789Sahrens } 279789Sahrens 280789Sahrens /* 281789Sahrens * Unmount this filesystem and any children inheriting the mountpoint property. 282789Sahrens * To do this, just act like we're changing the mountpoint property, but don't 283789Sahrens * remount the filesystems afterwards. 284789Sahrens */ 285789Sahrens int 286789Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags) 287789Sahrens { 288789Sahrens prop_changelist_t *clp; 289789Sahrens int ret; 290789Sahrens 291789Sahrens clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 292789Sahrens if (clp == NULL) 293789Sahrens return (-1); 294789Sahrens 295789Sahrens ret = changelist_prefix(clp); 296789Sahrens changelist_free(clp); 297789Sahrens 298789Sahrens return (ret); 299789Sahrens } 300789Sahrens 301789Sahrens /* 302789Sahrens * Check to see if the filesystem is currently shared. 303789Sahrens */ 3042082Seschrock boolean_t 305789Sahrens zfs_is_shared(zfs_handle_t *zhp, char **where) 306789Sahrens { 307789Sahrens char *mountpoint; 308789Sahrens 309789Sahrens if (!zfs_is_mounted(zhp, &mountpoint)) 3102082Seschrock return (B_FALSE); 311789Sahrens 3122082Seschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 313789Sahrens if (where != NULL) 314789Sahrens *where = mountpoint; 315789Sahrens else 316789Sahrens free(mountpoint); 3172082Seschrock return (B_TRUE); 318789Sahrens } else { 319789Sahrens free(mountpoint); 3202082Seschrock return (B_FALSE); 321789Sahrens } 322789Sahrens } 323789Sahrens 324789Sahrens /* 325789Sahrens * Share the given filesystem according to the options in 'sharenfs'. We rely 326789Sahrens * on share(1M) to the dirty work for us. 327789Sahrens */ 328789Sahrens int 329789Sahrens zfs_share(zfs_handle_t *zhp) 330789Sahrens { 331789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 332789Sahrens char shareopts[ZFS_MAXPROPLEN]; 333789Sahrens char buf[MAXPATHLEN]; 334789Sahrens FILE *fp; 3352082Seschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 336789Sahrens 337789Sahrens /* ignore non-filesystems */ 338789Sahrens if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) 339789Sahrens return (0); 340789Sahrens 341789Sahrens /* return success if there is no mountpoint set */ 342789Sahrens if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 3432082Seschrock mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0 || 344789Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 345789Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 346789Sahrens return (0); 347789Sahrens 348789Sahrens /* return success if there are no share options */ 349789Sahrens if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 3502082Seschrock NULL, NULL, 0, B_FALSE) != 0 || 351789Sahrens strcmp(shareopts, "off") == 0) 352789Sahrens return (0); 353789Sahrens 354789Sahrens /* 355789Sahrens * If the 'zoned' property is set, simply return success since: 356789Sahrens * 1. in a global zone, a dataset should not be shared if it's 357789Sahrens * managed in a local zone. 358789Sahrens * 2. in a local zone, NFS server is not available. 359789Sahrens */ 360789Sahrens if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 361789Sahrens return (0); 362789Sahrens } 363789Sahrens 364789Sahrens /* 365789Sahrens * Invoke the share(1M) command. We always do this, even if it's 366789Sahrens * currently shared, as the options may have changed. 367789Sahrens */ 368789Sahrens if (strcmp(shareopts, "on") == 0) 369789Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 370789Sahrens "-F nfs \"%s\" 2>&1", mountpoint); 371789Sahrens else 372789Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 373789Sahrens "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 374789Sahrens mountpoint); 375789Sahrens 3762082Seschrock if ((fp = popen(buf, "r")) == NULL) 3772082Seschrock return (zfs_error(hdl, EZFS_SHAREFAILED, 3782082Seschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 3792082Seschrock zfs_get_name(zhp))); 380789Sahrens 381789Sahrens /* 382789Sahrens * share(1M) should only produce output if there is some kind 383789Sahrens * of error. All output begins with "share_nfs: ", so we trim 384789Sahrens * this off to get to the real error. 385789Sahrens */ 386789Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 387789Sahrens char *colon = strchr(buf, ':'); 388789Sahrens 389789Sahrens while (buf[strlen(buf) - 1] == '\n') 390789Sahrens buf[strlen(buf) - 1] = '\0'; 391789Sahrens 3922082Seschrock if (colon != NULL) 3932082Seschrock zfs_error_aux(hdl, colon + 2); 3942082Seschrock 3952082Seschrock (void) zfs_error(hdl, EZFS_SHAREFAILED, 3962474Seschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 3972474Seschrock zfs_get_name(zhp)); 398789Sahrens 399789Sahrens verify(pclose(fp) != 0); 400789Sahrens return (-1); 401789Sahrens } 402789Sahrens 403789Sahrens verify(pclose(fp) == 0); 404789Sahrens 405789Sahrens return (0); 406789Sahrens } 407789Sahrens 408789Sahrens /* 4092474Seschrock * Unshare a filesystem by mountpoint. 4102474Seschrock */ 4112474Seschrock static int 4122474Seschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 4132474Seschrock { 4142474Seschrock char buf[MAXPATHLEN]; 4152474Seschrock FILE *fp; 4162474Seschrock 4172474Seschrock (void) snprintf(buf, sizeof (buf), 4182474Seschrock "/usr/sbin/unshare \"%s\" 2>&1", 4192474Seschrock mountpoint); 4202474Seschrock 4212474Seschrock if ((fp = popen(buf, "r")) == NULL) 4222474Seschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 4232474Seschrock dgettext(TEXT_DOMAIN, 4242474Seschrock "cannot unshare '%s'"), name)); 4252474Seschrock 4262474Seschrock /* 4272474Seschrock * unshare(1M) should only produce output if there is 4282474Seschrock * some kind of error. All output begins with "unshare 4292474Seschrock * nfs: ", so we trim this off to get to the real error. 4302474Seschrock */ 4312474Seschrock if (fgets(buf, sizeof (buf), fp) != NULL) { 4322474Seschrock char *colon = strchr(buf, ':'); 4332474Seschrock 4342474Seschrock while (buf[strlen(buf) - 1] == '\n') 4352474Seschrock buf[strlen(buf) - 1] = '\0'; 4362474Seschrock 4372474Seschrock if (colon != NULL) 4382474Seschrock zfs_error_aux(hdl, colon + 2); 4392474Seschrock 4402474Seschrock verify(pclose(fp) != 0); 4412474Seschrock 4422474Seschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 4432474Seschrock dgettext(TEXT_DOMAIN, 4442474Seschrock "cannot unshare '%s'"), name)); 4452474Seschrock } 4462474Seschrock 4472474Seschrock verify(pclose(fp) == 0); 4482474Seschrock 4492474Seschrock return (0); 4502474Seschrock } 4512474Seschrock 4522474Seschrock /* 453789Sahrens * Unshare the given filesystem. 454789Sahrens */ 455789Sahrens int 456789Sahrens zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) 457789Sahrens { 458789Sahrens struct mnttab search = { 0 }, entry; 459789Sahrens 460789Sahrens /* check to see if need to unmount the filesystem */ 461789Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 4621407Snd150628 search.mnt_fstype = MNTTYPE_ZFS; 4632082Seschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 464789Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 4652082Seschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 466789Sahrens 467789Sahrens if (mountpoint == NULL) 468789Sahrens mountpoint = entry.mnt_mountp; 469789Sahrens 4702474Seschrock if (is_shared(zhp->zfs_hdl, mountpoint) && 4712474Seschrock unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 4722474Seschrock return (-1); 473789Sahrens } 474789Sahrens 475789Sahrens return (0); 476789Sahrens } 477789Sahrens 478789Sahrens /* 479789Sahrens * Same as zfs_unmountall(), but for unshares. 480789Sahrens */ 481789Sahrens int 482789Sahrens zfs_unshareall(zfs_handle_t *zhp) 483789Sahrens { 484789Sahrens prop_changelist_t *clp; 485789Sahrens int ret; 486789Sahrens 487789Sahrens clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 488789Sahrens if (clp == NULL) 489789Sahrens return (-1); 490789Sahrens 491789Sahrens ret = changelist_unshare(clp); 492789Sahrens changelist_free(clp); 493789Sahrens 494789Sahrens return (ret); 495789Sahrens } 496789Sahrens 497789Sahrens /* 498789Sahrens * Remove the mountpoint associated with the current dataset, if necessary. 499789Sahrens * We only remove the underlying directory if: 500789Sahrens * 501789Sahrens * - The mountpoint is not 'none' or 'legacy' 502789Sahrens * - The mountpoint is non-empty 503789Sahrens * - The mountpoint is the default or inherited 504789Sahrens * - The 'zoned' property is set, or we're in a local zone 505789Sahrens * 506789Sahrens * Any other directories we leave alone. 507789Sahrens */ 508789Sahrens void 509789Sahrens remove_mountpoint(zfs_handle_t *zhp) 510789Sahrens { 511789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 512789Sahrens char source[ZFS_MAXNAMELEN]; 513789Sahrens zfs_source_t sourcetype; 5142082Seschrock int zoneid = getzoneid(); 515789Sahrens 516789Sahrens /* ignore non-filesystems */ 517789Sahrens if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 518789Sahrens sizeof (mountpoint), &sourcetype, source, sizeof (source), 5192082Seschrock B_FALSE) != 0) 520789Sahrens return; 521789Sahrens 522789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 && 523789Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && 524789Sahrens (sourcetype == ZFS_SRC_DEFAULT || 525789Sahrens sourcetype == ZFS_SRC_INHERITED) && 526789Sahrens (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) || 5272082Seschrock zoneid != GLOBAL_ZONEID)) { 528789Sahrens 529789Sahrens /* 530789Sahrens * Try to remove the directory, silently ignoring any errors. 531789Sahrens * The filesystem may have since been removed or moved around, 532789Sahrens * and this isn't really useful to the administrator in any 533789Sahrens * 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) { 5642474Seschrock zfs_handle_t **datasets; 5652474Seschrock 5662474Seschrock if ((datasets = zfs_alloc(zhp->zfs_hdl, cbp->cb_alloc * 2 * 5672474Seschrock sizeof (void *))) == NULL) 5682474Seschrock return (-1); 5692474Seschrock 5702474Seschrock (void) memcpy(cbp->cb_datasets, datasets, 5712474Seschrock cbp->cb_alloc * sizeof (void *)); 5722474Seschrock free(cbp->cb_datasets); 5732474Seschrock cbp->cb_datasets = datasets; 5742474Seschrock } 5752474Seschrock 5762474Seschrock cbp->cb_datasets[cbp->cb_used++] = zhp; 5772474Seschrock return (0); 5782474Seschrock } 5792474Seschrock 5802474Seschrock static int 5812474Seschrock dataset_compare(const void *a, const void *b) 5822474Seschrock { 5832474Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 5842474Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 5852474Seschrock char mounta[MAXPATHLEN]; 5862474Seschrock char mountb[MAXPATHLEN]; 5872474Seschrock 5882474Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 5892474Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 5902474Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 5912474Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 5922474Seschrock 5932474Seschrock return (strcmp(mounta, mountb)); 5942474Seschrock } 5952474Seschrock 5962474Seschrock int 597*2500Seschrock zpool_mount_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 5982474Seschrock { 5992474Seschrock mount_cbdata_t cb = { 0 }; 6002474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 6012474Seschrock zfs_handle_t *zfsp; 6022474Seschrock int i, ret = -1; 6032474Seschrock 6042474Seschrock /* 6052474Seschrock * Gather all datasets within the pool. 6062474Seschrock */ 6072474Seschrock if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 6082474Seschrock return (-1); 6092474Seschrock cb.cb_alloc = 4; 6102474Seschrock 6112474Seschrock if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 6122474Seschrock goto out; 6132474Seschrock 6142474Seschrock cb.cb_datasets[0] = zfsp; 6152474Seschrock cb.cb_used = 1; 6162474Seschrock 6172474Seschrock if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 6182474Seschrock goto out; 6192474Seschrock 6202474Seschrock /* 6212474Seschrock * Sort the datasets by mountpoint. 6222474Seschrock */ 6232474Seschrock qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_compare); 6242474Seschrock 6252474Seschrock /* 6262474Seschrock * And mount all the datasets. 6272474Seschrock */ 6282474Seschrock ret = 0; 6292474Seschrock for (i = 0; i < cb.cb_used; i++) { 630*2500Seschrock if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 6312474Seschrock zfs_share(cb.cb_datasets[i]) != 0) 6322474Seschrock ret = -1; 6332474Seschrock } 6342474Seschrock 6352474Seschrock out: 6362474Seschrock for (i = 0; i < cb.cb_used; i++) 6372474Seschrock zfs_close(cb.cb_datasets[i]); 6382474Seschrock free(cb.cb_datasets); 6392474Seschrock 6402474Seschrock return (ret); 6412474Seschrock } 6422474Seschrock 6432474Seschrock /* 6442474Seschrock * Unshare and unmount all datasets within the given pool. We don't want to 6452474Seschrock * rely on traversing the DSL to discover the filesystems within the pool, 6462474Seschrock * because this may be expensive (if not all of them are mounted), and can fail 6472474Seschrock * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 6482474Seschrock * gather all the filesystems that are currently mounted. 6492474Seschrock */ 6502474Seschrock static int 6512474Seschrock mountpoint_compare(const void *a, const void *b) 6522474Seschrock { 6532474Seschrock const char *mounta = *((char **)a); 6542474Seschrock const char *mountb = *((char **)b); 6552474Seschrock 6562474Seschrock return (strcmp(mountb, mounta)); 6572474Seschrock } 6582474Seschrock 6592474Seschrock int 6602474Seschrock zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force) 6612474Seschrock { 6622474Seschrock int used, alloc; 6632474Seschrock struct mnttab entry; 6642474Seschrock size_t namelen; 6652474Seschrock char **mountpoints = NULL; 6662474Seschrock zfs_handle_t **datasets = NULL; 6672474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 6682474Seschrock int i; 6692474Seschrock int ret = -1; 6702474Seschrock int flags = (force ? MS_FORCE : 0); 6712474Seschrock 6722474Seschrock namelen = strlen(zhp->zpool_name); 6732474Seschrock 6742474Seschrock rewind(hdl->libzfs_mnttab); 6752474Seschrock used = alloc = 0; 6762474Seschrock while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 6772474Seschrock /* 6782474Seschrock * Ignore non-ZFS entries. 6792474Seschrock */ 6802474Seschrock if (entry.mnt_fstype == NULL || 6812474Seschrock strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6822474Seschrock continue; 6832474Seschrock 6842474Seschrock /* 6852474Seschrock * Ignore filesystems not within this pool. 6862474Seschrock */ 6872474Seschrock if (entry.mnt_mountp == NULL || 6882474Seschrock strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 6892474Seschrock (entry.mnt_special[namelen] != '/' && 6902474Seschrock entry.mnt_special[namelen] != '\0')) 6912474Seschrock continue; 6922474Seschrock 6932474Seschrock /* 6942474Seschrock * At this point we've found a filesystem within our pool. Add 6952474Seschrock * it to our growing list. 6962474Seschrock */ 6972474Seschrock if (used == alloc) { 6982474Seschrock if (alloc == 0) { 6992474Seschrock if ((mountpoints = zfs_alloc(hdl, 7002474Seschrock 8 * sizeof (void *))) == NULL) 7012474Seschrock goto out; 7022474Seschrock 7032474Seschrock if ((datasets = zfs_alloc(hdl, 7042474Seschrock 8 * sizeof (void *))) == NULL) 7052474Seschrock goto out; 7062474Seschrock 7072474Seschrock alloc = 8; 7082474Seschrock } else { 7092474Seschrock char **dest; 7102474Seschrock 7112474Seschrock if ((dest = zfs_alloc(hdl, 7122474Seschrock alloc * 2 * sizeof (void *))) == NULL) 7132474Seschrock goto out; 7142474Seschrock (void) memcpy(dest, mountpoints, 7152474Seschrock alloc * sizeof (void *)); 7162474Seschrock free(mountpoints); 7172474Seschrock mountpoints = dest; 7182474Seschrock 7192474Seschrock if ((dest = zfs_alloc(hdl, 7202474Seschrock alloc * 2 * sizeof (void *))) == NULL) 7212474Seschrock goto out; 7222474Seschrock (void) memcpy(dest, datasets, 7232474Seschrock alloc * sizeof (void *)); 7242474Seschrock free(datasets); 7252474Seschrock datasets = (zfs_handle_t **)dest; 7262474Seschrock 7272474Seschrock alloc *= 2; 7282474Seschrock } 7292474Seschrock } 7302474Seschrock 7312474Seschrock if ((mountpoints[used] = zfs_strdup(hdl, 7322474Seschrock entry.mnt_mountp)) == NULL) 7332474Seschrock goto out; 7342474Seschrock 7352474Seschrock /* 7362474Seschrock * This is allowed to fail, in case there is some I/O error. It 7372474Seschrock * is only used to determine if we need to remove the underlying 7382474Seschrock * mountpoint, so failure is not fatal. 7392474Seschrock */ 7402474Seschrock datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 7412474Seschrock 7422474Seschrock used++; 7432474Seschrock } 7442474Seschrock 7452474Seschrock /* 7462474Seschrock * At this point, we have the entire list of filesystems, so sort it by 7472474Seschrock * mountpoint. 7482474Seschrock */ 7492474Seschrock qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 7502474Seschrock 7512474Seschrock /* 7522474Seschrock * Walk through and first unshare everything. 7532474Seschrock */ 7542474Seschrock for (i = 0; i < used; i++) { 7552474Seschrock if (is_shared(hdl, mountpoints[i]) && 7562474Seschrock unshare_one(hdl, datasets[i] ? datasets[i]->zfs_name : 7572474Seschrock mountpoints[i], mountpoints[i]) != 0) 7582474Seschrock goto out; 7592474Seschrock } 7602474Seschrock 7612474Seschrock /* 7622474Seschrock * Now unmount everything, removing the underlying directories as 7632474Seschrock * appropriate. 7642474Seschrock */ 7652474Seschrock for (i = 0; i < used; i++) { 7662474Seschrock if (unmount_one(hdl, mountpoints[i], flags) != 0) 7672474Seschrock goto out; 7682474Seschrock 7692474Seschrock if (datasets[i]) 7702474Seschrock remove_mountpoint(datasets[i]); 7712474Seschrock } 7722474Seschrock 7732474Seschrock ret = 0; 7742474Seschrock out: 7752474Seschrock for (i = 0; i < used; i++) { 7762474Seschrock if (datasets[i]) 7772474Seschrock zfs_close(datasets[i]); 7782474Seschrock free(mountpoints[i]); 7792474Seschrock } 7802474Seschrock free(datasets); 7812474Seschrock free(mountpoints); 7822474Seschrock 7832474Seschrock return (ret); 7842474Seschrock } 785