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/* 23219089Spjd * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24269003Sdelphij * Copyright (c) 2014 by Delphix. All rights reserved. 25168404Spjd */ 26168404Spjd 27168404Spjd/* 28168404Spjd * Routines to manage ZFS mounts. We separate all the nasty routines that have 29168404Spjd * to deal with the OS. The following functions are the main entry points -- 30168404Spjd * they are used by mount and unmount and when changing a filesystem's 31168404Spjd * mountpoint. 32168404Spjd * 33168404Spjd * zfs_is_mounted() 34168404Spjd * zfs_mount() 35168404Spjd * zfs_unmount() 36168404Spjd * zfs_unmountall() 37168404Spjd * 38168404Spjd * This file also contains the functions used to manage sharing filesystems via 39168404Spjd * NFS and iSCSI: 40168404Spjd * 41168404Spjd * zfs_is_shared() 42168404Spjd * zfs_share() 43168404Spjd * zfs_unshare() 44168404Spjd * 45168404Spjd * zfs_is_shared_nfs() 46185029Spjd * zfs_is_shared_smb() 47185029Spjd * zfs_share_proto() 48185029Spjd * zfs_shareall(); 49168404Spjd * zfs_unshare_nfs() 50185029Spjd * zfs_unshare_smb() 51168404Spjd * zfs_unshareall_nfs() 52185029Spjd * zfs_unshareall_smb() 53185029Spjd * zfs_unshareall() 54185029Spjd * zfs_unshareall_bypath() 55168404Spjd * 56168404Spjd * The following functions are available for pool consumers, and will 57168404Spjd * mount/unmount and share/unshare all datasets within pool: 58168404Spjd * 59168404Spjd * zpool_enable_datasets() 60168404Spjd * zpool_disable_datasets() 61168404Spjd */ 62168404Spjd 63168404Spjd#include <dirent.h> 64168404Spjd#include <dlfcn.h> 65168404Spjd#include <errno.h> 66168404Spjd#include <libgen.h> 67168404Spjd#include <libintl.h> 68168404Spjd#include <stdio.h> 69168404Spjd#include <stdlib.h> 70168404Spjd#include <strings.h> 71168404Spjd#include <unistd.h> 72168404Spjd#include <zone.h> 73168404Spjd#include <sys/mntent.h> 74168404Spjd#include <sys/mount.h> 75168404Spjd#include <sys/stat.h> 76168404Spjd 77168404Spjd#include <libzfs.h> 78168404Spjd 79168404Spjd#include "libzfs_impl.h" 80168404Spjd 81185029Spjd#include <libshare.h> 82185029Spjd#define MAXISALEN 257 /* based on sysinfo(2) man page */ 83185029Spjd 84185029Spjdstatic int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *); 85185029Spjdzfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **, 86185029Spjd zfs_share_proto_t); 87185029Spjd 88185029Spjd/* 89185029Spjd * The share protocols table must be in the same order as the zfs_share_prot_t 90185029Spjd * enum in libzfs_impl.h 91185029Spjd */ 92185029Spjdtypedef struct { 93185029Spjd zfs_prop_t p_prop; 94185029Spjd char *p_name; 95185029Spjd int p_share_err; 96185029Spjd int p_unshare_err; 97185029Spjd} proto_table_t; 98185029Spjd 99185029Spjdproto_table_t proto_table[PROTO_END] = { 100185029Spjd {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED}, 101185029Spjd {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED}, 102185029Spjd}; 103185029Spjd 104185029Spjdzfs_share_proto_t nfs_only[] = { 105185029Spjd PROTO_NFS, 106185029Spjd PROTO_END 107185029Spjd}; 108185029Spjd 109185029Spjdzfs_share_proto_t smb_only[] = { 110185029Spjd PROTO_SMB, 111185029Spjd PROTO_END 112185029Spjd}; 113185029Spjdzfs_share_proto_t share_all_proto[] = { 114185029Spjd PROTO_NFS, 115185029Spjd PROTO_SMB, 116185029Spjd PROTO_END 117185029Spjd}; 118185029Spjd 119168404Spjd/* 120185029Spjd * Search the sharetab for the given mountpoint and protocol, returning 121185029Spjd * a zfs_share_type_t value. 122168404Spjd */ 123185029Spjdstatic zfs_share_type_t 124185029Spjdis_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto) 125168404Spjd{ 126168404Spjd char buf[MAXPATHLEN], *tab; 127185029Spjd char *ptr; 128168404Spjd 129168404Spjd if (hdl->libzfs_sharetab == NULL) 130185029Spjd return (SHARED_NOT_SHARED); 131168404Spjd 132168404Spjd (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); 133168404Spjd 134168404Spjd while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { 135168404Spjd 136168404Spjd /* the mountpoint is the first entry on each line */ 137185029Spjd if ((tab = strchr(buf, '\t')) == NULL) 138185029Spjd continue; 139185029Spjd 140185029Spjd *tab = '\0'; 141185029Spjd if (strcmp(buf, mountpoint) == 0) { 142219089Spjd#ifdef sun 143185029Spjd /* 144185029Spjd * the protocol field is the third field 145185029Spjd * skip over second field 146185029Spjd */ 147185029Spjd ptr = ++tab; 148185029Spjd if ((tab = strchr(ptr, '\t')) == NULL) 149185029Spjd continue; 150185029Spjd ptr = ++tab; 151185029Spjd if ((tab = strchr(ptr, '\t')) == NULL) 152185029Spjd continue; 153168404Spjd *tab = '\0'; 154185029Spjd if (strcmp(ptr, 155185029Spjd proto_table[proto].p_name) == 0) { 156185029Spjd switch (proto) { 157185029Spjd case PROTO_NFS: 158185029Spjd return (SHARED_NFS); 159185029Spjd case PROTO_SMB: 160185029Spjd return (SHARED_SMB); 161185029Spjd default: 162185029Spjd return (0); 163185029Spjd } 164185029Spjd } 165196950Spjd#else 166196950Spjd if (proto == PROTO_NFS) 167196950Spjd return (SHARED_NFS); 168196950Spjd#endif 169168404Spjd } 170168404Spjd } 171168404Spjd 172185029Spjd return (SHARED_NOT_SHARED); 173168404Spjd} 174168404Spjd 175219089Spjd#ifdef sun 176168404Spjd/* 177168404Spjd * Returns true if the specified directory is empty. If we can't open the 178168404Spjd * directory at all, return true so that the mount can fail with a more 179168404Spjd * informative error message. 180168404Spjd */ 181168404Spjdstatic boolean_t 182168404Spjddir_is_empty(const char *dirname) 183168404Spjd{ 184168404Spjd DIR *dirp; 185168404Spjd struct dirent64 *dp; 186168404Spjd 187168404Spjd if ((dirp = opendir(dirname)) == NULL) 188168404Spjd return (B_TRUE); 189168404Spjd 190168404Spjd while ((dp = readdir64(dirp)) != NULL) { 191168404Spjd 192168404Spjd if (strcmp(dp->d_name, ".") == 0 || 193168404Spjd strcmp(dp->d_name, "..") == 0) 194168404Spjd continue; 195168404Spjd 196168404Spjd (void) closedir(dirp); 197168404Spjd return (B_FALSE); 198168404Spjd } 199168404Spjd 200168404Spjd (void) closedir(dirp); 201168404Spjd return (B_TRUE); 202168404Spjd} 203168404Spjd#endif 204168404Spjd 205168404Spjd/* 206168404Spjd * Checks to see if the mount is active. If the filesystem is mounted, we fill 207168404Spjd * in 'where' with the current mountpoint, and return 1. Otherwise, we return 208168404Spjd * 0. 209168404Spjd */ 210168404Spjdboolean_t 211168404Spjdis_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where) 212168404Spjd{ 213209962Smm struct mnttab entry; 214168404Spjd 215209962Smm if (libzfs_mnttab_find(zfs_hdl, special, &entry) != 0) 216168404Spjd return (B_FALSE); 217168404Spjd 218168404Spjd if (where != NULL) 219168404Spjd *where = zfs_strdup(zfs_hdl, entry.mnt_mountp); 220168404Spjd 221168404Spjd return (B_TRUE); 222168404Spjd} 223168404Spjd 224168404Spjdboolean_t 225168404Spjdzfs_is_mounted(zfs_handle_t *zhp, char **where) 226168404Spjd{ 227168404Spjd return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where)); 228168404Spjd} 229168404Spjd 230168404Spjd/* 231168404Spjd * Returns true if the given dataset is mountable, false otherwise. Returns the 232168404Spjd * mountpoint in 'buf'. 233168404Spjd */ 234168404Spjdstatic boolean_t 235168404Spjdzfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen, 236185029Spjd zprop_source_t *source) 237168404Spjd{ 238168404Spjd char sourceloc[ZFS_MAXNAMELEN]; 239185029Spjd zprop_source_t sourcetype; 240168404Spjd 241168404Spjd if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type)) 242168404Spjd return (B_FALSE); 243168404Spjd 244168404Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen, 245168404Spjd &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0); 246168404Spjd 247168404Spjd if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 || 248168404Spjd strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0) 249168404Spjd return (B_FALSE); 250168404Spjd 251185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF) 252168404Spjd return (B_FALSE); 253168404Spjd 254168404Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) && 255168404Spjd getzoneid() == GLOBAL_ZONEID) 256168404Spjd return (B_FALSE); 257168404Spjd 258168404Spjd if (source) 259168404Spjd *source = sourcetype; 260168404Spjd 261168404Spjd return (B_TRUE); 262168404Spjd} 263168404Spjd 264168404Spjd/* 265168404Spjd * Mount the given filesystem. 266168404Spjd */ 267168404Spjdint 268168404Spjdzfs_mount(zfs_handle_t *zhp, const char *options, int flags) 269168404Spjd{ 270168404Spjd struct stat buf; 271168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 272168404Spjd char mntopts[MNT_LINE_MAX]; 273168404Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 274168404Spjd 275168404Spjd if (options == NULL) 276168404Spjd mntopts[0] = '\0'; 277168404Spjd else 278168404Spjd (void) strlcpy(mntopts, options, sizeof (mntopts)); 279168404Spjd 280219089Spjd /* 281219089Spjd * If the pool is imported read-only then all mounts must be read-only 282219089Spjd */ 283219089Spjd if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) 284219089Spjd flags |= MS_RDONLY; 285219089Spjd 286168404Spjd if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 287168404Spjd return (0); 288168404Spjd 289168404Spjd /* Create the directory if it doesn't already exist */ 290168404Spjd if (lstat(mountpoint, &buf) != 0) { 291168404Spjd if (mkdirp(mountpoint, 0755) != 0) { 292168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 293168404Spjd "failed to create mountpoint")); 294168404Spjd return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 295168404Spjd dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 296168404Spjd mountpoint)); 297168404Spjd } 298168404Spjd } 299168404Spjd 300219089Spjd#ifdef sun /* FreeBSD: overlay mounts are not checked. */ 301168404Spjd /* 302168404Spjd * Determine if the mountpoint is empty. If so, refuse to perform the 303168404Spjd * mount. We don't perform this check if MS_OVERLAY is specified, which 304168404Spjd * would defeat the point. We also avoid this check if 'remount' is 305168404Spjd * specified. 306168404Spjd */ 307168404Spjd if ((flags & MS_OVERLAY) == 0 && 308168404Spjd strstr(mntopts, MNTOPT_REMOUNT) == NULL && 309168404Spjd !dir_is_empty(mountpoint)) { 310168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 311168404Spjd "directory is not empty")); 312168404Spjd return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 313168404Spjd dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint)); 314168404Spjd } 315168404Spjd#endif 316168404Spjd 317168404Spjd /* perform the mount */ 318168404Spjd if (zmount(zfs_get_name(zhp), mountpoint, flags, 319168404Spjd MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { 320168404Spjd /* 321168404Spjd * Generic errors are nasty, but there are just way too many 322168404Spjd * from mount(), and they're well-understood. We pick a few 323168404Spjd * common ones to improve upon. 324168404Spjd */ 325185029Spjd if (errno == EBUSY) { 326168404Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 327168404Spjd "mountpoint or dataset is busy")); 328185029Spjd } else if (errno == EPERM) { 329185029Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 330185029Spjd "Insufficient privileges")); 331219089Spjd } else if (errno == ENOTSUP) { 332219089Spjd char buf[256]; 333219089Spjd int spa_version; 334219089Spjd 335219089Spjd VERIFY(zfs_spa_version(zhp, &spa_version) == 0); 336219089Spjd (void) snprintf(buf, sizeof (buf), 337219089Spjd dgettext(TEXT_DOMAIN, "Can't mount a version %lld " 338219089Spjd "file system on a version %d pool. Pool must be" 339219089Spjd " upgraded to mount this file system."), 340219089Spjd (u_longlong_t)zfs_prop_get_int(zhp, 341219089Spjd ZFS_PROP_VERSION), spa_version); 342219089Spjd zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf)); 343185029Spjd } else { 344168404Spjd zfs_error_aux(hdl, strerror(errno)); 345185029Spjd } 346168404Spjd return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, 347168404Spjd dgettext(TEXT_DOMAIN, "cannot mount '%s'"), 348168404Spjd zhp->zfs_name)); 349168404Spjd } 350168404Spjd 351209962Smm /* add the mounted entry into our cache */ 352209962Smm libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, 353209962Smm mntopts); 354168404Spjd return (0); 355168404Spjd} 356168404Spjd 357168404Spjd/* 358168404Spjd * Unmount a single filesystem. 359168404Spjd */ 360168404Spjdstatic int 361168404Spjdunmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) 362168404Spjd{ 363219089Spjd if (umount2(mountpoint, flags) != 0) { 364168404Spjd zfs_error_aux(hdl, strerror(errno)); 365168404Spjd return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, 366168404Spjd dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), 367168404Spjd mountpoint)); 368168404Spjd } 369168404Spjd 370168404Spjd return (0); 371168404Spjd} 372168404Spjd 373168404Spjd/* 374168404Spjd * Unmount the given filesystem. 375168404Spjd */ 376168404Spjdint 377168404Spjdzfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) 378168404Spjd{ 379209962Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 380209962Smm struct mnttab entry; 381185029Spjd char *mntpt = NULL; 382168404Spjd 383209962Smm /* check to see if we need to unmount the filesystem */ 384168404Spjd if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 385209962Smm libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)) { 386185029Spjd /* 387185029Spjd * mountpoint may have come from a call to 388185029Spjd * getmnt/getmntany if it isn't NULL. If it is NULL, 389209962Smm * we know it comes from libzfs_mnttab_find which can 390209962Smm * then get freed later. We strdup it to play it safe. 391185029Spjd */ 392168404Spjd if (mountpoint == NULL) 393209962Smm mntpt = zfs_strdup(hdl, entry.mnt_mountp); 394185029Spjd else 395209962Smm mntpt = zfs_strdup(hdl, mountpoint); 396168404Spjd 397168404Spjd /* 398168404Spjd * Unshare and unmount the filesystem 399168404Spjd */ 400185029Spjd if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0) 401168404Spjd return (-1); 402185029Spjd 403209962Smm if (unmount_one(hdl, mntpt, flags) != 0) { 404185029Spjd free(mntpt); 405185029Spjd (void) zfs_shareall(zhp); 406185029Spjd return (-1); 407185029Spjd } 408209962Smm libzfs_mnttab_remove(hdl, zhp->zfs_name); 409185029Spjd free(mntpt); 410168404Spjd } 411168404Spjd 412168404Spjd return (0); 413168404Spjd} 414168404Spjd 415168404Spjd/* 416168404Spjd * Unmount this filesystem and any children inheriting the mountpoint property. 417168404Spjd * To do this, just act like we're changing the mountpoint property, but don't 418168404Spjd * remount the filesystems afterwards. 419168404Spjd */ 420168404Spjdint 421168404Spjdzfs_unmountall(zfs_handle_t *zhp, int flags) 422168404Spjd{ 423168404Spjd prop_changelist_t *clp; 424168404Spjd int ret; 425168404Spjd 426185029Spjd clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, 0, flags); 427168404Spjd if (clp == NULL) 428168404Spjd return (-1); 429168404Spjd 430168404Spjd ret = changelist_prefix(clp); 431168404Spjd changelist_free(clp); 432168404Spjd 433168404Spjd return (ret); 434168404Spjd} 435168404Spjd 436168404Spjdboolean_t 437168404Spjdzfs_is_shared(zfs_handle_t *zhp) 438168404Spjd{ 439185029Spjd zfs_share_type_t rc = 0; 440185029Spjd zfs_share_proto_t *curr_proto; 441185029Spjd 442168404Spjd if (ZFS_IS_VOLUME(zhp)) 443219089Spjd return (B_FALSE); 444168404Spjd 445185029Spjd for (curr_proto = share_all_proto; *curr_proto != PROTO_END; 446185029Spjd curr_proto++) 447185029Spjd rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto); 448185029Spjd 449185029Spjd return (rc ? B_TRUE : B_FALSE); 450168404Spjd} 451168404Spjd 452168404Spjdint 453168404Spjdzfs_share(zfs_handle_t *zhp) 454168404Spjd{ 455219089Spjd assert(!ZFS_IS_VOLUME(zhp)); 456185029Spjd return (zfs_share_proto(zhp, share_all_proto)); 457168404Spjd} 458168404Spjd 459168404Spjdint 460168404Spjdzfs_unshare(zfs_handle_t *zhp) 461168404Spjd{ 462219089Spjd assert(!ZFS_IS_VOLUME(zhp)); 463185029Spjd return (zfs_unshareall(zhp)); 464168404Spjd} 465168404Spjd 466168404Spjd/* 467168404Spjd * Check to see if the filesystem is currently shared. 468168404Spjd */ 469185029Spjdzfs_share_type_t 470185029Spjdzfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto) 471168404Spjd{ 472168404Spjd char *mountpoint; 473185029Spjd zfs_share_type_t rc; 474168404Spjd 475168404Spjd if (!zfs_is_mounted(zhp, &mountpoint)) 476185029Spjd return (SHARED_NOT_SHARED); 477168404Spjd 478185029Spjd if (rc = is_shared(zhp->zfs_hdl, mountpoint, proto)) { 479168404Spjd if (where != NULL) 480168404Spjd *where = mountpoint; 481168404Spjd else 482168404Spjd free(mountpoint); 483185029Spjd return (rc); 484168404Spjd } else { 485168404Spjd free(mountpoint); 486185029Spjd return (SHARED_NOT_SHARED); 487168404Spjd } 488168404Spjd} 489168404Spjd 490185029Spjdboolean_t 491185029Spjdzfs_is_shared_nfs(zfs_handle_t *zhp, char **where) 492185029Spjd{ 493185029Spjd return (zfs_is_shared_proto(zhp, where, 494185029Spjd PROTO_NFS) != SHARED_NOT_SHARED); 495185029Spjd} 496185029Spjd 497185029Spjdboolean_t 498185029Spjdzfs_is_shared_smb(zfs_handle_t *zhp, char **where) 499185029Spjd{ 500185029Spjd return (zfs_is_shared_proto(zhp, where, 501185029Spjd PROTO_SMB) != SHARED_NOT_SHARED); 502185029Spjd} 503185029Spjd 504168404Spjd/* 505185029Spjd * Make sure things will work if libshare isn't installed by using 506185029Spjd * wrapper functions that check to see that the pointers to functions 507185029Spjd * initialized in _zfs_init_libshare() are actually present. 508168404Spjd */ 509185029Spjd 510219089Spjd#ifdef sun 511185029Spjdstatic sa_handle_t (*_sa_init)(int); 512185029Spjdstatic void (*_sa_fini)(sa_handle_t); 513185029Spjdstatic sa_share_t (*_sa_find_share)(sa_handle_t, char *); 514185029Spjdstatic int (*_sa_enable_share)(sa_share_t, char *); 515185029Spjdstatic int (*_sa_disable_share)(sa_share_t, char *); 516185029Spjdstatic char *(*_sa_errorstr)(int); 517185029Spjdstatic int (*_sa_parse_legacy_options)(sa_group_t, char *, char *); 518185029Spjdstatic boolean_t (*_sa_needs_refresh)(sa_handle_t *); 519185029Spjdstatic libzfs_handle_t *(*_sa_get_zfs_handle)(sa_handle_t); 520185029Spjdstatic int (*_sa_zfs_process_share)(sa_handle_t, sa_group_t, sa_share_t, 521185029Spjd char *, char *, zprop_source_t, char *, char *, char *); 522185029Spjdstatic void (*_sa_update_sharetab_ts)(sa_handle_t); 523185029Spjd#endif 524185029Spjd 525185029Spjd/* 526185029Spjd * _zfs_init_libshare() 527185029Spjd * 528185029Spjd * Find the libshare.so.1 entry points that we use here and save the 529185029Spjd * values to be used later. This is triggered by the runtime loader. 530185029Spjd * Make sure the correct ISA version is loaded. 531185029Spjd */ 532185029Spjd 533185029Spjd#pragma init(_zfs_init_libshare) 534185029Spjdstatic void 535185029Spjd_zfs_init_libshare(void) 536185029Spjd{ 537219089Spjd#ifdef sun 538185029Spjd void *libshare; 539185029Spjd char path[MAXPATHLEN]; 540185029Spjd char isa[MAXISALEN]; 541185029Spjd 542185029Spjd#if defined(_LP64) 543185029Spjd if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1) 544185029Spjd isa[0] = '\0'; 545185029Spjd#else 546185029Spjd isa[0] = '\0'; 547185029Spjd#endif 548185029Spjd (void) snprintf(path, MAXPATHLEN, 549185029Spjd "/usr/lib/%s/libshare.so.1", isa); 550185029Spjd 551185029Spjd if ((libshare = dlopen(path, RTLD_LAZY | RTLD_GLOBAL)) != NULL) { 552185029Spjd _sa_init = (sa_handle_t (*)(int))dlsym(libshare, "sa_init"); 553185029Spjd _sa_fini = (void (*)(sa_handle_t))dlsym(libshare, "sa_fini"); 554185029Spjd _sa_find_share = (sa_share_t (*)(sa_handle_t, char *)) 555185029Spjd dlsym(libshare, "sa_find_share"); 556185029Spjd _sa_enable_share = (int (*)(sa_share_t, char *))dlsym(libshare, 557185029Spjd "sa_enable_share"); 558185029Spjd _sa_disable_share = (int (*)(sa_share_t, char *))dlsym(libshare, 559185029Spjd "sa_disable_share"); 560185029Spjd _sa_errorstr = (char *(*)(int))dlsym(libshare, "sa_errorstr"); 561185029Spjd _sa_parse_legacy_options = (int (*)(sa_group_t, char *, char *)) 562185029Spjd dlsym(libshare, "sa_parse_legacy_options"); 563185029Spjd _sa_needs_refresh = (boolean_t (*)(sa_handle_t *)) 564185029Spjd dlsym(libshare, "sa_needs_refresh"); 565185029Spjd _sa_get_zfs_handle = (libzfs_handle_t *(*)(sa_handle_t)) 566185029Spjd dlsym(libshare, "sa_get_zfs_handle"); 567185029Spjd _sa_zfs_process_share = (int (*)(sa_handle_t, sa_group_t, 568185029Spjd sa_share_t, char *, char *, zprop_source_t, char *, 569185029Spjd char *, char *))dlsym(libshare, "sa_zfs_process_share"); 570185029Spjd _sa_update_sharetab_ts = (void (*)(sa_handle_t)) 571185029Spjd dlsym(libshare, "sa_update_sharetab_ts"); 572185029Spjd if (_sa_init == NULL || _sa_fini == NULL || 573185029Spjd _sa_find_share == NULL || _sa_enable_share == NULL || 574185029Spjd _sa_disable_share == NULL || _sa_errorstr == NULL || 575185029Spjd _sa_parse_legacy_options == NULL || 576185029Spjd _sa_needs_refresh == NULL || _sa_get_zfs_handle == NULL || 577185029Spjd _sa_zfs_process_share == NULL || 578185029Spjd _sa_update_sharetab_ts == NULL) { 579185029Spjd _sa_init = NULL; 580185029Spjd _sa_fini = NULL; 581185029Spjd _sa_disable_share = NULL; 582185029Spjd _sa_enable_share = NULL; 583185029Spjd _sa_errorstr = NULL; 584185029Spjd _sa_parse_legacy_options = NULL; 585185029Spjd (void) dlclose(libshare); 586185029Spjd _sa_needs_refresh = NULL; 587185029Spjd _sa_get_zfs_handle = NULL; 588185029Spjd _sa_zfs_process_share = NULL; 589185029Spjd _sa_update_sharetab_ts = NULL; 590185029Spjd } 591185029Spjd } 592185029Spjd#endif 593185029Spjd} 594185029Spjd 595185029Spjd/* 596185029Spjd * zfs_init_libshare(zhandle, service) 597185029Spjd * 598185029Spjd * Initialize the libshare API if it hasn't already been initialized. 599185029Spjd * In all cases it returns 0 if it succeeded and an error if not. The 600185029Spjd * service value is which part(s) of the API to initialize and is a 601185029Spjd * direct map to the libshare sa_init(service) interface. 602185029Spjd */ 603168404Spjdint 604185029Spjdzfs_init_libshare(libzfs_handle_t *zhandle, int service) 605168404Spjd{ 606185029Spjd int ret = SA_OK; 607168404Spjd 608219089Spjd#ifdef sun 609185029Spjd if (_sa_init == NULL) 610185029Spjd ret = SA_CONFIG_ERR; 611168404Spjd 612185029Spjd if (ret == SA_OK && zhandle->libzfs_shareflags & ZFSSHARE_MISS) { 613185029Spjd /* 614185029Spjd * We had a cache miss. Most likely it is a new ZFS 615185029Spjd * dataset that was just created. We want to make sure 616185029Spjd * so check timestamps to see if a different process 617185029Spjd * has updated any of the configuration. If there was 618185029Spjd * some non-ZFS change, we need to re-initialize the 619185029Spjd * internal cache. 620185029Spjd */ 621185029Spjd zhandle->libzfs_shareflags &= ~ZFSSHARE_MISS; 622185029Spjd if (_sa_needs_refresh != NULL && 623185029Spjd _sa_needs_refresh(zhandle->libzfs_sharehdl)) { 624185029Spjd zfs_uninit_libshare(zhandle); 625185029Spjd zhandle->libzfs_sharehdl = _sa_init(service); 626185029Spjd } 627185029Spjd } 628168404Spjd 629185029Spjd if (ret == SA_OK && zhandle && zhandle->libzfs_sharehdl == NULL) 630185029Spjd zhandle->libzfs_sharehdl = _sa_init(service); 631168404Spjd 632185029Spjd if (ret == SA_OK && zhandle->libzfs_sharehdl == NULL) 633185029Spjd ret = SA_NO_MEMORY; 634185029Spjd#endif 635168404Spjd 636185029Spjd return (ret); 637185029Spjd} 638185029Spjd 639185029Spjd/* 640185029Spjd * zfs_uninit_libshare(zhandle) 641185029Spjd * 642185029Spjd * Uninitialize the libshare API if it hasn't already been 643185029Spjd * uninitialized. It is OK to call multiple times. 644185029Spjd */ 645185029Spjdvoid 646185029Spjdzfs_uninit_libshare(libzfs_handle_t *zhandle) 647185029Spjd{ 648185029Spjd if (zhandle != NULL && zhandle->libzfs_sharehdl != NULL) { 649219089Spjd#ifdef sun 650185029Spjd if (_sa_fini != NULL) 651185029Spjd _sa_fini(zhandle->libzfs_sharehdl); 652185029Spjd#endif 653185029Spjd zhandle->libzfs_sharehdl = NULL; 654168404Spjd } 655185029Spjd} 656185029Spjd 657185029Spjd/* 658185029Spjd * zfs_parse_options(options, proto) 659185029Spjd * 660185029Spjd * Call the legacy parse interface to get the protocol specific 661185029Spjd * options using the NULL arg to indicate that this is a "parse" only. 662185029Spjd */ 663185029Spjdint 664185029Spjdzfs_parse_options(char *options, zfs_share_proto_t proto) 665185029Spjd{ 666219089Spjd#ifdef sun 667185029Spjd if (_sa_parse_legacy_options != NULL) { 668185029Spjd return (_sa_parse_legacy_options(NULL, options, 669185029Spjd proto_table[proto].p_name)); 670168404Spjd } 671185029Spjd return (SA_CONFIG_ERR); 672168404Spjd#else 673185029Spjd return (SA_OK); 674185029Spjd#endif 675185029Spjd} 676168404Spjd 677219089Spjd#ifdef sun 678185029Spjd/* 679185029Spjd * zfs_sa_find_share(handle, path) 680185029Spjd * 681185029Spjd * wrapper around sa_find_share to find a share path in the 682185029Spjd * configuration. 683185029Spjd */ 684185029Spjdstatic sa_share_t 685185029Spjdzfs_sa_find_share(sa_handle_t handle, char *path) 686185029Spjd{ 687185029Spjd if (_sa_find_share != NULL) 688185029Spjd return (_sa_find_share(handle, path)); 689185029Spjd return (NULL); 690185029Spjd} 691168404Spjd 692185029Spjd/* 693185029Spjd * zfs_sa_enable_share(share, proto) 694185029Spjd * 695185029Spjd * Wrapper for sa_enable_share which enables a share for a specified 696185029Spjd * protocol. 697185029Spjd */ 698185029Spjdstatic int 699185029Spjdzfs_sa_enable_share(sa_share_t share, char *proto) 700185029Spjd{ 701185029Spjd if (_sa_enable_share != NULL) 702185029Spjd return (_sa_enable_share(share, proto)); 703185029Spjd return (SA_CONFIG_ERR); 704185029Spjd} 705168404Spjd 706185029Spjd/* 707185029Spjd * zfs_sa_disable_share(share, proto) 708185029Spjd * 709185029Spjd * Wrapper for sa_enable_share which disables a share for a specified 710185029Spjd * protocol. 711185029Spjd */ 712185029Spjdstatic int 713185029Spjdzfs_sa_disable_share(sa_share_t share, char *proto) 714185029Spjd{ 715185029Spjd if (_sa_disable_share != NULL) 716185029Spjd return (_sa_disable_share(share, proto)); 717185029Spjd return (SA_CONFIG_ERR); 718185029Spjd} 719219089Spjd#endif /* sun */ 720168404Spjd 721185029Spjd/* 722185029Spjd * Share the given filesystem according to the options in the specified 723185029Spjd * protocol specific properties (sharenfs, sharesmb). We rely 724185029Spjd * on "libshare" to the dirty work for us. 725185029Spjd */ 726185029Spjdstatic int 727185029Spjdzfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto) 728185029Spjd{ 729185029Spjd char mountpoint[ZFS_MAXPROPLEN]; 730185029Spjd char shareopts[ZFS_MAXPROPLEN]; 731185029Spjd char sourcestr[ZFS_MAXPROPLEN]; 732185029Spjd libzfs_handle_t *hdl = zhp->zfs_hdl; 733185029Spjd zfs_share_proto_t *curr_proto; 734185029Spjd zprop_source_t sourcetype; 735185029Spjd int error, ret; 736168404Spjd 737185029Spjd if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) 738185029Spjd return (0); 739168404Spjd 740185029Spjd for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) { 741185029Spjd /* 742185029Spjd * Return success if there are no share options. 743185029Spjd */ 744185029Spjd if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop, 745185029Spjd shareopts, sizeof (shareopts), &sourcetype, sourcestr, 746185029Spjd ZFS_MAXPROPLEN, B_FALSE) != 0 || 747185029Spjd strcmp(shareopts, "off") == 0) 748185029Spjd continue; 749168404Spjd 750269003Sdelphij#ifdef illumos 751269003Sdelphij ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API); 752269003Sdelphij if (ret != SA_OK) { 753269003Sdelphij (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 754269003Sdelphij dgettext(TEXT_DOMAIN, "cannot share '%s': %s"), 755269003Sdelphij zfs_get_name(zhp), _sa_errorstr != NULL ? 756269003Sdelphij _sa_errorstr(ret) : ""); 757269003Sdelphij return (-1); 758269003Sdelphij } 759269003Sdelphij#endif 760269003Sdelphij 761185029Spjd /* 762185029Spjd * If the 'zoned' property is set, then zfs_is_mountable() 763185029Spjd * will have already bailed out if we are in the global zone. 764185029Spjd * But local zones cannot be NFS servers, so we ignore it for 765185029Spjd * local zones as well. 766185029Spjd */ 767185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) 768185029Spjd continue; 769185029Spjd 770219089Spjd#ifdef sun 771185029Spjd share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint); 772185029Spjd if (share == NULL) { 773185029Spjd /* 774185029Spjd * This may be a new file system that was just 775185029Spjd * created so isn't in the internal cache 776185029Spjd * (second time through). Rather than 777185029Spjd * reloading the entire configuration, we can 778185029Spjd * assume ZFS has done the checking and it is 779185029Spjd * safe to add this to the internal 780185029Spjd * configuration. 781185029Spjd */ 782185029Spjd if (_sa_zfs_process_share(hdl->libzfs_sharehdl, 783185029Spjd NULL, NULL, mountpoint, 784185029Spjd proto_table[*curr_proto].p_name, sourcetype, 785185029Spjd shareopts, sourcestr, zhp->zfs_name) != SA_OK) { 786185029Spjd (void) zfs_error_fmt(hdl, 787185029Spjd proto_table[*curr_proto].p_share_err, 788185029Spjd dgettext(TEXT_DOMAIN, "cannot share '%s'"), 789185029Spjd zfs_get_name(zhp)); 790185029Spjd return (-1); 791185029Spjd } 792185029Spjd hdl->libzfs_shareflags |= ZFSSHARE_MISS; 793185029Spjd share = zfs_sa_find_share(hdl->libzfs_sharehdl, 794185029Spjd mountpoint); 795185029Spjd } 796185029Spjd if (share != NULL) { 797185029Spjd int err; 798185029Spjd err = zfs_sa_enable_share(share, 799185029Spjd proto_table[*curr_proto].p_name); 800185029Spjd if (err != SA_OK) { 801185029Spjd (void) zfs_error_fmt(hdl, 802185029Spjd proto_table[*curr_proto].p_share_err, 803185029Spjd dgettext(TEXT_DOMAIN, "cannot share '%s'"), 804185029Spjd zfs_get_name(zhp)); 805185029Spjd return (-1); 806185029Spjd } 807185029Spjd } else 808185029Spjd#else 809219089Spjd if (*curr_proto != PROTO_NFS) { 810219089Spjd fprintf(stderr, "Unsupported share protocol: %d.\n", 811219089Spjd *curr_proto); 812219089Spjd continue; 813219089Spjd } 814219089Spjd 815185029Spjd if (strcmp(shareopts, "on") == 0) 816185029Spjd error = fsshare(ZFS_EXPORTS_PATH, mountpoint, ""); 817185029Spjd else 818185029Spjd error = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts); 819185029Spjd if (error != 0) 820168404Spjd#endif 821185029Spjd { 822185029Spjd (void) zfs_error_fmt(hdl, 823185029Spjd proto_table[*curr_proto].p_share_err, 824185029Spjd dgettext(TEXT_DOMAIN, "cannot share '%s'"), 825185029Spjd zfs_get_name(zhp)); 826185029Spjd return (-1); 827185029Spjd } 828219089Spjd 829185029Spjd } 830168404Spjd return (0); 831168404Spjd} 832168404Spjd 833185029Spjd 834185029Spjdint 835185029Spjdzfs_share_nfs(zfs_handle_t *zhp) 836185029Spjd{ 837185029Spjd return (zfs_share_proto(zhp, nfs_only)); 838185029Spjd} 839185029Spjd 840185029Spjdint 841185029Spjdzfs_share_smb(zfs_handle_t *zhp) 842185029Spjd{ 843185029Spjd return (zfs_share_proto(zhp, smb_only)); 844185029Spjd} 845185029Spjd 846185029Spjdint 847185029Spjdzfs_shareall(zfs_handle_t *zhp) 848185029Spjd{ 849185029Spjd return (zfs_share_proto(zhp, share_all_proto)); 850185029Spjd} 851185029Spjd 852168404Spjd/* 853168404Spjd * Unshare a filesystem by mountpoint. 854168404Spjd */ 855168404Spjdstatic int 856185029Spjdunshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint, 857185029Spjd zfs_share_proto_t proto) 858168404Spjd{ 859219089Spjd#ifdef sun 860219089Spjd sa_share_t share; 861219089Spjd int err; 862219089Spjd char *mntpt; 863219089Spjd /* 864219089Spjd * Mountpoint could get trashed if libshare calls getmntany 865219089Spjd * which it does during API initialization, so strdup the 866219089Spjd * value. 867219089Spjd */ 868219089Spjd mntpt = zfs_strdup(hdl, mountpoint); 869219089Spjd 870219089Spjd /* make sure libshare initialized */ 871219089Spjd if ((err = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) { 872219089Spjd free(mntpt); /* don't need the copy anymore */ 873219089Spjd return (zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, 874219089Spjd dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"), 875219089Spjd name, _sa_errorstr(err))); 876219089Spjd } 877219089Spjd 878219089Spjd share = zfs_sa_find_share(hdl->libzfs_sharehdl, mntpt); 879219089Spjd free(mntpt); /* don't need the copy anymore */ 880219089Spjd 881219089Spjd if (share != NULL) { 882219089Spjd err = zfs_sa_disable_share(share, proto_table[proto].p_name); 883219089Spjd if (err != SA_OK) { 884219089Spjd return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 885219089Spjd dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"), 886219089Spjd name, _sa_errorstr(err))); 887219089Spjd } 888219089Spjd } else { 889219089Spjd return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 890219089Spjd dgettext(TEXT_DOMAIN, "cannot unshare '%s': not found"), 891219089Spjd name)); 892219089Spjd } 893219089Spjd#else 894168404Spjd char buf[MAXPATHLEN]; 895168404Spjd FILE *fp; 896219089Spjd int err; 897168404Spjd 898185029Spjd if (proto != PROTO_NFS) { 899185029Spjd fprintf(stderr, "No SMB support in FreeBSD yet.\n"); 900185029Spjd return (EOPNOTSUPP); 901185029Spjd } 902185029Spjd 903219089Spjd err = fsunshare(ZFS_EXPORTS_PATH, mountpoint); 904219089Spjd if (err != 0) { 905219089Spjd zfs_error_aux(hdl, "%s", strerror(err)); 906168404Spjd return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, 907168404Spjd dgettext(TEXT_DOMAIN, 908168404Spjd "cannot unshare '%s'"), name)); 909168404Spjd } 910219089Spjd#endif 911168404Spjd return (0); 912168404Spjd} 913168404Spjd 914168404Spjd/* 915168404Spjd * Unshare the given filesystem. 916168404Spjd */ 917168404Spjdint 918185029Spjdzfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint, 919185029Spjd zfs_share_proto_t *proto) 920168404Spjd{ 921209962Smm libzfs_handle_t *hdl = zhp->zfs_hdl; 922209962Smm struct mnttab entry; 923185029Spjd char *mntpt = NULL; 924168404Spjd 925168404Spjd /* check to see if need to unmount the filesystem */ 926168404Spjd rewind(zhp->zfs_hdl->libzfs_mnttab); 927185029Spjd if (mountpoint != NULL) 928209962Smm mountpoint = mntpt = zfs_strdup(hdl, mountpoint); 929185029Spjd 930168404Spjd if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && 931209962Smm libzfs_mnttab_find(hdl, zfs_get_name(zhp), &entry) == 0)) { 932185029Spjd zfs_share_proto_t *curr_proto; 933168404Spjd 934168404Spjd if (mountpoint == NULL) 935185029Spjd mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); 936168404Spjd 937185029Spjd for (curr_proto = proto; *curr_proto != PROTO_END; 938185029Spjd curr_proto++) { 939185029Spjd 940209962Smm if (is_shared(hdl, mntpt, *curr_proto) && 941209962Smm unshare_one(hdl, zhp->zfs_name, 942185029Spjd mntpt, *curr_proto) != 0) { 943185029Spjd if (mntpt != NULL) 944185029Spjd free(mntpt); 945185029Spjd return (-1); 946185029Spjd } 947185029Spjd } 948168404Spjd } 949185029Spjd if (mntpt != NULL) 950185029Spjd free(mntpt); 951168404Spjd 952168404Spjd return (0); 953168404Spjd} 954168404Spjd 955185029Spjdint 956185029Spjdzfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) 957185029Spjd{ 958185029Spjd return (zfs_unshare_proto(zhp, mountpoint, nfs_only)); 959185029Spjd} 960185029Spjd 961185029Spjdint 962185029Spjdzfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint) 963185029Spjd{ 964185029Spjd return (zfs_unshare_proto(zhp, mountpoint, smb_only)); 965185029Spjd} 966185029Spjd 967168404Spjd/* 968185029Spjd * Same as zfs_unmountall(), but for NFS and SMB unshares. 969168404Spjd */ 970168404Spjdint 971185029Spjdzfs_unshareall_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto) 972168404Spjd{ 973168404Spjd prop_changelist_t *clp; 974168404Spjd int ret; 975168404Spjd 976185029Spjd clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0, 0); 977168404Spjd if (clp == NULL) 978168404Spjd return (-1); 979168404Spjd 980185029Spjd ret = changelist_unshare(clp, proto); 981168404Spjd changelist_free(clp); 982168404Spjd 983168404Spjd return (ret); 984168404Spjd} 985168404Spjd 986185029Spjdint 987185029Spjdzfs_unshareall_nfs(zfs_handle_t *zhp) 988185029Spjd{ 989185029Spjd return (zfs_unshareall_proto(zhp, nfs_only)); 990185029Spjd} 991185029Spjd 992185029Spjdint 993185029Spjdzfs_unshareall_smb(zfs_handle_t *zhp) 994185029Spjd{ 995185029Spjd return (zfs_unshareall_proto(zhp, smb_only)); 996185029Spjd} 997185029Spjd 998185029Spjdint 999185029Spjdzfs_unshareall(zfs_handle_t *zhp) 1000185029Spjd{ 1001185029Spjd return (zfs_unshareall_proto(zhp, share_all_proto)); 1002185029Spjd} 1003185029Spjd 1004185029Spjdint 1005185029Spjdzfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint) 1006185029Spjd{ 1007185029Spjd return (zfs_unshare_proto(zhp, mountpoint, share_all_proto)); 1008185029Spjd} 1009185029Spjd 1010168404Spjd/* 1011168404Spjd * Remove the mountpoint associated with the current dataset, if necessary. 1012168404Spjd * We only remove the underlying directory if: 1013168404Spjd * 1014168404Spjd * - The mountpoint is not 'none' or 'legacy' 1015168404Spjd * - The mountpoint is non-empty 1016168404Spjd * - The mountpoint is the default or inherited 1017168404Spjd * - The 'zoned' property is set, or we're in a local zone 1018168404Spjd * 1019168404Spjd * Any other directories we leave alone. 1020168404Spjd */ 1021168404Spjdvoid 1022168404Spjdremove_mountpoint(zfs_handle_t *zhp) 1023168404Spjd{ 1024168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 1025185029Spjd zprop_source_t source; 1026168404Spjd 1027168404Spjd if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), 1028168404Spjd &source)) 1029168404Spjd return; 1030168404Spjd 1031185029Spjd if (source == ZPROP_SRC_DEFAULT || 1032185029Spjd source == ZPROP_SRC_INHERITED) { 1033168404Spjd /* 1034168404Spjd * Try to remove the directory, silently ignoring any errors. 1035168404Spjd * The filesystem may have since been removed or moved around, 1036168404Spjd * and this error isn't really useful to the administrator in 1037168404Spjd * any way. 1038168404Spjd */ 1039168404Spjd (void) rmdir(mountpoint); 1040168404Spjd } 1041168404Spjd} 1042168404Spjd 1043219089Spjdvoid 1044219089Spjdlibzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp) 1045168404Spjd{ 1046219089Spjd if (cbp->cb_alloc == cbp->cb_used) { 1047219089Spjd size_t newsz; 1048219089Spjd void *ptr; 1049185029Spjd 1050219089Spjd newsz = cbp->cb_alloc ? cbp->cb_alloc * 2 : 64; 1051219089Spjd ptr = zfs_realloc(zhp->zfs_hdl, 1052219089Spjd cbp->cb_handles, cbp->cb_alloc * sizeof (void *), 1053219089Spjd newsz * sizeof (void *)); 1054219089Spjd cbp->cb_handles = ptr; 1055219089Spjd cbp->cb_alloc = newsz; 1056185029Spjd } 1057219089Spjd cbp->cb_handles[cbp->cb_used++] = zhp; 1058168404Spjd} 1059168404Spjd 1060168404Spjdstatic int 1061168404Spjdmount_cb(zfs_handle_t *zhp, void *data) 1062168404Spjd{ 1063219089Spjd get_all_cb_t *cbp = data; 1064168404Spjd 1065219089Spjd if (!(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM)) { 1066168404Spjd zfs_close(zhp); 1067168404Spjd return (0); 1068168404Spjd } 1069168404Spjd 1070185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_NOAUTO) { 1071185029Spjd zfs_close(zhp); 1072185029Spjd return (0); 1073185029Spjd } 1074185029Spjd 1075219089Spjd libzfs_add_handle(cbp, zhp); 1076219089Spjd if (zfs_iter_filesystems(zhp, mount_cb, cbp) != 0) { 1077219089Spjd zfs_close(zhp); 1078219089Spjd return (-1); 1079168404Spjd } 1080219089Spjd return (0); 1081168404Spjd} 1082168404Spjd 1083219089Spjdint 1084219089Spjdlibzfs_dataset_cmp(const void *a, const void *b) 1085168404Spjd{ 1086168404Spjd zfs_handle_t **za = (zfs_handle_t **)a; 1087168404Spjd zfs_handle_t **zb = (zfs_handle_t **)b; 1088168404Spjd char mounta[MAXPATHLEN]; 1089168404Spjd char mountb[MAXPATHLEN]; 1090168404Spjd boolean_t gota, gotb; 1091168404Spjd 1092168404Spjd if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 1093168404Spjd verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 1094168404Spjd sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 1095168404Spjd if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 1096168404Spjd verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 1097168404Spjd sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 1098168404Spjd 1099168404Spjd if (gota && gotb) 1100168404Spjd return (strcmp(mounta, mountb)); 1101168404Spjd 1102168404Spjd if (gota) 1103168404Spjd return (-1); 1104168404Spjd if (gotb) 1105168404Spjd return (1); 1106168404Spjd 1107168404Spjd return (strcmp(zfs_get_name(a), zfs_get_name(b))); 1108168404Spjd} 1109168404Spjd 1110168404Spjd/* 1111168404Spjd * Mount and share all datasets within the given pool. This assumes that no 1112168404Spjd * datasets within the pool are currently mounted. Because users can create 1113168404Spjd * complicated nested hierarchies of mountpoints, we first gather all the 1114168404Spjd * datasets and mountpoints within the pool, and sort them by mountpoint. Once 1115168404Spjd * we have the list of all filesystems, we iterate over them in order and mount 1116168404Spjd * and/or share each one. 1117168404Spjd */ 1118168404Spjd#pragma weak zpool_mount_datasets = zpool_enable_datasets 1119168404Spjdint 1120168404Spjdzpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) 1121168404Spjd{ 1122219089Spjd get_all_cb_t cb = { 0 }; 1123168404Spjd libzfs_handle_t *hdl = zhp->zpool_hdl; 1124168404Spjd zfs_handle_t *zfsp; 1125168404Spjd int i, ret = -1; 1126185029Spjd int *good; 1127168404Spjd 1128168404Spjd /* 1129185029Spjd * Gather all non-snap datasets within the pool. 1130168404Spjd */ 1131185029Spjd if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL) 1132168404Spjd goto out; 1133168404Spjd 1134219089Spjd libzfs_add_handle(&cb, zfsp); 1135185029Spjd if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0) 1136168404Spjd goto out; 1137168404Spjd /* 1138168404Spjd * Sort the datasets by mountpoint. 1139168404Spjd */ 1140219089Spjd qsort(cb.cb_handles, cb.cb_used, sizeof (void *), 1141219089Spjd libzfs_dataset_cmp); 1142168404Spjd 1143168404Spjd /* 1144185029Spjd * And mount all the datasets, keeping track of which ones 1145208472Smm * succeeded or failed. 1146168404Spjd */ 1147208472Smm if ((good = zfs_alloc(zhp->zpool_hdl, 1148208472Smm cb.cb_used * sizeof (int))) == NULL) 1149208472Smm goto out; 1150208472Smm 1151168404Spjd ret = 0; 1152168404Spjd for (i = 0; i < cb.cb_used; i++) { 1153219089Spjd if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0) 1154168404Spjd ret = -1; 1155185029Spjd else 1156185029Spjd good[i] = 1; 1157168404Spjd } 1158168404Spjd 1159185029Spjd /* 1160185029Spjd * Then share all the ones that need to be shared. This needs 1161185029Spjd * to be a separate pass in order to avoid excessive reloading 1162185029Spjd * of the configuration. Good should never be NULL since 1163185029Spjd * zfs_alloc is supposed to exit if memory isn't available. 1164185029Spjd */ 1165185029Spjd for (i = 0; i < cb.cb_used; i++) { 1166219089Spjd if (good[i] && zfs_share(cb.cb_handles[i]) != 0) 1167185029Spjd ret = -1; 1168185029Spjd } 1169185029Spjd 1170185029Spjd free(good); 1171185029Spjd 1172168404Spjdout: 1173168404Spjd for (i = 0; i < cb.cb_used; i++) 1174219089Spjd zfs_close(cb.cb_handles[i]); 1175219089Spjd free(cb.cb_handles); 1176168404Spjd 1177168404Spjd return (ret); 1178168404Spjd} 1179168404Spjd 1180168404Spjdstatic int 1181168404Spjdmountpoint_compare(const void *a, const void *b) 1182168404Spjd{ 1183168404Spjd const char *mounta = *((char **)a); 1184168404Spjd const char *mountb = *((char **)b); 1185168404Spjd 1186168404Spjd return (strcmp(mountb, mounta)); 1187168404Spjd} 1188168404Spjd 1189219089Spjd/* alias for 2002/240 */ 1190219089Spjd#pragma weak zpool_unmount_datasets = zpool_disable_datasets 1191168404Spjd/* 1192168404Spjd * Unshare and unmount all datasets within the given pool. We don't want to 1193168404Spjd * rely on traversing the DSL to discover the filesystems within the pool, 1194168404Spjd * because this may be expensive (if not all of them are mounted), and can fail 1195168404Spjd * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and 1196168404Spjd * gather all the filesystems that are currently mounted. 1197168404Spjd */ 1198168404Spjdint 1199168404Spjdzpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) 1200168404Spjd{ 1201168404Spjd int used, alloc; 1202219089Spjd struct mnttab entry; 1203168404Spjd size_t namelen; 1204168404Spjd char **mountpoints = NULL; 1205168404Spjd zfs_handle_t **datasets = NULL; 1206168404Spjd libzfs_handle_t *hdl = zhp->zpool_hdl; 1207219089Spjd int i; 1208168404Spjd int ret = -1; 1209168404Spjd int flags = (force ? MS_FORCE : 0); 1210168404Spjd 1211168404Spjd namelen = strlen(zhp->zpool_name); 1212168404Spjd 1213219089Spjd rewind(hdl->libzfs_mnttab); 1214168404Spjd used = alloc = 0; 1215219089Spjd while (getmntent(hdl->libzfs_mnttab, &entry) == 0) { 1216168404Spjd /* 1217168404Spjd * Ignore non-ZFS entries. 1218168404Spjd */ 1219219089Spjd if (entry.mnt_fstype == NULL || 1220219089Spjd strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 1221168404Spjd continue; 1222168404Spjd 1223168404Spjd /* 1224168404Spjd * Ignore filesystems not within this pool. 1225168404Spjd */ 1226219089Spjd if (entry.mnt_mountp == NULL || 1227219089Spjd strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 || 1228219089Spjd (entry.mnt_special[namelen] != '/' && 1229219089Spjd entry.mnt_special[namelen] != '\0')) 1230168404Spjd continue; 1231168404Spjd 1232168404Spjd /* 1233168404Spjd * At this point we've found a filesystem within our pool. Add 1234168404Spjd * it to our growing list. 1235168404Spjd */ 1236168404Spjd if (used == alloc) { 1237168404Spjd if (alloc == 0) { 1238168404Spjd if ((mountpoints = zfs_alloc(hdl, 1239168404Spjd 8 * sizeof (void *))) == NULL) 1240168404Spjd goto out; 1241168404Spjd 1242168404Spjd if ((datasets = zfs_alloc(hdl, 1243168404Spjd 8 * sizeof (void *))) == NULL) 1244168404Spjd goto out; 1245168404Spjd 1246168404Spjd alloc = 8; 1247168404Spjd } else { 1248168404Spjd void *ptr; 1249168404Spjd 1250168404Spjd if ((ptr = zfs_realloc(hdl, mountpoints, 1251168404Spjd alloc * sizeof (void *), 1252168404Spjd alloc * 2 * sizeof (void *))) == NULL) 1253168404Spjd goto out; 1254168404Spjd mountpoints = ptr; 1255168404Spjd 1256168404Spjd if ((ptr = zfs_realloc(hdl, datasets, 1257168404Spjd alloc * sizeof (void *), 1258168404Spjd alloc * 2 * sizeof (void *))) == NULL) 1259168404Spjd goto out; 1260168404Spjd datasets = ptr; 1261168404Spjd 1262168404Spjd alloc *= 2; 1263168404Spjd } 1264168404Spjd } 1265168404Spjd 1266168404Spjd if ((mountpoints[used] = zfs_strdup(hdl, 1267219089Spjd entry.mnt_mountp)) == NULL) 1268168404Spjd goto out; 1269168404Spjd 1270168404Spjd /* 1271168404Spjd * This is allowed to fail, in case there is some I/O error. It 1272168404Spjd * is only used to determine if we need to remove the underlying 1273168404Spjd * mountpoint, so failure is not fatal. 1274168404Spjd */ 1275219089Spjd datasets[used] = make_dataset_handle(hdl, entry.mnt_special); 1276168404Spjd 1277168404Spjd used++; 1278168404Spjd } 1279168404Spjd 1280168404Spjd /* 1281168404Spjd * At this point, we have the entire list of filesystems, so sort it by 1282168404Spjd * mountpoint. 1283168404Spjd */ 1284168404Spjd qsort(mountpoints, used, sizeof (char *), mountpoint_compare); 1285168404Spjd 1286168404Spjd /* 1287168404Spjd * Walk through and first unshare everything. 1288168404Spjd */ 1289168404Spjd for (i = 0; i < used; i++) { 1290185029Spjd zfs_share_proto_t *curr_proto; 1291185029Spjd for (curr_proto = share_all_proto; *curr_proto != PROTO_END; 1292185029Spjd curr_proto++) { 1293185029Spjd if (is_shared(hdl, mountpoints[i], *curr_proto) && 1294185029Spjd unshare_one(hdl, mountpoints[i], 1295185029Spjd mountpoints[i], *curr_proto) != 0) 1296185029Spjd goto out; 1297185029Spjd } 1298168404Spjd } 1299168404Spjd 1300168404Spjd /* 1301168404Spjd * Now unmount everything, removing the underlying directories as 1302168404Spjd * appropriate. 1303168404Spjd */ 1304168404Spjd for (i = 0; i < used; i++) { 1305168404Spjd if (unmount_one(hdl, mountpoints[i], flags) != 0) 1306168404Spjd goto out; 1307168404Spjd } 1308168404Spjd 1309168404Spjd for (i = 0; i < used; i++) { 1310168404Spjd if (datasets[i]) 1311168404Spjd remove_mountpoint(datasets[i]); 1312168404Spjd } 1313168404Spjd 1314168404Spjd ret = 0; 1315168404Spjdout: 1316168404Spjd for (i = 0; i < used; i++) { 1317168404Spjd if (datasets[i]) 1318168404Spjd zfs_close(datasets[i]); 1319168404Spjd free(mountpoints[i]); 1320168404Spjd } 1321168404Spjd free(datasets); 1322168404Spjd free(mountpoints); 1323168404Spjd 1324168404Spjd return (ret); 1325168404Spjd} 1326