/* * 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 external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset) { zfs_cmd_t zc = { 0 }; /* * Check whether this dataset is a clone or has clones since * iterate_children() only checks the children. */ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) return (B_TRUE); 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 (B_TRUE); if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset)) zgp->zg_clone_count--; } if ((zc.zc_objset_stats.dds_num_clones) || iterate_children(hdl, zgp, dataset)) return (B_TRUE); return (zgp->zg_clone_count != 0); }
/* * If we rename a filesystem, child filesystem handles are no longer valid * since we identify each dataset by its name in the ZFS namespace. As a * result, we have to go through and fix up all the names appropriately. We * could do this automatically if libzfs kept track of all open handles, but * this is a lot less work. */ void changelist_rename(prop_changelist_t *clp, const char *src, const char *dst) { prop_changenode_t *cn; char newname[ZFS_MAXNAMELEN]; for (cn = uu_list_first(clp->cl_list); cn != NULL; cn = uu_list_next(clp->cl_list, cn)) { /* * Do not rename a clone that's not in the source hierarchy. */ if (!isa_child_of(cn->cn_handle->zfs_name, src)) continue; /* * Destroy the previous mountpoint if needed. */ remove_mountpoint(cn->cn_handle); (void) strlcpy(newname, dst, sizeof (newname)); (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src)); (void) strlcpy(cn->cn_handle->zfs_name, newname, sizeof (cn->cn_handle->zfs_name)); } }
/* * 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); }