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 https://opensource.org/licenses/CDDL-1.0.
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, 2019 by Delphix. All rights reserved.
25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
26 * Copyright (c) 2019 Datto Inc.
37 #include <sys/mntent.h>
39 #include "libzfs_impl.h"
42 zfs_iter_clones(zfs_handle_t
*zhp
, int flags __maybe_unused
, zfs_iter_f func
,
45 nvlist_t
*nvl
= zfs_get_clones_nvl(zhp
);
51 for (pair
= nvlist_next_nvpair(nvl
, NULL
); pair
!= NULL
;
52 pair
= nvlist_next_nvpair(nvl
, pair
)) {
53 zfs_handle_t
*clone
= zfs_open(zhp
->zfs_hdl
, nvpair_name(pair
),
54 ZFS_TYPE_FILESYSTEM
| ZFS_TYPE_VOLUME
);
56 int err
= func(clone
, data
);
65 zfs_do_list_ioctl(zfs_handle_t
*zhp
, int arg
, zfs_cmd_t
*zc
)
70 orig_cookie
= zc
->zc_cookie
;
72 (void) strlcpy(zc
->zc_name
, zhp
->zfs_name
, sizeof (zc
->zc_name
));
73 zc
->zc_objset_stats
.dds_creation_txg
= 0;
74 rc
= zfs_ioctl(zhp
->zfs_hdl
, arg
, zc
);
79 /* expand nvlist memory and try again */
80 zcmd_expand_dst_nvlist(zhp
->zfs_hdl
, zc
);
81 zc
->zc_cookie
= orig_cookie
;
84 * An errno value of ESRCH indicates normal completion.
85 * If ENOENT is returned, then the underlying dataset
86 * has been removed since we obtained the handle.
93 rc
= zfs_standard_error(zhp
->zfs_hdl
, errno
,
95 "cannot iterate filesystems"));
103 * Iterate over all child filesystems
106 zfs_iter_filesystems(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
108 return (zfs_iter_filesystems_v2(zhp
, 0, func
, data
));
112 zfs_iter_filesystems_v2(zfs_handle_t
*zhp
, int flags
, zfs_iter_f func
,
115 zfs_cmd_t zc
= {"\0"};
119 if (zhp
->zfs_type
!= ZFS_TYPE_FILESYSTEM
)
122 zcmd_alloc_dst_nvlist(zhp
->zfs_hdl
, &zc
, 0);
124 if ((flags
& ZFS_ITER_SIMPLE
) == ZFS_ITER_SIMPLE
)
125 zc
.zc_simple
= B_TRUE
;
127 while ((ret
= zfs_do_list_ioctl(zhp
, ZFS_IOC_DATASET_LIST_NEXT
,
130 nzhp
= make_dataset_simple_handle_zc(zhp
, &zc
);
132 nzhp
= make_dataset_handle_zc(zhp
->zfs_hdl
, &zc
);
134 * Silently ignore errors, as the only plausible explanation is
135 * that the pool has since been removed.
140 if ((ret
= func(nzhp
, data
)) != 0) {
141 zcmd_free_nvlists(&zc
);
145 zcmd_free_nvlists(&zc
);
146 return ((ret
< 0) ? ret
: 0);
150 * Iterate over all snapshots
153 zfs_iter_snapshots(zfs_handle_t
*zhp
, boolean_t simple
, zfs_iter_f func
,
154 void *data
, uint64_t min_txg
, uint64_t max_txg
)
156 return (zfs_iter_snapshots_v2(zhp
, simple
? ZFS_ITER_SIMPLE
: 0, func
,
157 data
, min_txg
, max_txg
));
161 zfs_iter_snapshots_v2(zfs_handle_t
*zhp
, int flags
, zfs_iter_f func
,
162 void *data
, uint64_t min_txg
, uint64_t max_txg
)
164 zfs_cmd_t zc
= {"\0"};
167 nvlist_t
*range_nvl
= NULL
;
169 if (zhp
->zfs_type
== ZFS_TYPE_SNAPSHOT
||
170 zhp
->zfs_type
== ZFS_TYPE_BOOKMARK
)
173 zc
.zc_simple
= (flags
& ZFS_ITER_SIMPLE
) != 0;
175 zcmd_alloc_dst_nvlist(zhp
->zfs_hdl
, &zc
, 0);
178 range_nvl
= fnvlist_alloc();
179 fnvlist_add_uint64(range_nvl
, SNAP_ITER_MIN_TXG
, min_txg
);
182 if (range_nvl
== NULL
)
183 range_nvl
= fnvlist_alloc();
184 fnvlist_add_uint64(range_nvl
, SNAP_ITER_MAX_TXG
, max_txg
);
187 if (range_nvl
!= NULL
)
188 zcmd_write_src_nvlist(zhp
->zfs_hdl
, &zc
, range_nvl
);
190 while ((ret
= zfs_do_list_ioctl(zhp
, ZFS_IOC_SNAPSHOT_LIST_NEXT
,
194 nzhp
= make_dataset_simple_handle_zc(zhp
, &zc
);
196 nzhp
= make_dataset_handle_zc(zhp
->zfs_hdl
, &zc
);
200 if ((ret
= func(nzhp
, data
)) != 0) {
201 zcmd_free_nvlists(&zc
);
202 fnvlist_free(range_nvl
);
206 zcmd_free_nvlists(&zc
);
207 fnvlist_free(range_nvl
);
208 return ((ret
< 0) ? ret
: 0);
212 * Iterate over all bookmarks
215 zfs_iter_bookmarks(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
217 return (zfs_iter_bookmarks_v2(zhp
, 0, func
, data
));
221 zfs_iter_bookmarks_v2(zfs_handle_t
*zhp
, int flags __maybe_unused
,
222 zfs_iter_f func
, void *data
)
225 nvlist_t
*props
= NULL
;
226 nvlist_t
*bmarks
= NULL
;
230 if ((zfs_get_type(zhp
) & (ZFS_TYPE_SNAPSHOT
| ZFS_TYPE_BOOKMARK
)) != 0)
233 /* Setup the requested properties nvlist. */
234 props
= fnvlist_alloc();
235 for (zfs_prop_t p
= 0; p
< ZFS_NUM_PROPS
; p
++) {
236 if (zfs_prop_valid_for_type(p
, ZFS_TYPE_BOOKMARK
, B_FALSE
)) {
237 fnvlist_add_boolean(props
, zfs_prop_to_name(p
));
240 fnvlist_add_boolean(props
, "redact_complete");
242 if ((err
= lzc_get_bookmarks(zhp
->zfs_name
, props
, &bmarks
)) != 0)
245 for (pair
= nvlist_next_nvpair(bmarks
, NULL
);
246 pair
!= NULL
; pair
= nvlist_next_nvpair(bmarks
, pair
)) {
247 char name
[ZFS_MAX_DATASET_NAME_LEN
];
248 const char *bmark_name
;
249 nvlist_t
*bmark_props
;
251 bmark_name
= nvpair_name(pair
);
252 bmark_props
= fnvpair_value_nvlist(pair
);
254 if (snprintf(name
, sizeof (name
), "%s#%s", zhp
->zfs_name
,
255 bmark_name
) >= sizeof (name
)) {
260 nzhp
= make_bookmark_handle(zhp
, name
, bmark_props
);
264 if ((err
= func(nzhp
, data
)) != 0)
270 fnvlist_free(bmarks
);
276 * Routines for dealing with the sorted snapshot functionality
278 typedef struct zfs_node
{
279 zfs_handle_t
*zn_handle
;
280 avl_node_t zn_avlnode
;
284 zfs_sort_snaps(zfs_handle_t
*zhp
, void *data
)
286 avl_tree_t
*avl
= data
;
290 search
.zn_handle
= zhp
;
291 node
= avl_find(avl
, &search
, NULL
);
294 * If this snapshot was renamed while we were creating the
295 * AVL tree, it's possible that we already inserted it under
296 * its old name. Remove the old handle before adding the new
299 zfs_close(node
->zn_handle
);
300 avl_remove(avl
, node
);
304 node
= zfs_alloc(zhp
->zfs_hdl
, sizeof (zfs_node_t
));
305 node
->zn_handle
= zhp
;
312 zfs_snapshot_compare(const void *larg
, const void *rarg
)
314 zfs_handle_t
*l
= ((zfs_node_t
*)larg
)->zn_handle
;
315 zfs_handle_t
*r
= ((zfs_node_t
*)rarg
)->zn_handle
;
316 uint64_t lcreate
, rcreate
;
319 * Sort them according to creation time. We use the hidden
320 * CREATETXG property to get an absolute ordering of snapshots.
322 lcreate
= zfs_prop_get_int(l
, ZFS_PROP_CREATETXG
);
323 rcreate
= zfs_prop_get_int(r
, ZFS_PROP_CREATETXG
);
325 return (TREE_CMP(lcreate
, rcreate
));
329 zfs_iter_snapshots_sorted(zfs_handle_t
*zhp
, zfs_iter_f callback
,
330 void *data
, uint64_t min_txg
, uint64_t max_txg
)
332 return (zfs_iter_snapshots_sorted_v2(zhp
, 0, callback
, data
,
337 zfs_iter_snapshots_sorted_v2(zfs_handle_t
*zhp
, int flags
, zfs_iter_f callback
,
338 void *data
, uint64_t min_txg
, uint64_t max_txg
)
345 avl_create(&avl
, zfs_snapshot_compare
,
346 sizeof (zfs_node_t
), offsetof(zfs_node_t
, zn_avlnode
));
348 ret
= zfs_iter_snapshots_v2(zhp
, flags
, zfs_sort_snaps
, &avl
, min_txg
,
351 for (node
= avl_first(&avl
); node
!= NULL
; node
= AVL_NEXT(&avl
, node
))
352 ret
|= callback(node
->zn_handle
, data
);
354 while ((node
= avl_destroy_nodes(&avl
, &cookie
)) != NULL
)
365 boolean_t ssa_seenfirst
;
366 boolean_t ssa_seenlast
;
372 snapspec_cb(zfs_handle_t
*zhp
, void *arg
)
374 snapspec_arg_t
*ssa
= arg
;
375 const char *shortsnapname
;
378 if (ssa
->ssa_seenlast
)
381 shortsnapname
= strchr(zfs_get_name(zhp
), '@') + 1;
382 if (!ssa
->ssa_seenfirst
&& strcmp(shortsnapname
, ssa
->ssa_first
) == 0)
383 ssa
->ssa_seenfirst
= B_TRUE
;
384 if (strcmp(shortsnapname
, ssa
->ssa_last
) == 0)
385 ssa
->ssa_seenlast
= B_TRUE
;
387 if (ssa
->ssa_seenfirst
) {
388 err
= ssa
->ssa_func(zhp
, ssa
->ssa_arg
);
397 * spec is a string like "A,B%C,D"
399 * <snaps>, where <snaps> can be:
400 * <snap> (single snapshot)
401 * <snap>%<snap> (range of snapshots, inclusive)
402 * %<snap> (range of snapshots, starting with earliest)
403 * <snap>% (range of snapshots, ending with last)
405 * <snaps>[,...] (comma separated list of the above)
407 * If a snapshot can not be opened, continue trying to open the others, but
408 * return ENOENT at the end.
411 zfs_iter_snapspec(zfs_handle_t
*fs_zhp
, const char *spec_orig
,
412 zfs_iter_f func
, void *arg
)
414 return (zfs_iter_snapspec_v2(fs_zhp
, 0, spec_orig
, func
, arg
));
418 zfs_iter_snapspec_v2(zfs_handle_t
*fs_zhp
, int flags
, const char *spec_orig
,
419 zfs_iter_f func
, void *arg
)
421 char *buf
, *comma_separated
, *cp
;
425 buf
= zfs_strdup(fs_zhp
->zfs_hdl
, spec_orig
);
428 while ((comma_separated
= strsep(&cp
, ",")) != NULL
) {
429 char *pct
= strchr(comma_separated
, '%');
431 snapspec_arg_t ssa
= { 0 };
435 if (pct
== comma_separated
)
436 ssa
.ssa_seenfirst
= B_TRUE
;
438 ssa
.ssa_first
= comma_separated
;
440 ssa
.ssa_last
= pct
+ 1;
443 * If there is a lastname specified, make sure it
446 if (ssa
.ssa_last
[0] != '\0') {
447 char snapname
[ZFS_MAX_DATASET_NAME_LEN
];
448 (void) snprintf(snapname
, sizeof (snapname
),
449 "%s@%s", zfs_get_name(fs_zhp
),
451 if (!zfs_dataset_exists(fs_zhp
->zfs_hdl
,
452 snapname
, ZFS_TYPE_SNAPSHOT
)) {
458 err
= zfs_iter_snapshots_sorted_v2(fs_zhp
, flags
,
459 snapspec_cb
, &ssa
, 0, 0);
462 if (ret
== 0 && (!ssa
.ssa_seenfirst
||
463 (ssa
.ssa_last
[0] != '\0' && !ssa
.ssa_seenlast
))) {
467 char snapname
[ZFS_MAX_DATASET_NAME_LEN
];
468 zfs_handle_t
*snap_zhp
;
469 (void) snprintf(snapname
, sizeof (snapname
), "%s@%s",
470 zfs_get_name(fs_zhp
), comma_separated
);
471 snap_zhp
= make_dataset_handle(fs_zhp
->zfs_hdl
,
473 if (snap_zhp
== NULL
) {
477 err
= func(snap_zhp
, arg
);
488 * Iterate over all children, snapshots and filesystems
489 * Process snapshots before filesystems because they are nearer the input
490 * handle: this is extremely important when used with zfs_iter_f functions
491 * looking for data, following the logic that we would like to find it as soon
492 * and as close as possible.
495 zfs_iter_children(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
497 return (zfs_iter_children_v2(zhp
, 0, func
, data
));
501 zfs_iter_children_v2(zfs_handle_t
*zhp
, int flags
, zfs_iter_f func
, void *data
)
505 if ((ret
= zfs_iter_snapshots_v2(zhp
, flags
, func
, data
, 0, 0)) != 0)
508 return (zfs_iter_filesystems_v2(zhp
, flags
, func
, data
));
512 typedef struct iter_stack_frame
{
513 struct iter_stack_frame
*next
;
515 } iter_stack_frame_t
;
517 typedef struct iter_dependents_arg
{
520 boolean_t allowrecursion
;
521 iter_stack_frame_t
*stack
;
524 } iter_dependents_arg_t
;
527 iter_dependents_cb(zfs_handle_t
*zhp
, void *arg
)
529 iter_dependents_arg_t
*ida
= arg
;
531 boolean_t first
= ida
->first
;
532 ida
->first
= B_FALSE
;
534 if (zhp
->zfs_type
== ZFS_TYPE_SNAPSHOT
) {
535 err
= zfs_iter_clones(zhp
, ida
->flags
, iter_dependents_cb
, ida
);
536 } else if (zhp
->zfs_type
!= ZFS_TYPE_BOOKMARK
) {
537 iter_stack_frame_t isf
;
538 iter_stack_frame_t
*f
;
541 * check if there is a cycle by seeing if this fs is already
544 for (f
= ida
->stack
; f
!= NULL
; f
= f
->next
) {
545 if (f
->zhp
->zfs_dmustats
.dds_guid
==
546 zhp
->zfs_dmustats
.dds_guid
) {
547 if (ida
->allowrecursion
) {
551 zfs_error_aux(zhp
->zfs_hdl
,
552 dgettext(TEXT_DOMAIN
,
553 "recursive dependency at '%s'"),
555 err
= zfs_error(zhp
->zfs_hdl
,
557 dgettext(TEXT_DOMAIN
,
558 "cannot determine dependent "
567 isf
.next
= ida
->stack
;
569 err
= zfs_iter_filesystems_v2(zhp
, ida
->flags
,
570 iter_dependents_cb
, ida
);
572 err
= zfs_iter_snapshots_v2(zhp
, ida
->flags
,
573 iter_dependents_cb
, ida
, 0, 0);
574 ida
->stack
= isf
.next
;
577 if (!first
&& err
== 0)
578 err
= ida
->func(zhp
, ida
->data
);
586 zfs_iter_dependents(zfs_handle_t
*zhp
, boolean_t allowrecursion
,
587 zfs_iter_f func
, void *data
)
589 return (zfs_iter_dependents_v2(zhp
, 0, allowrecursion
, func
, data
));
593 zfs_iter_dependents_v2(zfs_handle_t
*zhp
, int flags
, boolean_t allowrecursion
,
594 zfs_iter_f func
, void *data
)
596 iter_dependents_arg_t ida
;
598 ida
.allowrecursion
= allowrecursion
;
603 return (iter_dependents_cb(zfs_handle_dup(zhp
), &ida
));
607 * Iterate over mounted children of the specified dataset
610 zfs_iter_mounted(zfs_handle_t
*zhp
, zfs_iter_f func
, void *data
)
612 char mnt_prop
[ZFS_MAXPROPLEN
];
614 zfs_handle_t
*mtab_zhp
;
615 size_t namelen
= strlen(zhp
->zfs_name
);
619 if ((mnttab
= fopen(MNTTAB
, "re")) == NULL
)
622 while (err
== 0 && getmntent(mnttab
, &entry
) == 0) {
623 /* Ignore non-ZFS entries */
624 if (strcmp(entry
.mnt_fstype
, MNTTYPE_ZFS
) != 0)
627 /* Ignore datasets not within the provided dataset */
628 if (strncmp(entry
.mnt_special
, zhp
->zfs_name
, namelen
) != 0 ||
629 entry
.mnt_special
[namelen
] != '/')
632 /* Skip snapshot of any child dataset */
633 if (strchr(entry
.mnt_special
, '@') != NULL
)
636 if ((mtab_zhp
= zfs_open(zhp
->zfs_hdl
, entry
.mnt_special
,
637 ZFS_TYPE_FILESYSTEM
)) == NULL
)
640 /* Ignore legacy mounts as they are user managed */
641 verify(zfs_prop_get(mtab_zhp
, ZFS_PROP_MOUNTPOINT
, mnt_prop
,
642 sizeof (mnt_prop
), NULL
, NULL
, 0, B_FALSE
) == 0);
643 if (strcmp(mnt_prop
, "legacy") == 0) {
648 err
= func(mtab_zhp
, data
);