libzfs_mount.c revision 168404
1168404Spjd/* 2168404Spjd * CDDL HEADER START 3168404Spjd * 4168404Spjd * The contents of this file are subject to the terms of the 5168404Spjd * Common Development and Distribution License (the "License"). 6168404Spjd * You may not use this file except in compliance with the License. 7168404Spjd * 8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9168404Spjd * or http://www.opensolaris.org/os/licensing. 10168404Spjd * See the License for the specific language governing permissions 11168404Spjd * and limitations under the License. 12168404Spjd * 13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each 14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15168404Spjd * If applicable, add the following below this CDDL HEADER, with the 16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18168404Spjd * 19168404Spjd * CDDL HEADER END 20168404Spjd */ 21168404Spjd 22168404Spjd/* 23168404Spjd * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24168404Spjd * Use is subject to license terms. 25168404Spjd */ 26168404Spjd 27168404Spjd#pragma ident "%Z%%M% %I% %E% SMI" 28168404Spjd 29168404Spjd/* 30168404Spjd * Routines to manage ZFS mounts. We separate all the nasty routines that have 31168404Spjd * to deal with the OS. The following functions are the main entry points -- 32168404Spjd * they are used by mount and unmount and when changing a filesystem's 33168404Spjd * mountpoint. 34168404Spjd * 35168404Spjd * zfs_is_mounted() 36168404Spjd * zfs_mount() 37168404Spjd * zfs_unmount() 38168404Spjd * zfs_unmountall() 39168404Spjd * 40168404Spjd * This file also contains the functions used to manage sharing filesystems via 41168404Spjd * NFS and iSCSI: 42168404Spjd * 43168404Spjd * zfs_is_shared() 44168404Spjd * zfs_share() 45168404Spjd * zfs_unshare() 46168404Spjd * 47168404Spjd * zfs_is_shared_nfs() 48168404Spjd * zfs_share_nfs() 49168404Spjd * zfs_unshare_nfs() 50168404Spjd * zfs_unshareall_nfs() 51168404Spjd * zfs_is_shared_iscsi() 52168404Spjd * zfs_share_iscsi() 53168404Spjd * zfs_unshare_iscsi() 54168404Spjd * 55168404Spjd * The following functions are available for pool consumers, and will 56168404Spjd * mount/unmount and share/unshare all datasets within pool: 57168404Spjd * 58168404Spjd * zpool_enable_datasets() 59168404Spjd * zpool_disable_datasets() 60168404Spjd */ 61168404Spjd 62168404Spjd#include <dirent.h> 63168404Spjd#include <dlfcn.h> 64168404Spjd#include <errno.h> 65168404Spjd#include <libgen.h> 66168404Spjd#include <libintl.h> 67168404Spjd#include <stdio.h> 68168404Spjd#include <stdlib.h> 69168404Spjd#include <strings.h> 70168404Spjd#include <unistd.h> 71168404Spjd#include <zone.h> 72168404Spjd#include <sys/mntent.h> 73168404Spjd#include <sys/mnttab.h> 74168404Spjd#include <sys/mount.h> 75168404Spjd#include <sys/stat.h> 76168404Spjd 77168404Spjd#include <libzfs.h> 78168404Spjd 79168404Spjd#include "libzfs_impl.h" 80168404Spjd 81168404Spjdstatic int (*iscsitgt_zfs_share)(const char *); 82168404Spjdstatic int (*iscsitgt_zfs_unshare)(const char *); 83168404Spjdstatic int (*iscsitgt_zfs_is_shared)(const char *); 84168404Spjd 85168404Spjd#pragma init(zfs_iscsi_init) 86168404Spjdstatic void 87168404Spjdzfs_iscsi_init(void) 88168404Spjd{ 89168404Spjd void *libiscsitgt; 90168404Spjd 91168404Spjd if ((libiscsitgt = dlopen("/lib/libiscsitgt.so.1", 92168404Spjd RTLD_LAZY | RTLD_GLOBAL)) == NULL || 93168404Spjd (iscsitgt_zfs_share = (int (*)(const char *))dlsym(libiscsitgt, 94168404Spjd "iscsitgt_zfs_share")) == NULL || 95168404Spjd (iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt, 96168404Spjd "iscsitgt_zfs_unshare")) == NULL || 97168404Spjd (iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt, 98168404Spjd "iscsitgt_zfs_is_shared")) == NULL) { 99168404Spjd iscsitgt_zfs_share = NULL; 100168404Spjd iscsitgt_zfs_unshare = NULL; 101168404Spjd iscsitgt_zfs_is_shared = NULL; 102168404Spjd } 103168404Spjd} 104168404Spjd 105168404Spjd/* 106168404Spjd * Search the sharetab for the given mountpoint, returning true if it is found. 107168404Spjd */ 108168404Spjdstatic boolean_t 109168404Spjdis_shared(libzfs_handle_t *hdl, const char *mountpoint) 110168404Spjd{ 111168404Spjd char buf[MAXPATHLEN], *tab; 112168404Spjd 113168404Spjd if (hdl->libzfs_sharetab == NULL) 114168404Spjd return (0); 115168404Spjd 116168404Spjd (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 117168404Spjd 118168404Spjd while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 119168404Spjd 120168404Spjd /* the mountpoint is the first entry on each line */ 121168404Spjd if ((tab = strchr(buf, '\t')) != NULL) { 122168404Spjd *tab = '\0'; 123168404Spjd if (strcmp(buf, mountpoint) == 0) 124168404Spjd return (B_TRUE); 125168404Spjd } 126168404Spjd } 127168404Spjd 128168404Spjd return (B_FALSE); 129168404Spjd} 130168404Spjd 131168404Spjd#if 0 132168404Spjd/* 133168404Spjd * Returns true if the specified directory is empty. If we can't open the 134168404Spjd * directory at all, return true so that the mount can fail with a more 135168404Spjd * informative error message. 136168404Spjd */ 137168404Spjdstatic boolean_t 138168404Spjddir_is_empty(const char *dirname) 139168404Spjd{ 140168404Spjd DIR *dirp; 141168404Spjd struct dirent64 *dp; 142168404Spjd 143168404Spjd if ((dirp = opendir(dirname)) == NULL) 144168404Spjd return (B_TRUE); 145168404Spjd 146168404Spjd while ((dp = readdir64(dirp)) != NULL) { 147168404Spjd 148168404Spjd if (strcmp(dp->d_name, ".") == 0 || 149168404Spjd strcmp(dp->d_name, "..") == 0) 150168404Spjd continue; 151168404Spjd 152168404Spjd (void) closedir(dirp); 153168404Spjd return (B_FALSE); 154168404Spjd } 155168404Spjd 156168404Spjd (void) closedir(dirp); 157168404Spjd return (B_TRUE); 158168404Spjd} 159168404Spjd#endif 160168404Spjd 161168404Spjd/* 162168404Spjd * Checks to see if the mount is active. If the filesystem is mounted, we fill 163168404Spjd * in 'where' with the current mountpoint, and return 1. Otherwise, we return 164168404Spjd * 0. 165168404Spjd */ 166168404Spjdboolean_t 167168404Spjdis_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where) 168168404Spjd{ 169168404Spjd struct mnttab search = { 0 }, entry; 170168404Spjd 171168404Spjd /* 172168404Spjd * Search for the entry in /etc/mnttab. We don't bother getting the 173168404Spjd * mountpoint, as we can just search for the special device. This will 174168404Spjd * also let us find mounts when the mountpoint is 'legacy'. 175168404Spjd */ 176168404Spjd search.mnt_special = (char *)special; 177168404Spjd search.mnt_fstype = MNTTYPE_ZFS; 178168404Spjd 179168404Spjd rewind(zfs_hdl->libzfs_mnttab); 180168404Spjd if (getmntany(zfs_hdl->libzfs_mnttab, &entry, &search) != 0) 181168404Spjd return (B_FALSE); 182168404Spjd 183168404Spjd if (where != NULL) 184168404Spjd *where = zfs_strdup(zfs_hdl, entry.mnt_mountp); 185168404Spjd 186168404Spjd return (B_TRUE); 187168404Spjd} 188168404Spjd 189168404Spjdboolean_t 190168404Spjdzfs_is_mounted(zfs_handle_t *zhp, char **where) 191168404Spjd{ 192168404Spjd return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where)); 193168404Spjd} 194168404Spjd 195168404Spjd/* 196168404Spjd * Returns true if the given dataset is mountable, false otherwise. Returns the 197168404Spjd * mountpoint in 'buf'. 198168404Spjd */ 199168404Spjdstatic boolean_t 200168404Spjdzfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 201168404Spjd zfs_source_t *source) 202168404Spjd{ 203168404Spjd char sourceloc[ZFS_MAXNAMELEN]; 204168404Spjd zfs_source_t sourcetype; 205168404Spjd 206168404Spjd if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 207168404Spjd return (B_FALSE); 208168404Spjd 209168404Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 210168404Spjd &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 211168404Spjd 212168404Spjd if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 213168404Spjd strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 214168404Spjd return (B_FALSE); 215168404Spjd 216168404Spjd if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT)) 217168404Spjd return (B_FALSE); 218168404Spjd 219168404Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 220168404Spjd getzoneid() == GLOBAL_ZONEID) 221168404Spjd return (B_FALSE); 222168404Spjd 223168404Spjd if (source) 224168404Spjd *source = sourcetype; 225168404Spjd 226168404Spjd return (B_TRUE); 227168404Spjd} 228168404Spjd 229168404Spjd/* 230168404Spjd * Mount the given filesystem. 231168404Spjd */ 232168404Spjdint 233168404Spjdzfs_mount(zfs_handle_t *zhp, const char *options, int flags) 234168404Spjd{ 235168404Spjd struct stat buf; 236168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 237168404Spjd char mntopts[MNT_LINE_MAX]; 238168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 239168404Spjd 240168404Spjd if (options == NULL) 241168404Spjd mntopts[0] = '\0'; 242168404Spjd else 243168404Spjd (void) strlcpy(mntopts, options, sizeof (mntopts)); 244168404Spjd 245168404Spjd if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 246168404Spjd return (0); 247168404Spjd 248168404Spjd /* Create the directory if it doesn't already exist */ 249168404Spjd if (lstat(mountpoint, &buf) != 0) { 250168404Spjd if (mkdirp(mountpoint, 0755) != 0) { 251168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 252168404Spjd "failed to create mountpoint")); 253168404Spjd return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 254168404Spjd dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 255168404Spjd mountpoint)); 256168404Spjd } 257168404Spjd } 258168404Spjd 259168404Spjd#if 0 /* FreeBSD: overlay mounts are not checked. */ 260168404Spjd /* 261168404Spjd * Determine if the mountpoint is empty. If so, refuse to perform the 262168404Spjd * mount. We don't perform this check if MS_OVERLAY is specified, which 263168404Spjd * would defeat the point. We also avoid this check if 'remount' is 264168404Spjd * specified. 265168404Spjd */ 266168404Spjd if ((flags & MS_OVERLAY) == 0 && 267168404Spjd strstr(mntopts, MNTOPT_REMOUNT) == NULL && 268168404Spjd !dir_is_empty(mountpoint)) { 269168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 270168404Spjd "directory is not empty")); 271168404Spjd return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 272168404Spjd dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 273168404Spjd } 274168404Spjd#endif 275168404Spjd 276168404Spjd /* perform the mount */ 277168404Spjd if (zmount(zfs_get_name(zhp), mountpoint, flags, 278168404Spjd MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 279168404Spjd /* 280168404Spjd * Generic errors are nasty, but there are just way too many 281168404Spjd * from mount(), and they're well-understood. We pick a few 282168404Spjd * common ones to improve upon. 283168404Spjd */ 284168404Spjd if (errno == EBUSY) 285168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 286168404Spjd "mountpoint or dataset is busy")); 287168404Spjd else 288168404Spjd zfs_error_aux(hdl, strerror(errno)); 289168404Spjd 290168404Spjd return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 291168404Spjd dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 292168404Spjd zhp->zfs_name)); 293168404Spjd } 294168404Spjd 295168404Spjd return (0); 296168404Spjd} 297168404Spjd 298168404Spjd/* 299168404Spjd * Unmount a single filesystem. 300168404Spjd */ 301168404Spjdstatic int 302168404Spjdunmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 303168404Spjd{ 304168404Spjd if (unmount(mountpoint, flags) != 0) { 305168404Spjd zfs_error_aux(hdl, strerror(errno)); 306168404Spjd return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, 307168404Spjd dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 308168404Spjd mountpoint)); 309168404Spjd } 310168404Spjd 311168404Spjd return (0); 312168404Spjd} 313168404Spjd 314168404Spjd/* 315168404Spjd * Unmount the given filesystem. 316168404Spjd */ 317168404Spjdint 318168404Spjdzfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 319168404Spjd{ 320168404Spjd struct mnttab search = { 0 }, entry; 321168404Spjd 322168404Spjd /* check to see if need to unmount the filesystem */ 323168404Spjd search.mnt_special = zhp->zfs_name; 324168404Spjd search.mnt_fstype = MNTTYPE_ZFS; 325168404Spjd rewind(zhp->zfs_hdl->libzfs_mnttab); 326168404Spjd if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 327168404Spjd getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 328168404Spjd 329168404Spjd if (mountpoint == NULL) 330168404Spjd mountpoint = entry.mnt_mountp; 331168404Spjd 332168404Spjd /* 333168404Spjd * Unshare and unmount the filesystem 334168404Spjd */ 335168404Spjd if (zfs_unshare_nfs(zhp, mountpoint) != 0 || 336168404Spjd unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0) 337168404Spjd return (-1); 338168404Spjd } 339168404Spjd 340168404Spjd return (0); 341168404Spjd} 342168404Spjd 343168404Spjd/* 344168404Spjd * Unmount this filesystem and any children inheriting the mountpoint property. 345168404Spjd * To do this, just act like we're changing the mountpoint property, but don't 346168404Spjd * remount the filesystems afterwards. 347168404Spjd */ 348168404Spjdint 349168404Spjdzfs_unmountall(zfs_handle_t *zhp, int flags) 350168404Spjd{ 351168404Spjd prop_changelist_t *clp; 352168404Spjd int ret; 353168404Spjd 354168404Spjd clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags); 355168404Spjd if (clp == NULL) 356168404Spjd return (-1); 357168404Spjd 358168404Spjd ret = changelist_prefix(clp); 359168404Spjd changelist_free(clp); 360168404Spjd 361168404Spjd return (ret); 362168404Spjd} 363168404Spjd 364168404Spjdboolean_t 365168404Spjdzfs_is_shared(zfs_handle_t *zhp) 366168404Spjd{ 367168404Spjd if (ZFS_IS_VOLUME(zhp)) 368168404Spjd return (zfs_is_shared_iscsi(zhp)); 369168404Spjd 370168404Spjd return (zfs_is_shared_nfs(zhp, NULL)); 371168404Spjd} 372168404Spjd 373168404Spjdint 374168404Spjdzfs_share(zfs_handle_t *zhp) 375168404Spjd{ 376168404Spjd if (ZFS_IS_VOLUME(zhp)) 377168404Spjd return (zfs_share_iscsi(zhp)); 378168404Spjd 379168404Spjd return (zfs_share_nfs(zhp)); 380168404Spjd} 381168404Spjd 382168404Spjdint 383168404Spjdzfs_unshare(zfs_handle_t *zhp) 384168404Spjd{ 385168404Spjd if (ZFS_IS_VOLUME(zhp)) 386168404Spjd return (zfs_unshare_iscsi(zhp)); 387168404Spjd 388168404Spjd return (zfs_unshare_nfs(zhp, NULL)); 389168404Spjd} 390168404Spjd 391168404Spjd/* 392168404Spjd * Check to see if the filesystem is currently shared. 393168404Spjd */ 394168404Spjdboolean_t 395168404Spjdzfs_is_shared_nfs(zfs_handle_t *zhp, char **where) 396168404Spjd{ 397168404Spjd char *mountpoint; 398168404Spjd 399168404Spjd if (!zfs_is_mounted(zhp, &mountpoint)) 400168404Spjd return (B_FALSE); 401168404Spjd 402168404Spjd if (is_shared(zhp->zfs_hdl, mountpoint)) { 403168404Spjd if (where != NULL) 404168404Spjd *where = mountpoint; 405168404Spjd else 406168404Spjd free(mountpoint); 407168404Spjd return (B_TRUE); 408168404Spjd } else { 409168404Spjd free(mountpoint); 410168404Spjd return (B_FALSE); 411168404Spjd } 412168404Spjd} 413168404Spjd 414168404Spjd/* 415168404Spjd * Share the given filesystem according to the options in 'sharenfs'. We rely 416168404Spjd * on share(1M) to the dirty work for us. 417168404Spjd */ 418168404Spjdint 419168404Spjdzfs_share_nfs(zfs_handle_t *zhp) 420168404Spjd{ 421168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 422168404Spjd char shareopts[ZFS_MAXPROPLEN]; 423168404Spjd char buf[MAXPATHLEN]; 424168404Spjd FILE *fp; 425168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 426168404Spjd 427168404Spjd if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 428168404Spjd return (0); 429168404Spjd 430168404Spjd /* 431168404Spjd * Return success if there are no share options. 432168404Spjd */ 433168404Spjd if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), 434168404Spjd NULL, NULL, 0, B_FALSE) != 0 || 435168404Spjd strcmp(shareopts, "off") == 0) 436168404Spjd return (0); 437168404Spjd 438168404Spjd /* 439168404Spjd * If the 'zoned' property is set, then zfs_is_mountable() will have 440168404Spjd * already bailed out if we are in the global zone. But local 441168404Spjd * zones cannot be NFS servers, so we ignore it for local zones as well. 442168404Spjd */ 443168404Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 444168404Spjd return (0); 445168404Spjd 446168404Spjd#ifdef __FreeBSD__ 447168404Spjd { 448168404Spjd int error; 449168404Spjd 450168404Spjd if (strcmp(shareopts, "on") == 0) 451168404Spjd error = fsshare(ZFS_EXPORTS_PATH, mountpoint, ""); 452168404Spjd else 453168404Spjd error = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts); 454168404Spjd if (error != 0) { 455168404Spjd zfs_error_aux(hdl, "%s", strerror(error)); 456168404Spjd (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 457168404Spjd dgettext(TEXT_DOMAIN, "cannot share '%s'"), 458168404Spjd zfs_get_name(zhp)); 459168404Spjd return (-1); 460168404Spjd } 461168404Spjd } 462168404Spjd#else 463168404Spjd /* 464168404Spjd * Invoke the share(1M) command. We always do this, even if it's 465168404Spjd * currently shared, as the options may have changed. 466168404Spjd */ 467168404Spjd if (strcmp(shareopts, "on") == 0) 468168404Spjd (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 469168404Spjd "-F nfs \"%s\" 2>&1", mountpoint); 470168404Spjd else 471168404Spjd (void) snprintf(buf, sizeof (buf), "/usr/sbin/share " 472168404Spjd "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts, 473168404Spjd mountpoint); 474168404Spjd 475168404Spjd if ((fp = popen(buf, "r")) == NULL) 476168404Spjd return (zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 477168404Spjd dgettext(TEXT_DOMAIN, "cannot share '%s'"), 478168404Spjd zfs_get_name(zhp))); 479168404Spjd 480168404Spjd /* 481168404Spjd * share(1M) should only produce output if there is some kind 482168404Spjd * of error. All output begins with "share_nfs: ", so we trim 483168404Spjd * this off to get to the real error. 484168404Spjd */ 485168404Spjd if (fgets(buf, sizeof (buf), fp) != NULL) { 486168404Spjd char *colon = strchr(buf, ':'); 487168404Spjd 488168404Spjd while (buf[strlen(buf) - 1] == '\n') 489168404Spjd buf[strlen(buf) - 1] = '\0'; 490168404Spjd 491168404Spjd if (colon != NULL) 492168404Spjd zfs_error_aux(hdl, colon + 2); 493168404Spjd 494168404Spjd (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 495168404Spjd dgettext(TEXT_DOMAIN, "cannot share '%s'"), 496168404Spjd zfs_get_name(zhp)); 497168404Spjd 498168404Spjd verify(pclose(fp) != 0); 499168404Spjd return (-1); 500168404Spjd } 501168404Spjd 502168404Spjd verify(pclose(fp) == 0); 503168404Spjd#endif 504168404Spjd 505168404Spjd return (0); 506168404Spjd} 507168404Spjd 508168404Spjd/* 509168404Spjd * Unshare a filesystem by mountpoint. 510168404Spjd */ 511168404Spjdstatic int 512168404Spjdunshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) 513168404Spjd{ 514168404Spjd char buf[MAXPATHLEN]; 515168404Spjd FILE *fp; 516168404Spjd 517168404Spjd#ifdef __FreeBSD__ 518168404Spjd { 519168404Spjd int error; 520168404Spjd 521168404Spjd error = fsunshare(ZFS_EXPORTS_PATH, mountpoint); 522168404Spjd if (error != 0) { 523168404Spjd zfs_error_aux(hdl, "%s", strerror(error)); 524168404Spjd return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 525168404Spjd dgettext(TEXT_DOMAIN, 526168404Spjd "cannot unshare '%s'"), name)); 527168404Spjd } 528168404Spjd } 529168404Spjd#else 530168404Spjd (void) snprintf(buf, sizeof (buf), 531168404Spjd "/usr/sbin/unshare \"%s\" 2>&1", 532168404Spjd mountpoint); 533168404Spjd 534168404Spjd if ((fp = popen(buf, "r")) == NULL) 535168404Spjd return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 536168404Spjd dgettext(TEXT_DOMAIN, 537168404Spjd "cannot unshare '%s'"), name)); 538168404Spjd 539168404Spjd /* 540168404Spjd * unshare(1M) should only produce output if there is 541168404Spjd * some kind of error. All output begins with "unshare 542168404Spjd * nfs: ", so we trim this off to get to the real error. 543168404Spjd */ 544168404Spjd if (fgets(buf, sizeof (buf), fp) != NULL) { 545168404Spjd char *colon = strchr(buf, ':'); 546168404Spjd 547168404Spjd while (buf[strlen(buf) - 1] == '\n') 548168404Spjd buf[strlen(buf) - 1] = '\0'; 549168404Spjd 550168404Spjd if (colon != NULL) 551168404Spjd zfs_error_aux(hdl, colon + 2); 552168404Spjd 553168404Spjd verify(pclose(fp) != 0); 554168404Spjd 555168404Spjd return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 556168404Spjd dgettext(TEXT_DOMAIN, 557168404Spjd "cannot unshare '%s'"), name)); 558168404Spjd } 559168404Spjd 560168404Spjd verify(pclose(fp) == 0); 561168404Spjd#endif 562168404Spjd 563168404Spjd return (0); 564168404Spjd} 565168404Spjd 566168404Spjd/* 567168404Spjd * Unshare the given filesystem. 568168404Spjd */ 569168404Spjdint 570168404Spjdzfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) 571168404Spjd{ 572168404Spjd struct mnttab search = { 0 }, entry; 573168404Spjd 574168404Spjd /* check to see if need to unmount the filesystem */ 575168404Spjd search.mnt_special = (char *)zfs_get_name(zhp); 576168404Spjd search.mnt_fstype = MNTTYPE_ZFS; 577168404Spjd rewind(zhp->zfs_hdl->libzfs_mnttab); 578168404Spjd if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 579168404Spjd getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { 580168404Spjd 581168404Spjd if (mountpoint == NULL) 582168404Spjd mountpoint = entry.mnt_mountp; 583168404Spjd 584168404Spjd if (is_shared(zhp->zfs_hdl, mountpoint) && 585168404Spjd unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) 586168404Spjd return (-1); 587168404Spjd } 588168404Spjd 589168404Spjd return (0); 590168404Spjd} 591168404Spjd 592168404Spjd/* 593168404Spjd * Same as zfs_unmountall(), but for NFS unshares. 594168404Spjd */ 595168404Spjdint 596168404Spjdzfs_unshareall_nfs(zfs_handle_t *zhp) 597168404Spjd{ 598168404Spjd prop_changelist_t *clp; 599168404Spjd int ret; 600168404Spjd 601168404Spjd clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0); 602168404Spjd if (clp == NULL) 603168404Spjd return (-1); 604168404Spjd 605168404Spjd ret = changelist_unshare(clp); 606168404Spjd changelist_free(clp); 607168404Spjd 608168404Spjd return (ret); 609168404Spjd} 610168404Spjd 611168404Spjd/* 612168404Spjd * Remove the mountpoint associated with the current dataset, if necessary. 613168404Spjd * We only remove the underlying directory if: 614168404Spjd * 615168404Spjd * - The mountpoint is not 'none' or 'legacy' 616168404Spjd * - The mountpoint is non-empty 617168404Spjd * - The mountpoint is the default or inherited 618168404Spjd * - The 'zoned' property is set, or we're in a local zone 619168404Spjd * 620168404Spjd * Any other directories we leave alone. 621168404Spjd */ 622168404Spjdvoid 623168404Spjdremove_mountpoint(zfs_handle_t *zhp) 624168404Spjd{ 625168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 626168404Spjd zfs_source_t source; 627168404Spjd 628168404Spjd if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 629168404Spjd &source)) 630168404Spjd return; 631168404Spjd 632168404Spjd if (source == ZFS_SRC_DEFAULT || 633168404Spjd source == ZFS_SRC_INHERITED) { 634168404Spjd /* 635168404Spjd * Try to remove the directory, silently ignoring any errors. 636168404Spjd * The filesystem may have since been removed or moved around, 637168404Spjd * and this error isn't really useful to the administrator in 638168404Spjd * any way. 639168404Spjd */ 640168404Spjd (void) rmdir(mountpoint); 641168404Spjd } 642168404Spjd} 643168404Spjd 644168404Spjdboolean_t 645168404Spjdzfs_is_shared_iscsi(zfs_handle_t *zhp) 646168404Spjd{ 647168404Spjd return (iscsitgt_zfs_is_shared != NULL && 648168404Spjd iscsitgt_zfs_is_shared(zhp->zfs_name) != 0); 649168404Spjd} 650168404Spjd 651168404Spjdint 652168404Spjdzfs_share_iscsi(zfs_handle_t *zhp) 653168404Spjd{ 654168404Spjd char shareopts[ZFS_MAXPROPLEN]; 655168404Spjd const char *dataset = zhp->zfs_name; 656168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 657168404Spjd 658168404Spjd /* 659168404Spjd * Return success if there are no share options. 660168404Spjd */ 661168404Spjd if (zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 662168404Spjd sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0 || 663168404Spjd strcmp(shareopts, "off") == 0) 664168404Spjd return (0); 665168404Spjd 666168404Spjd/* We don't support iSCSI on FreeBSD yet. */ 667168404Spjd#ifdef TODO 668168404Spjd if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) 669168404Spjd return (zfs_error_fmt(hdl, EZFS_SHAREISCSIFAILED, 670168404Spjd dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset)); 671168404Spjd#endif 672168404Spjd 673168404Spjd return (0); 674168404Spjd} 675168404Spjd 676168404Spjdint 677168404Spjdzfs_unshare_iscsi(zfs_handle_t *zhp) 678168404Spjd{ 679168404Spjd const char *dataset = zfs_get_name(zhp); 680168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 681168404Spjd 682168404Spjd/* We don't support iSCSI on FreeBSD yet. */ 683168404Spjd#ifdef TODO 684168404Spjd /* 685168404Spjd * Return if the volume is not shared 686168404Spjd */ 687168404Spjd if (!zfs_is_shared_iscsi(zhp)) 688168404Spjd return (0); 689168404Spjd 690168404Spjd /* 691168404Spjd * If this fails with ENODEV it indicates that zvol wasn't shared so 692168404Spjd * we should return success in that case. 693168404Spjd */ 694168404Spjd if (iscsitgt_zfs_unshare == NULL || 695168404Spjd (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) 696168404Spjd return (zfs_error_fmt(hdl, EZFS_UNSHAREISCSIFAILED, 697168404Spjd dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset)); 698168404Spjd#endif 699168404Spjd 700168404Spjd return (0); 701168404Spjd} 702168404Spjd 703168404Spjdtypedef struct mount_cbdata { 704168404Spjd zfs_handle_t **cb_datasets; 705168404Spjd int cb_used; 706168404Spjd int cb_alloc; 707168404Spjd} mount_cbdata_t; 708168404Spjd 709168404Spjdstatic int 710168404Spjdmount_cb(zfs_handle_t *zhp, void *data) 711168404Spjd{ 712168404Spjd mount_cbdata_t *cbp = data; 713168404Spjd 714168404Spjd if (!(zfs_get_type(zhp) & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) { 715168404Spjd zfs_close(zhp); 716168404Spjd return (0); 717168404Spjd } 718168404Spjd 719168404Spjd if (cbp->cb_alloc == cbp->cb_used) { 720168404Spjd void *ptr; 721168404Spjd 722168404Spjd if ((ptr = zfs_realloc(zhp->zfs_hdl, 723168404Spjd cbp->cb_datasets, cbp->cb_alloc * sizeof (void *), 724168404Spjd cbp->cb_alloc * 2 * sizeof (void *))) == NULL) 725168404Spjd return (-1); 726168404Spjd cbp->cb_datasets = ptr; 727168404Spjd 728168404Spjd cbp->cb_alloc *= 2; 729168404Spjd } 730168404Spjd 731168404Spjd cbp->cb_datasets[cbp->cb_used++] = zhp; 732168404Spjd 733168404Spjd return (zfs_iter_children(zhp, mount_cb, cbp)); 734168404Spjd} 735168404Spjd 736168404Spjdstatic int 737168404Spjddataset_cmp(const void *a, const void *b) 738168404Spjd{ 739168404Spjd zfs_handle_t **za = (zfs_handle_t **)a; 740168404Spjd zfs_handle_t **zb = (zfs_handle_t **)b; 741168404Spjd char mounta[MAXPATHLEN]; 742168404Spjd char mountb[MAXPATHLEN]; 743168404Spjd boolean_t gota, gotb; 744168404Spjd 745168404Spjd if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 746168404Spjd verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 747168404Spjd sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 748168404Spjd if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 749168404Spjd verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 750168404Spjd sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 751168404Spjd 752168404Spjd if (gota && gotb) 753168404Spjd return (strcmp(mounta, mountb)); 754168404Spjd 755168404Spjd if (gota) 756168404Spjd return (-1); 757168404Spjd if (gotb) 758168404Spjd return (1); 759168404Spjd 760168404Spjd return (strcmp(zfs_get_name(a), zfs_get_name(b))); 761168404Spjd} 762168404Spjd 763168404Spjd/* 764168404Spjd * Mount and share all datasets within the given pool. This assumes that no 765168404Spjd * datasets within the pool are currently mounted. Because users can create 766168404Spjd * complicated nested hierarchies of mountpoints, we first gather all the 767168404Spjd * datasets and mountpoints within the pool, and sort them by mountpoint. Once 768168404Spjd * we have the list of all filesystems, we iterate over them in order and mount 769168404Spjd * and/or share each one. 770168404Spjd */ 771168404Spjd#pragma weak zpool_mount_datasets = zpool_enable_datasets 772168404Spjdint 773168404Spjdzpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 774168404Spjd{ 775168404Spjd mount_cbdata_t cb = { 0 }; 776168404Spjd libzfs_handle_t *hdl = zhp->zpool_hdl; 777168404Spjd zfs_handle_t *zfsp; 778168404Spjd int i, ret = -1; 779168404Spjd 780168404Spjd /* 781168404Spjd * Gather all datasets within the pool. 782168404Spjd */ 783168404Spjd if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL) 784168404Spjd return (-1); 785168404Spjd cb.cb_alloc = 4; 786168404Spjd 787168404Spjd if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL) 788168404Spjd goto out; 789168404Spjd 790168404Spjd cb.cb_datasets[0] = zfsp; 791168404Spjd cb.cb_used = 1; 792168404Spjd 793168404Spjd if (zfs_iter_children(zfsp, mount_cb, &cb) != 0) 794168404Spjd goto out; 795168404Spjd 796168404Spjd /* 797168404Spjd * Sort the datasets by mountpoint. 798168404Spjd */ 799168404Spjd qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp); 800168404Spjd 801168404Spjd /* 802168404Spjd * And mount all the datasets. 803168404Spjd */ 804168404Spjd ret = 0; 805168404Spjd for (i = 0; i < cb.cb_used; i++) { 806168404Spjd if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 || 807168404Spjd zfs_share(cb.cb_datasets[i]) != 0) 808168404Spjd ret = -1; 809168404Spjd } 810168404Spjd 811168404Spjdout: 812168404Spjd for (i = 0; i < cb.cb_used; i++) 813168404Spjd zfs_close(cb.cb_datasets[i]); 814168404Spjd free(cb.cb_datasets); 815168404Spjd 816168404Spjd return (ret); 817168404Spjd} 818168404Spjd 819168404Spjd 820168404Spjdstatic int 821168404Spjdzvol_cb(const char *dataset, void *data) 822168404Spjd{ 823168404Spjd libzfs_handle_t *hdl = data; 824168404Spjd zfs_handle_t *zhp; 825168404Spjd 826168404Spjd /* 827168404Spjd * Ignore snapshots and ignore failures from non-existant datasets. 828168404Spjd */ 829168404Spjd if (strchr(dataset, '@') != NULL || 830168404Spjd (zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL) 831168404Spjd return (0); 832168404Spjd 833168404Spjd (void) zfs_unshare_iscsi(zhp); 834168404Spjd 835168404Spjd zfs_close(zhp); 836168404Spjd 837168404Spjd return (0); 838168404Spjd} 839168404Spjd 840168404Spjdstatic int 841168404Spjdmountpoint_compare(const void *a, const void *b) 842168404Spjd{ 843168404Spjd const char *mounta = *((char **)a); 844168404Spjd const char *mountb = *((char **)b); 845168404Spjd 846168404Spjd return (strcmp(mountb, mounta)); 847168404Spjd} 848168404Spjd 849168404Spjd/* 850168404Spjd * Unshare and unmount all datasets within the given pool. We don't want to 851168404Spjd * rely on traversing the DSL to discover the filesystems within the pool, 852168404Spjd * because this may be expensive (if not all of them are mounted), and can fail 853168404Spjd * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 854168404Spjd * gather all the filesystems that are currently mounted. 855168404Spjd */ 856168404Spjd#pragma weak zpool_unmount_datasets = zpool_disable_datasets 857168404Spjdint 858168404Spjdzpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) 859168404Spjd{ 860168404Spjd int used, alloc; 861168404Spjd struct statfs *sfs; 862168404Spjd size_t namelen; 863168404Spjd char **mountpoints = NULL; 864168404Spjd zfs_handle_t **datasets = NULL; 865168404Spjd libzfs_handle_t *hdl = zhp->zpool_hdl; 866168404Spjd int i, j, n; 867168404Spjd int ret = -1; 868168404Spjd int flags = (force ? MS_FORCE : 0); 869168404Spjd 870168404Spjd /* 871168404Spjd * First unshare all zvols. 872168404Spjd */ 873168404Spjd if (zpool_iter_zvol(zhp, zvol_cb, hdl) != 0) 874168404Spjd return (-1); 875168404Spjd 876168404Spjd namelen = strlen(zhp->zpool_name); 877168404Spjd 878168404Spjd used = alloc = 0; 879168404Spjd if ((n = getmntinfo(&sfs, MNT_WAIT)) == 0) { 880168404Spjd fprintf(stderr, "getmntinfo(): %s\n", strerror(errno)); 881168404Spjd return (-1); 882168404Spjd } 883168404Spjd for (j = 0; j < n; j++) { 884168404Spjd /* 885168404Spjd * Ignore non-ZFS entries. 886168404Spjd */ 887168404Spjd if (strcmp(sfs[j].f_fstypename, MNTTYPE_ZFS) != 0) 888168404Spjd continue; 889168404Spjd 890168404Spjd /* 891168404Spjd * Ignore filesystems not within this pool. 892168404Spjd */ 893168404Spjd if (strncmp(sfs[j].f_mntfromname, zhp->zpool_name, namelen) != 0 || 894168404Spjd (sfs[j].f_mntfromname[namelen] != '/' && 895168404Spjd sfs[j].f_mntfromname[namelen] != '\0')) 896168404Spjd continue; 897168404Spjd 898168404Spjd /* 899168404Spjd * At this point we've found a filesystem within our pool. Add 900168404Spjd * it to our growing list. 901168404Spjd */ 902168404Spjd if (used == alloc) { 903168404Spjd if (alloc == 0) { 904168404Spjd if ((mountpoints = zfs_alloc(hdl, 905168404Spjd 8 * sizeof (void *))) == NULL) 906168404Spjd goto out; 907168404Spjd 908168404Spjd if ((datasets = zfs_alloc(hdl, 909168404Spjd 8 * sizeof (void *))) == NULL) 910168404Spjd goto out; 911168404Spjd 912168404Spjd alloc = 8; 913168404Spjd } else { 914168404Spjd void *ptr; 915168404Spjd 916168404Spjd if ((ptr = zfs_realloc(hdl, mountpoints, 917168404Spjd alloc * sizeof (void *), 918168404Spjd alloc * 2 * sizeof (void *))) == NULL) 919168404Spjd goto out; 920168404Spjd mountpoints = ptr; 921168404Spjd 922168404Spjd if ((ptr = zfs_realloc(hdl, datasets, 923168404Spjd alloc * sizeof (void *), 924168404Spjd alloc * 2 * sizeof (void *))) == NULL) 925168404Spjd goto out; 926168404Spjd datasets = ptr; 927168404Spjd 928168404Spjd alloc *= 2; 929168404Spjd } 930168404Spjd } 931168404Spjd 932168404Spjd if ((mountpoints[used] = zfs_strdup(hdl, 933168404Spjd sfs[j].f_mntonname)) == NULL) 934168404Spjd goto out; 935168404Spjd 936168404Spjd /* 937168404Spjd * This is allowed to fail, in case there is some I/O error. It 938168404Spjd * is only used to determine if we need to remove the underlying 939168404Spjd * mountpoint, so failure is not fatal. 940168404Spjd */ 941168404Spjd datasets[used] = make_dataset_handle(hdl, sfs[j].f_mntfromname); 942168404Spjd 943168404Spjd used++; 944168404Spjd } 945168404Spjd 946168404Spjd /* 947168404Spjd * At this point, we have the entire list of filesystems, so sort it by 948168404Spjd * mountpoint. 949168404Spjd */ 950168404Spjd qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 951168404Spjd 952168404Spjd /* 953168404Spjd * Walk through and first unshare everything. 954168404Spjd */ 955168404Spjd for (i = 0; i < used; i++) { 956168404Spjd if (is_shared(hdl, mountpoints[i]) && 957168404Spjd unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) 958168404Spjd goto out; 959168404Spjd } 960168404Spjd 961168404Spjd /* 962168404Spjd * Now unmount everything, removing the underlying directories as 963168404Spjd * appropriate. 964168404Spjd */ 965168404Spjd for (i = 0; i < used; i++) { 966168404Spjd if (unmount_one(hdl, mountpoints[i], flags) != 0) 967168404Spjd goto out; 968168404Spjd } 969168404Spjd 970168404Spjd for (i = 0; i < used; i++) { 971168404Spjd if (datasets[i]) 972168404Spjd remove_mountpoint(datasets[i]); 973168404Spjd } 974168404Spjd 975168404Spjd ret = 0; 976168404Spjdout: 977168404Spjd for (i = 0; i < used; i++) { 978168404Spjd if (datasets[i]) 979168404Spjd zfs_close(datasets[i]); 980168404Spjd free(mountpoints[i]); 981168404Spjd } 982168404Spjd free(datasets); 983168404Spjd free(mountpoints); 984168404Spjd 985168404Spjd return (ret); 986168404Spjd} 987