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]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2013 by Delphix. All rights reserved.
25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
36 #include "libzfs_impl.h"
39 zfs_iter_clones(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
41 nvlist_t
*nvl
= zfs_get_clones_nvl(zhp
);
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
);
52 int err
= func(clone
, data
);
61 zfs_do_list_ioctl(zfs_handle_t
*zhp
, int arg
, zfs_cmd_t
*zc
)
66 orig_cookie
= zc
->zc_cookie
;
68 (void) strlcpy(zc
->zc_name
, zhp
->zfs_name
, sizeof (zc
->zc_name
));
69 rc
= ioctl(zhp
->zfs_hdl
->libzfs_fd
, arg
, zc
);
74 /* expand nvlist memory and try again */
75 if (zcmd_expand_dst_nvlist(zhp
->zfs_hdl
, zc
) != 0) {
76 zcmd_free_nvlists(zc
);
79 zc
->zc_cookie
= orig_cookie
;
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.
91 rc
= zfs_standard_error(zhp
->zfs_hdl
, errno
,
93 "cannot iterate filesystems"));
101 * Iterate over all child filesystems
104 zfs_iter_filesystems(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
106 zfs_cmd_t zc
= {"\0"};
110 if (zhp
->zfs_type
!= ZFS_TYPE_FILESYSTEM
)
113 if (zcmd_alloc_dst_nvlist(zhp
->zfs_hdl
, &zc
, 0) != 0)
116 while ((ret
= zfs_do_list_ioctl(zhp
, ZFS_IOC_DATASET_LIST_NEXT
,
119 * Silently ignore errors, as the only plausible explanation is
120 * that the pool has since been removed.
122 if ((nzhp
= make_dataset_handle_zc(zhp
->zfs_hdl
,
127 if ((ret
= func(nzhp
, data
)) != 0) {
128 zcmd_free_nvlists(&zc
);
132 zcmd_free_nvlists(&zc
);
133 return ((ret
< 0) ? ret
: 0);
137 * Iterate over all snapshots
140 zfs_iter_snapshots(zfs_handle_t
*zhp
, boolean_t simple
, zfs_iter_f func
,
143 zfs_cmd_t zc
= {"\0"};
147 if (zhp
->zfs_type
== ZFS_TYPE_SNAPSHOT
||
148 zhp
->zfs_type
== ZFS_TYPE_BOOKMARK
)
151 zc
.zc_simple
= simple
;
153 if (zcmd_alloc_dst_nvlist(zhp
->zfs_hdl
, &zc
, 0) != 0)
155 while ((ret
= zfs_do_list_ioctl(zhp
, ZFS_IOC_SNAPSHOT_LIST_NEXT
,
159 nzhp
= make_dataset_simple_handle_zc(zhp
, &zc
);
161 nzhp
= make_dataset_handle_zc(zhp
->zfs_hdl
, &zc
);
165 if ((ret
= func(nzhp
, data
)) != 0) {
166 zcmd_free_nvlists(&zc
);
170 zcmd_free_nvlists(&zc
);
171 return ((ret
< 0) ? ret
: 0);
175 * Iterate over all bookmarks
178 zfs_iter_bookmarks(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
181 nvlist_t
*props
= NULL
;
182 nvlist_t
*bmarks
= NULL
;
186 if ((zfs_get_type(zhp
) & (ZFS_TYPE_SNAPSHOT
| ZFS_TYPE_BOOKMARK
)) != 0)
189 /* Setup the requested properties nvlist. */
190 props
= fnvlist_alloc();
191 fnvlist_add_boolean(props
, zfs_prop_to_name(ZFS_PROP_GUID
));
192 fnvlist_add_boolean(props
, zfs_prop_to_name(ZFS_PROP_CREATETXG
));
193 fnvlist_add_boolean(props
, zfs_prop_to_name(ZFS_PROP_CREATION
));
195 if ((err
= lzc_get_bookmarks(zhp
->zfs_name
, props
, &bmarks
)) != 0)
198 for (pair
= nvlist_next_nvpair(bmarks
, NULL
);
199 pair
!= NULL
; pair
= nvlist_next_nvpair(bmarks
, pair
)) {
200 char name
[ZFS_MAXNAMELEN
];
202 nvlist_t
*bmark_props
;
204 bmark_name
= nvpair_name(pair
);
205 bmark_props
= fnvpair_value_nvlist(pair
);
207 (void) snprintf(name
, sizeof (name
), "%s#%s", zhp
->zfs_name
,
210 nzhp
= make_bookmark_handle(zhp
, name
, bmark_props
);
214 if ((err
= func(nzhp
, data
)) != 0)
220 fnvlist_free(bmarks
);
226 * Routines for dealing with the sorted snapshot functionality
228 typedef struct zfs_node
{
229 zfs_handle_t
*zn_handle
;
230 avl_node_t zn_avlnode
;
234 zfs_sort_snaps(zfs_handle_t
*zhp
, void *data
)
236 avl_tree_t
*avl
= data
;
240 search
.zn_handle
= zhp
;
241 node
= avl_find(avl
, &search
, NULL
);
244 * If this snapshot was renamed while we were creating the
245 * AVL tree, it's possible that we already inserted it under
246 * its old name. Remove the old handle before adding the new
249 zfs_close(node
->zn_handle
);
250 avl_remove(avl
, node
);
254 node
= zfs_alloc(zhp
->zfs_hdl
, sizeof (zfs_node_t
));
255 node
->zn_handle
= zhp
;
262 zfs_snapshot_compare(const void *larg
, const void *rarg
)
264 zfs_handle_t
*l
= ((zfs_node_t
*)larg
)->zn_handle
;
265 zfs_handle_t
*r
= ((zfs_node_t
*)rarg
)->zn_handle
;
266 uint64_t lcreate
, rcreate
;
269 * Sort them according to creation time. We use the hidden
270 * CREATETXG property to get an absolute ordering of snapshots.
272 lcreate
= zfs_prop_get_int(l
, ZFS_PROP_CREATETXG
);
273 rcreate
= zfs_prop_get_int(r
, ZFS_PROP_CREATETXG
);
275 if (lcreate
< rcreate
)
277 else if (lcreate
> rcreate
)
284 zfs_iter_snapshots_sorted(zfs_handle_t
*zhp
, zfs_iter_f callback
, void *data
)
291 avl_create(&avl
, zfs_snapshot_compare
,
292 sizeof (zfs_node_t
), offsetof(zfs_node_t
, zn_avlnode
));
294 ret
= zfs_iter_snapshots(zhp
, B_FALSE
, zfs_sort_snaps
, &avl
);
296 for (node
= avl_first(&avl
); node
!= NULL
; node
= AVL_NEXT(&avl
, node
))
297 ret
|= callback(node
->zn_handle
, data
);
299 while ((node
= avl_destroy_nodes(&avl
, &cookie
)) != NULL
)
310 boolean_t ssa_seenfirst
;
311 boolean_t ssa_seenlast
;
317 snapspec_cb(zfs_handle_t
*zhp
, void *arg
) {
318 snapspec_arg_t
*ssa
= arg
;
322 if (ssa
->ssa_seenlast
)
324 shortsnapname
= zfs_strdup(zhp
->zfs_hdl
,
325 strchr(zfs_get_name(zhp
), '@') + 1);
327 if (!ssa
->ssa_seenfirst
&& strcmp(shortsnapname
, ssa
->ssa_first
) == 0)
328 ssa
->ssa_seenfirst
= B_TRUE
;
330 if (ssa
->ssa_seenfirst
) {
331 err
= ssa
->ssa_func(zhp
, ssa
->ssa_arg
);
336 if (strcmp(shortsnapname
, ssa
->ssa_last
) == 0)
337 ssa
->ssa_seenlast
= B_TRUE
;
344 * spec is a string like "A,B%C,D"
346 * <snaps>, where <snaps> can be:
347 * <snap> (single snapshot)
348 * <snap>%<snap> (range of snapshots, inclusive)
349 * %<snap> (range of snapshots, starting with earliest)
350 * <snap>% (range of snapshots, ending with last)
352 * <snaps>[,...] (comma separated list of the above)
354 * If a snapshot can not be opened, continue trying to open the others, but
355 * return ENOENT at the end.
358 zfs_iter_snapspec(zfs_handle_t
*fs_zhp
, const char *spec_orig
,
359 zfs_iter_f func
, void *arg
)
361 char *buf
, *comma_separated
, *cp
;
365 buf
= zfs_strdup(fs_zhp
->zfs_hdl
, spec_orig
);
368 while ((comma_separated
= strsep(&cp
, ",")) != NULL
) {
369 char *pct
= strchr(comma_separated
, '%');
371 snapspec_arg_t ssa
= { 0 };
375 if (pct
== comma_separated
)
376 ssa
.ssa_seenfirst
= B_TRUE
;
378 ssa
.ssa_first
= comma_separated
;
380 ssa
.ssa_last
= pct
+ 1;
383 * If there is a lastname specified, make sure it
386 if (ssa
.ssa_last
[0] != '\0') {
387 char snapname
[ZFS_MAXNAMELEN
];
388 (void) snprintf(snapname
, sizeof (snapname
),
389 "%s@%s", zfs_get_name(fs_zhp
),
391 if (!zfs_dataset_exists(fs_zhp
->zfs_hdl
,
392 snapname
, ZFS_TYPE_SNAPSHOT
)) {
398 err
= zfs_iter_snapshots_sorted(fs_zhp
,
402 if (ret
== 0 && (!ssa
.ssa_seenfirst
||
403 (ssa
.ssa_last
[0] != '\0' && !ssa
.ssa_seenlast
))) {
407 char snapname
[ZFS_MAXNAMELEN
];
408 zfs_handle_t
*snap_zhp
;
409 (void) snprintf(snapname
, sizeof (snapname
), "%s@%s",
410 zfs_get_name(fs_zhp
), comma_separated
);
411 snap_zhp
= make_dataset_handle(fs_zhp
->zfs_hdl
,
413 if (snap_zhp
== NULL
) {
417 err
= func(snap_zhp
, arg
);
428 * Iterate over all children, snapshots and filesystems
431 zfs_iter_children(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
435 if ((ret
= zfs_iter_filesystems(zhp
, func
, data
)) != 0)
438 return (zfs_iter_snapshots(zhp
, B_FALSE
, func
, data
));
442 typedef struct iter_stack_frame
{
443 struct iter_stack_frame
*next
;
445 } iter_stack_frame_t
;
447 typedef struct iter_dependents_arg
{
449 boolean_t allowrecursion
;
450 iter_stack_frame_t
*stack
;
453 } iter_dependents_arg_t
;
456 iter_dependents_cb(zfs_handle_t
*zhp
, void *arg
)
458 iter_dependents_arg_t
*ida
= arg
;
460 boolean_t first
= ida
->first
;
461 ida
->first
= B_FALSE
;
463 if (zhp
->zfs_type
== ZFS_TYPE_SNAPSHOT
) {
464 err
= zfs_iter_clones(zhp
, iter_dependents_cb
, ida
);
465 } else if (zhp
->zfs_type
!= ZFS_TYPE_BOOKMARK
) {
466 iter_stack_frame_t isf
;
467 iter_stack_frame_t
*f
;
470 * check if there is a cycle by seeing if this fs is already
473 for (f
= ida
->stack
; f
!= NULL
; f
= f
->next
) {
474 if (f
->zhp
->zfs_dmustats
.dds_guid
==
475 zhp
->zfs_dmustats
.dds_guid
) {
476 if (ida
->allowrecursion
) {
480 zfs_error_aux(zhp
->zfs_hdl
,
481 dgettext(TEXT_DOMAIN
,
482 "recursive dependency at '%s'"),
484 err
= zfs_error(zhp
->zfs_hdl
,
486 dgettext(TEXT_DOMAIN
,
487 "cannot determine dependent "
496 isf
.next
= ida
->stack
;
498 err
= zfs_iter_filesystems(zhp
, iter_dependents_cb
, ida
);
500 err
= zfs_iter_snapshots(zhp
, B_FALSE
,
501 iter_dependents_cb
, ida
);
502 ida
->stack
= isf
.next
;
505 if (!first
&& err
== 0)
506 err
= ida
->func(zhp
, ida
->data
);
514 zfs_iter_dependents(zfs_handle_t
*zhp
, boolean_t allowrecursion
,
515 zfs_iter_f func
, void *data
)
517 iter_dependents_arg_t ida
;
518 ida
.allowrecursion
= allowrecursion
;
523 return (iter_dependents_cb(zfs_handle_dup(zhp
), &ida
));