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