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