Esempio n. 1
0
/*
 * Given two dataset names, create an edge between them.  For the source vertex,
 * mark 'zv_visited' to indicate that we have seen this vertex, and not simply
 * created it as a destination of another edge.  If 'dest' is NULL, then this
 * is an individual vertex (i.e. the starting vertex), so don't add an edge.
 */
static int
zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
    const char *dest, uint64_t txg)
{
	zfs_vertex_t *svp, *dvp;

	if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
		return (-1);
	svp->zv_visited = VISIT_SEEN;
	if (dest != NULL) {
		dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
		if (dvp == NULL)
			return (-1);
		if (zfs_vertex_add_edge(hdl, svp, dvp) != 0)
			return (-1);
	}

	return (0);
}
Esempio n. 2
0
/*
 * 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);
}
Esempio n. 3
0
/*
 * Iterate over all children of the given dataset, adding any vertices as
 * necessary.  Returns 0 if no cloned snapshots were seen, -1 if there was an
 * error, or 1 otherwise.  This is a simple recursive algorithm - the ZFS
 * namespace typically is very flat.  We manually invoke the necessary ioctl()
 * calls to avoid the overhead and additional semantics of zfs_open().
 */
static int
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
{
	zfs_cmd_t zc = { 0 };
	int ret = 0, err;
	zfs_vertex_t *zvp;

	/*
	 * Look up the source vertex, and avoid it if we've seen it before.
	 */
	zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
	if (zvp == NULL)
		return (-1);
	if (zvp->zv_visited == VISIT_SEEN)
		return (0);

	/*
	 * We check the clone parent here instead of within the loop, so that if
	 * the root dataset has been promoted from a clone, we find its parent
	 * appropriately.
	 */
	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0 &&
	    zc.zc_objset_stats.dds_clone_of[0] != '\0') {
		if (zfs_graph_add(hdl, zgp, zc.zc_objset_stats.dds_clone_of,
		    zc.zc_name, zc.zc_objset_stats.dds_creation_txg) != 0)
			return (-1);
	}

	for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
	    ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
	    (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {

		/*
		 * Ignore private dataset names.
		 */
		if (dataset_name_hidden(zc.zc_name))
			continue;

		/*
		 * Get statistics for this dataset, to determine the type of the
		 * dataset and clone statistics.  If this fails, the dataset has
		 * since been removed, and we're pretty much screwed anyway.
		 */
		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
			continue;

		/*
		 * Add an edge between the parent and the child.
		 */
		if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
		    zc.zc_objset_stats.dds_creation_txg) != 0)
			return (-1);

		/*
		 * Iterate over all children
		 */
		err = iterate_children(hdl, zgp, zc.zc_name);
		if (err == -1)
			return (-1);
		else if (err == 1)
			ret = 1;

		/*
		 * Indicate if we found a dataset with a non-zero clone count.
		 */
		if (zc.zc_objset_stats.dds_num_clones != 0)
			ret = 1;
	}

	/*
	 * Now iterate over all snapshots.
	 */
	bzero(&zc, sizeof (zc));

	for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
	    ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
	    (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {

		/*
		 * Get statistics for this dataset, to determine the type of the
		 * dataset and clone statistics.  If this fails, the dataset has
		 * since been removed, and we're pretty much screwed anyway.
		 */
		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
			continue;

		/*
		 * Add an edge between the parent and the child.
		 */
		if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
		    zc.zc_objset_stats.dds_creation_txg) != 0)
			return (-1);

		/*
		 * Indicate if we found a dataset with a non-zero clone count.
		 */
		if (zc.zc_objset_stats.dds_num_clones != 0)
			ret = 1;
	}

	zvp->zv_visited = VISIT_SEEN;

	return (ret);
}
Esempio n. 4
0
/*
 * Iterate over all children of the given dataset, adding any vertices
 * as necessary.  Returns -1 if there was an error, or 0 otherwise.
 * This is a simple recursive algorithm - the ZFS namespace typically
 * is very flat.  We manually invoke the necessary ioctl() calls to
 * avoid the overhead and additional semantics of zfs_open().
 */
static int
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
{
	zfs_cmd_t zc = { 0 };
	zfs_vertex_t *zvp;

	/*
	 * Look up the source vertex, and avoid it if we've seen it before.
	 */
	zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
	if (zvp == NULL)
		return (-1);
	if (zvp->zv_visited == VISIT_SEEN)
		return (0);

	/*
	 * Iterate over all children
	 */
	for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
	    ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
	    (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {

		/*
		 * Ignore private dataset names.
		 */
		if (dataset_name_hidden(zc.zc_name))
			continue;

		/*
		 * Get statistics for this dataset, to determine the type of the
		 * dataset and clone statistics.  If this fails, the dataset has
		 * since been removed, and we're pretty much screwed anyway.
		 */
		zc.zc_objset_stats.dds_origin[0] = '\0';
		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
			continue;

		if (zc.zc_objset_stats.dds_origin[0] != '\0') {
			if (zfs_graph_add(hdl, zgp,
			    zc.zc_objset_stats.dds_origin, zc.zc_name,
			    zc.zc_objset_stats.dds_creation_txg) != 0)
				return (-1);
			/*
			 * Count origins only if they are contained in the graph
			 */
			if (isa_child_of(zc.zc_objset_stats.dds_origin,
			    zgp->zg_root))
				zgp->zg_clone_count--;
		}

		/*
		 * Add an edge between the parent and the child.
		 */
		if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
		    zc.zc_objset_stats.dds_creation_txg) != 0)
			return (-1);

		/*
		 * Recursively visit child
		 */
		if (iterate_children(hdl, zgp, zc.zc_name))
			return (-1);
	}

	/*
	 * Now iterate over all snapshots.
	 */
	bzero(&zc, sizeof (zc));

	for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
	    ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
	    (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {

		/*
		 * Get statistics for this dataset, to determine the type of the
		 * dataset and clone statistics.  If this fails, the dataset has
		 * since been removed, and we're pretty much screwed anyway.
		 */
		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
			continue;

		/*
		 * Add an edge between the parent and the child.
		 */
		if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
		    zc.zc_objset_stats.dds_creation_txg) != 0)
			return (-1);

		zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
	}

	zvp->zv_visited = VISIT_SEEN;

	return (0);
}