]> git.proxmox.com Git - mirror_zfs.git/blobdiff - cmd/zfs/zfs_iter.c
Do not iterate through filesystems unnecessarily
[mirror_zfs.git] / cmd / zfs / zfs_iter.c
index a22370a0279561f2b8082b7c38d06264f949c1f0..d10bbed7dbd62101fbb07d48dd1bbb2de88b6453 100644 (file)
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
 #include <libintl.h>
@@ -28,6 +31,7 @@
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <strings.h>
 
 #include <libzfs.h>
@@ -53,11 +57,14 @@ typedef struct zfs_node {
 } zfs_node_t;
 
 typedef struct callback_data {
-       uu_avl_t        *cb_avl;
-       int             cb_flags;
-       zfs_type_t      cb_types;
-       zfs_sort_column_t *cb_sortcol;
-       zprop_list_t    **cb_proplist;
+       uu_avl_t                *cb_avl;
+       int                     cb_flags;
+       zfs_type_t              cb_types;
+       zfs_sort_column_t       *cb_sortcol;
+       zprop_list_t            **cb_proplist;
+       int                     cb_depth_limit;
+       int                     cb_depth;
+       uint8_t                 cb_props_table[ZFS_NUM_PROPS];
 } callback_data_t;
 
 uu_avl_pool_t *avl_pool;
@@ -66,7 +73,7 @@ uu_avl_pool_t *avl_pool;
  * Include snaps if they were requested or if this a zfs list where types
  * were not specified and the "listsnapshots" property is set on this pool.
  */
-static int
+static boolean_t
 zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb)
 {
        zpool_handle_t *zph;
@@ -86,8 +93,9 @@ static int
 zfs_callback(zfs_handle_t *zhp, void *data)
 {
        callback_data_t *cb = data;
-       int dontclose = 0;
-       int include_snaps = zfs_include_snapshots(zhp, cb);
+       boolean_t should_close = B_TRUE;
+       boolean_t include_snaps = zfs_include_snapshots(zhp, cb);
+       boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK);
 
        if ((zfs_get_type(zhp) & cb->cb_types) ||
            ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {
@@ -98,13 +106,22 @@ zfs_callback(zfs_handle_t *zhp, void *data)
                uu_avl_node_init(node, &node->zn_avlnode, avl_pool);
                if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol,
                    &idx) == NULL) {
-                       if (cb->cb_proplist &&
-                           zfs_expand_proplist(zhp, cb->cb_proplist) != 0) {
-                               free(node);
-                               return (-1);
+                       if (cb->cb_proplist) {
+                               if ((*cb->cb_proplist) &&
+                                   !(*cb->cb_proplist)->pl_all)
+                                       zfs_prune_proplist(zhp,
+                                           cb->cb_props_table);
+
+                               if (zfs_expand_proplist(zhp, cb->cb_proplist,
+                                   (cb->cb_flags & ZFS_ITER_RECVD_PROPS),
+                                   (cb->cb_flags & ZFS_ITER_LITERAL_PROPS))
+                                   != 0) {
+                                       free(node);
+                                       return (-1);
+                               }
                        }
                        uu_avl_insert(cb->cb_avl, node, idx);
-                       dontclose = 1;
+                       should_close = B_FALSE;
                } else {
                        free(node);
                }
@@ -113,14 +130,39 @@ zfs_callback(zfs_handle_t *zhp, void *data)
        /*
         * Recurse if necessary.
         */
-       if (cb->cb_flags & ZFS_ITER_RECURSE) {
-               if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
+       if (cb->cb_flags & ZFS_ITER_RECURSE &&
+           ((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||
+           cb->cb_depth < cb->cb_depth_limit)) {
+               cb->cb_depth++;
+
+               /*
+                * If we are not looking for filesystems, we don't need to
+                * recurse into filesystems when we are at our depth limit.
+                */
+               if ((cb->cb_depth < cb->cb_depth_limit ||
+                   (cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||
+                   (cb->cb_types &
+                   (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) &&
+                   zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
                        (void) zfs_iter_filesystems(zhp, zfs_callback, data);
-               if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps)
-                       (void) zfs_iter_snapshots(zhp, zfs_callback, data);
+               }
+
+               if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
+                   ZFS_TYPE_BOOKMARK)) == 0) && include_snaps) {
+                       (void) zfs_iter_snapshots(zhp,
+                           (cb->cb_flags & ZFS_ITER_SIMPLE) != 0,
+                           zfs_callback, data, 0, 0);
+               }
+
+               if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
+                   ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks) {
+                       (void) zfs_iter_bookmarks(zhp, zfs_callback, data);
+               }
+
+               cb->cb_depth--;
        }
 
-       if (!dontclose)
+       if (should_close)
                zfs_close(zhp);
 
        return (0);
@@ -170,6 +212,13 @@ zfs_free_sort_columns(zfs_sort_column_t *sc)
        }
 }
 
+int
+zfs_sort_only_by_name(const zfs_sort_column_t *sc)
+{
+       return (sc != NULL && sc->sc_next == NULL &&
+           sc->sc_prop == ZFS_PROP_NAME);
+}
+
 /* ARGSUSED */
 static int
 zfs_compare(const void *larg, const void *rarg, void *unused)
@@ -210,7 +259,13 @@ zfs_compare(const void *larg, const void *rarg, void *unused)
                        lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
                        rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
 
