/* * Construct a complete graph of all necessary vertices. First, we iterate over * only our object's children. If we don't find any cloned snapshots, then we * simple return that. Otherwise, we have to start at the pool root and iterate * over all datasets. */ static zfs_graph_t * construct_graph(libzfs_handle_t *hdl, const char *dataset) { zfs_graph_t *zgp = zfs_graph_create(hdl, ZFS_GRAPH_SIZE); zfs_cmd_t zc = { 0 }; int ret = 0; if (zgp == NULL) return (zgp); /* * We need to explicitly check whether this dataset has clones or not, * since iterate_children() only checks the children. */ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); (void) ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc); if (zc.zc_objset_stats.dds_num_clones != 0 || (ret = iterate_children(hdl, zgp, dataset)) != 0) { /* * Determine pool name and try again. */ char *pool, *slash; if ((slash = strchr(dataset, '/')) != NULL || (slash = strchr(dataset, '@')) != NULL) { pool = zfs_alloc(hdl, slash - dataset + 1); if (pool == NULL) { zfs_graph_destroy(zgp); return (NULL); } (void) strncpy(pool, dataset, slash - dataset); pool[slash - dataset] = '\0'; if (iterate_children(hdl, zgp, pool) == -1 || zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) { free(pool); zfs_graph_destroy(zgp); return (NULL); } free(pool); } } if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) { zfs_graph_destroy(zgp); return (NULL); } return (zgp); }
/* * The only public interface for this file. Do the dirty work of constructing a * child list for the given object. Construct the graph, do the toplogical * sort, and then return the array of strings to the caller. * * The 'allowrecursion' parameter controls behavior when cycles are found. If * it is set, the the cycle is ignored and the results returned as if the cycle * did not exist. If it is not set, then the routine will generate an error if * a cycle is found. */ int get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion, const char *dataset, char ***result, size_t *count) { zfs_graph_t *zgp; zfs_vertex_t *zvp; if ((zgp = construct_graph(hdl, dataset)) == NULL) return (-1); if ((*result = zfs_alloc(hdl, zgp->zg_nvertex * sizeof (char *))) == NULL) { zfs_graph_destroy(zgp); return (-1); } if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) { free(*result); zfs_graph_destroy(zgp); return (-1); } *count = 0; if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) { free(*result); zfs_graph_destroy(zgp); return (-1); } /* * Get rid of the last entry, which is our starting vertex and not * strictly a dependent. */ assert(*count > 0); free((*result)[*count - 1]); (*count)--; zfs_graph_destroy(zgp); return (0); }
/* * Construct a complete graph of all necessary vertices. First, iterate over * only our object's children. If no cloned snapshots are found, or all of * the cloned snapshots are in this subtree then return a graph of the subtree. * Otherwise, start at the root of the pool and iterate over all datasets. */ static zfs_graph_t * construct_graph(libzfs_handle_t *hdl, const char *dataset) { zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE); int ret = 0; if (zgp == NULL) return (zgp); if ((strchr(dataset, '/') == NULL) || (external_dependents(hdl, zgp, dataset))) { /* * Determine pool name and try again. */ int len = strcspn(dataset, "/@") + 1; char *pool = zfs_alloc(hdl, len); if (pool == NULL) { zfs_graph_destroy(zgp); return (NULL); } (void) strlcpy(pool, dataset, len); if (iterate_children(hdl, zgp, pool) == -1 || zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) { free(pool); zfs_graph_destroy(zgp); return (NULL); } free(pool); } if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) { zfs_graph_destroy(zgp); return (NULL); } return (zgp); }