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 #include "zfs_iter.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. 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 zfs_sort_column_t *cb_sortcol; 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, cb->cb_sortcol, 86 &idx) == NULL) { 87 uu_avl_insert(cb->cb_avl, node, idx); 88 dontclose = 1; 89 } else { 90 free(node); 91 } 92 } 93 94 /* 95 * Recurse if necessary. 96 */ 97 if (cb->cb_recurse && (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM || 98 (zfs_get_type(zhp) == ZFS_TYPE_VOLUME && (cb->cb_types & 99 ZFS_TYPE_SNAPSHOT)))) 100 (void) zfs_iter_children(zhp, zfs_callback, data); 101 102 if (!dontclose) 103 zfs_close(zhp); 104 105 return (0); 106 } 107 108 void 109 zfs_add_sort_column(zfs_sort_column_t **sc, zfs_prop_t prop, 110 boolean_t reverse) 111 { 112 zfs_sort_column_t *col; 113 114 col = safe_malloc(sizeof (zfs_sort_column_t)); 115 116 col->sc_prop = prop; 117 col->sc_reverse = reverse; 118 col->sc_next = NULL; 119 120 if (*sc == NULL) { 121 col->sc_last = col; 122 *sc = col; 123 } else { 124 (*sc)->sc_last->sc_next = col; 125 (*sc)->sc_last = col; 126 } 127 } 128 129 void 130 zfs_free_sort_columns(zfs_sort_column_t *sc) 131 { 132 zfs_sort_column_t *col; 133 134 while (sc != NULL) { 135 col = sc->sc_next; 136 free(sc); 137 sc = col; 138 } 139 } 140 141 /* ARGSUSED */ 142 static int 143 zfs_compare(const void *larg, const void *rarg, void *unused) 144 { 145 zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 146 zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 147 const char *lname = zfs_get_name(l); 148 const char *rname = zfs_get_name(r); 149 char *lat, *rat; 150 uint64_t lcreate, rcreate; 151 int ret; 152 153 lat = (char *)strchr(lname, '@'); 154 rat = (char *)strchr(rname, '@'); 155 156 if (lat != NULL) 157 *lat = '\0'; 158 if (rat != NULL) 159 *rat = '\0'; 160 161 ret = strcmp(lname, rname); 162 if (ret == 0) { 163 /* 164 * If we're comparing a dataset to one of its snapshots, we 165 * always make the full dataset first. 166 */ 167 if (lat == NULL) { 168 ret = -1; 169 } else if (rat == NULL) { 170 ret = 1; 171 } else { 172 /* 173 * If we have two snapshots from the same dataset, then 174 * we want to sort them according to creation time. We 175 * use the hidden CREATETXG property to get an absolute 176 * ordering of snapshots. 177 */ 178 lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 179 rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 180 181 if (lcreate < rcreate) 182 ret = -1; 183 else if (lcreate > rcreate) 184 ret = 1; 185 } 186 } 187 188 if (lat != NULL) 189 *lat = '@'; 190 if (rat != NULL) 191 *rat = '@'; 192 193 return (ret); 194 } 195 196 /* 197 * Sort datasets by specified columns. 198 * 199 * o Numeric types sort in ascending order. 200 * o String types sort in alphabetical order. 201 * o Types inappropriate for a row sort that row to the literal 202 * bottom, regardless of the specified ordering. 203 * 204 * If no sort columns are specified, or two datasets compare equally 205 * across all specified columns, they are sorted alphabetically by name 206 * with snapshots grouped under their parents. 207 */ 208 static int 209 zfs_sort(const void *larg, const void *rarg, void *data) 210 { 211 zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 212 zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 213 zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 214 zfs_sort_column_t *psc; 215 216 for (psc = sc; psc != NULL; psc = psc->sc_next) { 217 char lstr[ZFS_MAXPROPLEN], rstr[ZFS_MAXPROPLEN]; 218 uint64_t lnum, rnum; 219 int lvalid, rvalid; 220 int ret = 0; 221 222 if (zfs_prop_is_string(psc->sc_prop)) { 223 lvalid = zfs_prop_get(l, psc->sc_prop, lstr, 224 sizeof (lstr), NULL, NULL, 0, B_TRUE); 225 rvalid = zfs_prop_get(r, psc->sc_prop, rstr, 226 sizeof (rstr), NULL, NULL, 0, B_TRUE); 227 228 if ((lvalid == -1) && (rvalid == -1)) 229 continue; 230 if (lvalid == -1) 231 return (1); 232 else if (rvalid == -1) 233 return (-1); 234 235 ret = strcmp(lstr, rstr); 236 } else { 237 lvalid = zfs_prop_valid_for_type(psc->sc_prop, 238 zfs_get_type(l)); 239 rvalid = zfs_prop_valid_for_type(psc->sc_prop, 240 zfs_get_type(r)); 241 242 if (!lvalid && !rvalid) 243 continue; 244 else if (!lvalid) 245 return (1); 246 else if (!rvalid) 247 return (-1); 248 249 (void) zfs_prop_get_numeric(l, psc->sc_prop, &lnum, 250 NULL, NULL, 0); 251 (void) zfs_prop_get_numeric(r, psc->sc_prop, &rnum, 252 NULL, NULL, 0); 253 254 if (lnum < rnum) 255 ret = -1; 256 else if (lnum > rnum) 257 ret = 1; 258 } 259 260 if (ret != 0) { 261 if (psc->sc_reverse == B_TRUE) 262 ret = (ret < 0) ? 1 : -1; 263 return (ret); 264 } 265 } 266 267 return (zfs_compare(larg, rarg, NULL)); 268 } 269 270 int 271 zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, 272 zfs_sort_column_t *sortcol, zfs_iter_f callback, void *data) 273 { 274 callback_data_t cb; 275 int ret = 0; 276 zfs_node_t *node; 277 uu_avl_walk_t *walk; 278 279 avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 280 offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 281 282 if (avl_pool == NULL) { 283 (void) fprintf(stderr, 284 gettext("internal error: out of memory\n")); 285 exit(1); 286 } 287 288 cb.cb_sortcol = sortcol; 289 cb.cb_recurse = recurse; 290 cb.cb_types = types; 291 if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 292 (void) fprintf(stderr, 293 gettext("internal error: out of memory\n")); 294 exit(1); 295 } 296 297 if (argc == 0) { 298 /* 299 * If given no arguments, iterate over all datasets. 300 */ 301 cb.cb_recurse = 1; 302 ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 303 } else { 304 int i; 305 zfs_handle_t *zhp; 306 zfs_type_t argtype; 307 308 /* 309 * If we're recursive, then we always allow filesystems as 310 * arguments. If we also are interested in snapshots, then we 311 * can take volumes as well. 312 */ 313 argtype = types; 314 if (recurse) { 315 argtype |= ZFS_TYPE_FILESYSTEM; 316 if (types & ZFS_TYPE_SNAPSHOT) 317 argtype |= ZFS_TYPE_VOLUME; 318 } 319 320 for (i = 0; i < argc; i++) { 321 if ((zhp = zfs_open(g_zfs, argv[i], argtype)) != NULL) 322 ret |= zfs_callback(zhp, &cb); 323 else 324 ret = 1; 325 } 326 } 327 328 /* 329 * At this point we've got our AVL tree full of zfs handles, so iterate 330 * over each one and execute the real user callback. 331 */ 332 for (node = uu_avl_first(cb.cb_avl); node != NULL; 333 node = uu_avl_next(cb.cb_avl, node)) 334 ret |= callback(node->zn_handle, data); 335 336 /* 337 * Finally, clean up the AVL tree. 338 */ 339 if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 340 (void) fprintf(stderr, 341 gettext("internal error: out of memory")); 342 exit(1); 343 } 344 345 while ((node = uu_avl_walk_next(walk)) != NULL) { 346 uu_avl_remove(cb.cb_avl, node); 347 zfs_close(node->zn_handle); 348 free(node); 349 } 350 351 uu_avl_walk_end(walk); 352 uu_avl_destroy(cb.cb_avl); 353 uu_avl_pool_destroy(avl_pool); 354 355 return (ret); 356 } 357