diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/zfs/zfs_main.c | 43 | ||||
-rw-r--r-- | usr/src/cmd/zoneadm/zfs.c | 6 | ||||
-rw-r--r-- | usr/src/cmd/zpool/Makefile | 9 | ||||
-rw-r--r-- | usr/src/cmd/zpool/zpool_dataset.c | 154 | ||||
-rw-r--r-- | usr/src/cmd/zpool/zpool_main.c | 8 | ||||
-rw-r--r-- | usr/src/cmd/zpool/zpool_util.h | 6 | ||||
-rw-r--r-- | usr/src/common/nvpair/nvpair.c | 16 | ||||
-rw-r--r-- | usr/src/common/zfs/zfs_prop.c | 7 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs.h | 9 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_changelist.c | 79 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_config.c | 3 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_dataset.c | 59 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_graph.c | 109 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_impl.h | 4 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_import.c | 7 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_mount.c | 384 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_util.c | 2 | ||||
-rw-r--r-- | usr/src/lib/libzfs/spec/libzfs.spec | 8 | ||||
-rw-r--r-- | usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c | 2 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_vfsops.c | 5 |
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); } |