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