1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <libintl.h> 29 #include <libuutil.h> 30 #include <stddef.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <strings.h> 34 35 #include <libzfs.h> 36 37 #include "zfs_util.h" 38 39 /* 40 * This is a private interface used to gather up all the datasets specified on 41 * the command line so that we can iterate over them in order. 42 * 43 * First, we iterate over all filesystems, gathering them together into an 44 * AVL tree sorted by name. For snapshots, we order them according to 45 * creation time. We report errors for any explicitly specified datasets 46 * that we couldn't open. 47 * 48 * When finished, we have an AVL tree of ZFS handles. We go through and execute 49 * the provided callback for each one, passing whatever data the user supplied. 50 */ 51 52 typedef struct zfs_node { 53 zfs_handle_t *zn_handle; 54 uu_avl_node_t zn_avlnode; 55 } zfs_node_t; 56 57 typedef struct callback_data { 58 uu_avl_t *cb_avl; 59 int cb_recurse; 60 zfs_type_t cb_types; 61 } callback_data_t; 62 63 uu_avl_pool_t *avl_pool; 64 65 /* 66 * Called for each dataset. If the object the object is of an appropriate type, 67 * add it to the avl tree and recurse over any children as necessary. 68 */ 69 int 70 zfs_callback(zfs_handle_t *zhp, void *data) 71 { 72 callback_data_t *cb = data; 73 int dontclose = 0; 74 75 /* 76 * If this object is of the appropriate type, add it to the AVL tree. 77 */ 78 if (zfs_get_type(zhp) & cb->cb_types) { 79 uu_avl_index_t idx; 80 zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); 81 82 node->zn_handle = zhp; 83 uu_avl_node_init(node, &node->zn_avlnode, avl_pool); 84 if (uu_avl_find(cb->cb_avl, node, NULL, &idx) == NULL) { 85 uu_avl_insert(cb->cb_avl, node, idx); 86 dontclose = 1; 87 } else { 88 free(node); 89 } 90 } 91 92 /* 93 * Recurse if necessary. 94 */ 95 if (cb->cb_recurse && (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM || 96 (zfs_get_type(zhp) == ZFS_TYPE_VOLUME && (cb->cb_types & 97 ZFS_TYPE_SNAPSHOT)))) 98 (void) zfs_iter_children(zhp, zfs_callback, data); 99 100 if (!dontclose) 101 zfs_close(zhp); 102 103 return (0); 104 } 105 106 /* ARGSUSED */ 107 static int 108 zfs_compare(const void *larg, const void *rarg, void *unused) 109 { 110 zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 111 zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 112 const char *lname = zfs_get_name(l); 113 const char *rname = zfs_get_name(r); 114 char *lat, *rat; 115 uint64_t lcreate, rcreate; 116 int ret; 117 118 lat = (char *)strchr(lname, '@'); 119 rat = (char *)strchr(rname, '@'); 120 121 if (lat != NULL) 122 *lat = '\0'; 123 if (rat != NULL) 124 *rat = '\0'; 125 126 ret = strcmp(lname, rname); 127 if (ret == 0) { 128 /* 129 * If we're comparing a dataset to one of its snapshots, we 130 * always make the full dataset first. 131 */ 132 if (lat == NULL) { 133 ret = -1; 134 } else if (rat == NULL) { 135 ret = 1; 136 } else { 137 /* 138 * If we have two snapshots from the same dataset, then 139 * we want to sort them according to creation time. We 140 * use the hidden CREATETXG property to get an absolute 141 * ordering of snapshots. 142 */ 143 lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 144 rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 145 146 if (lcreate < rcreate) 147 ret = -1; 148 else if (lcreate > rcreate) 149 ret = 1; 150 } 151 } 152 153 if (lat != NULL) 154 *lat = '@'; 155 if (rat != NULL) 156 *rat = '@'; 157 158 return (ret); 159 } 160 161 int 162 zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, 163 zfs_iter_f callback, void *data) 164 { 165 callback_data_t cb; 166 int ret = 0; 167 zfs_node_t *node; 168 uu_avl_walk_t *walk; 169 170 avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 171 offsetof(zfs_node_t, zn_avlnode), zfs_compare, UU_DEFAULT); 172 173 if (avl_pool == NULL) { 174 (void) fprintf(stderr, 175 gettext("internal error: out of memory\n")); 176 exit(1); 177 } 178 179 cb.cb_recurse = recurse; 180 cb.cb_types = types; 181 if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 182 (void) fprintf(stderr, 183 gettext("internal error: out of memory\n")); 184 exit(1); 185 } 186 187 if (argc == 0) { 188 /* 189 * If given no arguments, iterate over all datasets. 190 */ 191 cb.cb_recurse = 1; 192 ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 193 } else { 194 int i; 195 zfs_handle_t *zhp; 196 zfs_type_t argtype; 197 198 /* 199 * If we're recursive, then we always allow filesystems as 200 * arguments. If we also are interested in snapshots, then we 201 * can take volumes as well. 202 */ 203 argtype = types; 204 if (recurse) { 205 argtype |= ZFS_TYPE_FILESYSTEM; 206 if (types & ZFS_TYPE_SNAPSHOT) 207 argtype |= ZFS_TYPE_VOLUME; 208 } 209 210 for (i = 0; i < argc; i++) { 211 if ((zhp = zfs_open(g_zfs, argv[i], argtype)) != NULL) 212 ret |= zfs_callback(zhp, &cb); 213 else 214 ret = 1; 215 } 216 } 217 218 /* 219 * At this point we've got our AVL tree full of zfs handles, so iterate 220 * over each one and execute the real user callback. 221 */ 222 for (node = uu_avl_first(cb.cb_avl); node != NULL; 223 node = uu_avl_next(cb.cb_avl, node)) 224 ret |= callback(node->zn_handle, data); 225 226 /* 227 * Finally, clean up the AVL tree. 228 */ 229 if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 230 (void) fprintf(stderr, 231 gettext("internal error: out of memory")); 232 exit(1); 233 } 234 235 while ((node = uu_avl_walk_next(walk)) != NULL) { 236 uu_avl_remove(cb.cb_avl, node); 237 zfs_close(node->zn_handle); 238 free(node); 239 } 240 241 uu_avl_walk_end(walk); 242 uu_avl_destroy(cb.cb_avl); 243 uu_avl_pool_destroy(avl_pool); 244 245 return (ret); 246 } 247