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