xref: /illumos-gate/usr/src/lib/libzfs/common/libzfs_mount.c (revision 0173c38a73f34277e0c97a19fedfd25d81ba8380)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Routines to manage ZFS mounts.  We separate all the nasty routines that have
30  * to deal with the OS.  The main entry points are:
31  *
32  * 	zfs_is_mounted()
33  * 	zfs_mount()
34  * 	zfs_unmount()
35  * 	zfs_unmountall()
36  *
37  * These functions are used by mount and unmount, and when changing a
38  * filesystem's mountpoint.  This file also contains the functions used to
39  * manage sharing filesystems via NFS:
40  *
41  * 	zfs_is_shared()
42  * 	zfs_share()
43  * 	zfs_unshare()
44  * 	zfs_unshareall()
45  *
46  * The following functions are available for pool consumers, and will
47  * mount/unmount (and share/unshare) all datasets within pool:
48  *
49  * 	zpool_mount_datasets()
50  * 	zpool_unmount_datasets()
51  */
52 
53 #include <dirent.h>
54 #include <errno.h>
55 #include <libgen.h>
56 #include <libintl.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <strings.h>
60 #include <unistd.h>
61 #include <zone.h>
62 #include <sys/mntent.h>
63 #include <sys/mnttab.h>
64 #include <sys/mount.h>
65 #include <sys/stat.h>
66 
67 #include <libzfs.h>
68 
69 #include "libzfs_impl.h"
70 
71 /*
72  * Search the sharetab for the given mountpoint, returning true if it is found.
73  */
74 static boolean_t
75 is_shared(libzfs_handle_t *hdl, const char *mountpoint)
76 {
77 	char buf[MAXPATHLEN], *tab;
78 
79 	if (hdl->libzfs_sharetab == NULL)
80 		return (0);
81 
82 	(void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
83 
84 	while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
85 
86 		/* the mountpoint is the first entry on each line */
87 		if ((tab = strchr(buf, '\t')) != NULL) {
88 			*tab = '\0';
89 			if (strcmp(buf, mountpoint) == 0)
90 				return (B_TRUE);
91 		}
92 	}
93 
94 	return (B_FALSE);
95 }
96 
97 /*
98  * Returns true if the specified directory is empty.  If we can't open the
99  * directory at all, return true so that the mount can fail with a more
100  * informative error message.
101  */
102 static boolean_t
103 dir_is_empty(const char *dirname)
104 {
105 	DIR *dirp;
106 	struct dirent64 *dp;
107 
108 	if ((dirp = opendir(dirname)) == NULL)
109 		return (B_TRUE);
110 
111 	while ((dp = readdir64(dirp)) != NULL) {
112 
113 		if (strcmp(dp->d_name, ".") == 0 ||
114 		    strcmp(dp->d_name, "..") == 0)
115 			continue;
116 
117 		(void) closedir(dirp);
118 		return (B_FALSE);
119 	}
120 
121 	(void) closedir(dirp);
122 	return (B_TRUE);
123 }
124 
125 /*
126  * Checks to see if the mount is active.  If the filesystem is mounted, we fill
127  * in 'where' with the current mountpoint, and return 1.  Otherwise, we return
128  * 0.
129  */
130 boolean_t
131 zfs_is_mounted(zfs_handle_t *zhp, char **where)
132 {
133 	struct mnttab search = { 0 }, entry;
134 
135 	/*
136 	 * Search for the entry in /etc/mnttab.  We don't bother getting the
137 	 * mountpoint, as we can just search for the special device.  This will
138 	 * also let us find mounts when the mountpoint is 'legacy'.
139 	 */
140 	search.mnt_special = (char *)zfs_get_name(zhp);
141 	search.mnt_fstype = MNTTYPE_ZFS;
142 
143 	rewind(zhp->zfs_hdl->libzfs_mnttab);
144 	if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) != 0)
145 		return (B_FALSE);
146 
147 	if (where != NULL)
148 		*where = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
149 
150 	return (B_TRUE);
151 }
152 
153 /*
154  * Mount the given filesystem.
155  */
156 int
157 zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
158 {
159 	struct stat buf;
160 	char mountpoint[ZFS_MAXPROPLEN];
161 	char mntopts[MNT_LINE_MAX];
162 	libzfs_handle_t *hdl = zhp->zfs_hdl;
163 
164 	if (options == NULL)
165 		mntopts[0] = '\0';
166 	else
167 		(void) strlcpy(mntopts, options, sizeof (mntopts));
168 
169 	/* ignore non-filesystems */
170 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
171 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0)
172 		return (0);
173 
174 	/* return success if there is no mountpoint set */
175 	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
176 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
177 		return (0);
178 
179 	/*
180 	 * If the 'zoned' property is set, and we're in the global zone, simply
181 	 * return success.
182 	 */
183 	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
184 	    getzoneid() == GLOBAL_ZONEID)
185 		return (0);
186 
187 	/* Create the directory if it doesn't already exist */
188 	if (lstat(mountpoint, &buf) != 0) {
189 		if (mkdirp(mountpoint, 0755) != 0) {
190 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
191 			    "failed to create mountpoint"));
192 			return (zfs_error(hdl, EZFS_MOUNTFAILED,
193 			    dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
194 			    mountpoint));
195 		}
196 	}
197 
198 	/*
199 	 * Determine if the mountpoint is empty.  If so, refuse to perform the
200 	 * mount.  We don't perform this check if MS_OVERLAY is specified, which
201 	 * would defeat the point.  We also avoid this check if 'remount' is
202 	 * specified.
203 	 */
204 	if ((flags & MS_OVERLAY) == 0 &&
205 	    strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
206 	    !dir_is_empty(mountpoint)) {
207 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
208 		    "directory is not empty"));
209 		return (zfs_error(hdl, EZFS_MOUNTFAILED,
210 		    dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint));
211 	}
212 
213 	/* perform the mount */
214 	if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags,
215 	    MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
216 		/*
217 		 * Generic errors are nasty, but there are just way too many
218 		 * from mount(), and they're well-understood.  We pick a few
219 		 * common ones to improve upon.
220 		 */
221 		if (errno == EBUSY)
222 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
223 			    "mountpoint or dataset is busy"));
224 		else
225 			zfs_error_aux(hdl, strerror(errno));
226 
227 		return (zfs_error(hdl, EZFS_MOUNTFAILED,
228 		    dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
229 		    zhp->zfs_name));
230 	}
231 
232 	return (0);
233 }
234 
235 /*
236  * Unmount a single filesystem.
237  */
238 static int
239 unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
240 {
241 	if (umount2(mountpoint, flags) != 0) {
242 		zfs_error_aux(hdl, strerror(errno));
243 		return (zfs_error(hdl, EZFS_UMOUNTFAILED,
244 		    dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
245 		    mountpoint));
246 	}
247 
248 	return (0);
249 }
250 
251 /*
252  * Unmount the given filesystem.
253  */
254 int
255 zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
256 {
257 	struct mnttab search = { 0 }, entry;
258 
259 	/* check to see if need to unmount the filesystem */
260 	search.mnt_special = zhp->zfs_name;
261 	search.mnt_fstype = MNTTYPE_ZFS;
262 	rewind(zhp->zfs_hdl->libzfs_mnttab);
263 	if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
264 	    getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
265 
266 		if (mountpoint == NULL)
267 			mountpoint = entry.mnt_mountp;
268 
269 		/*
270 		 * Unshare and unmount the filesystem
271 		 */
272 		if (zfs_unshare(zhp, mountpoint) != 0 ||
273 		    unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0)
274 			return (-1);
275 	}
276 
277 	return (0);
278 }
279 
280 /*
281  * Unmount this filesystem and any children inheriting the mountpoint property.
282  * To do this, just act like we're changing the mountpoint property, but don't
283  * remount the filesystems afterwards.
284  */
285 int
286 zfs_unmountall(zfs_handle_t *zhp, int flags)
287 {
288 	prop_changelist_t *clp;
289 	int ret;
290 
291 	clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags);
292 	if (clp == NULL)
293 		return (-1);
294 
295 	ret = changelist_prefix(clp);
296 	changelist_free(clp);
297 
298 	return (ret);
299 }
300 
301 /*
302  * Check to see if the filesystem is currently shared.
303  */
304 boolean_t
305 zfs_is_shared(zfs_handle_t *zhp, char **where)
306 {
307 	char *mountpoint;
308 
309 	if (!zfs_is_mounted(zhp, &mountpoint))
310 		return (B_FALSE);
311 
312 	if (is_shared(zhp->zfs_hdl, mountpoint)) {
313 		if (where != NULL)
314 			*where = mountpoint;
315 		else
316 			free(mountpoint);
317 		return (B_TRUE);
318 	} else {
319 		free(mountpoint);
320 		return (B_FALSE);
321 	}
322 }
323 
324 /*
325  * Share the given filesystem according to the options in 'sharenfs'.  We rely
326  * on share(1M) to the dirty work for us.
327  */
328 int
329 zfs_share(zfs_handle_t *zhp)
330 {
331 	char mountpoint[ZFS_MAXPROPLEN];
332 	char shareopts[ZFS_MAXPROPLEN];
333 	char buf[MAXPATHLEN];
334 	FILE *fp;
335 	libzfs_handle_t *hdl = zhp->zfs_hdl;
336 
337 	/* ignore non-filesystems */
338 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM)
339 		return (0);
340 
341 	/* return success if there is no mountpoint set */
342 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
343 	    mountpoint, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0 ||
344 	    strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
345 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
346 		return (0);
347 
348 	/* return success if there are no share options */
349 	if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts),
350 	    NULL, NULL, 0, B_FALSE) != 0 ||
351 	    strcmp(shareopts, "off") == 0)
352 		return (0);
353 
354 	/*
355 	 * If the 'zoned' property is set, simply return success since:
356 	 * 1. in a global zone, a dataset should not be shared if it's
357 	 *    managed in a local zone.
358 	 * 2. in a local zone, NFS server is not available.
359 	 */
360 	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
361 		return (0);
362 	}
363 
364 	/*
365 	 * Invoke the share(1M) command.  We always do this, even if it's
366 	 * currently shared, as the options may have changed.
367 	 */
368 	if (strcmp(shareopts, "on") == 0)
369 		(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
370 		    "-F nfs \"%s\" 2>&1", mountpoint);
371 	else
372 		(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
373 		    "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts,
374 		    mountpoint);
375 
376 	if ((fp = popen(buf, "r")) == NULL)
377 		return (zfs_error(hdl, EZFS_SHAREFAILED,
378 		    dgettext(TEXT_DOMAIN, "cannot share '%s'"),
379 		    zfs_get_name(zhp)));
380 
381 	/*
382 	 * share(1M) should only produce output if there is some kind
383 	 * of error.  All output begins with "share_nfs: ", so we trim
384 	 * this off to get to the real error.
385 	 */
386 	if (fgets(buf, sizeof (buf), fp) != NULL) {
387 		char *colon = strchr(buf, ':');
388 
389 		while (buf[strlen(buf) - 1] == '\n')
390 			buf[strlen(buf) - 1] = '\0';
391 
392 		if (colon != NULL)
393 			zfs_error_aux(hdl, colon + 2);
394 
395 		(void) zfs_error(hdl, EZFS_SHAREFAILED,
396 		    dgettext(TEXT_DOMAIN, "cannot share '%s'"),
397 		    zfs_get_name(zhp));
398 
399 		verify(pclose(fp) != 0);
400 		return (-1);
401 	}
402 
403 	verify(pclose(fp) == 0);
404 
405 	return (0);
406 }
407 
408 /*
409  * Unshare a filesystem by mountpoint.
410  */
411 static int
412 unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
413 {
414 	char buf[MAXPATHLEN];
415 	FILE *fp;
416 
417 	(void) snprintf(buf, sizeof (buf),
418 	    "/usr/sbin/unshare  \"%s\" 2>&1",
419 	    mountpoint);
420 
421 	if ((fp = popen(buf, "r")) == NULL)
422 		return (zfs_error(hdl, EZFS_UNSHAREFAILED,
423 		    dgettext(TEXT_DOMAIN,
424 		    "cannot unshare '%s'"), name));
425 
426 	/*
427 	 * unshare(1M) should only produce output if there is
428 	 * some kind of error.  All output begins with "unshare
429 	 * nfs: ", so we trim this off to get to the real error.
430 	 */
431 	if (fgets(buf, sizeof (buf), fp) != NULL) {
432 		char *colon = strchr(buf, ':');
433 
434 		while (buf[strlen(buf) - 1] == '\n')
435 			buf[strlen(buf) - 1] = '\0';
436 
437 		if (colon != NULL)
438 			zfs_error_aux(hdl, colon + 2);
439 
440 		verify(pclose(fp) != 0);
441 
442 		return (zfs_error(hdl, EZFS_UNSHAREFAILED,
443 		    dgettext(TEXT_DOMAIN,
444 		    "cannot unshare '%s'"), name));
445 	}
446 
447 	verify(pclose(fp) == 0);
448 
449 	return (0);
450 }
451 
452 /*
453  * Unshare the given filesystem.
454  */
455 int
456 zfs_unshare(zfs_handle_t *zhp, const char *mountpoint)
457 {
458 	struct mnttab search = { 0 }, entry;
459 
460 	/* check to see if need to unmount the filesystem */
461 	search.mnt_special = (char *)zfs_get_name(zhp);
462 	search.mnt_fstype = MNTTYPE_ZFS;
463 	rewind(zhp->zfs_hdl->libzfs_mnttab);
464 	if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
465 	    getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
466 
467 		if (mountpoint == NULL)
468 			mountpoint = entry.mnt_mountp;
469 
470 		if (is_shared(zhp->zfs_hdl, mountpoint) &&
471 		    unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0)
472 			return (-1);
473 	}
474 
475 	return (0);
476 }
477 
478 /*
479  * Same as zfs_unmountall(), but for unshares.
480  */
481 int
482 zfs_unshareall(zfs_handle_t *zhp)
483 {
484 	prop_changelist_t *clp;
485 	int ret;
486 
487 	clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0);
488 	if (clp == NULL)
489 		return (-1);
490 
491 	ret = changelist_unshare(clp);
492 	changelist_free(clp);
493 
494 	return (ret);
495 }
496 
497 /*
498  * Remove the mountpoint associated with the current dataset, if necessary.
499  * We only remove the underlying directory if:
500  *
501  *	- The mountpoint is not 'none' or 'legacy'
502  *	- The mountpoint is non-empty
503  *	- The mountpoint is the default or inherited
504  *	- The 'zoned' property is set, or we're in a local zone
505  *
506  * Any other directories we leave alone.
507  */
508 void
509 remove_mountpoint(zfs_handle_t *zhp)
510 {
511 	char mountpoint[ZFS_MAXPROPLEN];
512 	char source[ZFS_MAXNAMELEN];
513 	zfs_source_t sourcetype;
514 	int zoneid = getzoneid();
515 
516 	/* ignore non-filesystems */
517 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
518 	    sizeof (mountpoint), &sourcetype, source, sizeof (source),
519 	    B_FALSE) != 0)
520 		return;
521 
522 	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 &&
523 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
524 	    (sourcetype == ZFS_SRC_DEFAULT ||
525 	    sourcetype == ZFS_SRC_INHERITED) &&
526 	    (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) ||
527 	    zoneid != GLOBAL_ZONEID)) {
528 
529 		/*
530 		 * Try to remove the directory, silently ignoring any errors.
531 		 * The filesystem may have since been removed or moved around,
532 		 * and this isn't really useful to the administrator in any
533 		 * way.
534 		 */
535 		(void) rmdir(mountpoint);
536 	}
537 }
538 
539 /*
540  * Mount and share all datasets within the given pool.  This assumes that no
541  * datasets within the pool are currently mounted.  Because users can create
542  * complicated nested hierarchies of mountpoints, we first gather all the
543  * datasets and mountpoints within the pool, and sort them by mountpoint.  Once
544  * we have the list of all filesystems, we iterate over them in order and mount
545  * and/or share each one.
546  */
547 typedef struct mount_cbdata {
548 	zfs_handle_t	**cb_datasets;
549 	int 		cb_used;
550 	int		cb_alloc;
551 } mount_cbdata_t;
552 
553 static int
554 mount_cb(zfs_handle_t *zhp, void *data)
555 {
556 	mount_cbdata_t *cbp = data;
557 
558 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
559 		zfs_close(zhp);
560 		return (0);
561 	}
562 
563 	if (cbp->cb_alloc == cbp->cb_used) {
564 		zfs_handle_t **datasets;
565 
566 		if ((datasets = zfs_alloc(zhp->zfs_hdl, cbp->cb_alloc * 2 *
567 		    sizeof (void *))) == NULL)
568 			return (-1);
569 
570 		(void) memcpy(cbp->cb_datasets, datasets,
571 		    cbp->cb_alloc * sizeof (void *));
572 		free(cbp->cb_datasets);
573 		cbp->cb_datasets = datasets;
574 	}
575 
576 	cbp->cb_datasets[cbp->cb_used++] = zhp;
577 	return (0);
578 }
579 
580 static int
581 dataset_compare(const void *a, const void *b)
582 {
583 	zfs_handle_t **za = (zfs_handle_t **)a;
584 	zfs_handle_t **zb = (zfs_handle_t **)b;
585 	char mounta[MAXPATHLEN];
586 	char mountb[MAXPATHLEN];
587 
588 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
589 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
590 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
591 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
592 
593 	return (strcmp(mounta, mountb));
594 }
595 
596 int
597 zpool_mount_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
598 {
599 	mount_cbdata_t cb = { 0 };
600 	libzfs_handle_t *hdl = zhp->zpool_hdl;
601 	zfs_handle_t *zfsp;
602 	int i, ret = -1;
603 
604 	/*
605 	 * Gather all datasets within the pool.
606 	 */
607 	if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
608 		return (-1);
609 	cb.cb_alloc = 4;
610 
611 	if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL)
612 		goto out;
613 
614 	cb.cb_datasets[0] = zfsp;
615 	cb.cb_used = 1;
616 
617 	if (zfs_iter_children(zfsp, mount_cb, &cb) != 0)
618 		goto out;
619 
620 	/*
621 	 * Sort the datasets by mountpoint.
622 	 */
623 	qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_compare);
624 
625 	/*
626 	 * And mount all the datasets.
627 	 */
628 	ret = 0;
629 	for (i = 0; i < cb.cb_used; i++) {
630 		if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 ||
631 		    zfs_share(cb.cb_datasets[i]) != 0)
632 			ret = -1;
633 	}
634 
635 out:
636 	for (i = 0; i < cb.cb_used; i++)
637 		zfs_close(cb.cb_datasets[i]);
638 	free(cb.cb_datasets);
639 
640 	return (ret);
641 }
642 
643 /*
644  * Unshare and unmount all datasets within the given pool.  We don't want to
645  * rely on traversing the DSL to discover the filesystems within the pool,
646  * because this may be expensive (if not all of them are mounted), and can fail
647  * arbitrarily (on I/O error, for example).  Instead, we walk /etc/mnttab and
648  * gather all the filesystems that are currently mounted.
649  */
650 static int
651 mountpoint_compare(const void *a, const void *b)
652 {
653 	const char *mounta = *((char **)a);
654 	const char *mountb = *((char **)b);
655 
656 	return (strcmp(mountb, mounta));
657 }
658 
659 int
660 zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force)
661 {
662 	int used, alloc;
663 	struct mnttab entry;
664 	size_t namelen;
665 	char **mountpoints = NULL;
666 	zfs_handle_t **datasets = NULL;
667 	libzfs_handle_t *hdl = zhp->zpool_hdl;
668 	int i;
669 	int ret = -1;
670 	int flags = (force ? MS_FORCE : 0);
671 
672 	namelen = strlen(zhp->zpool_name);
673 
674 	rewind(hdl->libzfs_mnttab);
675 	used = alloc = 0;
676 	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
677 		/*
678 		 * Ignore non-ZFS entries.
679 		 */
680 		if (entry.mnt_fstype == NULL ||
681 		    strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
682 			continue;
683 
684 		/*
685 		 * Ignore filesystems not within this pool.
686 		 */
687 		if (entry.mnt_mountp == NULL ||
688 		    strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
689 		    (entry.mnt_special[namelen] != '/' &&
690 		    entry.mnt_special[namelen] != '\0'))
691 			continue;
692 
693 		/*
694 		 * At this point we've found a filesystem within our pool.  Add
695 		 * it to our growing list.
696 		 */
697 		if (used == alloc) {
698 			if (alloc == 0) {
699 				if ((mountpoints = zfs_alloc(hdl,
700 				    8 * sizeof (void *))) == NULL)
701 					goto out;
702 
703 				if ((datasets = zfs_alloc(hdl,
704 				    8 * sizeof (void *))) == NULL)
705 					goto out;
706 
707 				alloc = 8;
708 			} else {
709 				char **dest;
710 
711 				if ((dest = zfs_alloc(hdl,
712 				    alloc * 2 * sizeof (void *))) == NULL)
713 					goto out;
714 				(void) memcpy(dest, mountpoints,
715 				    alloc * sizeof (void *));
716 				free(mountpoints);
717 				mountpoints = dest;
718 
719 				if ((dest = zfs_alloc(hdl,
720 				    alloc * 2 * sizeof (void *))) == NULL)
721 					goto out;
722 				(void) memcpy(dest, datasets,
723 				    alloc * sizeof (void *));
724 				free(datasets);
725 				datasets = (zfs_handle_t **)dest;
726 
727 				alloc *= 2;
728 			}
729 		}
730 
731 		if ((mountpoints[used] = zfs_strdup(hdl,
732 		    entry.mnt_mountp)) == NULL)
733 			goto out;
734 
735 		/*
736 		 * This is allowed to fail, in case there is some I/O error.  It
737 		 * is only used to determine if we need to remove the underlying
738 		 * mountpoint, so failure is not fatal.
739 		 */
740 		datasets[used] = make_dataset_handle(hdl, entry.mnt_special);
741 
742 		used++;
743 	}
744 
745 	/*
746 	 * At this point, we have the entire list of filesystems, so sort it by
747 	 * mountpoint.
748 	 */
749 	qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
750 
751 	/*
752 	 * Walk through and first unshare everything.
753 	 */
754 	for (i = 0; i < used; i++) {
755 		if (is_shared(hdl, mountpoints[i]) &&
756 		    unshare_one(hdl, datasets[i] ? datasets[i]->zfs_name :
757 		    mountpoints[i], mountpoints[i]) != 0)
758 			goto out;
759 	}
760 
761 	/*
762 	 * Now unmount everything, removing the underlying directories as
763 	 * appropriate.
764 	 */
765 	for (i = 0; i < used; i++) {
766 		if (unmount_one(hdl, mountpoints[i], flags) != 0)
767 			goto out;
768 
769 		if (datasets[i])
770 			remove_mountpoint(datasets[i]);
771 	}
772 
773 	ret = 0;
774 out:
775 	for (i = 0; i < used; i++) {
776 		if (datasets[i])
777 			zfs_close(datasets[i]);
778 		free(mountpoints[i]);
779 	}
780 	free(datasets);
781 	free(mountpoints);
782 
783 	return (ret);
784 }
785