xref: /titanic_44/usr/src/lib/libzfs/common/libzfs_mount.c (revision 18c2aff776a775d34a4c9893a4c72e0434d68e36)
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  * Returns true if the given dataset is mountable, false otherwise.  Returns the
155  * mountpoint in 'buf'.
156  */
157 static boolean_t
158 zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
159     zfs_source_t *source)
160 {
161 	char sourceloc[ZFS_MAXNAMELEN];
162 	zfs_source_t sourcetype;
163 
164 	if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type))
165 		return (B_FALSE);
166 
167 	verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen,
168 	    &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0);
169 
170 	if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 ||
171 	    strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0)
172 		return (B_FALSE);
173 
174 	if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT))
175 		return (B_FALSE);
176 
177 	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
178 	    getzoneid() == GLOBAL_ZONEID)
179 		return (B_FALSE);
180 
181 	if (source)
182 		*source = sourcetype;
183 
184 	return (B_TRUE);
185 }
186 
187 /*
188  * Mount the given filesystem.
189  */
190 int
191 zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
192 {
193 	struct stat buf;
194 	char mountpoint[ZFS_MAXPROPLEN];
195 	char mntopts[MNT_LINE_MAX];
196 	libzfs_handle_t *hdl = zhp->zfs_hdl;
197 
198 	if (options == NULL)
199 		mntopts[0] = '\0';
200 	else
201 		(void) strlcpy(mntopts, options, sizeof (mntopts));
202 
203 	if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
204 		return (0);
205 
206 	/* Create the directory if it doesn't already exist */
207 	if (lstat(mountpoint, &buf) != 0) {
208 		if (mkdirp(mountpoint, 0755) != 0) {
209 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
210 			    "failed to create mountpoint"));
211 			return (zfs_error(hdl, EZFS_MOUNTFAILED,
212 			    dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
213 			    mountpoint));
214 		}
215 	}
216 
217 	/*
218 	 * Determine if the mountpoint is empty.  If so, refuse to perform the
219 	 * mount.  We don't perform this check if MS_OVERLAY is specified, which
220 	 * would defeat the point.  We also avoid this check if 'remount' is
221 	 * specified.
222 	 */
223 	if ((flags & MS_OVERLAY) == 0 &&
224 	    strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
225 	    !dir_is_empty(mountpoint)) {
226 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
227 		    "directory is not empty"));
228 		return (zfs_error(hdl, EZFS_MOUNTFAILED,
229 		    dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint));
230 	}
231 
232 	/* perform the mount */
233 	if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags,
234 	    MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
235 		/*
236 		 * Generic errors are nasty, but there are just way too many
237 		 * from mount(), and they're well-understood.  We pick a few
238 		 * common ones to improve upon.
239 		 */
240 		if (errno == EBUSY)
241 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
242 			    "mountpoint or dataset is busy"));
243 		else
244 			zfs_error_aux(hdl, strerror(errno));
245 
246 		return (zfs_error(hdl, EZFS_MOUNTFAILED,
247 		    dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
248 		    zhp->zfs_name));
249 	}
250 
251 	return (0);
252 }
253 
254 /*
255  * Unmount a single filesystem.
256  */
257 static int
258 unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
259 {
260 	if (umount2(mountpoint, flags) != 0) {
261 		zfs_error_aux(hdl, strerror(errno));
262 		return (zfs_error(hdl, EZFS_UMOUNTFAILED,
263 		    dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
264 		    mountpoint));
265 	}
266 
267 	return (0);
268 }
269 
270 /*
271  * Unmount the given filesystem.
272  */
273 int
274 zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
275 {
276 	struct mnttab search = { 0 }, entry;
277 
278 	/* check to see if need to unmount the filesystem */
279 	search.mnt_special = zhp->zfs_name;
280 	search.mnt_fstype = MNTTYPE_ZFS;
281 	rewind(zhp->zfs_hdl->libzfs_mnttab);
282 	if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
283 	    getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
284 
285 		if (mountpoint == NULL)
286 			mountpoint = entry.mnt_mountp;
287 
288 		/*
289 		 * Unshare and unmount the filesystem
290 		 */
291 		if (zfs_unshare(zhp, mountpoint) != 0 ||
292 		    unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0)
293 			return (-1);
294 	}
295 
296 	return (0);
297 }
298 
299 /*
300  * Unmount this filesystem and any children inheriting the mountpoint property.
301  * To do this, just act like we're changing the mountpoint property, but don't
302  * remount the filesystems afterwards.
303  */
304 int
305 zfs_unmountall(zfs_handle_t *zhp, int flags)
306 {
307 	prop_changelist_t *clp;
308 	int ret;
309 
310 	clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags);
311 	if (clp == NULL)
312 		return (-1);
313 
314 	ret = changelist_prefix(clp);
315 	changelist_free(clp);
316 
317 	return (ret);
318 }
319 
320 /*
321  * Check to see if the filesystem is currently shared.
322  */
323 boolean_t
324 zfs_is_shared(zfs_handle_t *zhp, char **where)
325 {
326 	char *mountpoint;
327 
328 	if (!zfs_is_mounted(zhp, &mountpoint))
329 		return (B_FALSE);
330 
331 	if (is_shared(zhp->zfs_hdl, mountpoint)) {
332 		if (where != NULL)
333 			*where = mountpoint;
334 		else
335 			free(mountpoint);
336 		return (B_TRUE);
337 	} else {
338 		free(mountpoint);
339 		return (B_FALSE);
340 	}
341 }
342 
343 /*
344  * Share the given filesystem according to the options in 'sharenfs'.  We rely
345  * on share(1M) to the dirty work for us.
346  */
347 int
348 zfs_share(zfs_handle_t *zhp)
349 {
350 	char mountpoint[ZFS_MAXPROPLEN];
351 	char shareopts[ZFS_MAXPROPLEN];
352 	char buf[MAXPATHLEN];
353 	FILE *fp;
354 	libzfs_handle_t *hdl = zhp->zfs_hdl;
355 
356 	if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
357 		return (0);
358 
359 	/* return success if there are no share options */
360 	if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts),
361 	    NULL, NULL, 0, B_FALSE) != 0 ||
362 	    strcmp(shareopts, "off") == 0)
363 		return (0);
364 
365 	/*
366 	 * If the 'zoned' property is set, then zfs_is_mountable() will have
367 	 * already bailed out if we are in the global zone.  But local
368 	 * zones cannot be NFS servers, so we ignore it for local zones as well.
369 	 */
370 	if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
371 		return (0);
372 
373 	/*
374 	 * Invoke the share(1M) command.  We always do this, even if it's
375 	 * currently shared, as the options may have changed.
376 	 */
377 	if (strcmp(shareopts, "on") == 0)
378 		(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
379 		    "-F nfs \"%s\" 2>&1", mountpoint);
380 	else
381 		(void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
382 		    "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts,
383 		    mountpoint);
384 
385 	if ((fp = popen(buf, "r")) == NULL)
386 		return (zfs_error(hdl, EZFS_SHAREFAILED,
387 		    dgettext(TEXT_DOMAIN, "cannot share '%s'"),
388 		    zfs_get_name(zhp)));
389 
390 	/*
391 	 * share(1M) should only produce output if there is some kind
392 	 * of error.  All output begins with "share_nfs: ", so we trim
393 	 * this off to get to the real error.
394 	 */
395 	if (fgets(buf, sizeof (buf), fp) != NULL) {
396 		char *colon = strchr(buf, ':');
397 
398 		while (buf[strlen(buf) - 1] == '\n')
399 			buf[strlen(buf) - 1] = '\0';
400 
401 		if (colon != NULL)
402 			zfs_error_aux(hdl, colon + 2);
403 
404 		(void) zfs_error(hdl, EZFS_SHAREFAILED,
405 		    dgettext(TEXT_DOMAIN, "cannot share '%s'"),
406 		    zfs_get_name(zhp));
407 
408 		verify(pclose(fp) != 0);
409 		return (-1);
410 	}
411 
412 	verify(pclose(fp) == 0);
413 
414 	return (0);
415 }
416 
417 /*
418  * Unshare a filesystem by mountpoint.
419  */
420 static int
421 unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
422 {
423 	char buf[MAXPATHLEN];
424 	FILE *fp;
425 
426 	(void) snprintf(buf, sizeof (buf),
427 	    "/usr/sbin/unshare  \"%s\" 2>&1",
428 	    mountpoint);
429 
430 	if ((fp = popen(buf, "r")) == NULL)
431 		return (zfs_error(hdl, EZFS_UNSHAREFAILED,
432 		    dgettext(TEXT_DOMAIN,
433 		    "cannot unshare '%s'"), name));
434 
435 	/*
436 	 * unshare(1M) should only produce output if there is
437 	 * some kind of error.  All output begins with "unshare
438 	 * nfs: ", so we trim this off to get to the real error.
439 	 */
440 	if (fgets(buf, sizeof (buf), fp) != NULL) {
441 		char *colon = strchr(buf, ':');
442 
443 		while (buf[strlen(buf) - 1] == '\n')
444 			buf[strlen(buf) - 1] = '\0';
445 
446 		if (colon != NULL)
447 			zfs_error_aux(hdl, colon + 2);
448 
449 		verify(pclose(fp) != 0);
450 
451 		return (zfs_error(hdl, EZFS_UNSHAREFAILED,
452 		    dgettext(TEXT_DOMAIN,
453 		    "cannot unshare '%s'"), name));
454 	}
455 
456 	verify(pclose(fp) == 0);
457 
458 	return (0);
459 }
460 
461 /*
462  * Unshare the given filesystem.
463  */
464 int
465 zfs_unshare(zfs_handle_t *zhp, const char *mountpoint)
466 {
467 	struct mnttab search = { 0 }, entry;
468 
469 	/* check to see if need to unmount the filesystem */
470 	search.mnt_special = (char *)zfs_get_name(zhp);
471 	search.mnt_fstype = MNTTYPE_ZFS;
472 	rewind(zhp->zfs_hdl->libzfs_mnttab);
473 	if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
474 	    getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
475 
476 		if (mountpoint == NULL)
477 			mountpoint = entry.mnt_mountp;
478 
479 		if (is_shared(zhp->zfs_hdl, mountpoint) &&
480 		    unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0)
481 			return (-1);
482 	}
483 
484 	return (0);
485 }
486 
487 /*
488  * Same as zfs_unmountall(), but for unshares.
489  */
490 int
491 zfs_unshareall(zfs_handle_t *zhp)
492 {
493 	prop_changelist_t *clp;
494 	int ret;
495 
496 	clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0);
497 	if (clp == NULL)
498 		return (-1);
499 
500 	ret = changelist_unshare(clp);
501 	changelist_free(clp);
502 
503 	return (ret);
504 }
505 
506 /*
507  * Remove the mountpoint associated with the current dataset, if necessary.
508  * We only remove the underlying directory if:
509  *
510  *	- The mountpoint is not 'none' or 'legacy'
511  *	- The mountpoint is non-empty
512  *	- The mountpoint is the default or inherited
513  *	- The 'zoned' property is set, or we're in a local zone
514  *
515  * Any other directories we leave alone.
516  */
517 void
518 remove_mountpoint(zfs_handle_t *zhp)
519 {
520 	char mountpoint[ZFS_MAXPROPLEN];
521 	zfs_source_t source;
522 
523 	if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
524 	    &source))
525 		return;
526 
527 	if (source == ZFS_SRC_DEFAULT ||
528 	    source == ZFS_SRC_INHERITED) {
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 error isn't really useful to the administrator in
533 		 * any 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 		void *ptr;
565 
566 		if ((ptr = zfs_realloc(zhp->zfs_hdl,
567 		    cbp->cb_datasets, cbp->cb_alloc * sizeof (void *),
568 		    cbp->cb_alloc * 2 * sizeof (void *))) == NULL)
569 			return (-1);
570 		cbp->cb_datasets = ptr;
571 
572 		cbp->cb_alloc *= 2;
573 	}
574 
575 	cbp->cb_datasets[cbp->cb_used++] = zhp;
576 	return (0);
577 }
578 
579 static int
580 dataset_compare(const void *a, const void *b)
581 {
582 	zfs_handle_t **za = (zfs_handle_t **)a;
583 	zfs_handle_t **zb = (zfs_handle_t **)b;
584 	char mounta[MAXPATHLEN];
585 	char mountb[MAXPATHLEN];
586 
587 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
588 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
589 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
590 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
591 
592 	return (strcmp(mounta, mountb));
593 }
594 
595 int
596 zpool_mount_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
597 {
598 	mount_cbdata_t cb = { 0 };
599 	libzfs_handle_t *hdl = zhp->zpool_hdl;
600 	zfs_handle_t *zfsp;
601 	int i, ret = -1;
602 
603 	/*
604 	 * Gather all datasets within the pool.
605 	 */
606 	if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
607 		return (-1);
608 	cb.cb_alloc = 4;
609 
610 	if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL)
611 		goto out;
612 
613 	cb.cb_datasets[0] = zfsp;
614 	cb.cb_used = 1;
615 
616 	if (zfs_iter_children(zfsp, mount_cb, &cb) != 0)
617 		goto out;
618 
619 	/*
620 	 * Sort the datasets by mountpoint.
621 	 */
622 	qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_compare);
623 
624 	/*
625 	 * And mount all the datasets.
626 	 */
627 	ret = 0;
628 	for (i = 0; i < cb.cb_used; i++) {
629 		if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 ||
630 		    zfs_share(cb.cb_datasets[i]) != 0)
631 			ret = -1;
632 	}
633 
634 out:
635 	for (i = 0; i < cb.cb_used; i++)
636 		zfs_close(cb.cb_datasets[i]);
637 	free(cb.cb_datasets);
638 
639 	return (ret);
640 }
641 
642 /*
643  * Unshare and unmount all datasets within the given pool.  We don't want to
644  * rely on traversing the DSL to discover the filesystems within the pool,
645  * because this may be expensive (if not all of them are mounted), and can fail
646  * arbitrarily (on I/O error, for example).  Instead, we walk /etc/mnttab and
647  * gather all the filesystems that are currently mounted.
648  */
649 static int
650 mountpoint_compare(const void *a, const void *b)
651 {
652 	const char *mounta = *((char **)a);
653 	const char *mountb = *((char **)b);
654 
655 	return (strcmp(mountb, mounta));
656 }
657 
658 int
659 zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force)
660 {
661 	int used, alloc;
662 	struct mnttab entry;
663 	size_t namelen;
664 	char **mountpoints = NULL;
665 	zfs_handle_t **datasets = NULL;
666 	libzfs_handle_t *hdl = zhp->zpool_hdl;
667 	int i;
668 	int ret = -1;
669 	int flags = (force ? MS_FORCE : 0);
670 
671 	namelen = strlen(zhp->zpool_name);
672 
673 	rewind(hdl->libzfs_mnttab);
674 	used = alloc = 0;
675 	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
676 		/*
677 		 * Ignore non-ZFS entries.
678 		 */
679 		if (entry.mnt_fstype == NULL ||
680 		    strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
681 			continue;
682 
683 		/*
684 		 * Ignore filesystems not within this pool.
685 		 */
686 		if (entry.mnt_mountp == NULL ||
687 		    strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
688 		    (entry.mnt_special[namelen] != '/' &&
689 		    entry.mnt_special[namelen] != '\0'))
690 			continue;
691 
692 		/*
693 		 * At this point we've found a filesystem within our pool.  Add
694 		 * it to our growing list.
695 		 */
696 		if (used == alloc) {
697 			if (alloc == 0) {
698 				if ((mountpoints = zfs_alloc(hdl,
699 				    8 * sizeof (void *))) == NULL)
700 					goto out;
701 
702 				if ((datasets = zfs_alloc(hdl,
703 				    8 * sizeof (void *))) == NULL)
704 					goto out;
705 
706 				alloc = 8;
707 			} else {
708 				void *ptr;
709 
710 				if ((ptr = zfs_realloc(hdl, mountpoints,
711 				    alloc * sizeof (void *),
712 				    alloc * 2 * sizeof (void *))) == NULL)
713 					goto out;
714 				mountpoints = ptr;
715 
716 				if ((ptr = zfs_realloc(hdl, datasets,
717 				    alloc * sizeof (void *),
718 				    alloc * 2 * sizeof (void *))) == NULL)
719 					goto out;
720 				datasets = ptr;
721 
722 				alloc *= 2;
723 			}
724 		}
725 
726 		if ((mountpoints[used] = zfs_strdup(hdl,
727 		    entry.mnt_mountp)) == NULL)
728 			goto out;
729 
730 		/*
731 		 * This is allowed to fail, in case there is some I/O error.  It
732 		 * is only used to determine if we need to remove the underlying
733 		 * mountpoint, so failure is not fatal.
734 		 */
735 		datasets[used] = make_dataset_handle(hdl, entry.mnt_special);
736 
737 		used++;
738 	}
739 
740 	/*
741 	 * At this point, we have the entire list of filesystems, so sort it by
742 	 * mountpoint.
743 	 */
744 	qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
745 
746 	/*
747 	 * Walk through and first unshare everything.
748 	 */
749 	for (i = 0; i < used; i++) {
750 		if (is_shared(hdl, mountpoints[i]) &&
751 		    unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0)
752 			goto out;
753 	}
754 
755 	/*
756 	 * Now unmount everything, removing the underlying directories as
757 	 * appropriate.
758 	 */
759 	for (i = 0; i < used; i++) {
760 		if (unmount_one(hdl, mountpoints[i], flags) != 0)
761 			goto out;
762 	}
763 
764 	for (i = 0; i < used; i++) {
765 		if (datasets[i])
766 			remove_mountpoint(datasets[i]);
767 	}
768 
769 	ret = 0;
770 out:
771 	for (i = 0; i < used; i++) {
772 		if (datasets[i])
773 			zfs_close(datasets[i]);
774 		free(mountpoints[i]);
775 	}
776 	free(datasets);
777 	free(mountpoints);
778 
779 	return (ret);
780 }
781