/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Iterate over all children of the current object. This includes the normal
* dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
* walk all datasets in the pool, and construct a directed graph of the form:
*
* home
* |
* +----+----+
* | |
* v v ws
* bar baz |
* | |
* v v
* @yesterday ----> foo
*
* In order to construct this graph, we have to walk every dataset in the pool,
* because the clone parent is stored as a property of the child, not the
* parent. The parent only keeps track of the number of clones.
*
* In the normal case (without clones) this would be rather expensive. To avoid
* unnecessary computation, we first try a walk of the subtree hierarchy
* starting from the initial node. At each dataset, we construct a node in the
* graph and an edge leading from its parent. If we don't see any snapshots
* with a non-zero clone count, then we are finished.
*
* If we do find a cloned snapshot, then we finish the walk of the current
* subtree, but indicate that we need to do a complete walk. We then perform a
* global walk of all datasets, avoiding the subtree we already processed.
*
* At the end of this, we'll end up with a directed graph of all relevant (and
* possible some irrelevant) datasets in the system. We need to both find our
* limiting subgraph and determine a safe ordering in which to destroy the
* datasets. We do a topological ordering of our graph starting at our target
* dataset, and then walk the results in reverse.
*
* It's possible for the graph to have cycles if, for example, the user renames
* a clone to be the parent of its origin snapshot. The user can request to
* generate an error in this case, or ignore the cycle and continue.
*
* When removing datasets, we want to destroy the snapshots in chronological
* order (because this is the most efficient method). In order to accomplish
* this, we store the creation transaction group with each vertex and keep each
* vertex's edges sorted according to this value. The topological sort will
* automatically walk the snapshots in the correct order.
*/
#include <assert.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <libzfs.h>
#include "libzfs_impl.h"
/*
* Vertex structure. Indexed by dataset name, this structure maintains a list
* of edges to other vertices.
*/
struct zfs_edge;
typedef struct zfs_vertex {
int zv_visited;
int zv_edgecount;
int zv_edgealloc;
} zfs_vertex_t;
enum {
};
/*
* Edge structure. Simply maintains a pointer to the destination vertex. There
* is no need to store the source vertex, since we only use edges in the context
* of the source vertex.
*/
typedef struct zfs_edge {
} zfs_edge_t;
/*
* Graph structure. Vertices are maintained in a hash indexed by dataset name.
*/
typedef struct zfs_graph {
const char *zg_root;
int zg_clone_count;
} zfs_graph_t;
/*
* Allocate a new edge pointing to the target vertex.
*/
static zfs_edge_t *
{
return (NULL);
return (zep);
}
/*
* Destroy an edge.
*/
static void
{
}
/*
* Allocate a new vertex with the given name.
*/
static zfs_vertex_t *
{
return (NULL);
MIN_EDGECOUNT * sizeof (void *))) == NULL) {
return (NULL);
}
return (zvp);
}
/*
* Destroy a vertex. Frees up any associated edges.
*/
static void
{
int i;
for (i = 0; i < zvp->zv_edgecount; i++)
}
/*
* Given a vertex, add an edge to the destination vertex.
*/
static int
{
void *ptr;
zvp->zv_edgealloc * sizeof (void *),
return (-1);
}
return (-1);
return (0);
}
static int
zfs_edge_compare(const void *a, const void *b)
{
return (-1);
return (1);
return (0);
}
/*
* Sort the given vertex edges according to the creation txg of each vertex.
*/
static void
{
if (zvp->zv_edgecount == 0)
return;
}
/*
* Construct a new graph object. We allow the size to be specified as a
* parameter so in the future we can size the hash according to the number of
* datasets in the pool.
*/
static zfs_graph_t *
{
return (NULL);
return (NULL);
}
zgp->zg_clone_count = 0;
return (zgp);
}
/*
* Destroy a graph object. We have to iterate over all the hash chains,
* destroying each vertex in the process.
*/
static void
{
int i;
}
}
}
/*
* Graph hash function. Classic bernstein k=33 hash function, taken from
*/
static size_t
{
int c;
while ((c = *str++) != 0)
}
/*
* Given a dataset name, finds the associated vertex, creating it if necessary.
*/
static zfs_vertex_t *
{
return (zvp);
}
}
return (NULL);
zgp->zg_nvertex++;
return (zvp);
}
/*
* 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
{
return (-1);
return (-1);
return (-1);
}
return (0);
}
static int
{
int rc;
if (rc == -1) {
switch (errno) {
/*
* An errno value of ESRCH indicates normal completion.
* If ENOENT is returned, then the underlying dataset
* has been removed since we obtained the handle.
*/
case ESRCH:
case ENOENT:
break;
default:
break;
}
}
return (rc);
}
/*
* 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
{
/*
* Look up the source vertex, and avoid it if we've seen it before.
*/
return (-1);
return (0);
/*
* Iterate over all children
*/
return (-1);
/*
* Count origins only if they are contained in the graph
*/
zgp->zg_clone_count--;
}
/*
* Add an edge between the parent and the child.
*/
return (-1);
/*
* Recursively visit child
*/
return (-1);
}
/*
* Now iterate over all snapshots.
*/
/*
* Add an edge between the parent and the child.
*/
return (-1);
}
/*
* Now iterate over all shares.
*/
/*
* Add an edge between the parent and the child.
*/
return (-1);
}
return (0);
}
/*
* Returns false if there are no snapshots with dependent clones in this
* subtree or if all of those clones are also in this subtree. Returns
* true if there is an error or there are external dependents.
*/
static boolean_t
{
/*
* Check whether this dataset is a clone or has clones since
* iterate_children() only checks the children.
*/
return (B_TRUE);
return (B_TRUE);
zgp->zg_clone_count--;
}
return (B_TRUE);
return (zgp->zg_clone_count != 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 *
{
int ret = 0;
return (zgp);
/*
* Determine pool name and try again.
*/
return (NULL);
}
return (NULL);
}
}
return (NULL);
}
return (zgp);
}
/*
* Given a graph, do a recursive topological sort into the given array. This is
* really just a depth first search, so that the deepest nodes appear first.
* hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
*/
static int
{
int i;
/*
* If we've already seen this vertex as part of our depth-first
* search, then we have a cyclic dependency, and we must return
* an error.
*/
"recursive dependency at '%s'"),
zgv->zv_dataset);
"cannot determine dependent datasets")));
/*
* If we've already processed this as part of the topological
* sort, then don't bother doing so again.
*/
return (0);
}
/* avoid doing a search if we don't have to */
for (i = 0; i < zgv->zv_edgecount; i++) {
return (-1);
}
/* we may have visited this in the course of the above */
return (0);
return (-1);
*idx += 1;
return (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
{
return (-1);
return (-1);
}
return (-1);
}
*count = 0;
return (-1);
}
/*
* Get rid of the last entry, which is our starting vertex and not
* strictly a dependent.
*/
(*count)--;
return (0);
}