1*789Sahrens /* 2*789Sahrens * CDDL HEADER START 3*789Sahrens * 4*789Sahrens * The contents of this file are subject to the terms of the 5*789Sahrens * Common Development and Distribution License, Version 1.0 only 6*789Sahrens * (the "License"). You may not use this file except in compliance 7*789Sahrens * with the License. 8*789Sahrens * 9*789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*789Sahrens * or http://www.opensolaris.org/os/licensing. 11*789Sahrens * See the License for the specific language governing permissions 12*789Sahrens * and limitations under the License. 13*789Sahrens * 14*789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 15*789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*789Sahrens * If applicable, add the following below this CDDL HEADER, with the 17*789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 18*789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 19*789Sahrens * 20*789Sahrens * CDDL HEADER END 21*789Sahrens */ 22*789Sahrens /* 23*789Sahrens * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*789Sahrens * Use is subject to license terms. 25*789Sahrens */ 26*789Sahrens 27*789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28*789Sahrens 29*789Sahrens /* 30*789Sahrens * Iterate over all children of the current object. This includes the normal 31*789Sahrens * dataset hierarchy, but also arbitrary hierarchies due to clones. We want to 32*789Sahrens * walk all datasets in the pool, and construct a directed graph of the form: 33*789Sahrens * 34*789Sahrens * home 35*789Sahrens * | 36*789Sahrens * +----+----+ 37*789Sahrens * | | 38*789Sahrens * v v ws 39*789Sahrens * bar baz | 40*789Sahrens * | | 41*789Sahrens * v v 42*789Sahrens * @yesterday ----> foo 43*789Sahrens * 44*789Sahrens * In order to construct this graph, we have to walk every dataset in the pool, 45*789Sahrens * because the clone parent is stored as a property of the child, not the 46*789Sahrens * parent. The parent only keeps track of the number of clones. 47*789Sahrens * 48*789Sahrens * In the normal case (without clones) this would be rather expensive. To avoid 49*789Sahrens * unnecessary computation, we first try a walk of the subtree hierarchy 50*789Sahrens * starting from the initial node. At each dataset, we construct a node in the 51*789Sahrens * graph and an edge leading from its parent. If we don't see any snapshots 52*789Sahrens * with a non-zero clone count, then we are finished. 53*789Sahrens * 54*789Sahrens * If we do find a cloned snapshot, then we finish the walk of the current 55*789Sahrens * subtree, but indicate that we need to do a complete walk. We then perform a 56*789Sahrens * global walk of all datasets, avoiding the subtree we already processed. 57*789Sahrens * 58*789Sahrens * At the end of this, we'll end up with a directed graph of all relevant (and 59*789Sahrens * possible some irrelevant) datasets in the system. We need to both find our 60*789Sahrens * limiting subgraph and determine a safe ordering in which to destroy the 61*789Sahrens * datasets. We do a topological ordering of our graph starting at our target 62*789Sahrens * dataset, and then walk the results in reverse. 63*789Sahrens * 64*789Sahrens * When removing datasets, we want to destroy the snapshots in chronological 65*789Sahrens * order (because this is the most efficient method). In order to accomplish 66*789Sahrens * this, we store the creation transaction group with each vertex and keep each 67*789Sahrens * vertex's edges sorted according to this value. The topological sort will 68*789Sahrens * automatically walk the snapshots in the correct order. 69*789Sahrens */ 70*789Sahrens 71*789Sahrens #include <assert.h> 72*789Sahrens #include <stdio.h> 73*789Sahrens #include <stdlib.h> 74*789Sahrens #include <string.h> 75*789Sahrens #include <strings.h> 76*789Sahrens #include <unistd.h> 77*789Sahrens 78*789Sahrens #include <libzfs.h> 79*789Sahrens 80*789Sahrens #include "libzfs_impl.h" 81*789Sahrens #include "zfs_namecheck.h" 82*789Sahrens 83*789Sahrens #define MIN_EDGECOUNT 4 84*789Sahrens 85*789Sahrens /* 86*789Sahrens * Vertex structure. Indexed by dataset name, this structure maintains a list 87*789Sahrens * of edges to other vertices. 88*789Sahrens */ 89*789Sahrens struct zfs_edge; 90*789Sahrens typedef struct zfs_vertex { 91*789Sahrens char zv_dataset[ZFS_MAXNAMELEN]; 92*789Sahrens struct zfs_vertex *zv_next; 93*789Sahrens int zv_visited; 94*789Sahrens uint64_t zv_txg; 95*789Sahrens struct zfs_edge **zv_edges; 96*789Sahrens int zv_edgecount; 97*789Sahrens int zv_edgealloc; 98*789Sahrens } zfs_vertex_t; 99*789Sahrens 100*789Sahrens /* 101*789Sahrens * Edge structure. Simply maintains a pointer to the destination vertex. There 102*789Sahrens * is no need to store the source vertex, since we only use edges in the context 103*789Sahrens * of the source vertex. 104*789Sahrens */ 105*789Sahrens typedef struct zfs_edge { 106*789Sahrens zfs_vertex_t *ze_dest; 107*789Sahrens struct zfs_edge *ze_next; 108*789Sahrens } zfs_edge_t; 109*789Sahrens 110*789Sahrens #define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */ 111*789Sahrens 112*789Sahrens /* 113*789Sahrens * Graph structure. Vertices are maintained in a hash indexed by dataset name. 114*789Sahrens */ 115*789Sahrens typedef struct zfs_graph { 116*789Sahrens zfs_vertex_t **zg_hash; 117*789Sahrens size_t zg_size; 118*789Sahrens size_t zg_nvertex; 119*789Sahrens } zfs_graph_t; 120*789Sahrens 121*789Sahrens /* 122*789Sahrens * Allocate a new edge pointing to the target vertex. 123*789Sahrens */ 124*789Sahrens static zfs_edge_t * 125*789Sahrens zfs_edge_create(zfs_vertex_t *dest) 126*789Sahrens { 127*789Sahrens zfs_edge_t *zep = zfs_malloc(sizeof (zfs_edge_t)); 128*789Sahrens 129*789Sahrens zep->ze_dest = dest; 130*789Sahrens 131*789Sahrens return (zep); 132*789Sahrens } 133*789Sahrens 134*789Sahrens /* 135*789Sahrens * Destroy an edge. 136*789Sahrens */ 137*789Sahrens static void 138*789Sahrens zfs_edge_destroy(zfs_edge_t *zep) 139*789Sahrens { 140*789Sahrens free(zep); 141*789Sahrens } 142*789Sahrens 143*789Sahrens /* 144*789Sahrens * Allocate a new vertex with the given name. 145*789Sahrens */ 146*789Sahrens static zfs_vertex_t * 147*789Sahrens zfs_vertex_create(const char *dataset) 148*789Sahrens { 149*789Sahrens zfs_vertex_t *zvp = zfs_malloc(sizeof (zfs_vertex_t)); 150*789Sahrens 151*789Sahrens assert(strlen(dataset) < ZFS_MAXNAMELEN); 152*789Sahrens 153*789Sahrens (void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset)); 154*789Sahrens 155*789Sahrens zvp->zv_edges = zfs_malloc(MIN_EDGECOUNT * sizeof (void *)); 156*789Sahrens zvp->zv_edgealloc = MIN_EDGECOUNT; 157*789Sahrens 158*789Sahrens return (zvp); 159*789Sahrens } 160*789Sahrens 161*789Sahrens /* 162*789Sahrens * Destroy a vertex. Frees up any associated edges. 163*789Sahrens */ 164*789Sahrens static void 165*789Sahrens zfs_vertex_destroy(zfs_vertex_t *zvp) 166*789Sahrens { 167*789Sahrens int i; 168*789Sahrens 169*789Sahrens for (i = 0; i < zvp->zv_edgecount; i++) 170*789Sahrens zfs_edge_destroy(zvp->zv_edges[i]); 171*789Sahrens 172*789Sahrens free(zvp->zv_edges); 173*789Sahrens free(zvp); 174*789Sahrens } 175*789Sahrens 176*789Sahrens /* 177*789Sahrens * Given a vertex, add an edge to the destination vertex. 178*789Sahrens */ 179*789Sahrens static void 180*789Sahrens zfs_vertex_add_edge(zfs_vertex_t *zvp, zfs_vertex_t *dest) 181*789Sahrens { 182*789Sahrens zfs_edge_t *zep = zfs_edge_create(dest); 183*789Sahrens 184*789Sahrens if (zvp->zv_edgecount == zvp->zv_edgealloc) { 185*789Sahrens zfs_edge_t **newedges = zfs_malloc(zvp->zv_edgealloc * 2 * 186*789Sahrens sizeof (void *)); 187*789Sahrens 188*789Sahrens bcopy(zvp->zv_edges, newedges, 189*789Sahrens zvp->zv_edgealloc * sizeof (void *)); 190*789Sahrens 191*789Sahrens zvp->zv_edgealloc *= 2; 192*789Sahrens free(zvp->zv_edges); 193*789Sahrens zvp->zv_edges = newedges; 194*789Sahrens } 195*789Sahrens 196*789Sahrens zvp->zv_edges[zvp->zv_edgecount++] = zep; 197*789Sahrens } 198*789Sahrens 199*789Sahrens static int 200*789Sahrens zfs_edge_compare(const void *a, const void *b) 201*789Sahrens { 202*789Sahrens const zfs_edge_t *ea = *((zfs_edge_t **)a); 203*789Sahrens const zfs_edge_t *eb = *((zfs_edge_t **)b); 204*789Sahrens 205*789Sahrens if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg) 206*789Sahrens return (-1); 207*789Sahrens if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg) 208*789Sahrens return (1); 209*789Sahrens return (0); 210*789Sahrens } 211*789Sahrens 212*789Sahrens /* 213*789Sahrens * Sort the given vertex edges according to the creation txg of each vertex. 214*789Sahrens */ 215*789Sahrens static void 216*789Sahrens zfs_vertex_sort_edges(zfs_vertex_t *zvp) 217*789Sahrens { 218*789Sahrens if (zvp->zv_edgecount == 0) 219*789Sahrens return; 220*789Sahrens 221*789Sahrens qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *), 222*789Sahrens zfs_edge_compare); 223*789Sahrens } 224*789Sahrens 225*789Sahrens /* 226*789Sahrens * Construct a new graph object. We allow the size to be specified as a 227*789Sahrens * parameter so in the future we can size the hash according to the number of 228*789Sahrens * datasets in the pool. 229*789Sahrens */ 230*789Sahrens static zfs_graph_t * 231*789Sahrens zfs_graph_create(size_t size) 232*789Sahrens { 233*789Sahrens zfs_graph_t *zgp = zfs_malloc(sizeof (zfs_graph_t)); 234*789Sahrens 235*789Sahrens zgp->zg_size = size; 236*789Sahrens zgp->zg_hash = zfs_malloc(size * sizeof (zfs_vertex_t *)); 237*789Sahrens 238*789Sahrens return (zgp); 239*789Sahrens } 240*789Sahrens 241*789Sahrens /* 242*789Sahrens * Destroy a graph object. We have to iterate over all the hash chains, 243*789Sahrens * destroying each vertex in the process. 244*789Sahrens */ 245*789Sahrens static void 246*789Sahrens zfs_graph_destroy(zfs_graph_t *zgp) 247*789Sahrens { 248*789Sahrens int i; 249*789Sahrens zfs_vertex_t *current, *next; 250*789Sahrens 251*789Sahrens for (i = 0; i < zgp->zg_size; i++) { 252*789Sahrens current = zgp->zg_hash[i]; 253*789Sahrens while (current != NULL) { 254*789Sahrens next = current->zv_next; 255*789Sahrens zfs_vertex_destroy(current); 256*789Sahrens current = next; 257*789Sahrens } 258*789Sahrens } 259*789Sahrens 260*789Sahrens free(zgp->zg_hash); 261*789Sahrens free(zgp); 262*789Sahrens } 263*789Sahrens 264*789Sahrens /* 265*789Sahrens * Graph hash function. Classic bernstein k=33 hash function, taken from 266*789Sahrens * usr/src/cmd/sgs/tools/common/strhash.c 267*789Sahrens */ 268*789Sahrens static size_t 269*789Sahrens zfs_graph_hash(zfs_graph_t *zgp, const char *str) 270*789Sahrens { 271*789Sahrens size_t hash = 5381; 272*789Sahrens int c; 273*789Sahrens 274*789Sahrens while ((c = *str++) != 0) 275*789Sahrens hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 276*789Sahrens 277*789Sahrens return (hash % zgp->zg_size); 278*789Sahrens } 279*789Sahrens 280*789Sahrens /* 281*789Sahrens * Given a dataset name, finds the associated vertex, creating it if necessary. 282*789Sahrens */ 283*789Sahrens static zfs_vertex_t * 284*789Sahrens zfs_graph_lookup(zfs_graph_t *zgp, const char *dataset, uint64_t txg) 285*789Sahrens { 286*789Sahrens size_t idx = zfs_graph_hash(zgp, dataset); 287*789Sahrens zfs_vertex_t *zvp; 288*789Sahrens 289*789Sahrens for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) { 290*789Sahrens if (strcmp(zvp->zv_dataset, dataset) == 0) { 291*789Sahrens if (zvp->zv_txg == 0) 292*789Sahrens zvp->zv_txg = txg; 293*789Sahrens return (zvp); 294*789Sahrens } 295*789Sahrens } 296*789Sahrens 297*789Sahrens zvp = zfs_vertex_create(dataset); 298*789Sahrens zvp->zv_next = zgp->zg_hash[idx]; 299*789Sahrens zvp->zv_txg = txg; 300*789Sahrens zgp->zg_hash[idx] = zvp; 301*789Sahrens zgp->zg_nvertex++; 302*789Sahrens 303*789Sahrens return (zvp); 304*789Sahrens } 305*789Sahrens 306*789Sahrens /* 307*789Sahrens * Given two dataset names, create an edge between them. For the source vertex, 308*789Sahrens * mark 'zv_visited' to indicate that we have seen this vertex, and not simply 309*789Sahrens * created it as a destination of another edge. If 'dest' is NULL, then this 310*789Sahrens * is an individual vertex (i.e. the starting vertex), so don't add an edge. 311*789Sahrens */ 312*789Sahrens static void 313*789Sahrens zfs_graph_add(zfs_graph_t *zgp, const char *source, const char *dest, 314*789Sahrens uint64_t txg) 315*789Sahrens { 316*789Sahrens zfs_vertex_t *svp, *dvp; 317*789Sahrens 318*789Sahrens svp = zfs_graph_lookup(zgp, source, 0); 319*789Sahrens svp->zv_visited = 1; 320*789Sahrens if (dest != NULL) { 321*789Sahrens dvp = zfs_graph_lookup(zgp, dest, txg); 322*789Sahrens zfs_vertex_add_edge(svp, dvp); 323*789Sahrens } 324*789Sahrens } 325*789Sahrens 326*789Sahrens /* 327*789Sahrens * Iterate over all children of the given dataset, adding any vertices as 328*789Sahrens * necessary. Returns 0 if no cloned snapshots were seen, 1 otherwise. This is 329*789Sahrens * a simple recursive algorithm - the ZFS namespace typically is very flat. We 330*789Sahrens * manually invoke the necessary ioctl() calls to avoid the overhead and 331*789Sahrens * additional semantics of zfs_open(). 332*789Sahrens */ 333*789Sahrens static int 334*789Sahrens iterate_children(zfs_graph_t *zgp, const char *dataset) 335*789Sahrens { 336*789Sahrens zfs_cmd_t zc = { 0 }; 337*789Sahrens int ret = 0; 338*789Sahrens zfs_vertex_t *zvp; 339*789Sahrens 340*789Sahrens /* 341*789Sahrens * Look up the source vertex, and avoid it if we've seen it before. 342*789Sahrens */ 343*789Sahrens zvp = zfs_graph_lookup(zgp, dataset, 0); 344*789Sahrens if (zvp->zv_visited) 345*789Sahrens return (0); 346*789Sahrens 347*789Sahrens for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 348*789Sahrens ioctl(zfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0; 349*789Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) { 350*789Sahrens 351*789Sahrens /* 352*789Sahrens * Ignore private dataset names. 353*789Sahrens */ 354*789Sahrens if (dataset_name_hidden(zc.zc_name)) 355*789Sahrens continue; 356*789Sahrens 357*789Sahrens /* 358*789Sahrens * Get statistics for this dataset, to determine the type of the 359*789Sahrens * dataset and clone statistics. If this fails, the dataset has 360*789Sahrens * since been removed, and we're pretty much screwed anyway. 361*789Sahrens */ 362*789Sahrens if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) 363*789Sahrens continue; 364*789Sahrens 365*789Sahrens /* 366*789Sahrens * Add an edge between the parent and the child. 367*789Sahrens */ 368*789Sahrens zfs_graph_add(zgp, dataset, zc.zc_name, 369*789Sahrens zc.zc_objset_stats.dds_creation_txg); 370*789Sahrens 371*789Sahrens /* 372*789Sahrens * If this dataset has a clone parent, add an appropriate edge. 373*789Sahrens */ 374*789Sahrens if (zc.zc_objset_stats.dds_clone_of[0] != '\0') 375*789Sahrens zfs_graph_add(zgp, zc.zc_objset_stats.dds_clone_of, 376*789Sahrens zc.zc_name, zc.zc_objset_stats.dds_creation_txg); 377*789Sahrens 378*789Sahrens /* 379*789Sahrens * Iterate over all children 380*789Sahrens */ 381*789Sahrens ret |= iterate_children(zgp, zc.zc_name); 382*789Sahrens 383*789Sahrens /* 384*789Sahrens * Indicate if we found a dataset with a non-zero clone count. 385*789Sahrens */ 386*789Sahrens if (zc.zc_objset_stats.dds_num_clones != 0) 387*789Sahrens ret |= 1; 388*789Sahrens } 389*789Sahrens 390*789Sahrens /* 391*789Sahrens * Now iterate over all snapshots. 392*789Sahrens */ 393*789Sahrens bzero(&zc, sizeof (zc)); 394*789Sahrens 395*789Sahrens for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 396*789Sahrens ioctl(zfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0; 397*789Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) { 398*789Sahrens 399*789Sahrens /* 400*789Sahrens * Get statistics for this dataset, to determine the type of the 401*789Sahrens * dataset and clone statistics. If this fails, the dataset has 402*789Sahrens * since been removed, and we're pretty much screwed anyway. 403*789Sahrens */ 404*789Sahrens if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) 405*789Sahrens continue; 406*789Sahrens 407*789Sahrens /* 408*789Sahrens * Add an edge between the parent and the child. 409*789Sahrens */ 410*789Sahrens zfs_graph_add(zgp, dataset, zc.zc_name, 411*789Sahrens zc.zc_objset_stats.dds_creation_txg); 412*789Sahrens 413*789Sahrens /* 414*789Sahrens * Indicate if we found a dataset with a non-zero clone count. 415*789Sahrens */ 416*789Sahrens if (zc.zc_objset_stats.dds_num_clones != 0) 417*789Sahrens ret |= 1; 418*789Sahrens } 419*789Sahrens 420*789Sahrens zvp->zv_visited = 1; 421*789Sahrens 422*789Sahrens return (ret); 423*789Sahrens } 424*789Sahrens 425*789Sahrens /* 426*789Sahrens * Construct a complete graph of all necessary vertices. First, we iterate over 427*789Sahrens * only our object's children. If we don't find any cloned snapshots, then we 428*789Sahrens * simple return that. Otherwise, we have to start at the pool root and iterate 429*789Sahrens * over all datasets. 430*789Sahrens */ 431*789Sahrens static zfs_graph_t * 432*789Sahrens construct_graph(const char *dataset) 433*789Sahrens { 434*789Sahrens zfs_graph_t *zgp = zfs_graph_create(ZFS_GRAPH_SIZE); 435*789Sahrens zfs_cmd_t zc = { 0 }; 436*789Sahrens 437*789Sahrens /* 438*789Sahrens * We need to explicitly check whether this dataset has clones or not, 439*789Sahrens * since iterate_children() only checks the children. 440*789Sahrens */ 441*789Sahrens (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); 442*789Sahrens (void) ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc); 443*789Sahrens 444*789Sahrens if (zc.zc_objset_stats.dds_num_clones != 0 || 445*789Sahrens iterate_children(zgp, dataset) != 0) { 446*789Sahrens /* 447*789Sahrens * Determine pool name and try again. 448*789Sahrens */ 449*789Sahrens char *pool, *slash; 450*789Sahrens 451*789Sahrens if ((slash = strchr(dataset, '/')) != NULL || 452*789Sahrens (slash = strchr(dataset, '@')) != NULL) { 453*789Sahrens pool = zfs_malloc(slash - dataset + 1); 454*789Sahrens (void) strncpy(pool, dataset, slash - dataset); 455*789Sahrens pool[slash - dataset] = '\0'; 456*789Sahrens 457*789Sahrens (void) iterate_children(zgp, pool); 458*789Sahrens zfs_graph_add(zgp, pool, NULL, 0); 459*789Sahrens 460*789Sahrens free(pool); 461*789Sahrens } 462*789Sahrens } 463*789Sahrens zfs_graph_add(zgp, dataset, NULL, 0); 464*789Sahrens 465*789Sahrens return (zgp); 466*789Sahrens } 467*789Sahrens 468*789Sahrens /* 469*789Sahrens * Given a graph, do a recursive topological sort into the given array. This is 470*789Sahrens * really just a depth first search, so that the deepest nodes appear first. 471*789Sahrens * hijack the 'zv_visited' marker to avoid visiting the same vertex twice. 472*789Sahrens */ 473*789Sahrens static void 474*789Sahrens topo_sort(char **result, size_t *idx, zfs_vertex_t *zgv) 475*789Sahrens { 476*789Sahrens int i; 477*789Sahrens 478*789Sahrens /* avoid doing a search if we don't have to */ 479*789Sahrens if (zgv->zv_visited == 2) 480*789Sahrens return; 481*789Sahrens 482*789Sahrens zfs_vertex_sort_edges(zgv); 483*789Sahrens for (i = 0; i < zgv->zv_edgecount; i++) 484*789Sahrens topo_sort(result, idx, zgv->zv_edges[i]->ze_dest); 485*789Sahrens 486*789Sahrens /* we may have visited this in the course of the above */ 487*789Sahrens if (zgv->zv_visited == 2) 488*789Sahrens return; 489*789Sahrens 490*789Sahrens result[*idx] = zfs_malloc(strlen(zgv->zv_dataset) + 1); 491*789Sahrens (void) strcpy(result[*idx], zgv->zv_dataset); 492*789Sahrens *idx += 1; 493*789Sahrens zgv->zv_visited = 2; 494*789Sahrens } 495*789Sahrens 496*789Sahrens /* 497*789Sahrens * The only public interface for this file. Do the dirty work of constructing a 498*789Sahrens * child list for the given object. Construct the graph, do the toplogical 499*789Sahrens * sort, and then return the array of strings to the caller. 500*789Sahrens */ 501*789Sahrens char ** 502*789Sahrens get_dependents(const char *dataset, size_t *count) 503*789Sahrens { 504*789Sahrens char **result; 505*789Sahrens zfs_graph_t *zgp; 506*789Sahrens zfs_vertex_t *zvp; 507*789Sahrens 508*789Sahrens zgp = construct_graph(dataset); 509*789Sahrens result = zfs_malloc(zgp->zg_nvertex * sizeof (char *)); 510*789Sahrens 511*789Sahrens zvp = zfs_graph_lookup(zgp, dataset, 0); 512*789Sahrens 513*789Sahrens *count = 0; 514*789Sahrens topo_sort(result, count, zvp); 515*789Sahrens 516*789Sahrens /* 517*789Sahrens * Get rid of the last entry, which is our starting vertex and not 518*789Sahrens * strictly a dependent. 519*789Sahrens */ 520*789Sahrens assert(*count > 0); 521*789Sahrens free(result[*count - 1]); 522*789Sahrens (*count)--; 523*789Sahrens 524*789Sahrens zfs_graph_destroy(zgp); 525*789Sahrens 526*789Sahrens return (result); 527*789Sahrens } 528