1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51544Seschrock  * Common Development and Distribution License (the "License").
61544Seschrock  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
221371Seschrock  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27789Sahrens 
28789Sahrens /*
29789Sahrens  * Routines to manage ZFS mounts.  We separate all the nasty routines that have
30789Sahrens  * to deal with the OS.  The main entry points are:
31789Sahrens  *
32789Sahrens  * 	zfs_is_mounted()
33789Sahrens  * 	zfs_mount()
34789Sahrens  * 	zfs_unmount()
35789Sahrens  * 	zfs_unmountall()
36789Sahrens  *
37789Sahrens  * These functions are used by mount and unmount, and when changing a
38789Sahrens  * filesystem's mountpoint.  This file also contains the functions used to
39789Sahrens  * manage sharing filesystems via NFS:
40789Sahrens  *
41789Sahrens  * 	zfs_is_shared()
42789Sahrens  * 	zfs_share()
43789Sahrens  * 	zfs_unshare()
44789Sahrens  * 	zfs_unshareall()
452474Seschrock  *
462474Seschrock  * The following functions are available for pool consumers, and will
472474Seschrock  * mount/unmount (and share/unshare) all datasets within pool:
482474Seschrock  *
492474Seschrock  * 	zpool_mount_datasets()
502474Seschrock  * 	zpool_unmount_datasets()
51789Sahrens  */
52789Sahrens 
53789Sahrens #include <dirent.h>
54789Sahrens #include <errno.h>
55789Sahrens #include <libgen.h>
56789Sahrens #include <libintl.h>
57789Sahrens #include <stdio.h>
58789Sahrens #include <stdlib.h>
59789Sahrens #include <strings.h>
60789Sahrens #include <unistd.h>
61789Sahrens #include <zone.h>
62789Sahrens #include <sys/mntent.h>
63789Sahrens #include <sys/mnttab.h>
64789Sahrens #include <sys/mount.h>
65789Sahrens #include <sys/stat.h>
66789Sahrens 
67789Sahrens #include <libzfs.h>
68789Sahrens 
69789Sahrens #include "libzfs_impl.h"
70789Sahrens 
71789Sahrens /*
722082Seschrock  * Search the sharetab for the given mountpoint, returning true if it is found.
73789Sahrens  */
742082Seschrock static boolean_t
752082Seschrock is_shared(libzfs_handle_t *hdl, const char *mountpoint)
76789Sahrens {
77789Sahrens 	char buf[MAXPATHLEN], *tab;
78789Sahrens 
792082Seschrock 	if (hdl->libzfs_sharetab == NULL)
80789Sahrens 		return (0);
81789Sahrens 
822082Seschrock 	(void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
83789Sahrens 
842082Seschrock 	while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
85789Sahrens 
86789Sahrens 		/* the mountpoint is the first entry on each line */
87789Sahrens 		if ((tab = strchr(buf, '\t')) != NULL) {
88789Sahrens 			*tab = '\0';
89789Sahrens 			if (strcmp(buf, mountpoint) == 0)
902082Seschrock 				return (B_TRUE);
91789Sahrens 		}
92789Sahrens 	}
93789Sahrens 
942082Seschrock 	return (B_FALSE);
95789Sahrens }
96789Sahrens 
97789Sahrens /*
982082Seschrock  * Returns true if the specified directory is empty.  If we can't open the
992082Seschrock  * directory at all, return true so that the mount can fail with a more
100789Sahrens  * informative error message.
101789Sahrens  */
1022082Seschrock static boolean_t
103789Sahrens dir_is_empty(const char *dirname)
104789Sahrens {
105789Sahrens 	DIR *dirp;
106789Sahrens 	struct dirent64 *dp;
107789Sahrens 
108789Sahrens 	if ((dirp = opendir(dirname)) == NULL)
1092082Seschrock 		return (B_TRUE);
110789Sahrens 
111789Sahrens 	while ((dp = readdir64(dirp)) != NULL) {
112789Sahrens 
113789Sahrens 		if (strcmp(dp->d_name, ".") == 0 ||
114789Sahrens 		    strcmp(dp->d_name, "..") == 0)
115789Sahrens 			continue;
116789Sahrens 
117789Sahrens 		(void) closedir(dirp);
1182082Seschrock 		return (B_FALSE);
119789Sahrens 	}
120789Sahrens 
121789Sahrens 	(void) closedir(dirp);
1222082Seschrock 	return (B_TRUE);
123789Sahrens }
124789Sahrens 
125789Sahrens /*
126789Sahrens  * Checks to see if the mount is active.  If the filesystem is mounted, we fill
127789Sahrens  * in 'where' with the current mountpoint, and return 1.  Otherwise, we return
128789Sahrens  * 0.
129789Sahrens  */
1302082Seschrock boolean_t
131789Sahrens zfs_is_mounted(zfs_handle_t *zhp, char **where)
132789Sahrens {
133789Sahrens 	struct mnttab search = { 0 }, entry;
134789Sahrens 
135789Sahrens 	/*
136789Sahrens 	 * Search for the entry in /etc/mnttab.  We don't bother getting the
137789Sahrens 	 * mountpoint, as we can just search for the special device.  This will
138789Sahrens 	 * also let us find mounts when the mountpoint is 'legacy'.
139789Sahrens 	 */
140789Sahrens 	search.mnt_special = (char *)zfs_get_name(zhp);
1411407Snd150628 	search.mnt_fstype = MNTTYPE_ZFS;
142789Sahrens 
1432082Seschrock 	rewind(zhp->zfs_hdl->libzfs_mnttab);
1442082Seschrock 	if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0)
1452082Seschrock 		return (B_FALSE);
146789Sahrens 
147789Sahrens 	if (where != NULL)
1482082Seschrock 		*where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
149789Sahrens 
1502082Seschrock 	return (B_TRUE);
151789Sahrens }
152789Sahrens 
153789Sahrens /*
154789Sahrens  * Mount the given filesystem.
155789Sahrens  */
156789Sahrens int
157789Sahrens zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
158789Sahrens {
159789Sahrens 	struct stat buf;
160789Sahrens 	char mountpoint[ZFS_MAXPROPLEN];
161789Sahrens 	char mntopts[MNT_LINE_MAX];
1622082Seschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
163789Sahrens 
164789Sahrens 	if (options == NULL)
165789Sahrens 		mntopts[0] = '\0';
166789Sahrens 	else
167789Sahrens 		(void) strlcpy(mntopts, options, sizeof (mntopts));
168789Sahrens 
169789Sahrens 	/* ignore non-filesystems */
170789Sahrens 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
1712082Seschrock 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0)
172789Sahrens 		return (0);
173789Sahrens 
174789Sahrens 	/* return success if there is no mountpoint set */
175789Sahrens 	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
176789Sahrens 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
177789Sahrens 		return (0);
178789Sahrens 
179789Sahrens 	/*
180789Sahrens 	 * If the 'zoned' property is set, and we're in the global zone, simply
181789Sahrens 	 * return success.
182789Sahrens 	 */
1832082Seschrock 	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
1842082Seschrock 	    getzoneid() == GLOBAL_ZONEID)
1852082Seschrock 		return (0);
186789Sahrens 
187789Sahrens 	/* Create the directory if it doesn't already exist */
188789Sahrens 	if (lstat(mountpoint, &buf) != 0) {
189789Sahrens 		if (mkdirp(mountpoint, 0755) != 0) {
1902082Seschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1912082Seschrock 			    "failed to create mountpoint"));
1922082Seschrock 			return (zfs_error(hdl, EZFS_MOUNTFAILED,
1932082Seschrock 			    dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
1942082Seschrock 			    mountpoint));
195789Sahrens 		}
196789Sahrens 	}
197789Sahrens 
198789Sahrens 	/*
199789Sahrens 	 * Determine if the mountpoint is empty.  If so, refuse to perform the
200789Sahrens 	 * mount.  We don't perform this check if MS_OVERLAY is specified, which
201789Sahrens 	 * would defeat the point.  We also avoid this check if 'remount' is
202789Sahrens 	 * specified.
203789Sahrens 	 */
204789Sahrens 	if ((flags & MS_OVERLAY) == 0 &&
205789Sahrens 	    strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
206789Sahrens 	    !dir_is_empty(mountpoint)) {
2072082Seschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2082082Seschrock 		    "directory is not empty"));
2092082Seschrock 		return (zfs_error(hdl, EZFS_MOUNTFAILED,
2102082Seschrock 		    dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint));
211789Sahrens 	}
212789Sahrens 
213789Sahrens 	/* perform the mount */
214789Sahrens 	if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags,
215789Sahrens 	    MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
216789Sahrens 		/*
217789Sahrens 		 * Generic errors are nasty, but there are just way too many
218789Sahrens 		 * from mount(), and they're well-understood.  We pick a few
219789Sahrens 		 * common ones to improve upon.
220789Sahrens 		 */
2212082Seschrock 		if (errno == EBUSY)
2222082Seschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2232082Seschrock 			    "mountpoint or dataset is busy"));
2242082Seschrock 		else
2252082Seschrock 			zfs_error_aux(hdl, strerror(errno));
2262082Seschrock 
2272082Seschrock 		return (zfs_error(hdl, EZFS_MOUNTFAILED,
2282082Seschrock 		    dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
2292082Seschrock 		    zhp->zfs_name));
230789Sahrens 	}
231789Sahrens 
232789Sahrens 	return (0);
233789Sahrens }
234789Sahrens 
235789Sahrens /*
2362474Seschrock  * Unmount a single filesystem.
2372474Seschrock  */
2382474Seschrock static int
2392474Seschrock unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
2402474Seschrock {
2412474Seschrock 	if (umount2(mountpoint, flags) != 0) {
2422474Seschrock 		zfs_error_aux(hdl, strerror(errno));
2432474Seschrock 		return (zfs_error(hdl, EZFS_UMOUNTFAILED,
2442474Seschrock 		    dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
2452474Seschrock 		    mountpoint));
2462474Seschrock 	}
2472474Seschrock 
2482474Seschrock 	return (0);
2492474Seschrock }
2502474Seschrock 
2512474Seschrock /*
252789Sahrens  * Unmount the given filesystem.
253789Sahrens  */
254789Sahrens int
255789Sahrens zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
256789Sahrens {
257789Sahrens 	struct mnttab search = { 0 }, entry;
258789Sahrens 
259789Sahrens 	/* check to see if need to unmount the filesystem */
2602474Seschrock 	search.mnt_special = zhp->zfs_name;
2611407Snd150628 	search.mnt_fstype = MNTTYPE_ZFS;
2622082Seschrock 	rewind(zhp->zfs_hdl->libzfs_mnttab);
263789Sahrens 	if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
2642082Seschrock 	    getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
265789Sahrens 
266789Sahrens 		if (mountpoint == NULL)
267789Sahrens 			mountpoint = entry.mnt_mountp;
268789Sahrens 
269789Sahrens 		/*
2702474Seschrock 		 * Unshare and unmount the filesystem
271789Sahrens 		 */
2722474Seschrock 		if (zfs_unshare(zhp, mountpoint) != 0 ||
2732474Seschrock 		    unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0)
274789Sahrens 			return (-1);
275789Sahrens 	}
276789Sahrens 
277789Sahrens 	return (0);
278789Sahrens }
279789Sahrens 
280789Sahrens /*
281789Sahrens  * Unmount this filesystem and any children inheriting the mountpoint property.
282789Sahrens  * To do this, just act like we're changing the mountpoint property, but don't
283789Sahrens  * remount the filesystems afterwards.
284789Sahrens  */
285789Sahrens int
286789Sahrens zfs_unmountall(zfs_handle_t *zhp, int flags)
287789Sahrens {
288789Sahrens 	prop_changelist_t *clp;
289789Sahrens 	int ret;
290789Sahrens 
291789Sahrens 	clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags);
292789Sahrens 	if (clp == NULL)
293789Sahrens 		return (-1);
294789Sahrens 
295789Sahrens 	ret = changelist_prefix(clp);
296789Sahrens 	changelist_free(clp);
297789Sahrens 
298789Sahrens 	return (ret);
299789Sahrens }
300789Sahrens 
301789Sahrens /*
302789Sahrens  * Check to see if the filesystem is currently shared.
303789Sahrens  */
3042082Seschrock boolean_t
305789Sahrens zfs_is_shared(zfs_handle_t *zhp, char **where)
306789Sahrens {
307789Sahrens 	char *mountpoint;
308789Sahrens 
309789Sahrens 	if (!zfs_is_mounted(zhp, &mountpoint))
3102082Seschrock 		return (B_FALSE);
311789Sahrens 
3122082Seschrock 	if (is_shared(zhp->zfs_hdl, mountpoint)) {
313789Sahrens 		if (where != NULL)
314789Sahrens 			*where = mountpoint;
315789Sahrens 		else
316789Sahrens 			free(mountpoint);
3172082Seschrock 		return (B_TRUE);
318789Sahrens 	} else {
319789Sahrens 		free(mountpoint);
3202082Seschrock 		return (B_FALSE);
321789Sahrens 	}
322789Sahrens }
323789Sahrens 
324789Sahrens /*
325789Sahrens  * Share the given filesystem according to the options in 'sharenfs'.  We rely
326789Sahrens  * on share(1M) to the dirty work for us.
327789Sahrens  */
328789Sahrens int
329789Sahrens zfs_share(zfs_handle_t *zhp)
330789Sahrens {
331789Sahrens 	char mountpoint[ZFS_MAXPROPLEN];
332789Sahrens 	char shareopts[ZFS_MAXPROPLEN];
333789Sahrens 	char buf[MAXPATHLEN];
334789Sahrens 	FILE *fp;
3352082Seschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
336789Sahrens 
337789Sahrens 	/* ignore non-filesystems */
338789Sahrens 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM)
339789Sahrens 		return (0);
340789Sahrens 
341789Sahrens 	/* return success if there is no mountpoint set */
342789Sahrens 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
3432082Seschrock 	    mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0 ||
344789Sahrens 	    strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
345789Sahrens 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
346789Sahrens 		return (0);
347789Sahrens 
348789Sahrens 	/* return success if there are no share options */
349789Sahrens 	if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts),
3502082Seschrock 	    NULL, NULL, 0, B_FALSE) != 0 ||
351789Sahrens 	    strcmp(shareopts, "off") == 0)
352789Sahrens 		return (0);
353789Sahrens 
354789Sahrens 	/*
355789Sahrens 	 * If the 'zoned' property is set, simply return success since:
356789Sahrens 	 * 1. in a global zone, a dataset should not be shared if it's
357789Sahrens 	 *    managed in a local zone.
358789Sahrens 	 * 2. in a local zone, NFS server is not available.
359789Sahrens 	 */
360789Sahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
361789Sahrens 		return (0);
362789Sahrens 	}
363789Sahrens 
364789Sahrens 	/*
365789Sahrens 	 * Invoke the share(1M) command.  We always do this, even if it's
366789Sahrens 	 * currently shared, as the options may have changed.
367789Sahrens 	 */
368789Sahrens 	if (strcmp(shareopts, "on") == 0)
369789Sahrens 		(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
370789Sahrens 		    "-F nfs \"%s\" 2>&1", mountpoint);
371789Sahrens 	else
372789Sahrens 		(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
373789Sahrens 		    "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts,
374789Sahrens 		    mountpoint);
375789Sahrens 
3762082Seschrock 	if ((fp = popen(buf, "r")) == NULL)
3772082Seschrock 		return (zfs_error(hdl, EZFS_SHAREFAILED,
3782082Seschrock 		    dgettext(TEXT_DOMAIN, "cannot share '%s'"),
3792082Seschrock 		    zfs_get_name(zhp)));
380789Sahrens 
381789Sahrens 	/*
382789Sahrens 	 * share(1M) should only produce output if there is some kind
383789Sahrens 	 * of error.  All output begins with "share_nfs: ", so we trim
384789Sahrens 	 * this off to get to the real error.
385789Sahrens 	 */
386789Sahrens 	if (fgets(buf, sizeof (buf), fp) != NULL) {
387789Sahrens 		char *colon = strchr(buf, ':');
388789Sahrens 
389789Sahrens 		while (buf[strlen(buf) - 1] == '\n')
390789Sahrens 			buf[strlen(buf) - 1] = '\0';
391789Sahrens 
3922082Seschrock 		if (colon != NULL)
3932082Seschrock 			zfs_error_aux(hdl, colon + 2);
3942082Seschrock 
3952082Seschrock 		(void) zfs_error(hdl, EZFS_SHAREFAILED,
3962474Seschrock 		    dgettext(TEXT_DOMAIN, "cannot share '%s'"),
3972474Seschrock 		    zfs_get_name(zhp));
398789Sahrens 
399789Sahrens 		verify(pclose(fp) != 0);
400789Sahrens 		return (-1);
401789Sahrens 	}
402789Sahrens 
403789Sahrens 	verify(pclose(fp) == 0);
404789Sahrens 
405789Sahrens 	return (0);
406789Sahrens }
407789Sahrens 
408789Sahrens /*
4092474Seschrock  * Unshare a filesystem by mountpoint.
4102474Seschrock  */
4112474Seschrock static int
4122474Seschrock unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
4132474Seschrock {
4142474Seschrock 	char buf[MAXPATHLEN];
4152474Seschrock 	FILE *fp;
4162474Seschrock 
4172474Seschrock 	(void) snprintf(buf, sizeof (buf),
4182474Seschrock 	    "/usr/sbin/unshare  \"%s\" 2>&1",
4192474Seschrock 	    mountpoint);
4202474Seschrock 
4212474Seschrock 	if ((fp = popen(buf, "r")) == NULL)
4222474Seschrock 		return (zfs_error(hdl, EZFS_UNSHAREFAILED,
4232474Seschrock 		    dgettext(TEXT_DOMAIN,
4242474Seschrock 		    "cannot unshare '%s'"), name));
4252474Seschrock 
4262474Seschrock 	/*
4272474Seschrock 	 * unshare(1M) should only produce output if there is
4282474Seschrock 	 * some kind of error.  All output begins with "unshare
4292474Seschrock 	 * nfs: ", so we trim this off to get to the real error.
4302474Seschrock 	 */
4312474Seschrock 	if (fgets(buf, sizeof (buf), fp) != NULL) {
4322474Seschrock 		char *colon = strchr(buf, ':');
4332474Seschrock 
4342474Seschrock 		while (buf[strlen(buf) - 1] == '\n')
4352474Seschrock 			buf[strlen(buf) - 1] = '\0';
4362474Seschrock 
4372474Seschrock 		if (colon != NULL)
4382474Seschrock 			zfs_error_aux(hdl, colon + 2);
4392474Seschrock 
4402474Seschrock 		verify(pclose(fp) != 0);
4412474Seschrock 
4422474Seschrock 		return (zfs_error(hdl, EZFS_UNSHAREFAILED,
4432474Seschrock 		    dgettext(TEXT_DOMAIN,
4442474Seschrock 		    "cannot unshare '%s'"), name));
4452474Seschrock 	}
4462474Seschrock 
4472474Seschrock 	verify(pclose(fp) == 0);
4482474Seschrock 
4492474Seschrock 	return (0);
4502474Seschrock }
4512474Seschrock 
4522474Seschrock /*
453789Sahrens  * Unshare the given filesystem.
454789Sahrens  */
455789Sahrens int
456789Sahrens zfs_unshare(zfs_handle_t *zhp, const char *mountpoint)
457789Sahrens {
458789Sahrens 	struct mnttab search = { 0 }, entry;
459789Sahrens 
460789Sahrens 	/* check to see if need to unmount the filesystem */
461789Sahrens 	search.mnt_special = (char *)zfs_get_name(zhp);
4621407Snd150628 	search.mnt_fstype = MNTTYPE_ZFS;
4632082Seschrock 	rewind(zhp->zfs_hdl->libzfs_mnttab);
464789Sahrens 	if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
4652082Seschrock 	    getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
466789Sahrens 
467789Sahrens 		if (mountpoint == NULL)
468789Sahrens 			mountpoint = entry.mnt_mountp;
469789Sahrens 
4702474Seschrock 		if (is_shared(zhp->zfs_hdl, mountpoint) &&
4712474Seschrock 		    unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0)
4722474Seschrock 			return (-1);
473789Sahrens 	}
474789Sahrens 
475789Sahrens 	return (0);
476789Sahrens }
477789Sahrens 
478789Sahrens /*
479789Sahrens  * Same as zfs_unmountall(), but for unshares.
480789Sahrens  */
481789Sahrens int
482789Sahrens zfs_unshareall(zfs_handle_t *zhp)
483789Sahrens {
484789Sahrens 	prop_changelist_t *clp;
485789Sahrens 	int ret;
486789Sahrens 
487789Sahrens 	clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0);
488789Sahrens 	if (clp == NULL)
489789Sahrens 		return (-1);
490789Sahrens 
491789Sahrens 	ret = changelist_unshare(clp);
492789Sahrens 	changelist_free(clp);
493789Sahrens 
494789Sahrens 	return (ret);
495789Sahrens }
496789Sahrens 
497789Sahrens /*
498789Sahrens  * Remove the mountpoint associated with the current dataset, if necessary.
499789Sahrens  * We only remove the underlying directory if:
500789Sahrens  *
501789Sahrens  *	- The mountpoint is not 'none' or 'legacy'
502789Sahrens  *	- The mountpoint is non-empty
503789Sahrens  *	- The mountpoint is the default or inherited
504789Sahrens  *	- The 'zoned' property is set, or we're in a local zone
505789Sahrens  *
506789Sahrens  * Any other directories we leave alone.
507789Sahrens  */
508789Sahrens void
509789Sahrens remove_mountpoint(zfs_handle_t *zhp)
510789Sahrens {
511789Sahrens 	char mountpoint[ZFS_MAXPROPLEN];
512789Sahrens 	char source[ZFS_MAXNAMELEN];
513789Sahrens 	zfs_source_t sourcetype;
5142082Seschrock 	int zoneid = getzoneid();
515789Sahrens 
516789Sahrens 	/* ignore non-filesystems */
517789Sahrens 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
518789Sahrens 	    sizeof (mountpoint), &sourcetype, source, sizeof (source),
5192082Seschrock 	    B_FALSE) != 0)
520789Sahrens 		return;
521789Sahrens 
522789Sahrens 	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 &&
523789Sahrens 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
524789Sahrens 	    (sourcetype == ZFS_SRC_DEFAULT ||
525789Sahrens 	    sourcetype == ZFS_SRC_INHERITED) &&
526789Sahrens 	    (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) ||
5272082Seschrock 	    zoneid != GLOBAL_ZONEID)) {
528789Sahrens 
529789Sahrens 		/*
530789Sahrens 		 * Try to remove the directory, silently ignoring any errors.
531789Sahrens 		 * The filesystem may have since been removed or moved around,
532789Sahrens 		 * and this isn't really useful to the administrator in any
533789Sahrens 		 * way.
534789Sahrens 		 */
535789Sahrens 		(void) rmdir(mountpoint);
536789Sahrens 	}
537789Sahrens }
5382474Seschrock 
5392474Seschrock /*
5402474Seschrock  * Mount and share all datasets within the given pool.  This assumes that no
5412474Seschrock  * datasets within the pool are currently mounted.  Because users can create
5422474Seschrock  * complicated nested hierarchies of mountpoints, we first gather all the
5432474Seschrock  * datasets and mountpoints within the pool, and sort them by mountpoint.  Once
5442474Seschrock  * we have the list of all filesystems, we iterate over them in order and mount
5452474Seschrock  * and/or share each one.
5462474Seschrock  */
5472474Seschrock typedef struct mount_cbdata {
5482474Seschrock 	zfs_handle_t	**cb_datasets;
5492474Seschrock 	int 		cb_used;
5502474Seschrock 	int		cb_alloc;
5512474Seschrock } mount_cbdata_t;
5522474Seschrock 
5532474Seschrock static int
5542474Seschrock mount_cb(zfs_handle_t *zhp, void *data)
5552474Seschrock {
5562474Seschrock 	mount_cbdata_t *cbp = data;
5572474Seschrock 
5582474Seschrock 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
5592474Seschrock 		zfs_close(zhp);
5602474Seschrock 		return (0);
5612474Seschrock 	}
5622474Seschrock 
5632474Seschrock 	if (cbp->cb_alloc == cbp->cb_used) {
5642474Seschrock 		zfs_handle_t **datasets;
5652474Seschrock 
5662474Seschrock 		if ((datasets = zfs_alloc(zhp->zfs_hdl, cbp->cb_alloc * 2 *
5672474Seschrock 		    sizeof (void *))) == NULL)
5682474Seschrock 			return (-1);
5692474Seschrock 
5702474Seschrock 		(void) memcpy(cbp->cb_datasets, datasets,
5712474Seschrock 		    cbp->cb_alloc * sizeof (void *));
5722474Seschrock 		free(cbp->cb_datasets);
5732474Seschrock 		cbp->cb_datasets = datasets;
5742474Seschrock 	}
5752474Seschrock 
5762474Seschrock 	cbp->cb_datasets[cbp->cb_used++] = zhp;
5772474Seschrock 	return (0);
5782474Seschrock }
5792474Seschrock 
5802474Seschrock static int
5812474Seschrock dataset_compare(const void *a, const void *b)
5822474Seschrock {
5832474Seschrock 	zfs_handle_t **za = (zfs_handle_t **)a;
5842474Seschrock 	zfs_handle_t **zb = (zfs_handle_t **)b;
5852474Seschrock 	char mounta[MAXPATHLEN];
5862474Seschrock 	char mountb[MAXPATHLEN];
5872474Seschrock 
5882474Seschrock 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
5892474Seschrock 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
5902474Seschrock 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
5912474Seschrock 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
5922474Seschrock 
5932474Seschrock 	return (strcmp(mounta, mountb));
5942474Seschrock }
5952474Seschrock 
5962474Seschrock int
597*2500Seschrock zpool_mount_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
5982474Seschrock {
5992474Seschrock 	mount_cbdata_t cb = { 0 };
6002474Seschrock 	libzfs_handle_t *hdl = zhp->zpool_hdl;
6012474Seschrock 	zfs_handle_t *zfsp;
6022474Seschrock 	int i, ret = -1;
6032474Seschrock 
6042474Seschrock 	/*
6052474Seschrock 	 * Gather all datasets within the pool.
6062474Seschrock 	 */
6072474Seschrock 	if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
6082474Seschrock 		return (-1);
6092474Seschrock 	cb.cb_alloc = 4;
6102474Seschrock 
6112474Seschrock 	if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL)
6122474Seschrock 		goto out;
6132474Seschrock 
6142474Seschrock 	cb.cb_datasets[0] = zfsp;
6152474Seschrock 	cb.cb_used = 1;
6162474Seschrock 
6172474Seschrock 	if (zfs_iter_children(zfsp, mount_cb, &cb) != 0)
6182474Seschrock 		goto out;
6192474Seschrock 
6202474Seschrock 	/*
6212474Seschrock 	 * Sort the datasets by mountpoint.
6222474Seschrock 	 */
6232474Seschrock 	qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_compare);
6242474Seschrock 
6252474Seschrock 	/*
6262474Seschrock 	 * And mount all the datasets.
6272474Seschrock 	 */
6282474Seschrock 	ret = 0;
6292474Seschrock 	for (i = 0; i < cb.cb_used; i++) {
630*2500Seschrock 		if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 ||
6312474Seschrock 		    zfs_share(cb.cb_datasets[i]) != 0)
6322474Seschrock 			ret = -1;
6332474Seschrock 	}
6342474Seschrock 
6352474Seschrock out:
6362474Seschrock 	for (i = 0; i < cb.cb_used; i++)
6372474Seschrock 		zfs_close(cb.cb_datasets[i]);
6382474Seschrock 	free(cb.cb_datasets);
6392474Seschrock 
6402474Seschrock 	return (ret);
6412474Seschrock }
6422474Seschrock 
6432474Seschrock /*
6442474Seschrock  * Unshare and unmount all datasets within the given pool.  We don't want to
6452474Seschrock  * rely on traversing the DSL to discover the filesystems within the pool,
6462474Seschrock  * because this may be expensive (if not all of them are mounted), and can fail
6472474Seschrock  * arbitrarily (on I/O error, for example).  Instead, we walk /etc/mnttab and
6482474Seschrock  * gather all the filesystems that are currently mounted.
6492474Seschrock  */
6502474Seschrock static int
6512474Seschrock mountpoint_compare(const void *a, const void *b)
6522474Seschrock {
6532474Seschrock 	const char *mounta = *((char **)a);
6542474Seschrock 	const char *mountb = *((char **)b);
6552474Seschrock 
6562474Seschrock 	return (strcmp(mountb, mounta));
6572474Seschrock }
6582474Seschrock 
6592474Seschrock int
6602474Seschrock zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force)
6612474Seschrock {
6622474Seschrock 	int used, alloc;
6632474Seschrock 	struct mnttab entry;
6642474Seschrock 	size_t namelen;
6652474Seschrock 	char **mountpoints = NULL;
6662474Seschrock 	zfs_handle_t **datasets = NULL;
6672474Seschrock 	libzfs_handle_t *hdl = zhp->zpool_hdl;
6682474Seschrock 	int i;
6692474Seschrock 	int ret = -1;
6702474Seschrock 	int flags = (force ? MS_FORCE : 0);
6712474Seschrock 
6722474Seschrock 	namelen = strlen(zhp->zpool_name);
6732474Seschrock 
6742474Seschrock 	rewind(hdl->libzfs_mnttab);
6752474Seschrock 	used = alloc = 0;
6762474Seschrock 	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
6772474Seschrock 		/*
6782474Seschrock 		 * Ignore non-ZFS entries.
6792474Seschrock 		 */
6802474Seschrock 		if (entry.mnt_fstype == NULL ||
6812474Seschrock 		    strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
6822474Seschrock 			continue;
6832474Seschrock 
6842474Seschrock 		/*
6852474Seschrock 		 * Ignore filesystems not within this pool.
6862474Seschrock 		 */
6872474Seschrock 		if (entry.mnt_mountp == NULL ||
6882474Seschrock 		    strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
6892474Seschrock 		    (entry.mnt_special[namelen] != '/' &&
6902474Seschrock 		    entry.mnt_special[namelen] != '\0'))
6912474Seschrock 			continue;
6922474Seschrock 
6932474Seschrock 		/*
6942474Seschrock 		 * At this point we've found a filesystem within our pool.  Add
6952474Seschrock 		 * it to our growing list.
6962474Seschrock 		 */
6972474Seschrock 		if (used == alloc) {
6982474Seschrock 			if (alloc == 0) {
6992474Seschrock 				if ((mountpoints = zfs_alloc(hdl,
7002474Seschrock 				    8 * sizeof (void *))) == NULL)
7012474Seschrock 					goto out;
7022474Seschrock 
7032474Seschrock 				if ((datasets = zfs_alloc(hdl,
7042474Seschrock 				    8 * sizeof (void *))) == NULL)
7052474Seschrock 					goto out;
7062474Seschrock 
7072474Seschrock 				alloc = 8;
7082474Seschrock 			} else {
7092474Seschrock 				char **dest;
7102474Seschrock 
7112474Seschrock 				if ((dest = zfs_alloc(hdl,
7122474Seschrock 				    alloc * 2 * sizeof (void *))) == NULL)
7132474Seschrock 					goto out;
7142474Seschrock 				(void) memcpy(dest, mountpoints,
7152474Seschrock 				    alloc * sizeof (void *));
7162474Seschrock 				free(mountpoints);
7172474Seschrock 				mountpoints = dest;
7182474Seschrock 
7192474Seschrock 				if ((dest = zfs_alloc(hdl,
7202474Seschrock 				    alloc * 2 * sizeof (void *))) == NULL)
7212474Seschrock 					goto out;
7222474Seschrock 				(void) memcpy(dest, datasets,
7232474Seschrock 				    alloc * sizeof (void *));
7242474Seschrock 				free(datasets);
7252474Seschrock 				datasets = (zfs_handle_t **)dest;
7262474Seschrock 
7272474Seschrock 				alloc *= 2;
7282474Seschrock 			}
7292474Seschrock 		}
7302474Seschrock 
7312474Seschrock 		if ((mountpoints[used] = zfs_strdup(hdl,
7322474Seschrock 		    entry.mnt_mountp)) == NULL)
7332474Seschrock 			goto out;
7342474Seschrock 
7352474Seschrock 		/*
7362474Seschrock 		 * This is allowed to fail, in case there is some I/O error.  It
7372474Seschrock 		 * is only used to determine if we need to remove the underlying
7382474Seschrock 		 * mountpoint, so failure is not fatal.
7392474Seschrock 		 */
7402474Seschrock 		datasets[used] = make_dataset_handle(hdl, entry.mnt_special);
7412474Seschrock 
7422474Seschrock 		used++;
7432474Seschrock 	}
7442474Seschrock 
7452474Seschrock 	/*
7462474Seschrock 	 * At this point, we have the entire list of filesystems, so sort it by
7472474Seschrock 	 * mountpoint.
7482474Seschrock 	 */
7492474Seschrock 	qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
7502474Seschrock 
7512474Seschrock 	/*
7522474Seschrock 	 * Walk through and first unshare everything.
7532474Seschrock 	 */
7542474Seschrock 	for (i = 0; i < used; i++) {
7552474Seschrock 		if (is_shared(hdl, mountpoints[i]) &&
7562474Seschrock 		    unshare_one(hdl, datasets[i] ? datasets[i]->zfs_name :
7572474Seschrock 		    mountpoints[i], mountpoints[i]) != 0)
7582474Seschrock 			goto out;
7592474Seschrock 	}
7602474Seschrock 
7612474Seschrock 	/*
7622474Seschrock 	 * Now unmount everything, removing the underlying directories as
7632474Seschrock 	 * appropriate.
7642474Seschrock 	 */
7652474Seschrock 	for (i = 0; i < used; i++) {
7662474Seschrock 		if (unmount_one(hdl, mountpoints[i], flags) != 0)
7672474Seschrock 			goto out;
7682474Seschrock 
7692474Seschrock 		if (datasets[i])
7702474Seschrock 			remove_mountpoint(datasets[i]);
7712474Seschrock 	}
7722474Seschrock 
7732474Seschrock 	ret = 0;
7742474Seschrock out:
7752474Seschrock 	for (i = 0; i < used; i++) {
7762474Seschrock 		if (datasets[i])
7772474Seschrock 			zfs_close(datasets[i]);
7782474Seschrock 		free(mountpoints[i]);
7792474Seschrock 	}
7802474Seschrock 	free(datasets);
7812474Seschrock 	free(mountpoints);
7822474Seschrock 
7832474Seschrock 	return (ret);
7842474Seschrock }
785