119b94df9SMatthew Ahrens /* 219b94df9SMatthew Ahrens * CDDL HEADER START 319b94df9SMatthew Ahrens * 419b94df9SMatthew Ahrens * The contents of this file are subject to the terms of the 519b94df9SMatthew Ahrens * Common Development and Distribution License (the "License"). 619b94df9SMatthew Ahrens * You may not use this file except in compliance with the License. 719b94df9SMatthew Ahrens * 819b94df9SMatthew Ahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 919b94df9SMatthew Ahrens * or http://www.opensolaris.org/os/licensing. 1019b94df9SMatthew Ahrens * See the License for the specific language governing permissions 1119b94df9SMatthew Ahrens * and limitations under the License. 1219b94df9SMatthew Ahrens * 1319b94df9SMatthew Ahrens * When distributing Covered Code, include this CDDL HEADER in each 1419b94df9SMatthew Ahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1519b94df9SMatthew Ahrens * If applicable, add the following below this CDDL HEADER, with the 1619b94df9SMatthew Ahrens * fields enclosed by brackets "[]" replaced with your own identifying 1719b94df9SMatthew Ahrens * information: Portions Copyright [yyyy] [name of copyright owner] 1819b94df9SMatthew Ahrens * 1919b94df9SMatthew Ahrens * CDDL HEADER END 2019b94df9SMatthew Ahrens */ 2119b94df9SMatthew Ahrens 2219b94df9SMatthew Ahrens /* 2319b94df9SMatthew Ahrens * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 249a686fbcSPaul Dagnelie * Copyright (c) 2013, 2015 by Delphix. All rights reserved. 25b7070b7dSJan Kryl * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 2619b94df9SMatthew Ahrens */ 2719b94df9SMatthew Ahrens 2819b94df9SMatthew Ahrens #include <stdio.h> 2919b94df9SMatthew Ahrens #include <stdlib.h> 3019b94df9SMatthew Ahrens #include <strings.h> 3119b94df9SMatthew Ahrens #include <unistd.h> 3219b94df9SMatthew Ahrens #include <stddef.h> 3319b94df9SMatthew Ahrens #include <libintl.h> 3419b94df9SMatthew Ahrens #include <libzfs.h> 3519b94df9SMatthew Ahrens 3619b94df9SMatthew Ahrens #include "libzfs_impl.h" 3719b94df9SMatthew Ahrens 3819b94df9SMatthew Ahrens int 3919b94df9SMatthew Ahrens zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data) 4019b94df9SMatthew Ahrens { 4119b94df9SMatthew Ahrens nvlist_t *nvl = zfs_get_clones_nvl(zhp); 4219b94df9SMatthew Ahrens nvpair_t *pair; 4319b94df9SMatthew Ahrens 4419b94df9SMatthew Ahrens if (nvl == NULL) 4519b94df9SMatthew Ahrens return (0); 4619b94df9SMatthew Ahrens 4719b94df9SMatthew Ahrens for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL; 4819b94df9SMatthew Ahrens pair = nvlist_next_nvpair(nvl, pair)) { 4919b94df9SMatthew Ahrens zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair), 5019b94df9SMatthew Ahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 5119b94df9SMatthew Ahrens if (clone != NULL) { 5219b94df9SMatthew Ahrens int err = func(clone, data); 5319b94df9SMatthew Ahrens if (err != 0) 5419b94df9SMatthew Ahrens return (err); 5519b94df9SMatthew Ahrens } 5619b94df9SMatthew Ahrens } 5719b94df9SMatthew Ahrens return (0); 5819b94df9SMatthew Ahrens } 5919b94df9SMatthew Ahrens 6019b94df9SMatthew Ahrens static int 6119b94df9SMatthew Ahrens zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc) 6219b94df9SMatthew Ahrens { 6319b94df9SMatthew Ahrens int rc; 6419b94df9SMatthew Ahrens uint64_t orig_cookie; 6519b94df9SMatthew Ahrens 6619b94df9SMatthew Ahrens orig_cookie = zc->zc_cookie; 6719b94df9SMatthew Ahrens top: 6819b94df9SMatthew Ahrens (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 6919b94df9SMatthew Ahrens rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 7019b94df9SMatthew Ahrens 7119b94df9SMatthew Ahrens if (rc == -1) { 7219b94df9SMatthew Ahrens switch (errno) { 7319b94df9SMatthew Ahrens case ENOMEM: 7419b94df9SMatthew Ahrens /* expand nvlist memory and try again */ 7519b94df9SMatthew Ahrens if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 7619b94df9SMatthew Ahrens zcmd_free_nvlists(zc); 7719b94df9SMatthew Ahrens return (-1); 7819b94df9SMatthew Ahrens } 7919b94df9SMatthew Ahrens zc->zc_cookie = orig_cookie; 8019b94df9SMatthew Ahrens goto top; 8119b94df9SMatthew Ahrens /* 8219b94df9SMatthew Ahrens * An errno value of ESRCH indicates normal completion. 8319b94df9SMatthew Ahrens * If ENOENT is returned, then the underlying dataset 8419b94df9SMatthew Ahrens * has been removed since we obtained the handle. 8519b94df9SMatthew Ahrens */ 8619b94df9SMatthew Ahrens case ESRCH: 8719b94df9SMatthew Ahrens case ENOENT: 8819b94df9SMatthew Ahrens rc = 1; 8919b94df9SMatthew Ahrens break; 9019b94df9SMatthew Ahrens default: 9119b94df9SMatthew Ahrens rc = zfs_standard_error(zhp->zfs_hdl, errno, 9219b94df9SMatthew Ahrens dgettext(TEXT_DOMAIN, 9319b94df9SMatthew Ahrens "cannot iterate filesystems")); 9419b94df9SMatthew Ahrens break; 9519b94df9SMatthew Ahrens } 9619b94df9SMatthew Ahrens } 9719b94df9SMatthew Ahrens return (rc); 9819b94df9SMatthew Ahrens } 9919b94df9SMatthew Ahrens 10019b94df9SMatthew Ahrens /* 10119b94df9SMatthew Ahrens * Iterate over all child filesystems 10219b94df9SMatthew Ahrens */ 10319b94df9SMatthew Ahrens int 10419b94df9SMatthew Ahrens zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 10519b94df9SMatthew Ahrens { 10619b94df9SMatthew Ahrens zfs_cmd_t zc = { 0 }; 10719b94df9SMatthew Ahrens zfs_handle_t *nzhp; 10819b94df9SMatthew Ahrens int ret; 10919b94df9SMatthew Ahrens 11019b94df9SMatthew Ahrens if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 11119b94df9SMatthew Ahrens return (0); 11219b94df9SMatthew Ahrens 11319b94df9SMatthew Ahrens if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 11419b94df9SMatthew Ahrens return (-1); 11519b94df9SMatthew Ahrens 11619b94df9SMatthew Ahrens while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 11719b94df9SMatthew Ahrens &zc)) == 0) { 11819b94df9SMatthew Ahrens /* 11919b94df9SMatthew Ahrens * Silently ignore errors, as the only plausible explanation is 12019b94df9SMatthew Ahrens * that the pool has since been removed. 12119b94df9SMatthew Ahrens */ 12219b94df9SMatthew Ahrens if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 12319b94df9SMatthew Ahrens &zc)) == NULL) { 12419b94df9SMatthew Ahrens continue; 12519b94df9SMatthew Ahrens } 12619b94df9SMatthew Ahrens 12719b94df9SMatthew Ahrens if ((ret = func(nzhp, data)) != 0) { 12819b94df9SMatthew Ahrens zcmd_free_nvlists(&zc); 12919b94df9SMatthew Ahrens return (ret); 13019b94df9SMatthew Ahrens } 13119b94df9SMatthew Ahrens } 13219b94df9SMatthew Ahrens zcmd_free_nvlists(&zc); 13319b94df9SMatthew Ahrens return ((ret < 0) ? ret : 0); 13419b94df9SMatthew Ahrens } 13519b94df9SMatthew Ahrens 13619b94df9SMatthew Ahrens /* 13719b94df9SMatthew Ahrens * Iterate over all snapshots 13819b94df9SMatthew Ahrens */ 13919b94df9SMatthew Ahrens int 14019b94df9SMatthew Ahrens zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 14119b94df9SMatthew Ahrens { 14219b94df9SMatthew Ahrens zfs_cmd_t zc = { 0 }; 14319b94df9SMatthew Ahrens zfs_handle_t *nzhp; 14419b94df9SMatthew Ahrens int ret; 14519b94df9SMatthew Ahrens 14678f17100SMatthew Ahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT || 14778f17100SMatthew Ahrens zhp->zfs_type == ZFS_TYPE_BOOKMARK) 14819b94df9SMatthew Ahrens return (0); 14919b94df9SMatthew Ahrens 15019b94df9SMatthew Ahrens if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 15119b94df9SMatthew Ahrens return (-1); 15219b94df9SMatthew Ahrens while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 15319b94df9SMatthew Ahrens &zc)) == 0) { 15419b94df9SMatthew Ahrens 15519b94df9SMatthew Ahrens if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 15619b94df9SMatthew Ahrens &zc)) == NULL) { 15719b94df9SMatthew Ahrens continue; 15819b94df9SMatthew Ahrens } 15919b94df9SMatthew Ahrens 16019b94df9SMatthew Ahrens if ((ret = func(nzhp, data)) != 0) { 16119b94df9SMatthew Ahrens zcmd_free_nvlists(&zc); 16219b94df9SMatthew Ahrens return (ret); 16319b94df9SMatthew Ahrens } 16419b94df9SMatthew Ahrens } 16519b94df9SMatthew Ahrens zcmd_free_nvlists(&zc); 16619b94df9SMatthew Ahrens return ((ret < 0) ? ret : 0); 16719b94df9SMatthew Ahrens } 16819b94df9SMatthew Ahrens 16919b94df9SMatthew Ahrens /* 17078f17100SMatthew Ahrens * Iterate over all bookmarks 17178f17100SMatthew Ahrens */ 17278f17100SMatthew Ahrens int 17378f17100SMatthew Ahrens zfs_iter_bookmarks(zfs_handle_t *zhp, zfs_iter_f func, void *data) 17478f17100SMatthew Ahrens { 17578f17100SMatthew Ahrens zfs_handle_t *nzhp; 17678f17100SMatthew Ahrens nvlist_t *props = NULL; 17778f17100SMatthew Ahrens nvlist_t *bmarks = NULL; 17878f17100SMatthew Ahrens int err; 17978f17100SMatthew Ahrens 18078f17100SMatthew Ahrens if ((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) != 0) 18178f17100SMatthew Ahrens return (0); 18278f17100SMatthew Ahrens 18378f17100SMatthew Ahrens /* Setup the requested properties nvlist. */ 18478f17100SMatthew Ahrens props = fnvlist_alloc(); 18578f17100SMatthew Ahrens fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_GUID)); 18678f17100SMatthew Ahrens fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATETXG)); 18778f17100SMatthew Ahrens fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATION)); 18878f17100SMatthew Ahrens 18978f17100SMatthew Ahrens if ((err = lzc_get_bookmarks(zhp->zfs_name, props, &bmarks)) != 0) 19078f17100SMatthew Ahrens goto out; 19178f17100SMatthew Ahrens 19278f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); 19378f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(bmarks, pair)) { 194*40a5c998SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 19578f17100SMatthew Ahrens char *bmark_name; 19678f17100SMatthew Ahrens nvlist_t *bmark_props; 19778f17100SMatthew Ahrens 19878f17100SMatthew Ahrens bmark_name = nvpair_name(pair); 19978f17100SMatthew Ahrens bmark_props = fnvpair_value_nvlist(pair); 20078f17100SMatthew Ahrens 20178f17100SMatthew Ahrens (void) snprintf(name, sizeof (name), "%s#%s", zhp->zfs_name, 20278f17100SMatthew Ahrens bmark_name); 20378f17100SMatthew Ahrens 20478f17100SMatthew Ahrens nzhp = make_bookmark_handle(zhp, name, bmark_props); 20578f17100SMatthew Ahrens if (nzhp == NULL) 20678f17100SMatthew Ahrens continue; 20778f17100SMatthew Ahrens 20878f17100SMatthew Ahrens if ((err = func(nzhp, data)) != 0) 20978f17100SMatthew Ahrens goto out; 21078f17100SMatthew Ahrens } 21178f17100SMatthew Ahrens 21278f17100SMatthew Ahrens out: 21378f17100SMatthew Ahrens fnvlist_free(props); 21478f17100SMatthew Ahrens fnvlist_free(bmarks); 21578f17100SMatthew Ahrens 21678f17100SMatthew Ahrens return (err); 21778f17100SMatthew Ahrens } 21878f17100SMatthew Ahrens 21978f17100SMatthew Ahrens /* 22019b94df9SMatthew Ahrens * Routines for dealing with the sorted snapshot functionality 22119b94df9SMatthew Ahrens */ 22219b94df9SMatthew Ahrens typedef struct zfs_node { 22319b94df9SMatthew Ahrens zfs_handle_t *zn_handle; 22419b94df9SMatthew Ahrens avl_node_t zn_avlnode; 22519b94df9SMatthew Ahrens } zfs_node_t; 22619b94df9SMatthew Ahrens 22719b94df9SMatthew Ahrens static int 22819b94df9SMatthew Ahrens zfs_sort_snaps(zfs_handle_t *zhp, void *data) 22919b94df9SMatthew Ahrens { 23019b94df9SMatthew Ahrens avl_tree_t *avl = data; 23119b94df9SMatthew Ahrens zfs_node_t *node; 23219b94df9SMatthew Ahrens zfs_node_t search; 23319b94df9SMatthew Ahrens 23419b94df9SMatthew Ahrens search.zn_handle = zhp; 23519b94df9SMatthew Ahrens node = avl_find(avl, &search, NULL); 23619b94df9SMatthew Ahrens if (node) { 23719b94df9SMatthew Ahrens /* 23819b94df9SMatthew Ahrens * If this snapshot was renamed while we were creating the 23919b94df9SMatthew Ahrens * AVL tree, it's possible that we already inserted it under 24019b94df9SMatthew Ahrens * its old name. Remove the old handle before adding the new 24119b94df9SMatthew Ahrens * one. 24219b94df9SMatthew Ahrens */ 24319b94df9SMatthew Ahrens zfs_close(node->zn_handle); 24419b94df9SMatthew Ahrens avl_remove(avl, node); 24519b94df9SMatthew Ahrens free(node); 24619b94df9SMatthew Ahrens } 24719b94df9SMatthew Ahrens 24819b94df9SMatthew Ahrens node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t)); 24919b94df9SMatthew Ahrens node->zn_handle = zhp; 25019b94df9SMatthew Ahrens avl_add(avl, node); 25119b94df9SMatthew Ahrens 25219b94df9SMatthew Ahrens return (0); 25319b94df9SMatthew Ahrens } 25419b94df9SMatthew Ahrens 25519b94df9SMatthew Ahrens static int 25619b94df9SMatthew Ahrens zfs_snapshot_compare(const void *larg, const void *rarg) 25719b94df9SMatthew Ahrens { 25819b94df9SMatthew Ahrens zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 25919b94df9SMatthew Ahrens zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 26019b94df9SMatthew Ahrens uint64_t lcreate, rcreate; 26119b94df9SMatthew Ahrens 26219b94df9SMatthew Ahrens /* 26319b94df9SMatthew Ahrens * Sort them according to creation time. We use the hidden 26419b94df9SMatthew Ahrens * CREATETXG property to get an absolute ordering of snapshots. 26519b94df9SMatthew Ahrens */ 26619b94df9SMatthew Ahrens lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 26719b94df9SMatthew Ahrens rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 26819b94df9SMatthew Ahrens 26919b94df9SMatthew Ahrens if (lcreate < rcreate) 27019b94df9SMatthew Ahrens return (-1); 27119b94df9SMatthew Ahrens else if (lcreate > rcreate) 27219b94df9SMatthew Ahrens return (+1); 27319b94df9SMatthew Ahrens else 27419b94df9SMatthew Ahrens return (0); 27519b94df9SMatthew Ahrens } 27619b94df9SMatthew Ahrens 27719b94df9SMatthew Ahrens int 27819b94df9SMatthew Ahrens zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data) 27919b94df9SMatthew Ahrens { 28019b94df9SMatthew Ahrens int ret = 0; 28119b94df9SMatthew Ahrens zfs_node_t *node; 28219b94df9SMatthew Ahrens avl_tree_t avl; 28319b94df9SMatthew Ahrens void *cookie = NULL; 28419b94df9SMatthew Ahrens 28519b94df9SMatthew Ahrens avl_create(&avl, zfs_snapshot_compare, 28619b94df9SMatthew Ahrens sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode)); 28719b94df9SMatthew Ahrens 28819b94df9SMatthew Ahrens ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl); 28919b94df9SMatthew Ahrens 29019b94df9SMatthew Ahrens for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node)) 29119b94df9SMatthew Ahrens ret |= callback(node->zn_handle, data); 29219b94df9SMatthew Ahrens 29319b94df9SMatthew Ahrens while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL) 29419b94df9SMatthew Ahrens free(node); 29519b94df9SMatthew Ahrens 29619b94df9SMatthew Ahrens avl_destroy(&avl); 29719b94df9SMatthew Ahrens 29819b94df9SMatthew Ahrens return (ret); 29919b94df9SMatthew Ahrens } 30019b94df9SMatthew Ahrens 30119b94df9SMatthew Ahrens typedef struct { 30219b94df9SMatthew Ahrens char *ssa_first; 30319b94df9SMatthew Ahrens char *ssa_last; 30419b94df9SMatthew Ahrens boolean_t ssa_seenfirst; 30519b94df9SMatthew Ahrens boolean_t ssa_seenlast; 30619b94df9SMatthew Ahrens zfs_iter_f ssa_func; 30719b94df9SMatthew Ahrens void *ssa_arg; 30819b94df9SMatthew Ahrens } snapspec_arg_t; 30919b94df9SMatthew Ahrens 31019b94df9SMatthew Ahrens static int 3119a686fbcSPaul Dagnelie snapspec_cb(zfs_handle_t *zhp, void *arg) 3129a686fbcSPaul Dagnelie { 31319b94df9SMatthew Ahrens snapspec_arg_t *ssa = arg; 31419b94df9SMatthew Ahrens char *shortsnapname; 31519b94df9SMatthew Ahrens int err = 0; 31619b94df9SMatthew Ahrens 31719b94df9SMatthew Ahrens if (ssa->ssa_seenlast) 31819b94df9SMatthew Ahrens return (0); 31919b94df9SMatthew Ahrens shortsnapname = zfs_strdup(zhp->zfs_hdl, 32019b94df9SMatthew Ahrens strchr(zfs_get_name(zhp), '@') + 1); 32119b94df9SMatthew Ahrens 32219b94df9SMatthew Ahrens if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0) 32319b94df9SMatthew Ahrens ssa->ssa_seenfirst = B_TRUE; 32419b94df9SMatthew Ahrens 32519b94df9SMatthew Ahrens if (ssa->ssa_seenfirst) { 32619b94df9SMatthew Ahrens err = ssa->ssa_func(zhp, ssa->ssa_arg); 32719b94df9SMatthew Ahrens } else { 32819b94df9SMatthew Ahrens zfs_close(zhp); 32919b94df9SMatthew Ahrens } 33019b94df9SMatthew Ahrens 33119b94df9SMatthew Ahrens if (strcmp(shortsnapname, ssa->ssa_last) == 0) 33219b94df9SMatthew Ahrens ssa->ssa_seenlast = B_TRUE; 33319b94df9SMatthew Ahrens free(shortsnapname); 33419b94df9SMatthew Ahrens 33519b94df9SMatthew Ahrens return (err); 33619b94df9SMatthew Ahrens } 33719b94df9SMatthew Ahrens 33819b94df9SMatthew Ahrens /* 33919b94df9SMatthew Ahrens * spec is a string like "A,B%C,D" 34019b94df9SMatthew Ahrens * 34119b94df9SMatthew Ahrens * <snaps>, where <snaps> can be: 34219b94df9SMatthew Ahrens * <snap> (single snapshot) 34319b94df9SMatthew Ahrens * <snap>%<snap> (range of snapshots, inclusive) 34419b94df9SMatthew Ahrens * %<snap> (range of snapshots, starting with earliest) 34519b94df9SMatthew Ahrens * <snap>% (range of snapshots, ending with last) 34619b94df9SMatthew Ahrens * % (all snapshots) 34719b94df9SMatthew Ahrens * <snaps>[,...] (comma separated list of the above) 34819b94df9SMatthew Ahrens * 34919b94df9SMatthew Ahrens * If a snapshot can not be opened, continue trying to open the others, but 35019b94df9SMatthew Ahrens * return ENOENT at the end. 35119b94df9SMatthew Ahrens */ 35219b94df9SMatthew Ahrens int 35319b94df9SMatthew Ahrens zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig, 35419b94df9SMatthew Ahrens zfs_iter_f func, void *arg) 35519b94df9SMatthew Ahrens { 3564445fffbSMatthew Ahrens char *buf, *comma_separated, *cp; 35719b94df9SMatthew Ahrens int err = 0; 35819b94df9SMatthew Ahrens int ret = 0; 35919b94df9SMatthew Ahrens 3604445fffbSMatthew Ahrens buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig); 36119b94df9SMatthew Ahrens cp = buf; 36219b94df9SMatthew Ahrens 36319b94df9SMatthew Ahrens while ((comma_separated = strsep(&cp, ",")) != NULL) { 36419b94df9SMatthew Ahrens char *pct = strchr(comma_separated, '%'); 36519b94df9SMatthew Ahrens if (pct != NULL) { 36619b94df9SMatthew Ahrens snapspec_arg_t ssa = { 0 }; 36719b94df9SMatthew Ahrens ssa.ssa_func = func; 36819b94df9SMatthew Ahrens ssa.ssa_arg = arg; 36919b94df9SMatthew Ahrens 37019b94df9SMatthew Ahrens if (pct == comma_separated) 37119b94df9SMatthew Ahrens ssa.ssa_seenfirst = B_TRUE; 37219b94df9SMatthew Ahrens else 37319b94df9SMatthew Ahrens ssa.ssa_first = comma_separated; 37419b94df9SMatthew Ahrens *pct = '\0'; 37519b94df9SMatthew Ahrens ssa.ssa_last = pct + 1; 37619b94df9SMatthew Ahrens 37719b94df9SMatthew Ahrens /* 37819b94df9SMatthew Ahrens * If there is a lastname specified, make sure it 37919b94df9SMatthew Ahrens * exists. 38019b94df9SMatthew Ahrens */ 38119b94df9SMatthew Ahrens if (ssa.ssa_last[0] != '\0') { 382*40a5c998SMatthew Ahrens char snapname[ZFS_MAX_DATASET_NAME_LEN]; 38319b94df9SMatthew Ahrens (void) snprintf(snapname, sizeof (snapname), 38419b94df9SMatthew Ahrens "%s@%s", zfs_get_name(fs_zhp), 38519b94df9SMatthew Ahrens ssa.ssa_last); 38619b94df9SMatthew Ahrens if (!zfs_dataset_exists(fs_zhp->zfs_hdl, 38719b94df9SMatthew Ahrens snapname, ZFS_TYPE_SNAPSHOT)) { 38819b94df9SMatthew Ahrens ret = ENOENT; 38919b94df9SMatthew Ahrens continue; 39019b94df9SMatthew Ahrens } 39119b94df9SMatthew Ahrens } 39219b94df9SMatthew Ahrens 39319b94df9SMatthew Ahrens err = zfs_iter_snapshots_sorted(fs_zhp, 39419b94df9SMatthew Ahrens snapspec_cb, &ssa); 39519b94df9SMatthew Ahrens if (ret == 0) 39619b94df9SMatthew Ahrens ret = err; 39719b94df9SMatthew Ahrens if (ret == 0 && (!ssa.ssa_seenfirst || 39819b94df9SMatthew Ahrens (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) { 39919b94df9SMatthew Ahrens ret = ENOENT; 40019b94df9SMatthew Ahrens } 40119b94df9SMatthew Ahrens } else { 402*40a5c998SMatthew Ahrens char snapname[ZFS_MAX_DATASET_NAME_LEN]; 40319b94df9SMatthew Ahrens zfs_handle_t *snap_zhp; 40419b94df9SMatthew Ahrens (void) snprintf(snapname, sizeof (snapname), "%s@%s", 40519b94df9SMatthew Ahrens zfs_get_name(fs_zhp), comma_separated); 40619b94df9SMatthew Ahrens snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl, 40719b94df9SMatthew Ahrens snapname); 40819b94df9SMatthew Ahrens if (snap_zhp == NULL) { 40919b94df9SMatthew Ahrens ret = ENOENT; 41019b94df9SMatthew Ahrens continue; 41119b94df9SMatthew Ahrens } 41219b94df9SMatthew Ahrens err = func(snap_zhp, arg); 41319b94df9SMatthew Ahrens if (ret == 0) 41419b94df9SMatthew Ahrens ret = err; 41519b94df9SMatthew Ahrens } 41619b94df9SMatthew Ahrens } 41719b94df9SMatthew Ahrens 4184445fffbSMatthew Ahrens free(buf); 41919b94df9SMatthew Ahrens return (ret); 42019b94df9SMatthew Ahrens } 42119b94df9SMatthew Ahrens 42219b94df9SMatthew Ahrens /* 42319b94df9SMatthew Ahrens * Iterate over all children, snapshots and filesystems 42419b94df9SMatthew Ahrens */ 42519b94df9SMatthew Ahrens int 42619b94df9SMatthew Ahrens zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 42719b94df9SMatthew Ahrens { 42819b94df9SMatthew Ahrens int ret; 42919b94df9SMatthew Ahrens 43019b94df9SMatthew Ahrens if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 43119b94df9SMatthew Ahrens return (ret); 43219b94df9SMatthew Ahrens 43319b94df9SMatthew Ahrens return (zfs_iter_snapshots(zhp, func, data)); 43419b94df9SMatthew Ahrens } 43519b94df9SMatthew Ahrens 43619b94df9SMatthew Ahrens 43719b94df9SMatthew Ahrens typedef struct iter_stack_frame { 43819b94df9SMatthew Ahrens struct iter_stack_frame *next; 43919b94df9SMatthew Ahrens zfs_handle_t *zhp; 44019b94df9SMatthew Ahrens } iter_stack_frame_t; 44119b94df9SMatthew Ahrens 44219b94df9SMatthew Ahrens typedef struct iter_dependents_arg { 44319b94df9SMatthew Ahrens boolean_t first; 44419b94df9SMatthew Ahrens boolean_t allowrecursion; 44519b94df9SMatthew Ahrens iter_stack_frame_t *stack; 44619b94df9SMatthew Ahrens zfs_iter_f func; 44719b94df9SMatthew Ahrens void *data; 44819b94df9SMatthew Ahrens } iter_dependents_arg_t; 44919b94df9SMatthew Ahrens 45019b94df9SMatthew Ahrens static int 45119b94df9SMatthew Ahrens iter_dependents_cb(zfs_handle_t *zhp, void *arg) 45219b94df9SMatthew Ahrens { 45319b94df9SMatthew Ahrens iter_dependents_arg_t *ida = arg; 45478f17100SMatthew Ahrens int err = 0; 45519b94df9SMatthew Ahrens boolean_t first = ida->first; 45619b94df9SMatthew Ahrens ida->first = B_FALSE; 45719b94df9SMatthew Ahrens 45819b94df9SMatthew Ahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 45919b94df9SMatthew Ahrens err = zfs_iter_clones(zhp, iter_dependents_cb, ida); 46078f17100SMatthew Ahrens } else if (zhp->zfs_type != ZFS_TYPE_BOOKMARK) { 46119b94df9SMatthew Ahrens iter_stack_frame_t isf; 46219b94df9SMatthew Ahrens iter_stack_frame_t *f; 46319b94df9SMatthew Ahrens 46419b94df9SMatthew Ahrens /* 46519b94df9SMatthew Ahrens * check if there is a cycle by seeing if this fs is already 46619b94df9SMatthew Ahrens * on the stack. 46719b94df9SMatthew Ahrens */ 46819b94df9SMatthew Ahrens for (f = ida->stack; f != NULL; f = f->next) { 46919b94df9SMatthew Ahrens if (f->zhp->zfs_dmustats.dds_guid == 47019b94df9SMatthew Ahrens zhp->zfs_dmustats.dds_guid) { 47119b94df9SMatthew Ahrens if (ida->allowrecursion) { 47219b94df9SMatthew Ahrens zfs_close(zhp); 47319b94df9SMatthew Ahrens return (0); 47419b94df9SMatthew Ahrens } else { 47519b94df9SMatthew Ahrens zfs_error_aux(zhp->zfs_hdl, 47619b94df9SMatthew Ahrens dgettext(TEXT_DOMAIN, 47719b94df9SMatthew Ahrens "recursive dependency at '%s'"), 47819b94df9SMatthew Ahrens zfs_get_name(zhp)); 47919b94df9SMatthew Ahrens err = zfs_error(zhp->zfs_hdl, 48019b94df9SMatthew Ahrens EZFS_RECURSIVE, 48119b94df9SMatthew Ahrens dgettext(TEXT_DOMAIN, 48219b94df9SMatthew Ahrens "cannot determine dependent " 48319b94df9SMatthew Ahrens "datasets")); 48419b94df9SMatthew Ahrens zfs_close(zhp); 48519b94df9SMatthew Ahrens return (err); 48619b94df9SMatthew Ahrens } 48719b94df9SMatthew Ahrens } 48819b94df9SMatthew Ahrens } 48919b94df9SMatthew Ahrens 49019b94df9SMatthew Ahrens isf.zhp = zhp; 49119b94df9SMatthew Ahrens isf.next = ida->stack; 49219b94df9SMatthew Ahrens ida->stack = &isf; 49319b94df9SMatthew Ahrens err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida); 49419b94df9SMatthew Ahrens if (err == 0) 49519b94df9SMatthew Ahrens err = zfs_iter_snapshots(zhp, iter_dependents_cb, ida); 49619b94df9SMatthew Ahrens ida->stack = isf.next; 49719b94df9SMatthew Ahrens } 4982fbdf8dbSMarcel Telka 49919b94df9SMatthew Ahrens if (!first && err == 0) 50019b94df9SMatthew Ahrens err = ida->func(zhp, ida->data); 5012fbdf8dbSMarcel Telka else 5022fbdf8dbSMarcel Telka zfs_close(zhp); 5032fbdf8dbSMarcel Telka 50419b94df9SMatthew Ahrens return (err); 50519b94df9SMatthew Ahrens } 50619b94df9SMatthew Ahrens 50719b94df9SMatthew Ahrens int 50819b94df9SMatthew Ahrens zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 50919b94df9SMatthew Ahrens zfs_iter_f func, void *data) 51019b94df9SMatthew Ahrens { 51119b94df9SMatthew Ahrens iter_dependents_arg_t ida; 51219b94df9SMatthew Ahrens ida.allowrecursion = allowrecursion; 51319b94df9SMatthew Ahrens ida.stack = NULL; 51419b94df9SMatthew Ahrens ida.func = func; 51519b94df9SMatthew Ahrens ida.data = data; 51619b94df9SMatthew Ahrens ida.first = B_TRUE; 51719b94df9SMatthew Ahrens return (iter_dependents_cb(zfs_handle_dup(zhp), &ida)); 51819b94df9SMatthew Ahrens } 519