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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "@(#)zfs_iter.c 1.8 07/10/29 SMI"
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.
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.
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.
52 typedef struct zfs_node
{
53 zfs_handle_t
*zn_handle
;
54 uu_avl_node_t zn_avlnode
;
57 typedef struct callback_data
{
61 zfs_sort_column_t
*cb_sortcol
;
62 zprop_list_t
**cb_proplist
;
65 uu_avl_pool_t
*avl_pool
;
68 * Called for each dataset. If the object the object is of an appropriate type,
69 * add it to the avl tree and recurse over any children as necessary.
72 zfs_callback(zfs_handle_t
*zhp
, void *data
)
74 callback_data_t
*cb
= data
;
78 * If this object is of the appropriate type, add it to the AVL tree.
80 if (zfs_get_type(zhp
) & cb
->cb_types
) {
82 zfs_node_t
*node
= safe_malloc(sizeof (zfs_node_t
));
84 node
->zn_handle
= zhp
;
85 uu_avl_node_init(node
, &node
->zn_avlnode
, avl_pool
);
86 if (uu_avl_find(cb
->cb_avl
, node
, cb
->cb_sortcol
,
88 if (cb
->cb_proplist
&&
89 zfs_expand_proplist(zhp
, cb
->cb_proplist
) != 0) {
93 uu_avl_insert(cb
->cb_avl
, node
, idx
);
101 * Recurse if necessary.
103 if (cb
->cb_recurse
) {
104 if (zfs_get_type(zhp
) == ZFS_TYPE_FILESYSTEM
)
105 (void) zfs_iter_filesystems(zhp
, zfs_callback
, data
);
106 if (zfs_get_type(zhp
) != ZFS_TYPE_SNAPSHOT
&&
107 (cb
->cb_types
& ZFS_TYPE_SNAPSHOT
))
108 (void) zfs_iter_snapshots(zhp
, zfs_callback
, data
);
118 zfs_add_sort_column(zfs_sort_column_t
**sc
, const char *name
,
121 zfs_sort_column_t
*col
;
124 if ((prop
= zfs_name_to_prop(name
)) == ZPROP_INVAL
&&
125 !zfs_prop_user(name
))
128 col
= safe_malloc(sizeof (zfs_sort_column_t
));
131 col
->sc_reverse
= reverse
;
132 if (prop
== ZPROP_INVAL
) {
133 col
->sc_user_prop
= safe_malloc(strlen(name
) + 1);
134 (void) strcpy(col
->sc_user_prop
, name
);
141 (*sc
)->sc_last
->sc_next
= col
;
142 (*sc
)->sc_last
= col
;
149 zfs_free_sort_columns(zfs_sort_column_t
*sc
)
151 zfs_sort_column_t
*col
;
155 free(sc
->sc_user_prop
);
163 zfs_compare(const void *larg
, const void *rarg
, void *unused
)
165 zfs_handle_t
*l
= ((zfs_node_t
*)larg
)->zn_handle
;
166 zfs_handle_t
*r
= ((zfs_node_t
*)rarg
)->zn_handle
;
167 const char *lname
= zfs_get_name(l
);
168 const char *rname
= zfs_get_name(r
);
170 uint64_t lcreate
, rcreate
;
173 lat
= (char *)strchr(lname
, '@');
174 rat
= (char *)strchr(rname
, '@');
181 ret
= strcmp(lname
, rname
);
184 * If we're comparing a dataset to one of its snapshots, we
185 * always make the full dataset first.
189 } else if (rat
== NULL
) {
193 * If we have two snapshots from the same dataset, then
194 * we want to sort them according to creation time. We
195 * use the hidden CREATETXG property to get an absolute
196 * ordering of snapshots.
198 lcreate
= zfs_prop_get_int(l
, ZFS_PROP_CREATETXG
);
199 rcreate
= zfs_prop_get_int(r
, ZFS_PROP_CREATETXG
);
201 if (lcreate
< rcreate
)
203 else if (lcreate
> rcreate
)
217 * Sort datasets by specified columns.
219 * o Numeric types sort in ascending order.
220 * o String types sort in alphabetical order.
221 * o Types inappropriate for a row sort that row to the literal
222 * bottom, regardless of the specified ordering.
224 * If no sort columns are specified, or two datasets compare equally
225 * across all specified columns, they are sorted alphabetically by name
226 * with snapshots grouped under their parents.
229 zfs_sort(const void *larg
, const void *rarg
, void *data
)
231 zfs_handle_t
*l
= ((zfs_node_t
*)larg
)->zn_handle
;
232 zfs_handle_t
*r
= ((zfs_node_t
*)rarg
)->zn_handle
;
233 zfs_sort_column_t
*sc
= (zfs_sort_column_t
*)data
;
234 zfs_sort_column_t
*psc
;
236 for (psc
= sc
; psc
!= NULL
; psc
= psc
->sc_next
) {
237 char lbuf
[ZFS_MAXPROPLEN
], rbuf
[ZFS_MAXPROPLEN
];
240 boolean_t lvalid
, rvalid
;
244 * We group the checks below the generic code. If 'lstr' and
245 * 'rstr' are non-NULL, then we do a string based comparison.
246 * Otherwise, we compare 'lnum' and 'rnum'.
249 if (psc
->sc_prop
== ZPROP_INVAL
) {
250 nvlist_t
*luser
, *ruser
;
251 nvlist_t
*lval
, *rval
;
253 luser
= zfs_get_user_props(l
);
254 ruser
= zfs_get_user_props(r
);
256 lvalid
= (nvlist_lookup_nvlist(luser
,
257 psc
->sc_user_prop
, &lval
) == 0);
258 rvalid
= (nvlist_lookup_nvlist(ruser
,
259 psc
->sc_user_prop
, &rval
) == 0);
262 verify(nvlist_lookup_string(lval
,
263 ZPROP_VALUE
, &lstr
) == 0);
265 verify(nvlist_lookup_string(rval
,
266 ZPROP_VALUE
, &rstr
) == 0);
268 } else if (zfs_prop_is_string(psc
->sc_prop
)) {
269 lvalid
= (zfs_prop_get(l
, psc
->sc_prop
, lbuf
,
270 sizeof (lbuf
), NULL
, NULL
, 0, B_TRUE
) == 0);
271 rvalid
= (zfs_prop_get(r
, psc
->sc_prop
, rbuf
,
272 sizeof (rbuf
), NULL
, NULL
, 0, B_TRUE
) == 0);
277 lvalid
= zfs_prop_valid_for_type(psc
->sc_prop
,
279 rvalid
= zfs_prop_valid_for_type(psc
->sc_prop
,
283 (void) zfs_prop_get_numeric(l
, psc
->sc_prop
,
284 &lnum
, NULL
, NULL
, 0);
286 (void) zfs_prop_get_numeric(r
, psc
->sc_prop
,
287 &rnum
, NULL
, NULL
, 0);
290 if (!lvalid
&& !rvalid
)
298 ret
= strcmp(lstr
, rstr
);
301 else if (lnum
> rnum
)
305 if (psc
->sc_reverse
== B_TRUE
)
306 ret
= (ret
< 0) ? 1 : -1;
311 return (zfs_compare(larg
, rarg
, NULL
));
315 zfs_for_each(int argc
, char **argv
, boolean_t recurse
, zfs_type_t types
,
316 zfs_sort_column_t
*sortcol
, zprop_list_t
**proplist
, zfs_iter_f callback
,
317 void *data
, boolean_t args_can_be_paths
)
324 avl_pool
= uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t
),
325 offsetof(zfs_node_t
, zn_avlnode
), zfs_sort
, UU_DEFAULT
);
327 if (avl_pool
== NULL
) {
328 (void) fprintf(stderr
,
329 gettext("internal error: out of memory\n"));
333 cb
.cb_sortcol
= sortcol
;
334 cb
.cb_recurse
= recurse
;
335 cb
.cb_proplist
= proplist
;
337 if ((cb
.cb_avl
= uu_avl_create(avl_pool
, NULL
, UU_DEFAULT
)) == NULL
) {
338 (void) fprintf(stderr
,
339 gettext("internal error: out of memory\n"));
345 * If given no arguments, iterate over all datasets.
348 ret
= zfs_iter_root(g_zfs
, zfs_callback
, &cb
);
355 * If we're recursive, then we always allow filesystems as
356 * arguments. If we also are interested in snapshots, then we
357 * can take volumes as well.
361 argtype
|= ZFS_TYPE_FILESYSTEM
;
362 if (types
& ZFS_TYPE_SNAPSHOT
)
363 argtype
|= ZFS_TYPE_VOLUME
;
366 for (i
= 0; i
< argc
; i
++) {
367 if (args_can_be_paths
) {
368 zhp
= zfs_path_to_zhandle(g_zfs
, argv
[i
],
371 zhp
= zfs_open(g_zfs
, argv
[i
], argtype
);
374 ret
|= zfs_callback(zhp
, &cb
);
381 * At this point we've got our AVL tree full of zfs handles, so iterate
382 * over each one and execute the real user callback.
384 for (node
= uu_avl_first(cb
.cb_avl
); node
!= NULL
;
385 node
= uu_avl_next(cb
.cb_avl
, node
))
386 ret
|= callback(node
->zn_handle
, data
);
389 * Finally, clean up the AVL tree.
391 if ((walk
= uu_avl_walk_start(cb
.cb_avl
, UU_WALK_ROBUST
)) == NULL
) {
392 (void) fprintf(stderr
,
393 gettext("internal error: out of memory"));
397 while ((node
= uu_avl_walk_next(walk
)) != NULL
) {
398 uu_avl_remove(cb
.cb_avl
, node
);
399 zfs_close(node
->zn_handle
);
403 uu_avl_walk_end(walk
);
404 uu_avl_destroy(cb
.cb_avl
);
405 uu_avl_pool_destroy(avl_pool
);