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 */ 21fa9e4066Sahrens /* 22260921a4Seschrock * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23fa9e4066Sahrens * Use is subject to license terms. 24fa9e4066Sahrens */ 25fa9e4066Sahrens 26fa9e4066Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27fa9e4066Sahrens 28fa9e4066Sahrens /* 29fa9e4066Sahrens * Routines to manage ZFS mounts. We separate all the nasty routines that have 30fa9e4066Sahrens * to deal with the OS. The main entry points are: 31fa9e4066Sahrens * 32fa9e4066Sahrens * zfs_is_mounted() 33fa9e4066Sahrens * zfs_mount() 34fa9e4066Sahrens * zfs_unmount() 35fa9e4066Sahrens * zfs_unmountall() 36fa9e4066Sahrens * 37fa9e4066Sahrens * These functions are used by mount and unmount, and when changing a 38fa9e4066Sahrens * filesystem's mountpoint. This file also contains the functions used to 39fa9e4066Sahrens * manage sharing filesystems via NFS: 40fa9e4066Sahrens * 41fa9e4066Sahrens * zfs_is_shared() 42fa9e4066Sahrens * zfs_share() 43fa9e4066Sahrens * zfs_unshare() 44fa9e4066Sahrens * zfs_unshareall() 45fa9e4066Sahrens */ 46fa9e4066Sahrens 47fa9e4066Sahrens #include <dirent.h> 48fa9e4066Sahrens #include <errno.h> 49fa9e4066Sahrens #include <libgen.h> 50fa9e4066Sahrens #include <libintl.h> 51fa9e4066Sahrens #include <stdio.h> 52fa9e4066Sahrens #include <stdlib.h> 53fa9e4066Sahrens #include <strings.h> 54fa9e4066Sahrens #include <unistd.h> 55fa9e4066Sahrens #include <zone.h> 56fa9e4066Sahrens #include <sys/mntent.h> 57fa9e4066Sahrens #include <sys/mnttab.h> 58fa9e4066Sahrens #include <sys/mount.h> 59fa9e4066Sahrens #include <sys/stat.h> 60fa9e4066Sahrens 61fa9e4066Sahrens #include <libzfs.h> 62fa9e4066Sahrens 63fa9e4066Sahrens #include "libzfs_impl.h" 64fa9e4066Sahrens 65fa9e4066Sahrens /* 66*99653d4eSeschrock * Search the sharetab for the given mountpoint, returning true if it is found. 67fa9e4066Sahrens */ 68*99653d4eSeschrock static boolean_t 69*99653d4eSeschrock is_shared(libzfs_handle_t *hdl, const char *mountpoint) 70fa9e4066Sahrens { 71fa9e4066Sahrens char buf[MAXPATHLEN], *tab; 72fa9e4066Sahrens 73*99653d4eSeschrock if (hdl->libzfs_sharetab == NULL) 74fa9e4066Sahrens return (0); 75fa9e4066Sahrens 76*99653d4eSeschrock (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 77fa9e4066Sahrens 78*99653d4eSeschrock while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 79fa9e4066Sahrens 80fa9e4066Sahrens /* the mountpoint is the first entry on each line */ 81fa9e4066Sahrens if ((tab = strchr(buf, '\t')) != NULL) { 82fa9e4066Sahrens *tab = '\0'; 83fa9e4066Sahrens if (strcmp(buf, mountpoint) == 0) 84*99653d4eSeschrock return (B_TRUE); 85fa9e4066Sahrens } 86fa9e4066Sahrens } 87fa9e4066Sahrens 88*99653d4eSeschrock return (B_FALSE); 89fa9e4066Sahrens } 90fa9e4066Sahrens 91fa9e4066Sahrens /* 92*99653d4eSeschrock * Returns true if the specified directory is empty. If we can't open the 93*99653d4eSeschrock * directory at all, return true so that the mount can fail with a more 94fa9e4066Sahrens * informative error message. 95fa9e4066Sahrens */ 96*99653d4eSeschrock static boolean_t 97fa9e4066Sahrens dir_is_empty(const char *dirname) 98fa9e4066Sahrens { 99fa9e4066Sahrens DIR *dirp; 100fa9e4066Sahrens struct dirent64 *dp; 101fa9e4066Sahrens 102fa9e4066Sahrens if ((dirp = opendir(dirname)) == NULL) 103*99653d4eSeschrock return (B_TRUE); 104fa9e4066Sahrens 105fa9e4066Sahrens while ((dp = readdir64(dirp)) != NULL) { 106fa9e4066Sahrens 107fa9e4066Sahrens if (strcmp(dp->d_name, ".") == 0 || 108fa9e4066Sahrens strcmp(dp->d_name, "..") == 0) 109fa9e4066Sahrens continue; 110fa9e4066Sahrens 111fa9e4066Sahrens (void) closedir(dirp); 112*99653d4eSeschrock return (B_FALSE); 113fa9e4066Sahrens } 114fa9e4066Sahrens 115fa9e4066Sahrens (void) closedir(dirp); 116*99653d4eSeschrock return (B_TRUE); 117fa9e4066Sahrens } 118fa9e4066Sahrens 119fa9e4066Sahrens /* 120fa9e4066Sahrens * Checks to see if the mount is active. If the filesystem is mounted, we fill 121fa9e4066Sahrens * in 'where' with the current mountpoint, and return 1. Otherwise, we return 122fa9e4066Sahrens * 0. 123fa9e4066Sahrens */ 124*99653d4eSeschrock boolean_t 125fa9e4066Sahrens zfs_is_mounted(zfs_handle_t *zhp, char **where) 126fa9e4066Sahrens { 127fa9e4066Sahrens struct mnttab search = { 0 }, entry; 128fa9e4066Sahrens 129fa9e4066Sahrens /* 130fa9e4066Sahrens * Search for the entry in /etc/mnttab. We don't bother getting the 131fa9e4066Sahrens * mountpoint, as we can just search for the special device. This will 132fa9e4066Sahrens * also let us find mounts when the mountpoint is 'legacy'. 133fa9e4066Sahrens */ 134fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 13596dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 136fa9e4066Sahrens 137*99653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 138*99653d4eSeschrock if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 139*99653d4eSeschrock return (B_FALSE); 140fa9e4066Sahrens 141fa9e4066Sahrens if (where != NULL) 142*99653d4eSeschrock *where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); 143fa9e4066Sahrens 144*99653d4eSeschrock return (B_TRUE); 145fa9e4066Sahrens } 146fa9e4066Sahrens 147fa9e4066Sahrens /* 148fa9e4066Sahrens * Mount the given filesystem. 149fa9e4066Sahrens */ 150fa9e4066Sahrens int 151fa9e4066Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags) 152fa9e4066Sahrens { 153fa9e4066Sahrens struct stat buf; 154fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 155fa9e4066Sahrens char mntopts[MNT_LINE_MAX]; 156*99653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 157fa9e4066Sahrens 158fa9e4066Sahrens if (options == NULL) 159fa9e4066Sahrens mntopts[0] = '\0'; 160fa9e4066Sahrens else 161fa9e4066Sahrens (void) strlcpy(mntopts, options, sizeof (mntopts)); 162fa9e4066Sahrens 163fa9e4066Sahrens /* ignore non-filesystems */ 164fa9e4066Sahrens if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 165*99653d4eSeschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) 166fa9e4066Sahrens return (0); 167fa9e4066Sahrens 168fa9e4066Sahrens /* return success if there is no mountpoint set */ 169fa9e4066Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 170fa9e4066Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 171fa9e4066Sahrens return (0); 172fa9e4066Sahrens 173fa9e4066Sahrens /* 174fa9e4066Sahrens * If the 'zoned' property is set, and we're in the global zone, simply 175fa9e4066Sahrens * return success. 176fa9e4066Sahrens */ 177*99653d4eSeschrock if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 178*99653d4eSeschrock getzoneid() == GLOBAL_ZONEID) 179*99653d4eSeschrock return (0); 180fa9e4066Sahrens 181fa9e4066Sahrens /* Create the directory if it doesn't already exist */ 182fa9e4066Sahrens if (lstat(mountpoint, &buf) != 0) { 183fa9e4066Sahrens if (mkdirp(mountpoint, 0755) != 0) { 184*99653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 185*99653d4eSeschrock "failed to create mountpoint")); 186*99653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 187*99653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 188*99653d4eSeschrock mountpoint)); 189fa9e4066Sahrens } 190fa9e4066Sahrens } 191fa9e4066Sahrens 192fa9e4066Sahrens /* 193fa9e4066Sahrens * Determine if the mountpoint is empty. If so, refuse to perform the 194fa9e4066Sahrens * mount. We don't perform this check if MS_OVERLAY is specified, which 195fa9e4066Sahrens * would defeat the point. We also avoid this check if 'remount' is 196fa9e4066Sahrens * specified. 197fa9e4066Sahrens */ 198fa9e4066Sahrens if ((flags & MS_OVERLAY) == 0 && 199fa9e4066Sahrens strstr(mntopts, MNTOPT_REMOUNT) == NULL && 200fa9e4066Sahrens !dir_is_empty(mountpoint)) { 201*99653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 202*99653d4eSeschrock "directory is not empty")); 203*99653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 204*99653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 205fa9e4066Sahrens } 206fa9e4066Sahrens 207fa9e4066Sahrens /* perform the mount */ 208fa9e4066Sahrens if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, 209fa9e4066Sahrens MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 210fa9e4066Sahrens /* 211fa9e4066Sahrens * Generic errors are nasty, but there are just way too many 212fa9e4066Sahrens * from mount(), and they're well-understood. We pick a few 213fa9e4066Sahrens * common ones to improve upon. 214fa9e4066Sahrens */ 215*99653d4eSeschrock if (errno == EBUSY) 216*99653d4eSeschrock zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 217*99653d4eSeschrock "mountpoint or dataset is busy")); 218*99653d4eSeschrock else 219*99653d4eSeschrock zfs_error_aux(hdl, strerror(errno)); 220*99653d4eSeschrock 221*99653d4eSeschrock return (zfs_error(hdl, EZFS_MOUNTFAILED, 222*99653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 223*99653d4eSeschrock zhp->zfs_name)); 224fa9e4066Sahrens } 225fa9e4066Sahrens 226fa9e4066Sahrens return (0); 227fa9e4066Sahrens } 228fa9e4066Sahrens 229fa9e4066Sahrens /* 230fa9e4066Sahrens * Unmount the given filesystem. 231fa9e4066Sahrens */ 232fa9e4066Sahrens int 233fa9e4066Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 234fa9e4066Sahrens { 235fa9e4066Sahrens struct mnttab search = { 0 }, entry; 236fa9e4066Sahrens 237fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 238fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 23996dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 240*99653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 241fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 242*99653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 243fa9e4066Sahrens 244fa9e4066Sahrens if (mountpoint == NULL) 245fa9e4066Sahrens mountpoint = entry.mnt_mountp; 246fa9e4066Sahrens 247fa9e4066Sahrens /* 248fa9e4066Sahrens * Always unshare the filesystem first. 249fa9e4066Sahrens */ 250fa9e4066Sahrens if (zfs_unshare(zhp, mountpoint) != 0) 251fa9e4066Sahrens return (-1); 252fa9e4066Sahrens 253fa9e4066Sahrens /* 254fa9e4066Sahrens * Try to unmount the filesystem. There is no reason to try a 255fa9e4066Sahrens * forced unmount because the vnodes will still carry a 256fa9e4066Sahrens * reference to the underlying dataset, so we can't destroy it 257fa9e4066Sahrens * anyway. 258fa9e4066Sahrens * 259fa9e4066Sahrens * In the unmount case, we print out a slightly more informative 260fa9e4066Sahrens * error message, though we'll be relying on the poor error 261fa9e4066Sahrens * semantics from the kernel. 262fa9e4066Sahrens */ 263fa9e4066Sahrens if (umount2(mountpoint, flags) != 0) { 264*99653d4eSeschrock zfs_error_aux(zhp->zfs_hdl, strerror(errno)); 265*99653d4eSeschrock return (zfs_error(zhp->zfs_hdl, EZFS_UMOUNTFAILED, 266*99653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 267*99653d4eSeschrock mountpoint)); 268fa9e4066Sahrens } 269fa9e4066Sahrens 270fa9e4066Sahrens /* 271fa9e4066Sahrens * Don't actually destroy the underlying directory 272fa9e4066Sahrens */ 273fa9e4066Sahrens } 274fa9e4066Sahrens 275fa9e4066Sahrens return (0); 276fa9e4066Sahrens } 277fa9e4066Sahrens 278fa9e4066Sahrens /* 279fa9e4066Sahrens * Unmount this filesystem and any children inheriting the mountpoint property. 280fa9e4066Sahrens * To do this, just act like we're changing the mountpoint property, but don't 281fa9e4066Sahrens * remount the filesystems afterwards. 282fa9e4066Sahrens */ 283fa9e4066Sahrens int 284fa9e4066Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags) 285fa9e4066Sahrens { 286fa9e4066Sahrens prop_changelist_t *clp; 287fa9e4066Sahrens int ret; 288fa9e4066Sahrens 289fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 290fa9e4066Sahrens if (clp == NULL) 291fa9e4066Sahrens return (-1); 292fa9e4066Sahrens 293fa9e4066Sahrens ret = changelist_prefix(clp); 294fa9e4066Sahrens changelist_free(clp); 295fa9e4066Sahrens 296fa9e4066Sahrens return (ret); 297fa9e4066Sahrens } 298fa9e4066Sahrens 299fa9e4066Sahrens /* 300fa9e4066Sahrens * Check to see if the filesystem is currently shared. 301fa9e4066Sahrens */ 302*99653d4eSeschrock boolean_t 303fa9e4066Sahrens zfs_is_shared(zfs_handle_t *zhp, char **where) 304fa9e4066Sahrens { 305fa9e4066Sahrens char *mountpoint; 306fa9e4066Sahrens 307fa9e4066Sahrens if (!zfs_is_mounted(zhp, &mountpoint)) 308*99653d4eSeschrock return (B_FALSE); 309fa9e4066Sahrens 310*99653d4eSeschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 311fa9e4066Sahrens if (where != NULL) 312fa9e4066Sahrens *where = mountpoint; 313fa9e4066Sahrens else 314fa9e4066Sahrens free(mountpoint); 315*99653d4eSeschrock return (B_TRUE); 316fa9e4066Sahrens } else { 317fa9e4066Sahrens free(mountpoint); 318*99653d4eSeschrock return (B_FALSE); 319fa9e4066Sahrens } 320fa9e4066Sahrens } 321fa9e4066Sahrens 322fa9e4066Sahrens /* 323fa9e4066Sahrens * Share the given filesystem according to the options in 'sharenfs'. We rely 324fa9e4066Sahrens * on share(1M) to the dirty work for us. 325fa9e4066Sahrens */ 326fa9e4066Sahrens int 327fa9e4066Sahrens zfs_share(zfs_handle_t *zhp) 328fa9e4066Sahrens { 329fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 330fa9e4066Sahrens char shareopts[ZFS_MAXPROPLEN]; 331fa9e4066Sahrens char buf[MAXPATHLEN]; 332fa9e4066Sahrens FILE *fp; 333*99653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 334fa9e4066Sahrens 335fa9e4066Sahrens /* ignore non-filesystems */ 336fa9e4066Sahrens if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) 337fa9e4066Sahrens return (0); 338fa9e4066Sahrens 339fa9e4066Sahrens /* return success if there is no mountpoint set */ 340fa9e4066Sahrens if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 341*99653d4eSeschrock mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0 || 342fa9e4066Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 || 343fa9e4066Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) 344fa9e4066Sahrens return (0); 345fa9e4066Sahrens 346fa9e4066Sahrens /* return success if there are no share options */ 347fa9e4066Sahrens if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 348*99653d4eSeschrock NULL, NULL, 0, B_FALSE) != 0 || 349fa9e4066Sahrens strcmp(shareopts, "off") == 0) 350fa9e4066Sahrens return (0); 351fa9e4066Sahrens 352fa9e4066Sahrens /* 353fa9e4066Sahrens * If the 'zoned' property is set, simply return success since: 354fa9e4066Sahrens * 1. in a global zone, a dataset should not be shared if it's 355fa9e4066Sahrens * managed in a local zone. 356fa9e4066Sahrens * 2. in a local zone, NFS server is not available. 357fa9e4066Sahrens */ 358fa9e4066Sahrens if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) { 359fa9e4066Sahrens return (0); 360fa9e4066Sahrens } 361fa9e4066Sahrens 362fa9e4066Sahrens /* 363fa9e4066Sahrens * Invoke the share(1M) command. We always do this, even if it's 364fa9e4066Sahrens * currently shared, as the options may have changed. 365fa9e4066Sahrens */ 366fa9e4066Sahrens if (strcmp(shareopts, "on") == 0) 367fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 368fa9e4066Sahrens "-F nfs \"%s\" 2>&1", mountpoint); 369fa9e4066Sahrens else 370fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 371fa9e4066Sahrens "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 372fa9e4066Sahrens mountpoint); 373fa9e4066Sahrens 374*99653d4eSeschrock if ((fp = popen(buf, "r")) == NULL) 375*99653d4eSeschrock return (zfs_error(hdl, EZFS_SHAREFAILED, 376*99653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'"), 377*99653d4eSeschrock zfs_get_name(zhp))); 378fa9e4066Sahrens 379fa9e4066Sahrens /* 380fa9e4066Sahrens * share(1M) should only produce output if there is some kind 381fa9e4066Sahrens * of error. All output begins with "share_nfs: ", so we trim 382fa9e4066Sahrens * this off to get to the real error. 383fa9e4066Sahrens */ 384fa9e4066Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 385fa9e4066Sahrens char *colon = strchr(buf, ':'); 386fa9e4066Sahrens 387fa9e4066Sahrens while (buf[strlen(buf) - 1] == '\n') 388fa9e4066Sahrens buf[strlen(buf) - 1] = '\0'; 389fa9e4066Sahrens 390*99653d4eSeschrock if (colon != NULL) 391*99653d4eSeschrock zfs_error_aux(hdl, colon + 2); 392*99653d4eSeschrock 393*99653d4eSeschrock (void) zfs_error(hdl, EZFS_SHAREFAILED, 394*99653d4eSeschrock dgettext(TEXT_DOMAIN, "cannot share '%s'")); 395fa9e4066Sahrens 396fa9e4066Sahrens verify(pclose(fp) != 0); 397fa9e4066Sahrens return (-1); 398fa9e4066Sahrens } 399fa9e4066Sahrens 400fa9e4066Sahrens verify(pclose(fp) == 0); 401fa9e4066Sahrens 402fa9e4066Sahrens return (0); 403fa9e4066Sahrens } 404fa9e4066Sahrens 405fa9e4066Sahrens /* 406fa9e4066Sahrens * Unshare the given filesystem. 407fa9e4066Sahrens */ 408fa9e4066Sahrens int 409fa9e4066Sahrens zfs_unshare(zfs_handle_t *zhp, const char *mountpoint) 410fa9e4066Sahrens { 411fa9e4066Sahrens char buf[MAXPATHLEN]; 412fa9e4066Sahrens struct mnttab search = { 0 }, entry; 413*99653d4eSeschrock libzfs_handle_t *hdl = zhp->zfs_hdl; 414fa9e4066Sahrens 415fa9e4066Sahrens /* check to see if need to unmount the filesystem */ 416fa9e4066Sahrens search.mnt_special = (char *)zfs_get_name(zhp); 41796dedd18Snd search.mnt_fstype = MNTTYPE_ZFS; 418*99653d4eSeschrock rewind(zhp->zfs_hdl->libzfs_mnttab); 419fa9e4066Sahrens if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 420*99653d4eSeschrock getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 421fa9e4066Sahrens 422fa9e4066Sahrens if (mountpoint == NULL) 423fa9e4066Sahrens mountpoint = entry.mnt_mountp; 424fa9e4066Sahrens 425*99653d4eSeschrock if (is_shared(zhp->zfs_hdl, mountpoint)) { 426fa9e4066Sahrens FILE *fp; 427fa9e4066Sahrens 428fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), 429fa9e4066Sahrens "/usr/sbin/unshare \"%s\" 2>&1", 430fa9e4066Sahrens mountpoint); 431fa9e4066Sahrens 432*99653d4eSeschrock if ((fp = popen(buf, "r")) == NULL) 433*99653d4eSeschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 434*99653d4eSeschrock dgettext(TEXT_DOMAIN, 435*99653d4eSeschrock "cannot unshare '%s'"), zfs_get_name(zhp))); 436fa9e4066Sahrens 437fa9e4066Sahrens /* 438fa9e4066Sahrens * unshare(1M) should only produce output if there is 439fa9e4066Sahrens * some kind of error. All output begins with "unshare 440fa9e4066Sahrens * nfs: ", so we trim this off to get to the real error. 441fa9e4066Sahrens */ 442fa9e4066Sahrens if (fgets(buf, sizeof (buf), fp) != NULL) { 443fa9e4066Sahrens char *colon = strchr(buf, ':'); 444fa9e4066Sahrens 445fa9e4066Sahrens while (buf[strlen(buf) - 1] == '\n') 446fa9e4066Sahrens buf[strlen(buf) - 1] = '\0'; 447fa9e4066Sahrens 448*99653d4eSeschrock if (colon != NULL) 449*99653d4eSeschrock zfs_error_aux(hdl, colon + 2); 450fa9e4066Sahrens 451fa9e4066Sahrens verify(pclose(fp) != 0); 452*99653d4eSeschrock 453*99653d4eSeschrock return (zfs_error(hdl, EZFS_UNSHAREFAILED, 454*99653d4eSeschrock dgettext(TEXT_DOMAIN, 455*99653d4eSeschrock "cannot unshare '%s'"), zfs_get_name(zhp))); 456fa9e4066Sahrens } 457fa9e4066Sahrens 458fa9e4066Sahrens verify(pclose(fp) == 0); 459fa9e4066Sahrens } 460fa9e4066Sahrens } 461fa9e4066Sahrens 462fa9e4066Sahrens return (0); 463fa9e4066Sahrens } 464fa9e4066Sahrens 465fa9e4066Sahrens /* 466fa9e4066Sahrens * Same as zfs_unmountall(), but for unshares. 467fa9e4066Sahrens */ 468fa9e4066Sahrens int 469fa9e4066Sahrens zfs_unshareall(zfs_handle_t *zhp) 470fa9e4066Sahrens { 471fa9e4066Sahrens prop_changelist_t *clp; 472fa9e4066Sahrens int ret; 473fa9e4066Sahrens 474fa9e4066Sahrens clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 475fa9e4066Sahrens if (clp == NULL) 476fa9e4066Sahrens return (-1); 477fa9e4066Sahrens 478fa9e4066Sahrens ret = changelist_unshare(clp); 479fa9e4066Sahrens changelist_free(clp); 480fa9e4066Sahrens 481fa9e4066Sahrens return (ret); 482fa9e4066Sahrens } 483fa9e4066Sahrens 484fa9e4066Sahrens /* 485fa9e4066Sahrens * Remove the mountpoint associated with the current dataset, if necessary. 486fa9e4066Sahrens * We only remove the underlying directory if: 487fa9e4066Sahrens * 488fa9e4066Sahrens * - The mountpoint is not 'none' or 'legacy' 489fa9e4066Sahrens * - The mountpoint is non-empty 490fa9e4066Sahrens * - The mountpoint is the default or inherited 491fa9e4066Sahrens * - The 'zoned' property is set, or we're in a local zone 492fa9e4066Sahrens * 493fa9e4066Sahrens * Any other directories we leave alone. 494fa9e4066Sahrens */ 495fa9e4066Sahrens void 496fa9e4066Sahrens remove_mountpoint(zfs_handle_t *zhp) 497fa9e4066Sahrens { 498fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 499fa9e4066Sahrens char source[ZFS_MAXNAMELEN]; 500fa9e4066Sahrens zfs_source_t sourcetype; 501*99653d4eSeschrock int zoneid = getzoneid(); 502fa9e4066Sahrens 503fa9e4066Sahrens /* ignore non-filesystems */ 504fa9e4066Sahrens if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 505fa9e4066Sahrens sizeof (mountpoint), &sourcetype, source, sizeof (source), 506*99653d4eSeschrock B_FALSE) != 0) 507fa9e4066Sahrens return; 508fa9e4066Sahrens 509fa9e4066Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 && 510fa9e4066Sahrens strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && 511fa9e4066Sahrens (sourcetype == ZFS_SRC_DEFAULT || 512fa9e4066Sahrens sourcetype == ZFS_SRC_INHERITED) && 513fa9e4066Sahrens (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) || 514*99653d4eSeschrock zoneid != GLOBAL_ZONEID)) { 515fa9e4066Sahrens 516fa9e4066Sahrens /* 517fa9e4066Sahrens * Try to remove the directory, silently ignoring any errors. 518fa9e4066Sahrens * The filesystem may have since been removed or moved around, 519fa9e4066Sahrens * and this isn't really useful to the administrator in any 520fa9e4066Sahrens * way. 521fa9e4066Sahrens */ 522fa9e4066Sahrens (void) rmdir(mountpoint); 523fa9e4066Sahrens } 524fa9e4066Sahrens } 525