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