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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <libintl.h> 27 #include <libuutil.h> 28 #include <stddef.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <strings.h> 32 33 #include <libzfs.h> 34 35 #include "zfs_util.h" 36 #include "zfs_iter.h" 37 38 /* 39 * This is a private interface used to gather up all the datasets specified on 40 * the command line so that we can iterate over them in order. 41 * 42 * First, we iterate over all filesystems, gathering them together into an 43 * AVL tree. We report errors for any explicitly specified datasets 44 * that we couldn't open. 45 * 46 * When finished, we have an AVL tree of ZFS handles. We go through and execute 47 * the provided callback for each one, passing whatever data the user supplied. 48 */ 49 50 typedef struct zfs_node { 51 zfs_handle_t *zn_handle; 52 uu_avl_node_t zn_avlnode; 53 } zfs_node_t; 54 55 typedef struct callback_data { 56 uu_avl_t *cb_avl; 57 int cb_flags; 58 zfs_type_t cb_types; 59 zfs_sort_column_t *cb_sortcol; 60 zprop_list_t **cb_proplist; 61 int cb_depth_limit; 62 int cb_depth; 63 uint8_t cb_props_table[ZFS_NUM_PROPS]; 64 } callback_data_t; 65 66 uu_avl_pool_t *avl_pool; 67 68 /* 69 * Include snaps if they were requested or if this a zfs list where types 70 * were not specified and the "listsnapshots" property is set on this pool. 71 */ 72 static int 73 zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb) 74 { 75 zpool_handle_t *zph; 76 77 if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0) 78 return (cb->cb_types & ZFS_TYPE_SNAPSHOT); 79 80 zph = zfs_get_pool_handle(zhp); 81 return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL)); 82 } 83 84 /* 85 * Called for each dataset. If the object is of an appropriate type, 86 * add it to the avl tree and recurse over any children as necessary. 87 */ 88 static int 89 zfs_callback(zfs_handle_t *zhp, void *data) 90 { 91 callback_data_t *cb = data; 92 int dontclose = 0; 93 int include_snaps = zfs_include_snapshots(zhp, cb); 94 95 if ((zfs_get_type(zhp) & cb->cb_types) || 96 ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) { 97 uu_avl_index_t idx; 98 zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); 99 100 node->zn_handle = zhp; 101 uu_avl_node_init(node, &node->zn_avlnode, avl_pool); 102 if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, 103 &idx) == NULL) { 104 if (cb->cb_proplist) { 105 if ((*cb->cb_proplist) && 106 !(*cb->cb_proplist)->pl_all) 107 zfs_prune_proplist(zhp, 108 cb->cb_props_table); 109 110 if (zfs_expand_proplist(zhp, cb->cb_proplist, 111 (cb->cb_flags & ZFS_ITER_RECVD_PROPS)) 112 != 0) { 113 free(node); 114 return (-1); 115 } 116 } 117 uu_avl_insert(cb->cb_avl, node, idx); 118 dontclose = 1; 119 } else { 120 free(node); 121 } 122 } 123 124 /* 125 * Recurse if necessary. 126 */ 127 if (cb->cb_flags & ZFS_ITER_RECURSE && 128 ((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 || 129 cb->cb_depth < cb->cb_depth_limit)) { 130 cb->cb_depth++; 131 if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) 132 (void) zfs_iter_filesystems(zhp, zfs_callback, data); 133 if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps) 134 (void) zfs_iter_snapshots(zhp, zfs_callback, data); 135 cb->cb_depth--; 136 } 137 138 if (!dontclose) 139 zfs_close(zhp); 140 141 return (0); 142 } 143 144 int 145 zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, 146 boolean_t reverse) 147 { 148 zfs_sort_column_t *col; 149 zfs_prop_t prop; 150 151 if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL && 152 !zfs_prop_user(name)) 153 return (-1); 154 155 col = safe_malloc(sizeof (zfs_sort_column_t)); 156 157 col->sc_prop = prop; 158 col->sc_reverse = reverse; 159 if (prop == ZPROP_INVAL) { 160 col->sc_user_prop = safe_malloc(strlen(name) + 1); 161 (void) strcpy(col->sc_user_prop, name); 162 } 163 164 if (*sc == NULL) { 165 col->sc_last = col; 166 *sc = col; 167 } else { 168 (*sc)->sc_last->sc_next = col; 169 (*sc)->sc_last = col; 170 } 171 172 return (0); 173 } 174 175 void 176 zfs_free_sort_columns(zfs_sort_column_t *sc) 177 { 178 zfs_sort_column_t *col; 179 180 while (sc != NULL) { 181 col = sc->sc_next; 182 free(sc->sc_user_prop); 183 free(sc); 184 sc = col; 185 } 186 } 187 188 /* ARGSUSED */ 189 static int 190 zfs_compare(const void *larg, const void *rarg, void *unused) 191 { 192 zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 193 zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 194 const char *lname = zfs_get_name(l); 195 const char *rname = zfs_get_name(r); 196 char *lat, *rat; 197 uint64_t lcreate, rcreate; 198 int ret; 199 200 lat = (char *)strchr(lname, '@'); 201 rat = (char *)strchr(rname, '@'); 202 203 if (lat != NULL) 204 *lat = '\0'; 205 if (rat != NULL) 206 *rat = '\0'; 207 208 ret = strcmp(lname, rname); 209 if (ret == 0) { 210 /* 211 * If we're comparing a dataset to one of its snapshots, we 212 * always make the full dataset first. 213 */ 214 if (lat == NULL) { 215 ret = -1; 216 } else if (rat == NULL) { 217 ret = 1; 218 } else { 219 /* 220 * If we have two snapshots from the same dataset, then 221 * we want to sort them according to creation time. We 222 * use the hidden CREATETXG property to get an absolute 223 * ordering of snapshots. 224 */ 225 lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 226 rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 227 228 if (lcreate < rcreate) 229 ret = -1; 230 else if (lcreate > rcreate) 231 ret = 1; 232 } 233 } 234 235 if (lat != NULL) 236 *lat = '@'; 237 if (rat != NULL) 238 *rat = '@'; 239 240 return (ret); 241 } 242 243 /* 244 * Sort datasets by specified columns. 245 * 246 * o Numeric types sort in ascending order. 247 * o String types sort in alphabetical order. 248 * o Types inappropriate for a row sort that row to the literal 249 * bottom, regardless of the specified ordering. 250 * 251 * If no sort columns are specified, or two datasets compare equally 252 * across all specified columns, they are sorted alphabetically by name 253 * with snapshots grouped under their parents. 254 */ 255 static int 256 zfs_sort(const void *larg, const void *rarg, void *data) 257 { 258 zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 259 zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 260 zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 261 zfs_sort_column_t *psc; 262 263 for (psc = sc; psc != NULL; psc = psc->sc_next) { 264 char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; 265 char *lstr, *rstr; 266 uint64_t lnum, rnum; 267 boolean_t lvalid, rvalid; 268 int ret = 0; 269 270 /* 271 * We group the checks below the generic code. If 'lstr' and 272 * 'rstr' are non-NULL, then we do a string based comparison. 273 * Otherwise, we compare 'lnum' and 'rnum'. 274 */ 275 lstr = rstr = NULL; 276 if (psc->sc_prop == ZPROP_INVAL) { 277 nvlist_t *luser, *ruser; 278 nvlist_t *lval, *rval; 279 280 luser = zfs_get_user_props(l); 281 ruser = zfs_get_user_props(r); 282 283 lvalid = (nvlist_lookup_nvlist(luser, 284 psc->sc_user_prop, &lval) == 0); 285 rvalid = (nvlist_lookup_nvlist(ruser, 286 psc->sc_user_prop, &rval) == 0); 287 288 if (lvalid) 289 verify(nvlist_lookup_string(lval, 290 ZPROP_VALUE, &lstr) == 0); 291 if (rvalid) 292 verify(nvlist_lookup_string(rval, 293 ZPROP_VALUE, &rstr) == 0); 294 295 } else if (zfs_prop_is_string(psc->sc_prop)) { 296 lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, 297 sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); 298 rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, 299 sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); 300 301 lstr = lbuf; 302 rstr = rbuf; 303 } else { 304 lvalid = zfs_prop_valid_for_type(psc->sc_prop, 305 zfs_get_type(l)); 306 rvalid = zfs_prop_valid_for_type(psc->sc_prop, 307 zfs_get_type(r)); 308 309 if (lvalid) 310 (void) zfs_prop_get_numeric(l, psc->sc_prop, 311 &lnum, NULL, NULL, 0); 312 if (rvalid) 313 (void) zfs_prop_get_numeric(r, psc->sc_prop, 314 &rnum, NULL, NULL, 0); 315 } 316 317 if (!lvalid && !rvalid) 318 continue; 319 else if (!lvalid) 320 return (1); 321 else if (!rvalid) 322 return (-1); 323 324 if (lstr) 325 ret = strcmp(lstr, rstr); 326 else if (lnum < rnum) 327 ret = -1; 328 else if (lnum > rnum) 329 ret = 1; 330 331 if (ret != 0) { 332 if (psc->sc_reverse == B_TRUE) 333 ret = (ret < 0) ? 1 : -1; 334 return (ret); 335 } 336 } 337 338 return (zfs_compare(larg, rarg, NULL)); 339 } 340 341 int 342 zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, 343 zfs_sort_column_t *sortcol, zprop_list_t **proplist, int limit, 344 zfs_iter_f callback, void *data) 345 { 346 callback_data_t cb = {0}; 347 int ret = 0; 348 zfs_node_t *node; 349 uu_avl_walk_t *walk; 350 351 avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 352 offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 353 354 if (avl_pool == NULL) { 355 (void) fprintf(stderr, 356 gettext("internal error: out of memory\n")); 357 exit(1); 358 } 359 360 cb.cb_sortcol = sortcol; 361 cb.cb_flags = flags; 362 cb.cb_proplist = proplist; 363 cb.cb_types = types; 364 cb.cb_depth_limit = limit; 365 /* 366 * If cb_proplist is provided then in the zfs_handles created we 367 * retain only those properties listed in cb_proplist and sortcol. 368 * The rest are pruned. So, the caller should make sure that no other 369 * properties other than those listed in cb_proplist/sortcol are 370 * accessed. 371 * 372 * If cb_proplist is NULL then we retain all the properties. We 373 * always retain the zoned property, which some other properties 374 * need (userquota & friends), and the createtxg property, which 375 * we need to sort snapshots. 376 */ 377 if (cb.cb_proplist && *cb.cb_proplist) { 378 zprop_list_t *p = *cb.cb_proplist; 379 380 while (p) { 381 if (p->pl_prop >= ZFS_PROP_TYPE && 382 p->pl_prop < ZFS_NUM_PROPS) { 383 cb.cb_props_table[p->pl_prop] = B_TRUE; 384 } 385 p = p->pl_next; 386 } 387 388 while (sortcol) { 389 if (sortcol->sc_prop >= ZFS_PROP_TYPE && 390 sortcol->sc_prop < ZFS_NUM_PROPS) { 391 cb.cb_props_table[sortcol->sc_prop] = B_TRUE; 392 } 393 sortcol = sortcol->sc_next; 394 } 395 396 cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE; 397 cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE; 398 } else { 399 (void) memset(cb.cb_props_table, B_TRUE, 400 sizeof (cb.cb_props_table)); 401 } 402 403 if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 404 (void) fprintf(stderr, 405 gettext("internal error: out of memory\n")); 406 exit(1); 407 } 408 409 if (argc == 0) { 410 /* 411 * If given no arguments, iterate over all datasets. 412 */ 413 cb.cb_flags |= ZFS_ITER_RECURSE; 414 ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 415 } else { 416 int i; 417 zfs_handle_t *zhp; 418 zfs_type_t argtype; 419 420 /* 421 * If we're recursive, then we always allow filesystems as 422 * arguments. If we also are interested in snapshots, then we 423 * can take volumes as well. 424 */ 425 argtype = types; 426 if (flags & ZFS_ITER_RECURSE) { 427 argtype |= ZFS_TYPE_FILESYSTEM; 428 if (types & ZFS_TYPE_SNAPSHOT) 429 argtype |= ZFS_TYPE_VOLUME; 430 } 431 432 for (i = 0; i < argc; i++) { 433 if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) { 434 zhp = zfs_path_to_zhandle(g_zfs, argv[i], 435 argtype); 436 } else { 437 zhp = zfs_open(g_zfs, argv[i], argtype); 438 } 439 if (zhp != NULL) 440 ret |= zfs_callback(zhp, &cb); 441 else 442 ret = 1; 443 } 444 } 445 446 /* 447 * At this point we've got our AVL tree full of zfs handles, so iterate 448 * over each one and execute the real user callback. 449 */ 450 for (node = uu_avl_first(cb.cb_avl); node != NULL; 451 node = uu_avl_next(cb.cb_avl, node)) 452 ret |= callback(node->zn_handle, data); 453 454 /* 455 * Finally, clean up the AVL tree. 456 */ 457 if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 458 (void) fprintf(stderr, 459 gettext("internal error: out of memory")); 460 exit(1); 461 } 462 463 while ((node = uu_avl_walk_next(walk)) != NULL) { 464 uu_avl_remove(cb.cb_avl, node); 465 zfs_close(node->zn_handle); 466 free(node); 467 } 468 469 uu_avl_walk_end(walk); 470 uu_avl_destroy(cb.cb_avl); 471 uu_avl_pool_destroy(avl_pool); 472 473 return (ret); 474 } 475