summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authoreschrock <none@none>2006-07-31 15:13:30 -0700
committereschrock <none@none>2006-07-31 15:13:30 -0700
commit3bb79bece53191f2cf27aa61a72ea1784a7ce700 (patch)
tree2ef162103e713655e9e6889a7ac7389d304f7c55 /usr/src
parent79033acb8a0c499540c5c46e165cff4ccc2ebf59 (diff)
downloadillumos-gate-3bb79bece53191f2cf27aa61a72ea1784a7ce700.tar.gz
6368751 libzfs interface for mount/umounting all the file systems for a given pool
6385349 zpool import -d /dev hangs 6397052 unmounting datasets should process /etc/mnttab instead of traverse DSL 6403510 zfs remount,noatime option broken 6423412 Two spaces lines are unnecessary after 'zpool import -a' 6434054 'zfs destroy' core dumps if clone is namespace-parent of origin 6440515 namespace_reload() can leak memory on allocation faiure 6440592 nvlist_dup() should not fill in destination pointer on error 6446060 zfs get does not consistently report temporary properties 6448326 zfs(1) 'list' command crashes if hidden property createtxg is requested 6450653 get_dependents() has poor error semantics --HG-- rename : usr/src/cmd/zpool/zpool_dataset.c => deleted_files/usr/src/cmd/zpool/zpool_dataset.c
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/zfs/zfs_main.c43
-rw-r--r--usr/src/cmd/zoneadm/zfs.c6
-rw-r--r--usr/src/cmd/zpool/Makefile9
-rw-r--r--usr/src/cmd/zpool/zpool_dataset.c154
-rw-r--r--usr/src/cmd/zpool/zpool_main.c8
-rw-r--r--usr/src/cmd/zpool/zpool_util.h6
-rw-r--r--usr/src/common/nvpair/nvpair.c16
-rw-r--r--usr/src/common/zfs/zfs_prop.c7
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h9
-rw-r--r--usr/src/lib/libzfs/common/libzfs_changelist.c79
-rw-r--r--usr/src/lib/libzfs/common/libzfs_config.c3
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c59
-rw-r--r--usr/src/lib/libzfs/common/libzfs_graph.c109
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h4
-rw-r--r--usr/src/lib/libzfs/common/libzfs_import.c7
-rw-r--r--usr/src/lib/libzfs/common/libzfs_mount.c384
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c2
-rw-r--r--usr/src/lib/libzfs/spec/libzfs.spec8
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c2
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c5
20 files changed, 595 insertions, 325 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index a8b734a933..c88777bc0b 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -609,7 +609,11 @@ destroy_snap_clones(zfs_handle_t *zhp, void *arg)
/*
* Destroy any clones of this snapshot
*/
- (void) zfs_iter_dependents(szhp, destroy_callback, cbp);
+ if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback,
+ cbp) != 0) {
+ zfs_close(szhp);
+ return (-1);
+ }
zfs_close(szhp);
}
@@ -673,7 +677,10 @@ zfs_do_destroy(int argc, char **argv)
if (cb.cb_doclones) {
cb.cb_snapname = cp;
- (void) destroy_snap_clones(zhp, &cb);
+ if (destroy_snap_clones(zhp, &cb) != 0) {
+ zfs_close(zhp);
+ return (1);
+ }
}
ret = zfs_destroy_snaps(zhp, cp);
@@ -713,22 +720,28 @@ zfs_do_destroy(int argc, char **argv)
* Check for any dependents and/or clones.
*/
cb.cb_first = B_TRUE;
- if (!cb.cb_doclones)
- (void) zfs_iter_dependents(zhp, destroy_check_dependent, &cb);
+ if (!cb.cb_doclones &&
+ zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
+ &cb) != 0) {
+ zfs_close(zhp);
+ return (1);
+ }
+
- if (cb.cb_error) {
+ if (cb.cb_error ||
+ zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) {
zfs_close(zhp);
return (1);
}
/*
- * Do the real thing.
+ * Do the real thing. The callback will close the handle regardless of
+ * whether it succeeds or not.
*/
- if (zfs_iter_dependents(zhp, destroy_callback, &cb) == 0 &&
- destroy_callback(zhp, &cb) == 0)
- return (0);
+ if (destroy_callback(zhp, &cb) != 0)
+ return (1);
- return (1);
+ return (0);
}
/*
@@ -1514,8 +1527,11 @@ rollback_check(zfs_handle_t *zhp, void *data)
if (cbp->cb_recurse) {
cbp->cb_dependent = B_TRUE;
- (void) zfs_iter_dependents(zhp, rollback_check,
- cbp);
+ if (zfs_iter_dependents(zhp, B_TRUE,
+ rollback_check, cbp) != 0) {
+ zfs_close(zhp);
+ return (-1);
+ }
cbp->cb_dependent = B_FALSE;
} else {
(void) fprintf(stderr, "%s\n",
@@ -1606,7 +1622,8 @@ zfs_do_rollback(int argc, char **argv)
cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
cb.cb_first = B_TRUE;
cb.cb_error = 0;
- (void) zfs_iter_children(zhp, rollback_check, &cb);
+ if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0)
+ goto out;
if ((ret = cb.cb_error) != 0)
goto out;
diff --git a/usr/src/cmd/zoneadm/zfs.c b/usr/src/cmd/zoneadm/zfs.c
index 5be5dbebc9..1fe567b218 100644
--- a/usr/src/cmd/zoneadm/zfs.c
+++ b/usr/src/cmd/zoneadm/zfs.c
@@ -753,7 +753,7 @@ destroy_zfs(char *zonepath)
/*
* We can't destroy the file system if it has dependents.
*/
- if (zfs_iter_dependents(zhp, has_dependent, NULL) != 0 ||
+ if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0 ||
zfs_unmount(zhp, NULL, 0) != 0) {
zfs_close(zhp);
return (Z_ERR);
@@ -789,8 +789,8 @@ destroy_zfs(char *zonepath)
*/
if ((ohp = zfs_open(g_zfs, origin,
ZFS_TYPE_SNAPSHOT)) != NULL) {
- if (zfs_iter_dependents(ohp, has_dependent, NULL)
- == 0 && zfs_unmount(ohp, NULL, 0) == 0)
+ if (zfs_iter_dependents(ohp, B_TRUE, has_dependent,
+ NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0)
(void) zfs_destroy(ohp);
zfs_close(ohp);
}
diff --git a/usr/src/cmd/zpool/Makefile b/usr/src/cmd/zpool/Makefile
index 174fe73722..7a94a77122 100644
--- a/usr/src/cmd/zpool/Makefile
+++ b/usr/src/cmd/zpool/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -20,14 +19,14 @@
# CDDL HEADER END
#
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
#
PROG= zpool
-OBJS= zpool_main.o zpool_vdev.o zpool_iter.o zpool_util.o zpool_dataset.o
+OBJS= zpool_main.o zpool_vdev.o zpool_iter.o zpool_util.o
SRCS= $(OBJS:%.o=%.c)
POFILES=$(OBJS:%.o=%.po)
POFILE= zpool.po
diff --git a/usr/src/cmd/zpool/zpool_dataset.c b/usr/src/cmd/zpool/zpool_dataset.c
deleted file mode 100644
index 0b4c6a15fe..0000000000
--- a/usr/src/cmd/zpool/zpool_dataset.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-/*
- * For export and destroy, we have to support iterating over all datasets and
- * unmounting and/or destroying them.
- *
- * For import, we need to iterate over all datasets, mounting and sharing
- * them as indicated by the mountpoint and sharenfs properties.
- *
- * This file contains the routines to support this.
- */
-
-#include <libintl.h>
-#include <libzfs.h>
-#include <sys/mount.h>
-
-#include "zpool_util.h"
-
-typedef struct cbdata {
- int cb_force;
- int cb_failed;
- const char *cb_mntopts;
-} cbdata_t;
-
-/*
- * Unmount a single ZFS dataset.
- */
-int
-do_unmount(zfs_handle_t *zfsp, void *data)
-{
- cbdata_t *cbp = data;
-
- if (zfs_unmount(zfsp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0)
- cbp->cb_failed = 1;
-
- zfs_close(zfsp);
-
- return (0);
-}
-
-/*
- * Unmount all datasets within the given pool.
- *
- * XXZFS it would be much more efficient, and correct, to iterate over
- * mountpoints based on /etc/mnttab.
- */
-int
-unmount_datasets(zpool_handle_t *zhp, int force)
-{
- cbdata_t cb = { 0 };
- zfs_handle_t *zfsp;
-
- /* For unavailable pools, we don't do anything */
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL)
- return (0);
-
- if ((zfsp = zfs_open(g_zfs, zpool_get_name(zhp),
- ZFS_TYPE_FILESYSTEM)) == NULL)
- return (-1);
-
- cb.cb_force = force;
-
- if (zfs_iter_dependents(zfsp, do_unmount, &cb) != 0 ||
- cb.cb_failed != 0) {
- zfs_close(zfsp);
- return (-1);
- }
-
- if (do_unmount(zfsp, &cb) != 0 || cb.cb_failed != 0)
- return (-1);
-
- return (0);
-}
-
-/*
- * Mount and share a single dataset
- */
-static int
-do_mount_share(zfs_handle_t *zfsp, void *data)
-{
- cbdata_t *cbp = data;
- int ret;
-
- if (zfs_get_type(zfsp) != ZFS_TYPE_FILESYSTEM) {
- zfs_close(zfsp);
- return (0);
- }
-
- if (zfs_mount(zfsp, cbp->cb_mntopts, 0) != 0)
- cbp->cb_failed = 1;
- else if (zfs_share(zfsp) != 0)
- cbp->cb_failed = 1;
-
- ret = zfs_iter_children(zfsp, do_mount_share, data);
-
- zfs_close(zfsp);
- return (ret);
-}
-
-/*
- * Go through and mount all datasets within a pool. We need to mount all
- * datasets in order, so that we mount parents before any children. A complete
- * fix would gather all mountpoints, sort them, and mount them in lexical order.
- * There are many more problems if you start to have nested filesystems - we
- * just want to get inherited filesystems right.
- *
- * Perform share as needed when mounting a dataset is successful.
- */
-int
-mount_share_datasets(zpool_handle_t *zhp, const char *options)
-{
- cbdata_t cb = { 0 };
- zfs_handle_t *zfsp;
-
- cb.cb_mntopts = options;
-
- /* For unavailable pools, we don't do anything */
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL)
- return (0);
-
- if ((zfsp = zfs_open(g_zfs, zpool_get_name(zhp),
- ZFS_TYPE_FILESYSTEM)) == NULL)
- return (-1);
-
- if (do_mount_share(zfsp, &cb) != 0 || cb.cb_failed != 0)
- return (-1);
-
- return (0);
-}
diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c
index 9a82a148ea..35b71cd426 100644
--- a/usr/src/cmd/zpool/zpool_main.c
+++ b/usr/src/cmd/zpool/zpool_main.c
@@ -723,7 +723,7 @@ zpool_do_destroy(int argc, char **argv)
return (1);
}
- if (unmount_datasets(zhp, force) != 0) {
+ if (zpool_unmount_datasets(zhp, force) != 0) {
(void) fprintf(stderr, gettext("could not destroy '%s': "
"could not unmount datasets\n"), zpool_get_name(zhp));
return (1);
@@ -783,7 +783,7 @@ zpool_do_export(int argc, char **argv)
continue;
}
- if (unmount_datasets(zhp, force) != 0) {
+ if (zpool_unmount_datasets(zhp, force) != 0) {
ret = 1;
zpool_close(zhp);
continue;
@@ -1100,7 +1100,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
verify((zhp = zpool_open(g_zfs, name)) != NULL);
- if (mount_share_datasets(zhp, mntopts) != 0) {
+ if (zpool_mount_datasets(zhp, mntopts) != 0) {
zpool_close(zhp);
return (1);
}
@@ -1277,7 +1277,7 @@ zpool_do_import(int argc, char **argv)
if (argc == 0) {
if (first)
first = B_FALSE;
- else
+ else if (!do_all)
(void) printf("\n");
if (do_all)
diff --git a/usr/src/cmd/zpool/zpool_util.h b/usr/src/cmd/zpool/zpool_util.h
index 3cb91756de..a20e70381e 100644
--- a/usr/src/cmd/zpool/zpool_util.h
+++ b/usr/src/cmd/zpool/zpool_util.h
@@ -62,12 +62,6 @@ void pool_list_free(zpool_list_t *);
int pool_list_count(zpool_list_t *);
void pool_list_remove(zpool_list_t *, zpool_handle_t *);
-/*
- * Dataset functions
- */
-int unmount_datasets(zpool_handle_t *, int);
-int mount_share_datasets(zpool_handle_t *, const char *);
-
libzfs_handle_t *g_zfs;
#ifdef __cplusplus
diff --git a/usr/src/common/nvpair/nvpair.c b/usr/src/common/nvpair/nvpair.c
index 1dcfdae41a..f76e28686c 100644
--- a/usr/src/common/nvpair/nvpair.c
+++ b/usr/src/common/nvpair/nvpair.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -21,7 +20,7 @@
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -608,15 +607,18 @@ int
nvlist_xdup(nvlist_t *nvl, nvlist_t **nvlp, nv_alloc_t *nva)
{
int err;
+ nvlist_t *ret;
if (nvl == NULL || nvlp == NULL)
return (EINVAL);
- if ((err = nvlist_xalloc(nvlp, nvl->nvl_nvflag, nva)) != 0)
+ if ((err = nvlist_xalloc(&ret, nvl->nvl_nvflag, nva)) != 0)
return (err);
- if ((err = nvlist_copy_pairs(nvl, *nvlp)) != 0)
- nvlist_free(*nvlp);
+ if ((err = nvlist_copy_pairs(nvl, ret)) != 0)
+ nvlist_free(ret);
+ else
+ *nvlp = ret;
return (err);
}
diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c
index f0351728cc..6f4504d6f2 100644
--- a/usr/src/common/zfs/zfs_prop.c
+++ b/usr/src/common/zfs/zfs_prop.c
@@ -173,12 +173,17 @@ propname_match(const char *p, int prop, size_t len)
int c;
#endif
+#ifndef _KERNEL
+ if (colname == NULL)
+ return (0);
+#endif
+
if (len == strlen(propname) &&
strncmp(p, propname, len) == 0)
return (1);
#ifndef _KERNEL
- if (colname == NULL || len != strlen(colname))
+ if (len != strlen(colname))
return (0);
for (c = 0; c < len; c++)
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index dcaccba2d3..a60957df6f 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -89,6 +89,7 @@ enum {
EZFS_INTR, /* signal received */
EZFS_ISSPARE, /* device is a hot spare */
EZFS_INVALCONFIG, /* invalid vdev configuration */
+ EZFS_RECURSIVE, /* recursive dependency */
EZFS_UNKNOWN /* unknown error */
};
@@ -274,7 +275,7 @@ int zfs_get_proplist(char *fields, zfs_prop_t *proplist, int max, int *count,
typedef int (*zfs_iter_f)(zfs_handle_t *, void *);
extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_dependents(zfs_handle_t *, zfs_iter_f, void *);
+extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
@@ -354,6 +355,12 @@ extern int zpool_read_label(int, nvlist_t **);
extern int zpool_create_zvol_links(zpool_handle_t *);
extern int zpool_remove_zvol_links(zpool_handle_t *);
+/*
+ * Mount and unmount datasets within a pool
+ */
+extern int zpool_mount_datasets(zpool_handle_t *, const char *);
+extern int zpool_unmount_datasets(zpool_handle_t *, boolean_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c
index ed1b291ef3..7b7b5cd9d4 100644
--- a/usr/src/lib/libzfs/common/libzfs_changelist.c
+++ b/usr/src/lib/libzfs/common/libzfs_changelist.c
@@ -77,6 +77,7 @@ struct prop_changelist {
boolean_t cl_alldependents;
int cl_flags;
boolean_t cl_haszonedchild;
+ boolean_t cl_sorted;
};
/*
@@ -384,15 +385,20 @@ change_one(zfs_handle_t *zhp, void *data)
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
- if (clp->cl_alldependents) {
- verify(uu_list_insert_after(clp->cl_list,
- uu_list_last(clp->cl_list), cn) == 0);
- return (0);
+ if (clp->cl_sorted) {
+ uu_list_index_t idx;
+
+ (void) uu_list_find(clp->cl_list, cn, NULL,
+ &idx);
+ uu_list_insert(clp->cl_list, cn, idx);
} else {
+ ASSERT(!clp->cl_alldependents);
verify(uu_list_insert_before(clp->cl_list,
uu_list_first(clp->cl_list), cn) == 0);
- return (zfs_iter_children(zhp, change_one, data));
}
+
+ if (!clp->cl_alldependents)
+ return (zfs_iter_children(zhp, change_one, data));
} else {
zfs_close(zhp);
}
@@ -400,6 +406,40 @@ change_one(zfs_handle_t *zhp, void *data)
return (0);
}
+/*ARGSUSED*/
+static int
+compare_mountpoints(const void *a, const void *b, void *unused)
+{
+ const prop_changenode_t *ca = a;
+ const prop_changenode_t *cb = b;
+
+ char mounta[MAXPATHLEN];
+ char mountb[MAXPATHLEN];
+
+ boolean_t hasmounta, hasmountb;
+
+ /*
+ * When unsharing or unmounting filesystems, we need to do it in
+ * mountpoint order. This allows the user to have a mountpoint
+ * hierarchy that is different from the dataset hierarchy, and still
+ * allow it to be changed. However, if either dataset doesn't have a
+ * mountpoint (because it is a volume or a snapshot), we place it at the
+ * end of the list, because it doesn't affect our change at all.
+ */
+ hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
+ sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
+ hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
+ sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
+
+ if (!hasmounta && hasmountb)
+ return (-1);
+ else if (hasmounta && !hasmountb)
+ return (1);
+ else if (!hasmounta && !hasmountb)
+ return (0);
+ else
+ return (strcmp(mountb, mounta));
+}
/*
* Given a ZFS handle and a property, construct a complete list of datasets that
@@ -416,14 +456,26 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
prop_changenode_t *cn;
zfs_handle_t *temp;
char property[ZFS_MAXPROPLEN];
+ uu_compare_fn_t *compare = NULL;
if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
return (NULL);
+ /*
+ * For mountpoint-related tasks, we want to sort everything by
+ * mountpoint, so that we mount and unmount them in the appropriate
+ * order, regardless of their position in the hierarchy.
+ */
+ if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
+ prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
+ compare = compare_mountpoints;
+ clp->cl_sorted = B_TRUE;
+ }
+
clp->cl_pool = uu_list_pool_create("changelist_pool",
sizeof (prop_changenode_t),
offsetof(prop_changenode_t, cn_listnode),
- NULL, 0);
+ compare, 0);
if (clp->cl_pool == NULL) {
assert(uu_error() == UU_ERROR_NO_MEMORY);
(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
@@ -431,7 +483,8 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
return (NULL);
}
- clp->cl_list = uu_list_create(clp->cl_pool, NULL, 0);
+ clp->cl_list = uu_list_create(clp->cl_pool, NULL,
+ clp->cl_sorted ? UU_LIST_SORTED : 0);
clp->cl_flags = flags;
if (clp->cl_list == NULL) {
@@ -465,7 +518,7 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
return (clp);
if (clp->cl_alldependents) {
- if (zfs_iter_dependents(zhp, change_one, clp) != 0) {
+ if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
changelist_free(clp);
return (NULL);
}
@@ -501,8 +554,14 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
- verify(uu_list_insert_after(clp->cl_list,
- uu_list_last(clp->cl_list), cn) == 0);
+ if (clp->cl_sorted) {
+ uu_list_index_t idx;
+ (void) uu_list_find(clp->cl_list, cn, NULL, &idx);
+ uu_list_insert(clp->cl_list, cn, idx);
+ } else {
+ verify(uu_list_insert_after(clp->cl_list,
+ uu_list_last(clp->cl_list), cn) == 0);
+ }
/*
* If the property was previously 'legacy' or 'none', record this fact,
diff --git a/usr/src/lib/libzfs/common/libzfs_config.c b/usr/src/lib/libzfs/common/libzfs_config.c
index 53f6bc2df4..593131cea6 100644
--- a/usr/src/lib/libzfs/common/libzfs_config.c
+++ b/usr/src/lib/libzfs/common/libzfs_config.c
@@ -211,11 +211,14 @@ namespace_reload(libzfs_handle_t *hdl)
if ((cn->cn_name = zfs_strdup(hdl,
nvpair_name(elem))) == NULL) {
free(cn);
+ nvlist_free(config);
return (-1);
}
verify(nvpair_value_nvlist(elem, &child) == 0);
if (nvlist_dup(child, &cn->cn_config, 0) != 0) {
+ free(cn->cn_name);
+ free(cn);
nvlist_free(config);
return (no_memory(hdl));
}
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 5b766f7818..4d150bbf79 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -1084,6 +1084,33 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
*source = NULL;
+ /*
+ * Because looking up the mount options is potentially expensive
+ * (iterating over all of /etc/mnttab), we defer its calculation until
+ * we're looking up a property which requires its presence.
+ */
+ if (!zhp->zfs_mntcheck &&
+ (prop == ZFS_PROP_ATIME ||
+ prop == ZFS_PROP_DEVICES ||
+ prop == ZFS_PROP_EXEC ||
+ prop == ZFS_PROP_READONLY ||
+ prop == ZFS_PROP_SETUID ||
+ prop == ZFS_PROP_MOUNTED)) {
+ struct mnttab search = { 0 }, entry;
+
+ search.mnt_special = (char *)zhp->zfs_name;
+ search.mnt_fstype = MNTTYPE_ZFS;
+ rewind(zhp->zfs_hdl->libzfs_mnttab);
+
+ if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry,
+ &search) == 0 && (zhp->zfs_mntopts =
+ zfs_strdup(zhp->zfs_hdl,
+ entry.mnt_mntopts)) == NULL)
+ return (-1);
+
+ zhp->zfs_mntcheck = B_TRUE;
+ }
+
if (zhp->zfs_mntopts == NULL)
mnt.mnt_mntopts = "";
else
@@ -1229,26 +1256,6 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
break;
case ZFS_PROP_MOUNTED:
- /*
- * Unlike other properties, we defer calculation of 'MOUNTED'
- * until actually requested. This is because the getmntany()
- * call can be extremely expensive on systems with a large
- * number of filesystems, and the property isn't needed in
- * normal use cases.
- */
- if (zhp->zfs_mntopts == NULL) {
- struct mnttab search = { 0 }, entry;
-
- search.mnt_special = (char *)zhp->zfs_name;
- search.mnt_fstype = MNTTYPE_ZFS;
- rewind(zhp->zfs_hdl->libzfs_mnttab);
-
- if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry,
- &search) == 0 && (zhp->zfs_mntopts =
- zfs_strdup(zhp->zfs_hdl,
- entry.mnt_mntopts)) == NULL)
- return (-1);
- }
*val = (zhp->zfs_mntopts != NULL);
break;
@@ -2729,7 +2736,9 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
cbp->cb_create) {
cbp->cb_dependent = B_TRUE;
- (void) zfs_iter_dependents(zhp, rollback_destroy, cbp);
+ if (zfs_iter_dependents(zhp, B_FALSE, rollback_destroy,
+ cbp) != 0)
+ cbp->cb_error = 1;
cbp->cb_dependent = B_FALSE;
if (zfs_destroy(zhp) != 0)
@@ -2857,7 +2866,8 @@ out:
* libzfs_graph.c.
*/
int
-zfs_iter_dependents(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
+ zfs_iter_f func, void *data)
{
char **dependents;
size_t count;
@@ -2865,7 +2875,10 @@ zfs_iter_dependents(zfs_handle_t *zhp, zfs_iter_f func, void *data)
zfs_handle_t *child;
int ret = 0;
- dependents = get_dependents(zhp->zfs_hdl, zhp->zfs_name, &count);
+ if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
+ &dependents, &count) != 0)
+ return (-1);
+
for (i = 0; i < count; i++) {
if ((child = make_dataset_handle(zhp->zfs_hdl,
dependents[i])) == NULL)
diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c
index e86a6c9377..8ea76e51a6 100644
--- a/usr/src/lib/libzfs/common/libzfs_graph.c
+++ b/usr/src/lib/libzfs/common/libzfs_graph.c
@@ -60,6 +60,10 @@
* datasets. We do a topological ordering of our graph starting at our target
* dataset, and then walk the results in reverse.
*
+ * It's possible for the graph to have cycles if, for example, the user renames
+ * a clone to be the parent of its origin snapshot. The user can request to
+ * generate an error in this case, or ignore the cycle and continue.
+ *
* When removing datasets, we want to destroy the snapshots in chronological
* order (because this is the most efficient method). In order to accomplish
* this, we store the creation transaction group with each vertex and keep each
@@ -68,6 +72,7 @@
*/
#include <assert.h>
+#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -96,6 +101,12 @@ typedef struct zfs_vertex {
int zv_edgealloc;
} zfs_vertex_t;
+enum {
+ VISIT_SEEN = 1,
+ VISIT_SORT_PRE,
+ VISIT_SORT_POST
+};
+
/*
* Edge structure. Simply maintains a pointer to the destination vertex. There
* is no need to store the source vertex, since we only use edges in the context
@@ -346,7 +357,7 @@ zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
return (-1);
- svp->zv_visited = 1;
+ svp->zv_visited = VISIT_SEEN;
if (dest != NULL) {
dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
if (dvp == NULL)
@@ -361,10 +372,9 @@ zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
/*
* Iterate over all children of the given dataset, adding any vertices as
* necessary. Returns 0 if no cloned snapshots were seen, -1 if there was an
- * error, or 1 otherwise. This is
- * a simple recursive algorithm - the ZFS namespace typically is very flat. We
- * manually invoke the necessary ioctl() calls to avoid the overhead and
- * additional semantics of zfs_open().
+ * error, or 1 otherwise. This is a simple recursive algorithm - the ZFS
+ * namespace typically is very flat. We manually invoke the necessary ioctl()
+ * calls to avoid the overhead and additional semantics of zfs_open().
*/
static int
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
@@ -379,9 +389,22 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
if (zvp == NULL)
return (-1);
- if (zvp->zv_visited)
+ if (zvp->zv_visited == VISIT_SEEN)
return (0);
+ /*
+ * We check the clone parent here instead of within the loop, so that if
+ * the root dataset has been promoted from a clone, we find its parent
+ * appropriately.
+ */
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0 &&
+ zc.zc_objset_stats.dds_clone_of[0] != '\0') {
+ if (zfs_graph_add(hdl, zgp, zc.zc_objset_stats.dds_clone_of,
+ zc.zc_name, zc.zc_objset_stats.dds_creation_txg) != 0)
+ return (-1);
+ }
+
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
@@ -408,14 +431,6 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
return (-1);
/*
- * If this dataset has a clone parent, add an appropriate edge.
- */
- if (zc.zc_objset_stats.dds_clone_of[0] != '\0' &&
- zfs_graph_add(hdl, zgp, zc.zc_objset_stats.dds_clone_of,
- zc.zc_name, zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
-
- /*
* Iterate over all children
*/
err = iterate_children(hdl, zgp, zc.zc_name);
@@ -462,7 +477,7 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
ret = 1;
}
- zvp->zv_visited = 1;
+ zvp->zv_visited = VISIT_SEEN;
return (ret);
}
@@ -532,22 +547,43 @@ construct_graph(libzfs_handle_t *hdl, const char *dataset)
* hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
*/
static int
-topo_sort(libzfs_handle_t *hdl, char **result, size_t *idx, zfs_vertex_t *zgv)
+topo_sort(libzfs_handle_t *hdl, boolean_t allowrecursion, char **result,
+ size_t *idx, zfs_vertex_t *zgv)
{
int i;
- /* avoid doing a search if we don't have to */
- if (zgv->zv_visited == 2)
+ if (zgv->zv_visited == VISIT_SORT_PRE && !allowrecursion) {
+ /*
+ * If we've already seen this vertex as part of our depth-first
+ * search, then we have a cyclic dependency, and we must return
+ * an error.
+ */
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "recursive dependency at '%s'"),
+ zgv->zv_dataset);
+ return (zfs_error(hdl, EZFS_RECURSIVE,
+ dgettext(TEXT_DOMAIN,
+ "cannot determine dependent datasets")));
+ } else if (zgv->zv_visited >= VISIT_SORT_PRE) {
+ /*
+ * If we've already processed this as part of the topological
+ * sort, then don't bother doing so again.
+ */
return (0);
+ }
+ zgv->zv_visited = VISIT_SORT_PRE;
+
+ /* avoid doing a search if we don't have to */
zfs_vertex_sort_edges(zgv);
for (i = 0; i < zgv->zv_edgecount; i++) {
- if (topo_sort(hdl, result, idx, zgv->zv_edges[i]->ze_dest) != 0)
+ if (topo_sort(hdl, allowrecursion, result, idx,
+ zgv->zv_edges[i]->ze_dest) != 0)
return (-1);
}
/* we may have visited this in the course of the above */
- if (zgv->zv_visited == 2)
+ if (zgv->zv_visited == VISIT_SORT_POST)
return (0);
if ((result[*idx] = zfs_alloc(hdl,
@@ -556,7 +592,7 @@ topo_sort(libzfs_handle_t *hdl, char **result, size_t *idx, zfs_vertex_t *zgv)
(void) strcpy(result[*idx], zgv->zv_dataset);
*idx += 1;
- zgv->zv_visited = 2;
+ zgv->zv_visited = VISIT_SORT_POST;
return (0);
}
@@ -564,34 +600,39 @@ topo_sort(libzfs_handle_t *hdl, char **result, size_t *idx, zfs_vertex_t *zgv)
* The only public interface for this file. Do the dirty work of constructing a
* child list for the given object. Construct the graph, do the toplogical
* sort, and then return the array of strings to the caller.
+ *
+ * The 'allowrecursion' parameter controls behavior when cycles are found. If
+ * it is set, the the cycle is ignored and the results returned as if the cycle
+ * did not exist. If it is not set, then the routine will generate an error if
+ * a cycle is found.
*/
-char **
-get_dependents(libzfs_handle_t *hdl, const char *dataset, size_t *count)
+int
+get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion,
+ const char *dataset, char ***result, size_t *count)
{
- char **result;
zfs_graph_t *zgp;
zfs_vertex_t *zvp;
if ((zgp = construct_graph(hdl, dataset)) == NULL)
- return (NULL);
+ return (-1);
- if ((result = zfs_alloc(hdl,
+ if ((*result = zfs_alloc(hdl,
zgp->zg_nvertex * sizeof (char *))) == NULL) {
zfs_graph_destroy(zgp);
- return (NULL);
+ return (-1);
}
if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) {
- free(result);
+ free(*result);
zfs_graph_destroy(zgp);
- return (NULL);
+ return (-1);
}
*count = 0;
- if (topo_sort(hdl, result, count, zvp) != 0) {
- free(result);
+ if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) {
+ free(*result);
zfs_graph_destroy(zgp);
- return (NULL);
+ return (-1);
}
/*
@@ -599,10 +640,10 @@ get_dependents(libzfs_handle_t *hdl, const char *dataset, size_t *count)
* strictly a dependent.
*/
assert(*count > 0);
- free(result[*count - 1]);
+ free((*result)[*count - 1]);
(*count)--;
zfs_graph_destroy(zgp);
- return (result);
+ return (0);
}
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index 9341a97546..3e0f737a4b 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -63,6 +63,7 @@ struct zfs_handle {
nvlist_t *zfs_props;
uint64_t zfs_volsize;
uint64_t zfs_volblocksize;
+ boolean_t zfs_mntcheck;
char *zfs_mntopts;
char zfs_root[MAXPATHLEN];
};
@@ -87,7 +88,8 @@ int no_memory(libzfs_handle_t *);
int zfs_standard_error(libzfs_handle_t *, int, const char *, ...);
int zpool_standard_error(libzfs_handle_t *, int, const char *, ...);
-char **get_dependents(libzfs_handle_t *, const char *, size_t *);
+int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
+ size_t *);
typedef struct prop_changelist prop_changelist_t;
diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c
index dbc3b9dd1a..6b76b2922c 100644
--- a/usr/src/lib/libzfs/common/libzfs_import.c
+++ b/usr/src/lib/libzfs/common/libzfs_import.c
@@ -788,6 +788,13 @@ zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv)
if (S_ISDIR(statbuf.st_mode))
continue;
+ /*
+ * Ignore special (non-character or non-block) files.
+ */
+ if (!S_ISREG(statbuf.st_mode) &&
+ !S_ISBLK(statbuf.st_mode))
+ continue;
+
if ((fd = open64(path, O_RDONLY)) < 0)
continue;
diff --git a/usr/src/lib/libzfs/common/libzfs_mount.c b/usr/src/lib/libzfs/common/libzfs_mount.c
index 894bcc0d03..5eab867e36 100644
--- a/usr/src/lib/libzfs/common/libzfs_mount.c
+++ b/usr/src/lib/libzfs/common/libzfs_mount.c
@@ -42,6 +42,12 @@
* zfs_share()
* zfs_unshare()
* zfs_unshareall()
+ *
+ * The following functions are available for pool consumers, and will
+ * mount/unmount (and share/unshare) all datasets within pool:
+ *
+ * zpool_mount_datasets()
+ * zpool_unmount_datasets()
*/
#include <dirent.h>
@@ -227,6 +233,22 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
}
/*
+ * Unmount a single filesystem.
+ */
+static int
+unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
+{
+ if (umount2(mountpoint, flags) != 0) {
+ zfs_error_aux(hdl, strerror(errno));
+ return (zfs_error(hdl, EZFS_UMOUNTFAILED,
+ dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
+ mountpoint));
+ }
+
+ return (0);
+}
+
+/*
* Unmount the given filesystem.
*/
int
@@ -235,7 +257,7 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
struct mnttab search = { 0 }, entry;
/* check to see if need to unmount the filesystem */
- search.mnt_special = (char *)zfs_get_name(zhp);
+ search.mnt_special = zhp->zfs_name;
search.mnt_fstype = MNTTYPE_ZFS;
rewind(zhp->zfs_hdl->libzfs_mnttab);
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
@@ -245,31 +267,11 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
mountpoint = entry.mnt_mountp;
/*
- * Always unshare the filesystem first.
+ * Unshare and unmount the filesystem
*/
- if (zfs_unshare(zhp, mountpoint) != 0)
+ if (zfs_unshare(zhp, mountpoint) != 0 ||
+ unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0)
return (-1);
-
- /*
- * Try to unmount the filesystem. There is no reason to try a
- * forced unmount because the vnodes will still carry a
- * reference to the underlying dataset, so we can't destroy it
- * anyway.
- *
- * In the unmount case, we print out a slightly more informative
- * error message, though we'll be relying on the poor error
- * semantics from the kernel.
- */
- if (umount2(mountpoint, flags) != 0) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- return (zfs_error(zhp->zfs_hdl, EZFS_UMOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
- mountpoint));
- }
-
- /*
- * Don't actually destroy the underlying directory
- */
}
return (0);
@@ -391,7 +393,8 @@ zfs_share(zfs_handle_t *zhp)
zfs_error_aux(hdl, colon + 2);
(void) zfs_error(hdl, EZFS_SHAREFAILED,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"));
+ dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+ zfs_get_name(zhp));
verify(pclose(fp) != 0);
return (-1);
@@ -403,14 +406,56 @@ zfs_share(zfs_handle_t *zhp)
}
/*
+ * Unshare a filesystem by mountpoint.
+ */
+static int
+unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
+{
+ char buf[MAXPATHLEN];
+ FILE *fp;
+
+ (void) snprintf(buf, sizeof (buf),
+ "/usr/sbin/unshare \"%s\" 2>&1",
+ mountpoint);
+
+ if ((fp = popen(buf, "r")) == NULL)
+ return (zfs_error(hdl, EZFS_UNSHAREFAILED,
+ dgettext(TEXT_DOMAIN,
+ "cannot unshare '%s'"), name));
+
+ /*
+ * unshare(1M) should only produce output if there is
+ * some kind of error. All output begins with "unshare
+ * nfs: ", so we trim this off to get to the real error.
+ */
+ if (fgets(buf, sizeof (buf), fp) != NULL) {
+ char *colon = strchr(buf, ':');
+
+ while (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+
+ if (colon != NULL)
+ zfs_error_aux(hdl, colon + 2);
+
+ verify(pclose(fp) != 0);
+
+ return (zfs_error(hdl, EZFS_UNSHAREFAILED,
+ dgettext(TEXT_DOMAIN,
+ "cannot unshare '%s'"), name));
+ }
+
+ verify(pclose(fp) == 0);
+
+ return (0);
+}
+
+/*
* Unshare the given filesystem.
*/
int
zfs_unshare(zfs_handle_t *zhp, const char *mountpoint)
{
- char buf[MAXPATHLEN];
struct mnttab search = { 0 }, entry;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
/* check to see if need to unmount the filesystem */
search.mnt_special = (char *)zfs_get_name(zhp);
@@ -422,41 +467,9 @@ zfs_unshare(zfs_handle_t *zhp, const char *mountpoint)
if (mountpoint == NULL)
mountpoint = entry.mnt_mountp;
- if (is_shared(zhp->zfs_hdl, mountpoint)) {
- FILE *fp;
-
- (void) snprintf(buf, sizeof (buf),
- "/usr/sbin/unshare \"%s\" 2>&1",
- mountpoint);
-
- if ((fp = popen(buf, "r")) == NULL)
- return (zfs_error(hdl, EZFS_UNSHAREFAILED,
- dgettext(TEXT_DOMAIN,
- "cannot unshare '%s'"), zfs_get_name(zhp)));
-
- /*
- * unshare(1M) should only produce output if there is
- * some kind of error. All output begins with "unshare
- * nfs: ", so we trim this off to get to the real error.
- */
- if (fgets(buf, sizeof (buf), fp) != NULL) {
- char *colon = strchr(buf, ':');
-
- while (buf[strlen(buf) - 1] == '\n')
- buf[strlen(buf) - 1] = '\0';
-
- if (colon != NULL)
- zfs_error_aux(hdl, colon + 2);
-
- verify(pclose(fp) != 0);
-
- return (zfs_error(hdl, EZFS_UNSHAREFAILED,
- dgettext(TEXT_DOMAIN,
- "cannot unshare '%s'"), zfs_get_name(zhp)));
- }
-
- verify(pclose(fp) == 0);
- }
+ if (is_shared(zhp->zfs_hdl, mountpoint) &&
+ unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0)
+ return (-1);
}
return (0);
@@ -522,3 +535,250 @@ remove_mountpoint(zfs_handle_t *zhp)
(void) rmdir(mountpoint);
}
}
+
+/*
+ * Mount and share all datasets within the given pool. This assumes that no
+ * datasets within the pool are currently mounted. Because users can create
+ * complicated nested hierarchies of mountpoints, we first gather all the
+ * datasets and mountpoints within the pool, and sort them by mountpoint. Once
+ * we have the list of all filesystems, we iterate over them in order and mount
+ * and/or share each one.
+ */
+typedef struct mount_cbdata {
+ zfs_handle_t **cb_datasets;
+ int cb_used;
+ int cb_alloc;
+} mount_cbdata_t;
+
+static int
+mount_cb(zfs_handle_t *zhp, void *data)
+{
+ mount_cbdata_t *cbp = data;
+
+ if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ if (cbp->cb_alloc == cbp->cb_used) {
+ zfs_handle_t **datasets;
+
+ if ((datasets = zfs_alloc(zhp->zfs_hdl, cbp->cb_alloc * 2 *
+ sizeof (void *))) == NULL)
+ return (-1);
+
+ (void) memcpy(cbp->cb_datasets, datasets,
+ cbp->cb_alloc * sizeof (void *));
+ free(cbp->cb_datasets);
+ cbp->cb_datasets = datasets;
+ }
+
+ cbp->cb_datasets[cbp->cb_used++] = zhp;
+ return (0);
+}
+
+static int
+dataset_compare(const void *a, const void *b)
+{
+ zfs_handle_t **za = (zfs_handle_t **)a;
+ zfs_handle_t **zb = (zfs_handle_t **)b;
+ char mounta[MAXPATHLEN];
+ char mountb[MAXPATHLEN];
+
+ verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
+ sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
+ verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
+ sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
+
+ return (strcmp(mounta, mountb));
+}
+
+int
+zpool_mount_datasets(zpool_handle_t *zhp, const char *mntopts)
+{
+ mount_cbdata_t cb = { 0 };
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+ zfs_handle_t *zfsp;
+ int i, ret = -1;
+
+ /*
+ * Gather all datasets within the pool.
+ */
+ if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
+ return (-1);
+ cb.cb_alloc = 4;
+
+ if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL)
+ goto out;
+
+ cb.cb_datasets[0] = zfsp;
+ cb.cb_used = 1;
+
+ if (zfs_iter_children(zfsp, mount_cb, &cb) != 0)
+ goto out;
+
+ /*
+ * Sort the datasets by mountpoint.
+ */
+ qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_compare);
+
+ /*
+ * And mount all the datasets.
+ */
+ ret = 0;
+ for (i = 0; i < cb.cb_used; i++) {
+ if (zfs_mount(cb.cb_datasets[i], mntopts, 0) != 0 ||
+ zfs_share(cb.cb_datasets[i]) != 0)
+ ret = -1;
+ }
+
+out:
+ for (i = 0; i < cb.cb_used; i++)
+ zfs_close(cb.cb_datasets[i]);
+ free(cb.cb_datasets);
+
+ return (ret);
+}
+
+/*
+ * Unshare and unmount all datasets within the given pool. We don't want to
+ * rely on traversing the DSL to discover the filesystems within the pool,
+ * because this may be expensive (if not all of them are mounted), and can fail
+ * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and
+ * gather all the filesystems that are currently mounted.
+ */
+static int
+mountpoint_compare(const void *a, const void *b)
+{
+ const char *mounta = *((char **)a);
+ const char *mountb = *((char **)b);
+
+ return (strcmp(mountb, mounta));
+}
+
+int
+zpool_unmount_datasets(zpool_handle_t *zhp, boolean_t force)
+{
+ int used, alloc;
+ struct mnttab entry;
+ size_t namelen;
+ char **mountpoints = NULL;
+ zfs_handle_t **datasets = NULL;
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+ int i;
+ int ret = -1;
+ int flags = (force ? MS_FORCE : 0);
+
+ namelen = strlen(zhp->zpool_name);
+
+ rewind(hdl->libzfs_mnttab);
+ used = alloc = 0;
+ while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
+ /*
+ * Ignore non-ZFS entries.
+ */
+ if (entry.mnt_fstype == NULL ||
+ strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
+ continue;
+
+ /*
+ * Ignore filesystems not within this pool.
+ */
+ if (entry.mnt_mountp == NULL ||
+ strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
+ (entry.mnt_special[namelen] != '/' &&
+ entry.mnt_special[namelen] != '\0'))
+ continue;
+
+ /*
+ * At this point we've found a filesystem within our pool. Add
+ * it to our growing list.
+ */
+ if (used == alloc) {
+ if (alloc == 0) {
+ if ((mountpoints = zfs_alloc(hdl,
+ 8 * sizeof (void *))) == NULL)
+ goto out;
+
+ if ((datasets = zfs_alloc(hdl,
+ 8 * sizeof (void *))) == NULL)
+ goto out;
+
+ alloc = 8;
+ } else {
+ char **dest;
+
+ if ((dest = zfs_alloc(hdl,
+ alloc * 2 * sizeof (void *))) == NULL)
+ goto out;
+ (void) memcpy(dest, mountpoints,
+ alloc * sizeof (void *));
+ free(mountpoints);
+ mountpoints = dest;
+
+ if ((dest = zfs_alloc(hdl,
+ alloc * 2 * sizeof (void *))) == NULL)
+ goto out;
+ (void) memcpy(dest, datasets,
+ alloc * sizeof (void *));
+ free(datasets);
+ datasets = (zfs_handle_t **)dest;
+
+ alloc *= 2;
+ }
+ }
+
+ if ((mountpoints[used] = zfs_strdup(hdl,
+ entry.mnt_mountp)) == NULL)
+ goto out;
+
+ /*
+ * This is allowed to fail, in case there is some I/O error. It
+ * is only used to determine if we need to remove the underlying
+ * mountpoint, so failure is not fatal.
+ */
+ datasets[used] = make_dataset_handle(hdl, entry.mnt_special);
+
+ used++;
+ }
+
+ /*
+ * At this point, we have the entire list of filesystems, so sort it by
+ * mountpoint.
+ */
+ qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
+
+ /*
+ * Walk through and first unshare everything.
+ */
+ for (i = 0; i < used; i++) {
+ if (is_shared(hdl, mountpoints[i]) &&
+ unshare_one(hdl, datasets[i] ? datasets[i]->zfs_name :
+ mountpoints[i], mountpoints[i]) != 0)
+ goto out;
+ }
+
+ /*
+ * Now unmount everything, removing the underlying directories as
+ * appropriate.
+ */
+ for (i = 0; i < used; i++) {
+ if (unmount_one(hdl, mountpoints[i], flags) != 0)
+ goto out;
+
+ if (datasets[i])
+ remove_mountpoint(datasets[i]);
+ }
+
+ ret = 0;
+out:
+ for (i = 0; i < used; i++) {
+ if (datasets[i])
+ zfs_close(datasets[i]);
+ free(mountpoints[i]);
+ }
+ free(datasets);
+ free(mountpoints);
+
+ return (ret);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 29e99dc5b1..b09834e57d 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -146,6 +146,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
"spare"));
case EZFS_INVALCONFIG:
return (dgettext(TEXT_DOMAIN, "invalid vdev configuration"));
+ case EZFS_RECURSIVE:
+ return (dgettext(TEXT_DOMAIN, "recursive dataset dependency"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
diff --git a/usr/src/lib/libzfs/spec/libzfs.spec b/usr/src/lib/libzfs/spec/libzfs.spec
index 0ebb03f0b8..e5d33c678a 100644
--- a/usr/src/lib/libzfs/spec/libzfs.spec
+++ b/usr/src/lib/libzfs/spec/libzfs.spec
@@ -352,6 +352,10 @@ function zpool_iter
version SUNWprivate_1.1
end
+function zpool_mount_datasets
+version SUNWprivate_1.1
+end
+
function zpool_open
version SUNWprivate_1.1
end
@@ -372,6 +376,10 @@ function zpool_remove_zvol_links
version SUNWprivate_1.1
end
+function zpool_unmount_datasets
+version SUNWprivate_1.1
+end
+
function zpool_upgrade
version SUNWprivate_1.1
end
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c
index 2daeca32e2..1b580a9078 100644
--- a/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c
@@ -754,7 +754,7 @@ zjni_get_Datasets_dependents(JNIEnv *env, jobjectArray paths)
zfs_handle_t *zhp = zfs_open(g_zfs, path, ZFS_TYPE_ANY);
if (zhp != NULL) {
/* Add all dependents of this Dataset to list */
- (void) zfs_iter_dependents(zhp,
+ (void) zfs_iter_dependents(zhp, B_FALSE,
zjni_create_add_Dataset, &data);
/* Add this Dataset to list (and close zhp) */
diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
index 3a3cb03935..f9d6d76f03 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
@@ -366,6 +366,11 @@ zfs_refresh_properties(vfs_t *vfsp)
else if (vfs_optionisset(vfsp, MNTOPT_EXEC, NULL))
exec_changed_cb(zfsvfs, B_TRUE);
+ if (vfs_optionisset(vfsp, MNTOPT_ATIME, NULL))
+ atime_changed_cb(zfsvfs, B_TRUE);
+ else if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
+ atime_changed_cb(zfsvfs, B_FALSE);
+
return (0);
}