libzfs_iter.c revision 228103
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 2010 Nexenta Systems, Inc. All rights reserved.
25 * Copyright (c) 2011 by Delphix. All rights reserved.
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <strings.h>
31#include <unistd.h>
32#include <stddef.h>
33#include <libintl.h>
34#include <libzfs.h>
35
36#include "libzfs_impl.h"
37
38int
39zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
40{
41	nvlist_t *nvl = zfs_get_clones_nvl(zhp);
42	nvpair_t *pair;
43
44	if (nvl == NULL)
45		return (0);
46
47	for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
48	    pair = nvlist_next_nvpair(nvl, pair)) {
49		zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
50		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
51		if (clone != NULL) {
52			int err = func(clone, data);
53			if (err != 0)
54				return (err);
55		}
56	}
57	return (0);
58}
59
60static int
61zfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc)
62{
63	int rc;
64	uint64_t	orig_cookie;
65
66	orig_cookie = zc->zc_cookie;
67top:
68	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
69	rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
70
71	if (rc == -1) {
72		switch (errno) {
73		case ENOMEM:
74			/* expand nvlist memory and try again */
75			if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
76				zcmd_free_nvlists(zc);
77				return (-1);
78			}
79			zc->zc_cookie = orig_cookie;
80			goto top;
81		/*
82		 * An errno value of ESRCH indicates normal completion.
83		 * If ENOENT is returned, then the underlying dataset
84		 * has been removed since we obtained the handle.
85		 */
86		case ESRCH:
87		case ENOENT:
88			rc = 1;
89			break;
90		default:
91			rc = zfs_standard_error(zhp->zfs_hdl, errno,
92			    dgettext(TEXT_DOMAIN,
93			    "cannot iterate filesystems"));
94			break;
95		}
96	}
97	return (rc);
98}
99
100/*
101 * Iterate over all child filesystems
102 */
103int
104zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
105{
106	zfs_cmd_t zc = { 0 };
107	zfs_handle_t *nzhp;
108	int ret;
109
110	if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
111		return (0);
112
113	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
114		return (-1);
115
116	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
117	    &zc)) == 0) {
118		/*
119		 * Silently ignore errors, as the only plausible explanation is
120		 * that the pool has since been removed.
121		 */
122		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
123		    &zc)) == NULL) {
124			continue;
125		}
126
127		if ((ret = func(nzhp, data)) != 0) {
128			zcmd_free_nvlists(&zc);
129			return (ret);
130		}
131	}
132	zcmd_free_nvlists(&zc);
133	return ((ret < 0) ? ret : 0);
134}
135
136/*
137 * Iterate over all snapshots
138 */
139int
140zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
141{
142	zfs_cmd_t zc = { 0 };
143	zfs_handle_t *nzhp;
144	int ret;
145
146	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
147		return (0);
148
149	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
150		return (-1);
151	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
152	    &zc)) == 0) {
153
154		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
155		    &zc)) == NULL) {
156			continue;
157		}
158
159		if ((ret = func(nzhp, data)) != 0) {
160			zcmd_free_nvlists(&zc);
161			return (ret);
162		}
163	}
164	zcmd_free_nvlists(&zc);
165	return ((ret < 0) ? ret : 0);
166}
167
168/*
169 * Routines for dealing with the sorted snapshot functionality
170 */
171typedef struct zfs_node {
172	zfs_handle_t	*zn_handle;
173	avl_node_t	zn_avlnode;
174} zfs_node_t;
175
176static int
177zfs_sort_snaps(zfs_handle_t *zhp, void *data)
178{
179	avl_tree_t *avl = data;
180	zfs_node_t *node;
181	zfs_node_t search;
182
183	search.zn_handle = zhp;
184	node = avl_find(avl, &search, NULL);
185	if (node) {
186		/*
187		 * If this snapshot was renamed while we were creating the
188		 * AVL tree, it's possible that we already inserted it under
189		 * its old name. Remove the old handle before adding the new
190		 * one.
191		 */
192		zfs_close(node->zn_handle);
193		avl_remove(avl, node);
194		free(node);
195	}
196
197	node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
198	node->zn_handle = zhp;
199	avl_add(avl, node);
200
201	return (0);
202}
203
204static int
205zfs_snapshot_compare(const void *larg, const void *rarg)
206{
207	zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
208	zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
209	uint64_t lcreate, rcreate;
210
211	/*
212	 * Sort them according to creation time.  We use the hidden
213	 * CREATETXG property to get an absolute ordering of snapshots.
214	 */
215	lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
216	rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
217
218	if (lcreate < rcreate)
219		return (-1);
220	else if (lcreate > rcreate)
221		return (+1);
222	else
223		return (0);
224}
225
226int
227zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
228{
229	int ret = 0;
230	zfs_node_t *node;
231	avl_tree_t avl;
232	void *cookie = NULL;
233
234	avl_create(&avl, zfs_snapshot_compare,
235	    sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
236
237	ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
238
239	for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
240		ret |= callback(node->zn_handle, data);
241
242	while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
243		free(node);
244
245	avl_destroy(&avl);
246
247	return (ret);
248}
249
250typedef struct {
251	char *ssa_first;
252	char *ssa_last;
253	boolean_t ssa_seenfirst;
254	boolean_t ssa_seenlast;
255	zfs_iter_f ssa_func;
256	void *ssa_arg;
257} snapspec_arg_t;
258
259static int
260snapspec_cb(zfs_handle_t *zhp, void *arg) {
261	snapspec_arg_t *ssa = arg;
262	char *shortsnapname;
263	int err = 0;
264
265	if (ssa->ssa_seenlast)
266		return (0);
267	shortsnapname = zfs_strdup(zhp->zfs_hdl,
268	    strchr(zfs_get_name(zhp), '@') + 1);
269
270	if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
271		ssa->ssa_seenfirst = B_TRUE;
272
273	if (ssa->ssa_seenfirst) {
274		err = ssa->ssa_func(zhp, ssa->ssa_arg);
275	} else {
276		zfs_close(zhp);
277	}
278
279	if (strcmp(shortsnapname, ssa->ssa_last) == 0)
280		ssa->ssa_seenlast = B_TRUE;
281	free(shortsnapname);
282
283	return (err);
284}
285
286/*
287 * spec is a string like "A,B%C,D"
288 *
289 * <snaps>, where <snaps> can be:
290 *      <snap>          (single snapshot)
291 *      <snap>%<snap>   (range of snapshots, inclusive)
292 *      %<snap>         (range of snapshots, starting with earliest)
293 *      <snap>%         (range of snapshots, ending with last)
294 *      %               (all snapshots)
295 *      <snaps>[,...]   (comma separated list of the above)
296 *
297 * If a snapshot can not be opened, continue trying to open the others, but
298 * return ENOENT at the end.
299 */
300int
301zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
302    zfs_iter_f func, void *arg)
303{
304	char buf[ZFS_MAXNAMELEN];
305	char *comma_separated, *cp;
306	int err = 0;
307	int ret = 0;
308
309	(void) strlcpy(buf, spec_orig, sizeof (buf));
310	cp = buf;
311
312	while ((comma_separated = strsep(&cp, ",")) != NULL) {
313		char *pct = strchr(comma_separated, '%');
314		if (pct != NULL) {
315			snapspec_arg_t ssa = { 0 };
316			ssa.ssa_func = func;
317			ssa.ssa_arg = arg;
318
319			if (pct == comma_separated)
320				ssa.ssa_seenfirst = B_TRUE;
321			else
322				ssa.ssa_first = comma_separated;
323			*pct = '\0';
324			ssa.ssa_last = pct + 1;
325
326			/*
327			 * If there is a lastname specified, make sure it
328			 * exists.
329			 */
330			if (ssa.ssa_last[0] != '\0') {
331				char snapname[ZFS_MAXNAMELEN];
332				(void) snprintf(snapname, sizeof (snapname),
333				    "%s@%s", zfs_get_name(fs_zhp),
334				    ssa.ssa_last);
335				if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
336				    snapname, ZFS_TYPE_SNAPSHOT)) {
337					ret = ENOENT;
338					continue;
339				}
340			}
341
342			err = zfs_iter_snapshots_sorted(fs_zhp,
343			    snapspec_cb, &ssa);
344			if (ret == 0)
345				ret = err;
346			if (ret == 0 && (!ssa.ssa_seenfirst ||
347			    (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
348				ret = ENOENT;
349			}
350		} else {
351			char snapname[ZFS_MAXNAMELEN];
352			zfs_handle_t *snap_zhp;
353			(void) snprintf(snapname, sizeof (snapname), "%s@%s",
354			    zfs_get_name(fs_zhp), comma_separated);
355			snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
356			    snapname);
357			if (snap_zhp == NULL) {
358				ret = ENOENT;
359				continue;
360			}
361			err = func(snap_zhp, arg);
362			if (ret == 0)
363				ret = err;
364		}
365	}
366
367	return (ret);
368}
369
370/*
371 * Iterate over all children, snapshots and filesystems
372 */
373int
374zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
375{
376	int ret;
377
378	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
379		return (ret);
380
381	return (zfs_iter_snapshots(zhp, func, data));
382}
383
384
385typedef struct iter_stack_frame {
386	struct iter_stack_frame *next;
387	zfs_handle_t *zhp;
388} iter_stack_frame_t;
389
390typedef struct iter_dependents_arg {
391	boolean_t first;
392	boolean_t allowrecursion;
393	iter_stack_frame_t *stack;
394	zfs_iter_f func;
395	void *data;
396} iter_dependents_arg_t;
397
398static int
399iter_dependents_cb(zfs_handle_t *zhp, void *arg)
400{
401	iter_dependents_arg_t *ida = arg;
402	int err;
403	boolean_t first = ida->first;
404	ida->first = B_FALSE;
405
406	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
407		err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
408	} else {
409		iter_stack_frame_t isf;
410		iter_stack_frame_t *f;
411
412		/*
413		 * check if there is a cycle by seeing if this fs is already
414		 * on the stack.
415		 */
416		for (f = ida->stack; f != NULL; f = f->next) {
417			if (f->zhp->zfs_dmustats.dds_guid ==
418			    zhp->zfs_dmustats.dds_guid) {
419				if (ida->allowrecursion) {
420					zfs_close(zhp);
421					return (0);
422				} else {
423					zfs_error_aux(zhp->zfs_hdl,
424					    dgettext(TEXT_DOMAIN,
425					    "recursive dependency at '%s'"),
426					    zfs_get_name(zhp));
427					err = zfs_error(zhp->zfs_hdl,
428					    EZFS_RECURSIVE,
429					    dgettext(TEXT_DOMAIN,
430					    "cannot determine dependent "
431					    "datasets"));
432					zfs_close(zhp);
433					return (err);
434				}
435			}
436		}
437
438		isf.zhp = zhp;
439		isf.next = ida->stack;
440		ida->stack = &isf;
441		err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
442		if (err == 0)
443			err = zfs_iter_snapshots(zhp, iter_dependents_cb, ida);
444		ida->stack = isf.next;
445	}
446	if (!first && err == 0)
447		err = ida->func(zhp, ida->data);
448	return (err);
449}
450
451int
452zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
453    zfs_iter_f func, void *data)
454{
455	iter_dependents_arg_t ida;
456	ida.allowrecursion = allowrecursion;
457	ida.stack = NULL;
458	ida.func = func;
459	ida.data = data;
460	ida.first = B_TRUE;
461	return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
462}
463