-                       if (lcreate < rcreate)
+                       /*
+                        * Both lcreate and rcreate being 0 means we don't have
+                        * properties and we should compare full name.
+                        */
+                       if (lcreate == 0 && rcreate == 0)
+                               ret = strcmp(lat + 1, rat + 1);
+                       else if (lcreate < rcreate)
                                ret = -1;
                        else if (lcreate > rcreate)
                                ret = 1;
@@ -276,7 +331,14 @@ zfs_sort(const void *larg, const void *rarg, void *data)
                        if (rvalid)
                                verify(nvlist_lookup_string(rval,
                                    ZPROP_VALUE, &rstr) == 0);
+               } else if (psc->sc_prop == ZFS_PROP_NAME) {
+                       lvalid = rvalid = B_TRUE;
 
+                       (void) strlcpy(lbuf, zfs_get_name(l), sizeof (lbuf));
+                       (void) strlcpy(rbuf, zfs_get_name(r), sizeof (rbuf));
+
+                       lstr = lbuf;
+                       rstr = rbuf;
                } else if (zfs_prop_is_string(psc->sc_prop)) {
                        lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,
                            sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0);
@@ -287,9 +349,9 @@ zfs_sort(const void *larg, const void *rarg, void *data)
                        rstr = rbuf;
                } else {
                        lvalid = zfs_prop_valid_for_type(psc->sc_prop,
-                           zfs_get_type(l));
+                           zfs_get_type(l), B_FALSE);
                        rvalid = zfs_prop_valid_for_type(psc->sc_prop,
-                           zfs_get_type(r));
+                           zfs_get_type(r), B_FALSE);
 
                        if (lvalid)
                                (void) zfs_prop_get_numeric(l, psc->sc_prop,
@@ -325,10 +387,10 @@ zfs_sort(const void *larg, const void *rarg, void *data)
 
 int
 zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,
-    zfs_sort_column_t *sortcol, zprop_list_t **proplist,
+    zfs_sort_column_t *sortcol, zprop_list_t **proplist, int limit,
     zfs_iter_f callback, void *data)
 {
-       callback_data_t cb;
+       callback_data_t cb = {0};
        int ret = 0;
        zfs_node_t *node;
        uu_avl_walk_t *walk;
@@ -336,22 +398,55 @@ zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,
        avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t),
            offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT);
 
-       if (avl_pool == NULL) {
-               (void) fprintf(stderr,
-                   gettext("internal error: out of memory\n"));
-               exit(1);
-       }
+       if (avl_pool == NULL)
+               nomem();
 
        cb.cb_sortcol = sortcol;
        cb.cb_flags = flags;
        cb.cb_proplist = proplist;
        cb.cb_types = types;
-       if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) {
-               (void) fprintf(stderr,
-                   gettext("internal error: out of memory\n"));
-               exit(1);
+       cb.cb_depth_limit = limit;
+       /*
+        * If cb_proplist is provided then in the zfs_handles created we
+        * retain only those properties listed in cb_proplist and sortcol.
+        * The rest are pruned. So, the caller should make sure that no other
+        * properties other than those listed in cb_proplist/sortcol are
+        * accessed.
+        *
+        * If cb_proplist is NULL then we retain all the properties.  We
+        * always retain the zoned property, which some other properties
+        * need (userquota & friends), and the createtxg property, which
+        * we need to sort snapshots.
+        */
+       if (cb.cb_proplist && *cb.cb_proplist) {
+               zprop_list_t *p = *cb.cb_proplist;
+
+               while (p) {
+                       if (p->pl_prop >= ZFS_PROP_TYPE &&
+                           p->pl_prop < ZFS_NUM_PROPS) {
+                               cb.cb_props_table[p->pl_prop] = B_TRUE;
+                       }
+                       p = p->pl_next;
+               }
+
+               while (sortcol) {
+                       if (sortcol->sc_prop >= ZFS_PROP_TYPE &&
+                           sortcol->sc_prop < ZFS_NUM_PROPS) {
+                               cb.cb_props_table[sortcol->sc_prop] = B_TRUE;
+                       }
+                       sortcol = sortcol->sc_next;
+               }
+
+               cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE;
+               cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE;
+       } else {
+               (void) memset(cb.cb_props_table, B_TRUE,
+                   sizeof (cb.cb_props_table));
        }
 
+       if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
+               nomem();
+
        if (argc == 0) {
                /*
                 * If given no arguments, iterate over all datasets.
@@ -365,13 +460,13 @@ zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,
 
                /*
                 * If we're recursive, then we always allow filesystems as
-                * arguments.  If we also are interested in snapshots, then we
-                * can take volumes as well.
+                * arguments.  If we also are interested in snapshots or
+                * bookmarks, then we can take volumes as well.
                 */
                argtype = types;
                if (flags & ZFS_ITER_RECURSE) {
                        argtype |= ZFS_TYPE_FILESYSTEM;
-                       if (types & ZFS_TYPE_SNAPSHOT)
+                       if (types & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK))
                                argtype |= ZFS_TYPE_VOLUME;
                }
 
@@ -400,11 +495,8 @@ zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,
        /*
         * Finally, clean up the AVL tree.
         */
-       if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) {
-               (void) fprintf(stderr,
-                   gettext("internal error: out of memory"));
-               exit(1);
-       }
+       if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
+               nomem();
 
        while ((node = uu_avl_walk_next(walk)) != NULL) {
                uu_avl_remove(cb.cb_avl, node);