]> git.proxmox.com Git - mirror_zfs-debian.git/blame - lib/libzfs/libzfs_mount.c
Imported Upstream version 0.6.5.8
[mirror_zfs-debian.git] / lib / libzfs / libzfs_mount.c
CommitLineData
34dc7c2f
BB
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/*
428870ff 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
87dac73d 24 * Copyright (c) 2014 by Delphix. All rights reserved.
34dc7c2f
BB
25 */
26
34dc7c2f
BB
27/*
28 * Routines to manage ZFS mounts. We separate all the nasty routines that have
29 * to deal with the OS. The following functions are the main entry points --
30 * they are used by mount and unmount and when changing a filesystem's
31 * mountpoint.
32 *
33 * zfs_is_mounted()
34 * zfs_mount()
35 * zfs_unmount()
36 * zfs_unmountall()
37 *
38 * This file also contains the functions used to manage sharing filesystems via
39 * NFS and iSCSI:
40 *
41 * zfs_is_shared()
42 * zfs_share()
43 * zfs_unshare()
44 *
45 * zfs_is_shared_nfs()
46 * zfs_is_shared_smb()
34dc7c2f
BB
47 * zfs_share_proto()
48 * zfs_shareall();
34dc7c2f
BB
49 * zfs_unshare_nfs()
50 * zfs_unshare_smb()
51 * zfs_unshareall_nfs()
52 * zfs_unshareall_smb()
53 * zfs_unshareall()
54 * zfs_unshareall_bypath()
34dc7c2f
BB
55 *
56 * The following functions are available for pool consumers, and will
57 * mount/unmount and share/unshare all datasets within pool:
58 *
59 * zpool_enable_datasets()
60 * zpool_disable_datasets()
61 */
62
63#include <dirent.h>
64#include <dlfcn.h>
65#include <errno.h>
66#include <libgen.h>
67#include <libintl.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <strings.h>
71#include <unistd.h>
72#include <zone.h>
73#include <sys/mntent.h>
34dc7c2f
BB
74#include <sys/mount.h>
75#include <sys/stat.h>
76
77#include <libzfs.h>
78
79#include "libzfs_impl.h"
80
81#include <libshare.h>
82#include <sys/systeminfo.h>
83#define MAXISALEN 257 /* based on sysinfo(2) man page */
84
85static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
86zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
87 zfs_share_proto_t);
88
34dc7c2f
BB
89/*
90 * The share protocols table must be in the same order as the zfs_share_prot_t
91 * enum in libzfs_impl.h
92 */
93typedef struct {
94 zfs_prop_t p_prop;
95 char *p_name;
96 int p_share_err;
97 int p_unshare_err;
98} proto_table_t;
99
100proto_table_t proto_table[PROTO_END] = {
101 {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED},
102 {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED},
103};
104
105zfs_share_proto_t nfs_only[] = {
106 PROTO_NFS,
107 PROTO_END
108};
109
110zfs_share_proto_t smb_only[] = {
111 PROTO_SMB,
112 PROTO_END
113};
114zfs_share_proto_t share_all_proto[] = {
115 PROTO_NFS,
116 PROTO_SMB,
117 PROTO_END
118};
119
34dc7c2f 120/*
46e18b3f 121 * Search the sharetab for the given mountpoint and protocol, returning
34dc7c2f
BB
122 * a zfs_share_type_t value.
123 */
124static zfs_share_type_t
125is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto)
126{
127 char buf[MAXPATHLEN], *tab;
46e18b3f 128 char *ptr;
34dc7c2f
BB
129
130 if (hdl->libzfs_sharetab == NULL)
131 return (SHARED_NOT_SHARED);
132
133 (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
134
135 while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
136
137 /* the mountpoint is the first entry on each line */
138 if ((tab = strchr(buf, '\t')) == NULL)
139 continue;
140
141 *tab = '\0';
142 if (strcmp(buf, mountpoint) == 0) {
46e18b3f
GB
143 /*
144 * the protocol field is the third field
145 * skip over second field
146 */
147 ptr = ++tab;
148 if ((tab = strchr(ptr, '\t')) == NULL)
149 continue;
150 ptr = ++tab;
151 if ((tab = strchr(ptr, '\t')) == NULL)
152 continue;
153 *tab = '\0';
154 if (strcmp(ptr,
155 proto_table[proto].p_name) == 0) {
156 switch (proto) {
157 case PROTO_NFS:
158 return (SHARED_NFS);
159 case PROTO_SMB:
160 return (SHARED_SMB);
161 default:
162 return (0);
163 }
164 }
34dc7c2f
BB
165 }
166 }
167
168 return (SHARED_NOT_SHARED);
169}
170
171/*
172 * Returns true if the specified directory is empty. If we can't open the
173 * directory at all, return true so that the mount can fail with a more
174 * informative error message.
175 */
176static boolean_t
177dir_is_empty(const char *dirname)
178{
179 DIR *dirp;
180 struct dirent64 *dp;
181
182 if ((dirp = opendir(dirname)) == NULL)
183 return (B_TRUE);
184
185 while ((dp = readdir64(dirp)) != NULL) {
186
187 if (strcmp(dp->d_name, ".") == 0 ||
188 strcmp(dp->d_name, "..") == 0)
189 continue;
190
191 (void) closedir(dirp);
192 return (B_FALSE);
193 }
194
195 (void) closedir(dirp);
196 return (B_TRUE);
197}
198
199/*
200 * Checks to see if the mount is active. If the filesystem is mounted, we fill
201 * in 'where' with the current mountpoint, and return 1. Otherwise, we return
202 * 0.
203 */
204boolean_t
205is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where)
206{
fb5f0bc8 207 struct mnttab entry;
34dc7c2f 208
fb5f0bc8 209 if (libzfs_mnttab_find(zfs_hdl, special, &entry) != 0)
34dc7c2f
BB
210 return (B_FALSE);
211
212 if (where != NULL)
213 *where = zfs_strdup(zfs_hdl, entry.mnt_mountp);
214
215 return (B_TRUE);
216}
217
218boolean_t
219zfs_is_mounted(zfs_handle_t *zhp, char **where)
220{
221 return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where));
222}
223
224/*
225 * Returns true if the given dataset is mountable, false otherwise. Returns the
226 * mountpoint in 'buf'.
227 */
228static boolean_t
229zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
230 zprop_source_t *source)
231{
232 char sourceloc[ZFS_MAXNAMELEN];
233 zprop_source_t sourcetype;
234
ea04106b
AX
235 if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type,
236 B_FALSE))
34dc7c2f
BB
237 return (B_FALSE);
238
239 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen,
240 &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0);
241
242 if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 ||
243 strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0)
244 return (B_FALSE);
245
246 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
247 return (B_FALSE);
248
249 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
250 getzoneid() == GLOBAL_ZONEID)
251 return (B_FALSE);
252
253 if (source)
254 *source = sourcetype;
255
256 return (B_TRUE);
257}
258
3fb1fcde
BB
259/*
260 * The filesystem is mounted by invoking the system mount utility rather
261 * than by the system call mount(2). This ensures that the /etc/mtab
262 * file is correctly locked for the update. Performing our own locking
263 * and /etc/mtab update requires making an unsafe assumption about how
264 * the mount utility performs its locking. Unfortunately, this also means
265 * in the case of a mount failure we do not have the exact errno. We must
266 * make due with return value from the mount process.
267 *
268 * In the long term a shared library called libmount is under development
269 * which provides a common API to address the locking and errno issues.
270 * Once the standard mount utility has been updated to use this library
271 * we can add an autoconf check to conditionally use it.
272 *
273 * http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
274 */
275
276static int
277do_mount(const char *src, const char *mntpt, char *opts)
278{
279 char *argv[8] = {
280 "/bin/mount",
281 "-t", MNTTYPE_ZFS,
282 "-o", opts,
283 (char *)src,
a08ee875 284 (char *)mntpt,
3fb1fcde
BB
285 (char *)NULL };
286 int rc;
287
288 /* Return only the most critical mount error */
9ac97c2a 289 rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
3fb1fcde
BB
290 if (rc) {
291 if (rc & MOUNT_FILEIO)
a08ee875 292 return (EIO);
3fb1fcde 293 if (rc & MOUNT_USER)
a08ee875 294 return (EINTR);
3fb1fcde 295 if (rc & MOUNT_SOFTWARE)
a08ee875
LG
296 return (EPIPE);
297 if (rc & MOUNT_BUSY)
298 return (EBUSY);
3fb1fcde 299 if (rc & MOUNT_SYSERR)
a08ee875 300 return (EAGAIN);
3fb1fcde 301 if (rc & MOUNT_USAGE)
a08ee875 302 return (EINVAL);
3fb1fcde 303
a08ee875 304 return (ENXIO); /* Generic error */
3fb1fcde
BB
305 }
306
a08ee875 307 return (0);
3fb1fcde
BB
308}
309
310static int
311do_unmount(const char *mntpt, int flags)
312{
313 char force_opt[] = "-f";
314 char lazy_opt[] = "-l";
315 char *argv[7] = {
316 "/bin/umount",
317 "-t", MNTTYPE_ZFS,
318 NULL, NULL, NULL, NULL };
319 int rc, count = 3;
320
321 if (flags & MS_FORCE) {
322 argv[count] = force_opt;
323 count++;
324 }
325
326 if (flags & MS_DETACH) {
327 argv[count] = lazy_opt;
328 count++;
329 }
330
331 argv[count] = (char *)mntpt;
9ac97c2a 332 rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
3fb1fcde
BB
333
334 return (rc ? EINVAL : 0);
335}
336
2cf7f52b
BB
337static int
338zfs_add_option(zfs_handle_t *zhp, char *options, int len,
339 zfs_prop_t prop, char *on, char *off)
340{
341 char *source;
342 uint64_t value;
343
344 /* Skip adding duplicate default options */
345 if ((strstr(options, on) != NULL) || (strstr(options, off) != NULL))
346 return (0);
347
348 /*
349 * zfs_prop_get_int() to not used to ensure our mount options
350 * are not influenced by the current /etc/mtab contents.
351 */
352 value = getprop_uint64(zhp, prop, &source);
353
354 (void) strlcat(options, ",", len);
355 (void) strlcat(options, value ? on : off, len);
356
357 return (0);
358}
359
360static int
361zfs_add_options(zfs_handle_t *zhp, char *options, int len)
362{
363 int error = 0;
364
365 error = zfs_add_option(zhp, options, len,
366 ZFS_PROP_ATIME, MNTOPT_ATIME, MNTOPT_NOATIME);
367 error = error ? error : zfs_add_option(zhp, options, len,
368 ZFS_PROP_DEVICES, MNTOPT_DEVICES, MNTOPT_NODEVICES);
369 error = error ? error : zfs_add_option(zhp, options, len,
370 ZFS_PROP_EXEC, MNTOPT_EXEC, MNTOPT_NOEXEC);
371 error = error ? error : zfs_add_option(zhp, options, len,
372 ZFS_PROP_READONLY, MNTOPT_RO, MNTOPT_RW);
373 error = error ? error : zfs_add_option(zhp, options, len,
374 ZFS_PROP_SETUID, MNTOPT_SETUID, MNTOPT_NOSETUID);
2cf7f52b
BB
375 error = error ? error : zfs_add_option(zhp, options, len,
376 ZFS_PROP_NBMAND, MNTOPT_NBMAND, MNTOPT_NONBMAND);
377
378 return (error);
379}
380
34dc7c2f
BB
381/*
382 * Mount the given filesystem.
383 */
384int
385zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
386{
387 struct stat buf;
388 char mountpoint[ZFS_MAXPROPLEN];
389 char mntopts[MNT_LINE_MAX];
ea04106b 390 char overlay[ZFS_MAXPROPLEN];
34dc7c2f 391 libzfs_handle_t *hdl = zhp->zfs_hdl;
2cf7f52b 392 int remount = 0, rc;
34dc7c2f 393
2cf7f52b 394 if (options == NULL) {
3fb1fcde 395 (void) strlcpy(mntopts, MNTOPT_DEFAULTS, sizeof (mntopts));
2cf7f52b 396 } else {
34dc7c2f 397 (void) strlcpy(mntopts, options, sizeof (mntopts));
2cf7f52b
BB
398 }
399
400 if (strstr(mntopts, MNTOPT_REMOUNT) != NULL)
401 remount = 1;
34dc7c2f 402
572e2857
BB
403 /*
404 * If the pool is imported read-only then all mounts must be read-only
405 */
406 if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL))
3fb1fcde
BB
407 (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts));
408
ea04106b
AX
409 if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
410 return (0);
411
2cf7f52b
BB
412 /*
413 * Append default mount options which apply to the mount point.
414 * This is done because under Linux (unlike Solaris) multiple mount
415 * points may reference a single super block. This means that just
416 * given a super block there is no back reference to update the per
417 * mount point options.
418 */
419 rc = zfs_add_options(zhp, mntopts, sizeof (mntopts));
420 if (rc) {
421 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
422 "default options unavailable"));
423 return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
424 dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
425 mountpoint));
426 }
427
3fb1fcde
BB
428 /*
429 * Append zfsutil option so the mount helper allow the mount
430 */
431 strlcat(mntopts, "," MNTOPT_ZFSUTIL, sizeof (mntopts));
572e2857 432
34dc7c2f
BB
433 /* Create the directory if it doesn't already exist */
434 if (lstat(mountpoint, &buf) != 0) {
435 if (mkdirp(mountpoint, 0755) != 0) {
436 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
437 "failed to create mountpoint"));
438 return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
439 dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
440 mountpoint));
441 }
442 }
443
ea04106b
AX
444 /*
445 * Overlay mounts are disabled by default but may be enabled
446 * via the 'overlay' property or the 'zfs mount -O' option.
447 */
448 if (!(flags & MS_OVERLAY)) {
449 if (zfs_prop_get(zhp, ZFS_PROP_OVERLAY, overlay,
450 sizeof (overlay), NULL, NULL, 0, B_FALSE) == 0) {
451 if (strcmp(overlay, "on") == 0) {
452 flags |= MS_OVERLAY;
453 }
454 }
455 }
456
34dc7c2f
BB
457 /*
458 * Determine if the mountpoint is empty. If so, refuse to perform the
e18be9a6
SC
459 * mount. We don't perform this check if 'remount' is
460 * specified or if overlay option(-O) is given
34dc7c2f 461 */
e18be9a6
SC
462 if ((flags & MS_OVERLAY) == 0 && !remount &&
463 !dir_is_empty(mountpoint)) {
34dc7c2f
BB
464 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
465 "directory is not empty"));
466 return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
467 dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint));
468 }
469
470 /* perform the mount */
3fb1fcde
BB
471 rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts);
472 if (rc) {
34dc7c2f
BB
473 /*
474 * Generic errors are nasty, but there are just way too many
475 * from mount(), and they're well-understood. We pick a few
476 * common ones to improve upon.
477 */
3fb1fcde 478 if (rc == EBUSY) {
34dc7c2f
BB
479 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
480 "mountpoint or dataset is busy"));
3fb1fcde 481 } else if (rc == EPERM) {
34dc7c2f
BB
482 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
483 "Insufficient privileges"));
3fb1fcde 484 } else if (rc == ENOTSUP) {
428870ff
BB
485 char buf[256];
486 int spa_version;
487
488 VERIFY(zfs_spa_version(zhp, &spa_version) == 0);
489 (void) snprintf(buf, sizeof (buf),
490 dgettext(TEXT_DOMAIN, "Can't mount a version %lld "
491 "file system on a version %d pool. Pool must be"
492 " upgraded to mount this file system."),
493 (u_longlong_t)zfs_prop_get_int(zhp,
494 ZFS_PROP_VERSION), spa_version);
495 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf));
34dc7c2f 496 } else {
3fb1fcde 497 zfs_error_aux(hdl, strerror(rc));
34dc7c2f 498 }
34dc7c2f
BB
499 return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
500 dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
501 zhp->zfs_name));
502 }
503
2cf7f52b
BB
504 /* remove the mounted entry before re-adding on remount */
505 if (remount)
506 libzfs_mnttab_remove(hdl, zhp->zfs_name);
507
fb5f0bc8 508 /* add the mounted entry into our cache */
3fb1fcde 509 libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, mntopts);
34dc7c2f
BB
510 return (0);
511}
512
513/*
514 * Unmount a single filesystem.
515 */
516static int
517unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
518{
9ac97c2a
BB
519 int error;
520
521 error = do_unmount(mountpoint, flags);
522 if (error != 0) {
34dc7c2f
BB
523 return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
524 dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
525 mountpoint));
526 }
527
528 return (0);
529}
530
531/*
532 * Unmount the given filesystem.
533 */
534int
535zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
536{
fb5f0bc8
BB
537 libzfs_handle_t *hdl = zhp->zfs_hdl;
538 struct mnttab entry;
34dc7c2f
BB
539 char *mntpt = NULL;
540
fb5f0bc8 541 /* check to see if we need to unmount the filesystem */
34dc7c2f 542 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
fb5f0bc8 543 libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)) {
34dc7c2f
BB
544 /*
545 * mountpoint may have come from a call to
546 * getmnt/getmntany if it isn't NULL. If it is NULL,
fb5f0bc8
BB
547 * we know it comes from libzfs_mnttab_find which can
548 * then get freed later. We strdup it to play it safe.
34dc7c2f
BB
549 */
550 if (mountpoint == NULL)
fb5f0bc8 551 mntpt = zfs_strdup(hdl, entry.mnt_mountp);
34dc7c2f 552 else
fb5f0bc8 553 mntpt = zfs_strdup(hdl, mountpoint);
34dc7c2f
BB
554
555 /*
556 * Unshare and unmount the filesystem
557 */
558 if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0)
559 return (-1);
560
fb5f0bc8 561 if (unmount_one(hdl, mntpt, flags) != 0) {
34dc7c2f
BB
562 free(mntpt);
563 (void) zfs_shareall(zhp);
564 return (-1);
565 }
fb5f0bc8 566 libzfs_mnttab_remove(hdl, zhp->zfs_name);
34dc7c2f
BB
567 free(mntpt);
568 }
569
570 return (0);
571}
572
573/*
574 * Unmount this filesystem and any children inheriting the mountpoint property.
575 * To do this, just act like we're changing the mountpoint property, but don't
576 * remount the filesystems afterwards.
577 */
578int
579zfs_unmountall(zfs_handle_t *zhp, int flags)
580{
581 prop_changelist_t *clp;
582 int ret;
583
b128c09f 584 clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, 0, flags);
34dc7c2f
BB
585 if (clp == NULL)
586 return (-1);
587
588 ret = changelist_prefix(clp);
589 changelist_free(clp);
590
591 return (ret);
592}
593
594boolean_t
595zfs_is_shared(zfs_handle_t *zhp)
596{
597 zfs_share_type_t rc = 0;
598 zfs_share_proto_t *curr_proto;
599
600 if (ZFS_IS_VOLUME(zhp))
428870ff 601 return (B_FALSE);
34dc7c2f
BB
602
603 for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
604 curr_proto++)
605 rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto);
606
607 return (rc ? B_TRUE : B_FALSE);
608}
609
610int
611zfs_share(zfs_handle_t *zhp)
612{
572e2857 613 assert(!ZFS_IS_VOLUME(zhp));
34dc7c2f
BB
614 return (zfs_share_proto(zhp, share_all_proto));
615}
616
617int
618zfs_unshare(zfs_handle_t *zhp)
619{
572e2857 620 assert(!ZFS_IS_VOLUME(zhp));
34dc7c2f
BB
621 return (zfs_unshareall(zhp));
622}
623
624/*
625 * Check to see if the filesystem is currently shared.
626 */
627zfs_share_type_t
628zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto)
629{
630 char *mountpoint;
631 zfs_share_type_t rc;
632
633 if (!zfs_is_mounted(zhp, &mountpoint))
634 return (SHARED_NOT_SHARED);
635
149e873a 636 if ((rc = is_shared(zhp->zfs_hdl, mountpoint, proto))) {
34dc7c2f
BB
637 if (where != NULL)
638 *where = mountpoint;
639 else
640 free(mountpoint);
641 return (rc);
642 } else {
643 free(mountpoint);
644 return (SHARED_NOT_SHARED);
645 }
646}
647
648boolean_t
649zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
650{
651 return (zfs_is_shared_proto(zhp, where,
652 PROTO_NFS) != SHARED_NOT_SHARED);
653}
654
655boolean_t
656zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
657{
658 return (zfs_is_shared_proto(zhp, where,
659 PROTO_SMB) != SHARED_NOT_SHARED);
660}
661
34dc7c2f
BB
662/*
663 * zfs_init_libshare(zhandle, service)
664 *
665 * Initialize the libshare API if it hasn't already been initialized.
666 * In all cases it returns 0 if it succeeded and an error if not. The
667 * service value is which part(s) of the API to initialize and is a
668 * direct map to the libshare sa_init(service) interface.
669 */
670int
671zfs_init_libshare(libzfs_handle_t *zhandle, int service)
672{
673 int ret = SA_OK;
674
34dc7c2f
BB
675 if (ret == SA_OK && zhandle->libzfs_shareflags & ZFSSHARE_MISS) {
676 /*
677 * We had a cache miss. Most likely it is a new ZFS
678 * dataset that was just created. We want to make sure
679 * so check timestamps to see if a different process
680 * has updated any of the configuration. If there was
681 * some non-ZFS change, we need to re-initialize the
682 * internal cache.
683 */
684 zhandle->libzfs_shareflags &= ~ZFSSHARE_MISS;
52e7c3a2 685 if (sa_needs_refresh(zhandle->libzfs_sharehdl)) {
34dc7c2f 686 zfs_uninit_libshare(zhandle);
52e7c3a2 687 zhandle->libzfs_sharehdl = sa_init(service);
34dc7c2f
BB
688 }
689 }
690
691 if (ret == SA_OK && zhandle && zhandle->libzfs_sharehdl == NULL)
52e7c3a2 692 zhandle->libzfs_sharehdl = sa_init(service);
34dc7c2f
BB
693
694 if (ret == SA_OK && zhandle->libzfs_sharehdl == NULL)
695 ret = SA_NO_MEMORY;
696
697 return (ret);
698}
699
700/*
701 * zfs_uninit_libshare(zhandle)
702 *
703 * Uninitialize the libshare API if it hasn't already been
704 * uninitialized. It is OK to call multiple times.
705 */
706void
707zfs_uninit_libshare(libzfs_handle_t *zhandle)
708{
709 if (zhandle != NULL && zhandle->libzfs_sharehdl != NULL) {
52e7c3a2 710 sa_fini(zhandle->libzfs_sharehdl);
34dc7c2f
BB
711 zhandle->libzfs_sharehdl = NULL;
712 }
713}
714
715/*
716 * zfs_parse_options(options, proto)
717 *
718 * Call the legacy parse interface to get the protocol specific
719 * options using the NULL arg to indicate that this is a "parse" only.
720 */
721int
722zfs_parse_options(char *options, zfs_share_proto_t proto)
723{
52e7c3a2
GB
724 return (sa_parse_legacy_options(NULL, options,
725 proto_table[proto].p_name));
34dc7c2f
BB
726}
727
728/*
729 * Share the given filesystem according to the options in the specified
730 * protocol specific properties (sharenfs, sharesmb). We rely
645fb9cc 731 * on "libshare" to do the dirty work for us.
34dc7c2f
BB
732 */
733static int
734zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
735{
736 char mountpoint[ZFS_MAXPROPLEN];
737 char shareopts[ZFS_MAXPROPLEN];
738 char sourcestr[ZFS_MAXPROPLEN];
739 libzfs_handle_t *hdl = zhp->zfs_hdl;
740 sa_share_t share;
741 zfs_share_proto_t *curr_proto;
742 zprop_source_t sourcetype;
743 int ret;
744
745 if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
746 return (0);
747
34dc7c2f
BB
748 for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
749 /*
750 * Return success if there are no share options.
751 */
752 if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
753 shareopts, sizeof (shareopts), &sourcetype, sourcestr,
754 ZFS_MAXPROPLEN, B_FALSE) != 0 ||
755 strcmp(shareopts, "off") == 0)
756 continue;
757
87dac73d
AX
758 ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API);
759 if (ret != SA_OK) {
760 (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
761 dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
762 zfs_get_name(zhp), sa_errorstr(ret));
763 return (-1);
764 }
765
34dc7c2f
BB
766 /*
767 * If the 'zoned' property is set, then zfs_is_mountable()
768 * will have already bailed out if we are in the global zone.
769 * But local zones cannot be NFS servers, so we ignore it for
770 * local zones as well.
771 */
772 if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
773 continue;
774
52e7c3a2 775 share = sa_find_share(hdl->libzfs_sharehdl, mountpoint);
34dc7c2f
BB
776 if (share == NULL) {
777 /*
778 * This may be a new file system that was just
779 * created so isn't in the internal cache
780 * (second time through). Rather than
781 * reloading the entire configuration, we can
782 * assume ZFS has done the checking and it is
783 * safe to add this to the internal
784 * configuration.
785 */
52e7c3a2 786 if (sa_zfs_process_share(hdl->libzfs_sharehdl,
34dc7c2f
BB
787 NULL, NULL, mountpoint,
788 proto_table[*curr_proto].p_name, sourcetype,
789 shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
790 (void) zfs_error_fmt(hdl,
791 proto_table[*curr_proto].p_share_err,
792 dgettext(TEXT_DOMAIN, "cannot share '%s'"),
793 zfs_get_name(zhp));
794 return (-1);
795 }
796 hdl->libzfs_shareflags |= ZFSSHARE_MISS;
52e7c3a2 797 share = sa_find_share(hdl->libzfs_sharehdl,
34dc7c2f
BB
798 mountpoint);
799 }
800 if (share != NULL) {
801 int err;
52e7c3a2 802 err = sa_enable_share(share,
34dc7c2f
BB
803 proto_table[*curr_proto].p_name);
804 if (err != SA_OK) {
805 (void) zfs_error_fmt(hdl,
806 proto_table[*curr_proto].p_share_err,
807 dgettext(TEXT_DOMAIN, "cannot share '%s'"),
808 zfs_get_name(zhp));
809 return (-1);
810 }
811 } else {
812 (void) zfs_error_fmt(hdl,
813 proto_table[*curr_proto].p_share_err,
814 dgettext(TEXT_DOMAIN, "cannot share '%s'"),
815 zfs_get_name(zhp));
816 return (-1);
817 }
818
819 }
820 return (0);
821}
822
823
824int
825zfs_share_nfs(zfs_handle_t *zhp)
826{
827 return (zfs_share_proto(zhp, nfs_only));
828}
829
830int
831zfs_share_smb(zfs_handle_t *zhp)
832{
833 return (zfs_share_proto(zhp, smb_only));
834}
835
836int
837zfs_shareall(zfs_handle_t *zhp)
838{
839 return (zfs_share_proto(zhp, share_all_proto));
840}
841
842/*
843 * Unshare a filesystem by mountpoint.
844 */
845static int
846unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
847 zfs_share_proto_t proto)
848{
849 sa_share_t share;
850 int err;
851 char *mntpt;
852 /*
853 * Mountpoint could get trashed if libshare calls getmntany
fb5f0bc8 854 * which it does during API initialization, so strdup the
34dc7c2f
BB
855 * value.
856 */
857 mntpt = zfs_strdup(hdl, mountpoint);
858
859 /* make sure libshare initialized */
860 if ((err = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) {
861 free(mntpt); /* don't need the copy anymore */
862 return (zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
863 dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
52e7c3a2 864 name, sa_errorstr(err)));
34dc7c2f
BB
865 }
866
52e7c3a2 867 share = sa_find_share(hdl->libzfs_sharehdl, mntpt);
34dc7c2f
BB
868 free(mntpt); /* don't need the copy anymore */
869
870 if (share != NULL) {
52e7c3a2 871 err = sa_disable_share(share, proto_table[proto].p_name);
34dc7c2f
BB
872 if (err != SA_OK) {
873 return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
874 dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
52e7c3a2 875 name, sa_errorstr(err)));
34dc7c2f
BB
876 }
877 } else {
878 return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
879 dgettext(TEXT_DOMAIN, "cannot unshare '%s': not found"),
880 name));
881 }
882 return (0);
883}
884
885/*
886 * Unshare the given filesystem.
887 */
888int
889zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
890 zfs_share_proto_t *proto)
891{
fb5f0bc8
BB
892 libzfs_handle_t *hdl = zhp->zfs_hdl;
893 struct mnttab entry;
34dc7c2f
BB
894 char *mntpt = NULL;
895
896 /* check to see if need to unmount the filesystem */
34dc7c2f 897 if (mountpoint != NULL)
fb5f0bc8 898 mountpoint = mntpt = zfs_strdup(hdl, mountpoint);
34dc7c2f
BB
899
900 if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
fb5f0bc8 901 libzfs_mnttab_find(hdl, zfs_get_name(zhp), &entry) == 0)) {
34dc7c2f
BB
902 zfs_share_proto_t *curr_proto;
903
904 if (mountpoint == NULL)
905 mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
906
907 for (curr_proto = proto; *curr_proto != PROTO_END;
a08ee875 908 curr_proto++) {
34dc7c2f 909
fb5f0bc8
BB
910 if (is_shared(hdl, mntpt, *curr_proto) &&
911 unshare_one(hdl, zhp->zfs_name,
645fb9cc 912 mntpt, *curr_proto) != 0) {
34dc7c2f
BB
913 if (mntpt != NULL)
914 free(mntpt);
915 return (-1);
916 }
917 }
918 }
919 if (mntpt != NULL)
920 free(mntpt);
921
922 return (0);
923}
924
925int
926zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
927{
928 return (zfs_unshare_proto(zhp, mountpoint, nfs_only));
929}
930
931int
932zfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint)
933{
934 return (zfs_unshare_proto(zhp, mountpoint, smb_only));
935}
936
937/*
938 * Same as zfs_unmountall(), but for NFS and SMB unshares.
939 */
940int
941zfs_unshareall_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
942{
943 prop_changelist_t *clp;
944 int ret;
945
b128c09f 946 clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0, 0);
34dc7c2f
BB
947 if (clp == NULL)
948 return (-1);
949
950 ret = changelist_unshare(clp, proto);
951 changelist_free(clp);
952
953 return (ret);
954}
955
956int
957zfs_unshareall_nfs(zfs_handle_t *zhp)
958{
959 return (zfs_unshareall_proto(zhp, nfs_only));
960}
961
962int
963zfs_unshareall_smb(zfs_handle_t *zhp)
964{
965 return (zfs_unshareall_proto(zhp, smb_only));
966}
967
968int
969zfs_unshareall(zfs_handle_t *zhp)
970{
971 return (zfs_unshareall_proto(zhp, share_all_proto));
972}
973
974int
975zfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint)
976{
977 return (zfs_unshare_proto(zhp, mountpoint, share_all_proto));
978}
979
980/*
981 * Remove the mountpoint associated with the current dataset, if necessary.
982 * We only remove the underlying directory if:
983 *
984 * - The mountpoint is not 'none' or 'legacy'
985 * - The mountpoint is non-empty
986 * - The mountpoint is the default or inherited
987 * - The 'zoned' property is set, or we're in a local zone
988 *
989 * Any other directories we leave alone.
990 */
991void
992remove_mountpoint(zfs_handle_t *zhp)
993{
994 char mountpoint[ZFS_MAXPROPLEN];
995 zprop_source_t source;
996
997 if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
998 &source))
999 return;
1000
1001 if (source == ZPROP_SRC_DEFAULT ||
1002 source == ZPROP_SRC_INHERITED) {
1003 /*
1004 * Try to remove the directory, silently ignoring any errors.
1005 * The filesystem may have since been removed or moved around,
1006 * and this error isn't really useful to the administrator in
1007 * any way.
1008 */
1009 (void) rmdir(mountpoint);
1010 }
1011}
1012
572e2857
BB
1013void
1014libzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp)
1015{
1016 if (cbp->cb_alloc == cbp->cb_used) {
1017 size_t newsz;
1018 void *ptr;
1019
1020 newsz = cbp->cb_alloc ? cbp->cb_alloc * 2 : 64;
1021 ptr = zfs_realloc(zhp->zfs_hdl,
1022 cbp->cb_handles, cbp->cb_alloc * sizeof (void *),
1023 newsz * sizeof (void *));
1024 cbp->cb_handles = ptr;
1025 cbp->cb_alloc = newsz;
1026 }
1027 cbp->cb_handles[cbp->cb_used++] = zhp;
1028}
34dc7c2f
BB
1029
1030static int
1031mount_cb(zfs_handle_t *zhp, void *data)
1032{
572e2857 1033 get_all_cb_t *cbp = data;
34dc7c2f 1034
572e2857 1035 if (!(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM)) {
34dc7c2f
BB
1036 zfs_close(zhp);
1037 return (0);
1038 }
1039
1040 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_NOAUTO) {
1041 zfs_close(zhp);
1042 return (0);
1043 }
1044
572e2857
BB
1045 libzfs_add_handle(cbp, zhp);
1046 if (zfs_iter_filesystems(zhp, mount_cb, cbp) != 0) {
1047 zfs_close(zhp);
1048 return (-1);
34dc7c2f 1049 }
572e2857 1050 return (0);
34dc7c2f
BB
1051}
1052
572e2857
BB
1053int
1054libzfs_dataset_cmp(const void *a, const void *b)
34dc7c2f
BB
1055{
1056 zfs_handle_t **za = (zfs_handle_t **)a;
1057 zfs_handle_t **zb = (zfs_handle_t **)b;
1058 char mounta[MAXPATHLEN];
1059 char mountb[MAXPATHLEN];
1060 boolean_t gota, gotb;
1061
1062 if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0)
1063 verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
1064 sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
1065 if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0)
1066 verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
1067 sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
1068
1069 if (gota && gotb)
1070 return (strcmp(mounta, mountb));
1071
1072 if (gota)
1073 return (-1);
1074 if (gotb)
1075 return (1);
1076
87dac73d 1077 return (strcmp(zfs_get_name(*za), zfs_get_name(*zb)));
34dc7c2f
BB
1078}
1079
1080/*
1081 * Mount and share all datasets within the given pool. This assumes that no
1082 * datasets within the pool are currently mounted. Because users can create
1083 * complicated nested hierarchies of mountpoints, we first gather all the
1084 * datasets and mountpoints within the pool, and sort them by mountpoint. Once
1085 * we have the list of all filesystems, we iterate over them in order and mount
1086 * and/or share each one.
1087 */
1088#pragma weak zpool_mount_datasets = zpool_enable_datasets
1089int
1090zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
1091{
572e2857 1092 get_all_cb_t cb = { 0 };
34dc7c2f
BB
1093 libzfs_handle_t *hdl = zhp->zpool_hdl;
1094 zfs_handle_t *zfsp;
1095 int i, ret = -1;
1096 int *good;
1097
1098 /*
1099 * Gather all non-snap datasets within the pool.
1100 */
34dc7c2f
BB
1101 if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL)
1102 goto out;
1103
572e2857 1104 libzfs_add_handle(&cb, zfsp);
34dc7c2f
BB
1105 if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0)
1106 goto out;
34dc7c2f
BB
1107 /*
1108 * Sort the datasets by mountpoint.
1109 */
572e2857
BB
1110 qsort(cb.cb_handles, cb.cb_used, sizeof (void *),
1111 libzfs_dataset_cmp);
34dc7c2f
BB
1112
1113 /*
1114 * And mount all the datasets, keeping track of which ones
d164b209 1115 * succeeded or failed.
34dc7c2f 1116 */
d164b209
BB
1117 if ((good = zfs_alloc(zhp->zpool_hdl,
1118 cb.cb_used * sizeof (int))) == NULL)
1119 goto out;
1120
34dc7c2f
BB
1121 ret = 0;
1122 for (i = 0; i < cb.cb_used; i++) {
572e2857 1123 if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0)
34dc7c2f
BB
1124 ret = -1;
1125 else
1126 good[i] = 1;
1127 }
1128
1129 /*
1130 * Then share all the ones that need to be shared. This needs
1131 * to be a separate pass in order to avoid excessive reloading
1132 * of the configuration. Good should never be NULL since
1133 * zfs_alloc is supposed to exit if memory isn't available.
1134 */
1135 for (i = 0; i < cb.cb_used; i++) {
572e2857 1136 if (good[i] && zfs_share(cb.cb_handles[i]) != 0)
34dc7c2f
BB
1137 ret = -1;
1138 }
1139
1140 free(good);
1141
1142out:
1143 for (i = 0; i < cb.cb_used; i++)
572e2857
BB
1144 zfs_close(cb.cb_handles[i]);
1145 free(cb.cb_handles);
34dc7c2f
BB
1146
1147 return (ret);
1148}
1149
34dc7c2f
BB
1150static int
1151mountpoint_compare(const void *a, const void *b)
1152{
1153 const char *mounta = *((char **)a);
1154 const char *mountb = *((char **)b);
1155
1156 return (strcmp(mountb, mounta));
1157}
1158
428870ff
BB
1159/* alias for 2002/240 */
1160#pragma weak zpool_unmount_datasets = zpool_disable_datasets
34dc7c2f
BB
1161/*
1162 * Unshare and unmount all datasets within the given pool. We don't want to
1163 * rely on traversing the DSL to discover the filesystems within the pool,
1164 * because this may be expensive (if not all of them are mounted), and can fail
9a616b5d 1165 * arbitrarily (on I/O error, for example). Instead, we walk /etc/mtab and
34dc7c2f
BB
1166 * gather all the filesystems that are currently mounted.
1167 */
34dc7c2f
BB
1168int
1169zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
1170{
1171 int used, alloc;
1172 struct mnttab entry;
1173 size_t namelen;
1174 char **mountpoints = NULL;
1175 zfs_handle_t **datasets = NULL;
1176 libzfs_handle_t *hdl = zhp->zpool_hdl;
1177 int i;
1178 int ret = -1;
1179 int flags = (force ? MS_FORCE : 0);
1180
34dc7c2f
BB
1181 namelen = strlen(zhp->zpool_name);
1182
ea04106b
AX
1183 /* Reopen MNTTAB to prevent reading stale data from open file */
1184 if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL)
1185 return (ENOENT);
1186
34dc7c2f
BB
1187 used = alloc = 0;
1188 while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
1189 /*
1190 * Ignore non-ZFS entries.
1191 */
1192 if (entry.mnt_fstype == NULL ||
1193 strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
1194 continue;
1195
1196 /*
1197 * Ignore filesystems not within this pool.
1198 */
1199 if (entry.mnt_mountp == NULL ||
1200 strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
1201 (entry.mnt_special[namelen] != '/' &&
1202 entry.mnt_special[namelen] != '\0'))
1203 continue;
1204
1205 /*
1206 * At this point we've found a filesystem within our pool. Add
1207 * it to our growing list.
1208 */
1209 if (used == alloc) {
1210 if (alloc == 0) {
1211 if ((mountpoints = zfs_alloc(hdl,
1212 8 * sizeof (void *))) == NULL)
1213 goto out;
1214
1215 if ((datasets = zfs_alloc(hdl,
1216 8 * sizeof (void *))) == NULL)
1217 goto out;
1218
1219 alloc = 8;
1220 } else {
1221 void *ptr;
1222
1223 if ((ptr = zfs_realloc(hdl, mountpoints,
1224 alloc * sizeof (void *),
1225 alloc * 2 * sizeof (void *))) == NULL)
1226 goto out;
1227 mountpoints = ptr;
1228
1229 if ((ptr = zfs_realloc(hdl, datasets,
1230 alloc * sizeof (void *),
1231 alloc * 2 * sizeof (void *))) == NULL)
1232 goto out;
1233 datasets = ptr;
1234
1235 alloc *= 2;
1236 }
1237 }
1238
1239 if ((mountpoints[used] = zfs_strdup(hdl,
1240 entry.mnt_mountp)) == NULL)
1241 goto out;
1242
1243 /*
1244 * This is allowed to fail, in case there is some I/O error. It
1245 * is only used to determine if we need to remove the underlying
1246 * mountpoint, so failure is not fatal.
1247 */
1248 datasets[used] = make_dataset_handle(hdl, entry.mnt_special);
1249
1250 used++;
1251 }
1252
1253 /*
1254 * At this point, we have the entire list of filesystems, so sort it by
1255 * mountpoint.
1256 */
1257 qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
1258
1259 /*
1260 * Walk through and first unshare everything.
1261 */
1262 for (i = 0; i < used; i++) {
1263 zfs_share_proto_t *curr_proto;
1264 for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
1265 curr_proto++) {
1266 if (is_shared(hdl, mountpoints[i], *curr_proto) &&
1267 unshare_one(hdl, mountpoints[i],
1268 mountpoints[i], *curr_proto) != 0)
1269 goto out;
1270 }
1271 }
1272
1273 /*
1274 * Now unmount everything, removing the underlying directories as
1275 * appropriate.
1276 */
1277 for (i = 0; i < used; i++) {
1278 if (unmount_one(hdl, mountpoints[i], flags) != 0)
1279 goto out;
1280 }
1281
1282 for (i = 0; i < used; i++) {
1283 if (datasets[i])
1284 remove_mountpoint(datasets[i]);
1285 }
1286
1287 ret = 0;
1288out:
1289 for (i = 0; i < used; i++) {
1290 if (datasets[i])
1291 zfs_close(datasets[i]);
1292 free(mountpoints[i]);
1293 }
1294 free(datasets);
1295 free(mountpoints);
1296
1297 return (ret);
1298}