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() 45*2474Seschrock * 46*2474Seschrock * The following functions are available for pool consumers, and will 47*2474Seschrock * mount/unmount (and share/unshare) all datasets within pool: 48*2474Seschrock * 49*2474Seschrock * zpool_mount_datasets() 50*2474Seschrock * 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 /* 236*2474Seschrock * Unmount a single filesystem. 237*2474Seschrock */ 238*2474Seschrock static int 239*2474Seschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 240*2474Seschrock { 241*2474Seschrock if (umount2(mountpoint, flags) != 0) { 242*2474Seschrock zfs_error_aux(hdl, strerror(errno)); 243*2474Seschrock return (zfs_error(hdl, EZFS_UMOUNTFAILED, 244*2474Seschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 245*2474Seschrock mountpoint)); 246*2474Seschrock } 247*2474Seschrock 248*2474Seschrock return (0); 249*2474Seschrock } 250*2474Seschrock 251*2474Seschrock /* 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 */ 260*2474Seschrock 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 /* 270*2474Seschrock * Unshare and unmount the filesystem 271789Sahrens */ 272*2474Seschrock if (zfs_unshare(zhp, mountpoint) != 0 || 273*2474Seschrock 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, 396*2474Seschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 397*2474Seschrock 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 /* 409*2474Seschrock * Unshare a filesystem by mountpoint. 410*2474Seschrock */ 411*2474Seschrock static int 412*2474Seschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 413*2474Seschrock { 414*2474Seschrock char buf[MAXPATHLEN]; 415*2474Seschrock FILE *fp; 416*2474Seschrock 417*2474Seschrock (void) snprintf(buf, sizeof (buf), 418*2474Seschrock "/usr/sbin/unshare \"%s\" 2>&1", 419*2474Seschrock mountpoint); 420*2474Seschrock 421*2474Seschrock if ((fp = popen(buf, "r")) == NULL) 422*2474Seschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 423*2474Seschrock dgettext(TEXT_DOMAIN, 424*2474Seschrock "cannot unshare '%s'"), name)); 425*2474Seschrock 426*2474Seschrock /* 427*2474Seschrock * unshare(1M) should only produce output if there is 428*2474Seschrock * some kind of error. All output begins with "unshare 429*2474Seschrock * nfs: ", so we trim this off to get to the real error. 430*2474Seschrock */ 431*2474Seschrock if (fgets(buf, sizeof (buf), fp) != NULL) { 432*2474Seschrock char *colon = strchr(buf, ':'); 433*2474Seschrock 434*2474Seschrock while (buf[strlen(buf) - 1] == '\n') 435*2474Seschrock buf[strlen(buf) - 1] = '\0'; 436*2474Seschrock 437*2474Seschrock if (colon != NULL) 438*2474Seschrock zfs_error_aux(hdl, colon + 2); 439*2474Seschrock 440*2474Seschrock verify(pclose(fp) != 0); 441*2474Seschrock 442*2474Seschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 443*2474Seschrock dgettext(TEXT_DOMAIN, 444*2474Seschrock "cannot unshare '%s'"), name)); 445*2474Seschrock } 446*2474Seschrock 447*2474Seschrock verify(pclose(fp) == 0); 448*2474Seschrock 449*2474Seschrock return (0); 450*2474Seschrock } 451*2474Seschrock 452*2474Seschrock /* 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 470*2474Seschrock if (is_shared(zhp->zfs_hdl, mountpoint) && 471*2474Seschrock unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 472*2474Seschrock 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 } 538*2474Seschrock 539*2474Seschrock /* 540*2474Seschrock * Mount and share all datasets within the given pool. This assumes that no 541*2474Seschrock * datasets within the pool are currently mounted. Because users can create 542*2474Seschrock * complicated nested hierarchies of mountpoints, we first gather all the 543*2474Seschrock * datasets and mountpoints within the pool, and sort them by mountpoint. Once 544*2474Seschrock * we have the list of all filesystems, we iterate over them in order and mount 545*2474Seschrock * and/or share each one. 546*2474Seschrock */ 547*2474Seschrock typedef struct mount_cbdata { 548*2474Seschrock zfs_handle_t **cb_datasets; 549*2474Seschrock int cb_used; 550*2474Seschrock int cb_alloc; 551*2474Seschrock } mount_cbdata_t; 552*2474Seschrock 553*2474Seschrock static int 554*2474Seschrock mount_cb(zfs_handle_t *zhp, void *data) 555*2474Seschrock { 556*2474Seschrock mount_cbdata_t *cbp = data; 557*2474Seschrock 558*2474Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 559*2474Seschrock zfs_close(zhp); 560*2474Seschrock return (0); 561*2474Seschrock } 562*2474Seschrock 563*2474Seschrock if (cbp->cb_alloc == cbp->cb_used) { 564*2474Seschrock zfs_handle_t **datasets; 565*2474Seschrock 566*2474Seschrock if ((datasets = zfs_alloc(zhp->zfs_hdl, cbp->cb_alloc * 2 * 567*2474Seschrock sizeof (void *))) == NULL) 568*2474Seschrock return (-1); 569*2474Seschrock 570*2474Seschrock (void) memcpy(cbp->cb_datasets, datasets, 571*2474Seschrock cbp->cb_alloc * sizeof (void *)); 572*2474Seschrock free(cbp->cb_datasets); 573*2474Seschrock cbp->cb_datasets = datasets; 574*2474Seschrock } 575*2474Seschrock 576*2474Seschrock cbp->cb_datasets[cbp->cb_used++] = zhp; 577*2474Seschrock return (0); 578*2474Seschrock } 579*2474Seschrock 580*2474Seschrock static int 581*2474Seschrock dataset_compare(const void *a, const void *b) 582*2474Seschrock { 583*2474Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 584*2474Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 585*2474Seschrock char mounta[MAXPATHLEN]; 586*2474Seschrock char mountb[MAXPATHLEN]; 587*2474Seschrock 588*2474Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 589*2474Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 590*2474Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 591*2474Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 592*2474Seschrock 593*2474Seschrock return (strcmp(mounta, mountb)); 594*2474Seschrock } 595*2474Seschrock 596*2474Seschrock int 597*2474Seschrock zpool_mount_datasets(zpool_handle_t *zhp, const char *mntopts) 598*2474Seschrock { 599*2474Seschrock mount_cbdata_t cb = { 0 }; 600*2474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 601*2474Seschrock zfs_handle_t *zfsp; 602*2474Seschrock int i, ret = -1; 603*2474Seschrock 604*2474Seschrock /* 605*2474Seschrock * Gather all datasets within the pool. 606*2474Seschrock */ 607*2474Seschrock if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 608*2474Seschrock return (-1); 609*2474Seschrock cb.cb_alloc = 4; 610*2474Seschrock 611*2474Seschrock if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 612*2474Seschrock goto out; 613*2474Seschrock 614*2474Seschrock cb.cb_datasets[0] = zfsp; 615*2474Seschrock cb.cb_used = 1; 616*2474Seschrock 617*2474Seschrock if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 618*2474Seschrock goto out; 619*2474Seschrock 620*2474Seschrock /* 621*2474Seschrock * Sort the datasets by mountpoint. 622*2474Seschrock */ 623*2474Seschrock qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_compare); 624*2474Seschrock 625*2474Seschrock /* 626*2474Seschrock * And mount all the datasets. 627*2474Seschrock */ 628*2474Seschrock ret = 0; 629*2474Seschrock for (i = 0; i < cb.cb_used; i++) { 630*2474Seschrock if (zfs_mount(cb.cb_datasets[i], mntopts, 0) != 0 || 631*2474Seschrock zfs_share(cb.cb_datasets[i]) != 0) 632*2474Seschrock ret = -1; 633*2474Seschrock } 634*2474Seschrock 635*2474Seschrock out: 636*2474Seschrock for (i = 0; i < cb.cb_used; i++) 637*2474Seschrock zfs_close(cb.cb_datasets[i]); 638*2474Seschrock free(cb.cb_datasets); 639*2474Seschrock 640*2474Seschrock return (ret); 641*2474Seschrock } 642*2474Seschrock 643*2474Seschrock /* 644*2474Seschrock * Unshare and unmount all datasets within the given pool. We don't want to 645*2474Seschrock * rely on traversing the DSL to discover the filesystems within the pool, 646*2474Seschrock * because this may be expensive (if not all of them are mounted), and can fail 647*2474Seschrock * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 648*2474Seschrock * gather all the filesystems that are currently mounted. 649*2474Seschrock */ 650*2474Seschrock static int 651*2474Seschrock mountpoint_compare(const void *a, const void *b) 652*2474Seschrock { 653*2474Seschrock const char *mounta = *((char **)a); 654*2474Seschrock const char *mountb = *((char **)b); 655*2474Seschrock 656*2474Seschrock return (strcmp(mountb, mounta)); 657*2474Seschrock } 658*2474Seschrock 659*2474Seschrock int 660*2474Seschrock zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force) 661*2474Seschrock { 662*2474Seschrock int used, alloc; 663*2474Seschrock struct mnttab entry; 664*2474Seschrock size_t namelen; 665*2474Seschrock char **mountpoints = NULL; 666*2474Seschrock zfs_handle_t **datasets = NULL; 667*2474Seschrock libzfs_handle_t *hdl = zhp->zpool_hdl; 668*2474Seschrock int i; 669*2474Seschrock int ret = -1; 670*2474Seschrock int flags = (force ? MS_FORCE : 0); 671*2474Seschrock 672*2474Seschrock namelen = strlen(zhp->zpool_name); 673*2474Seschrock 674*2474Seschrock rewind(hdl->libzfs_mnttab); 675*2474Seschrock used = alloc = 0; 676*2474Seschrock while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 677*2474Seschrock /* 678*2474Seschrock * Ignore non-ZFS entries. 679*2474Seschrock */ 680*2474Seschrock if (entry.mnt_fstype == NULL || 681*2474Seschrock strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 682*2474Seschrock continue; 683*2474Seschrock 684*2474Seschrock /* 685*2474Seschrock * Ignore filesystems not within this pool. 686*2474Seschrock */ 687*2474Seschrock if (entry.mnt_mountp == NULL || 688*2474Seschrock strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 689*2474Seschrock (entry.mnt_special[namelen] != '/' && 690*2474Seschrock entry.mnt_special[namelen] != '\0')) 691*2474Seschrock continue; 692*2474Seschrock 693*2474Seschrock /* 694*2474Seschrock * At this point we've found a filesystem within our pool. Add 695*2474Seschrock * it to our growing list. 696*2474Seschrock */ 697*2474Seschrock if (used == alloc) { 698*2474Seschrock if (alloc == 0) { 699*2474Seschrock if ((mountpoints = zfs_alloc(hdl, 700*2474Seschrock 8 * sizeof (void *))) == NULL) 701*2474Seschrock goto out; 702*2474Seschrock 703*2474Seschrock if ((datasets = zfs_alloc(hdl, 704*2474Seschrock 8 * sizeof (void *))) == NULL) 705*2474Seschrock goto out; 706*2474Seschrock 707*2474Seschrock alloc = 8; 708*2474Seschrock } else { 709*2474Seschrock char **dest; 710*2474Seschrock 711*2474Seschrock if ((dest = zfs_alloc(hdl, 712*2474Seschrock alloc * 2 * sizeof (void *))) == NULL) 713*2474Seschrock goto out; 714*2474Seschrock (void) memcpy(dest, mountpoints, 715*2474Seschrock alloc * sizeof (void *)); 716*2474Seschrock free(mountpoints); 717*2474Seschrock mountpoints = dest; 718*2474Seschrock 719*2474Seschrock if ((dest = zfs_alloc(hdl, 720*2474Seschrock alloc * 2 * sizeof (void *))) == NULL) 721*2474Seschrock goto out; 722*2474Seschrock (void) memcpy(dest, datasets, 723*2474Seschrock alloc * sizeof (void *)); 724*2474Seschrock free(datasets); 725*2474Seschrock datasets = (zfs_handle_t **)dest; 726*2474Seschrock 727*2474Seschrock alloc *= 2; 728*2474Seschrock } 729*2474Seschrock } 730*2474Seschrock 731*2474Seschrock if ((mountpoints[used] = zfs_strdup(hdl, 732*2474Seschrock entry.mnt_mountp)) == NULL) 733*2474Seschrock goto out; 734*2474Seschrock 735*2474Seschrock /* 736*2474Seschrock * This is allowed to fail, in case there is some I/O error. It 737*2474Seschrock * is only used to determine if we need to remove the underlying 738*2474Seschrock * mountpoint, so failure is not fatal. 739*2474Seschrock */ 740*2474Seschrock datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 741*2474Seschrock 742*2474Seschrock used++; 743*2474Seschrock } 744*2474Seschrock 745*2474Seschrock /* 746*2474Seschrock * At this point, we have the entire list of filesystems, so sort it by 747*2474Seschrock * mountpoint. 748*2474Seschrock */ 749*2474Seschrock qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 750*2474Seschrock 751*2474Seschrock /* 752*2474Seschrock * Walk through and first unshare everything. 753*2474Seschrock */ 754*2474Seschrock for (i = 0; i < used; i++) { 755*2474Seschrock if (is_shared(hdl, mountpoints[i]) && 756*2474Seschrock unshare_one(hdl, datasets[i] ? datasets[i]->zfs_name : 757*2474Seschrock mountpoints[i], mountpoints[i]) != 0) 758*2474Seschrock goto out; 759*2474Seschrock } 760*2474Seschrock 761*2474Seschrock /* 762*2474Seschrock * Now unmount everything, removing the underlying directories as 763*2474Seschrock * appropriate. 764*2474Seschrock */ 765*2474Seschrock for (i = 0; i < used; i++) { 766*2474Seschrock if (unmount_one(hdl, mountpoints[i], flags) != 0) 767*2474Seschrock goto out; 768*2474Seschrock 769*2474Seschrock if (datasets[i]) 770*2474Seschrock remove_mountpoint(datasets[i]); 771*2474Seschrock } 772*2474Seschrock 773*2474Seschrock ret = 0; 774*2474Seschrock out: 775*2474Seschrock for (i = 0; i < used; i++) { 776*2474Seschrock if (datasets[i]) 777*2474Seschrock zfs_close(datasets[i]); 778*2474Seschrock free(mountpoints[i]); 779*2474Seschrock } 780*2474Seschrock free(datasets); 781*2474Seschrock free(mountpoints); 782*2474Seschrock 783*2474Seschrock return (ret); 784*2474Seschrock } 785