libzfs_iter.c revision 228103
1228103Smm/* 2228103Smm * CDDL HEADER START 3228103Smm * 4228103Smm * The contents of this file are subject to the terms of the 5228103Smm * Common Development and Distribution License (the "License"). 6228103Smm * You may not use this file except in compliance with the License. 7228103Smm * 8228103Smm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9228103Smm * or http://www.opensolaris.org/os/licensing. 10228103Smm * See the License for the specific language governing permissions 11228103Smm * and limitations under the License. 12228103Smm * 13228103Smm * When distributing Covered Code, include this CDDL HEADER in each 14228103Smm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15228103Smm * If applicable, add the following below this CDDL HEADER, with the 16228103Smm * fields enclosed by brackets "[]" replaced with your own identifying 17228103Smm * information: Portions Copyright [yyyy] [name of copyright owner] 18228103Smm * 19228103Smm * CDDL HEADER END 20228103Smm */ 21228103Smm 22228103Smm/* 23228103Smm * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24228103Smm * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 25228103Smm * Copyright (c) 2011 by Delphix. All rights reserved. 26228103Smm */ 27228103Smm 28228103Smm#include <stdio.h> 29228103Smm#include <stdlib.h> 30228103Smm#include <strings.h> 31228103Smm#include <unistd.h> 32228103Smm#include <stddef.h> 33228103Smm#include <libintl.h> 34228103Smm#include <libzfs.h> 35228103Smm 36228103Smm#include "libzfs_impl.h" 37228103Smm 38228103Smmint 39228103Smmzfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data) 40228103Smm{ 41228103Smm nvlist_t *nvl = zfs_get_clones_nvl(zhp); 42228103Smm nvpair_t *pair; 43228103Smm 44228103Smm if (nvl == NULL) 45228103Smm return (0); 46228103Smm 47228103Smm for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL; 48228103Smm pair = nvlist_next_nvpair(nvl, pair)) { 49228103Smm zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair), 50228103Smm ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 51228103Smm if (clone != NULL) { 52228103Smm int err = func(clone, data); 53228103Smm if (err != 0) 54228103Smm return (err); 55228103Smm } 56228103Smm } 57228103Smm return (0); 58228103Smm} 59228103Smm 60228103Smmstatic int 61228103Smmzfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc) 62228103Smm{ 63228103Smm int rc; 64228103Smm uint64_t orig_cookie; 65228103Smm 66228103Smm orig_cookie = zc->zc_cookie; 67228103Smmtop: 68228103Smm (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); 69228103Smm rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc); 70228103Smm 71228103Smm if (rc == -1) { 72228103Smm switch (errno) { 73228103Smm case ENOMEM: 74228103Smm /* expand nvlist memory and try again */ 75228103Smm if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) { 76228103Smm zcmd_free_nvlists(zc); 77228103Smm return (-1); 78228103Smm } 79228103Smm zc->zc_cookie = orig_cookie; 80228103Smm goto top; 81228103Smm /* 82228103Smm * An errno value of ESRCH indicates normal completion. 83228103Smm * If ENOENT is returned, then the underlying dataset 84228103Smm * has been removed since we obtained the handle. 85228103Smm */ 86228103Smm case ESRCH: 87228103Smm case ENOENT: 88228103Smm rc = 1; 89228103Smm break; 90228103Smm default: 91228103Smm rc = zfs_standard_error(zhp->zfs_hdl, errno, 92228103Smm dgettext(TEXT_DOMAIN, 93228103Smm "cannot iterate filesystems")); 94228103Smm break; 95228103Smm } 96228103Smm } 97228103Smm return (rc); 98228103Smm} 99228103Smm 100228103Smm/* 101228103Smm * Iterate over all child filesystems 102228103Smm */ 103228103Smmint 104228103Smmzfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) 105228103Smm{ 106228103Smm zfs_cmd_t zc = { 0 }; 107228103Smm zfs_handle_t *nzhp; 108228103Smm int ret; 109228103Smm 110228103Smm if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) 111228103Smm return (0); 112228103Smm 113228103Smm if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 114228103Smm return (-1); 115228103Smm 116228103Smm while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT, 117228103Smm &zc)) == 0) { 118228103Smm /* 119228103Smm * Silently ignore errors, as the only plausible explanation is 120228103Smm * that the pool has since been removed. 121228103Smm */ 122228103Smm if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 123228103Smm &zc)) == NULL) { 124228103Smm continue; 125228103Smm } 126228103Smm 127228103Smm if ((ret = func(nzhp, data)) != 0) { 128228103Smm zcmd_free_nvlists(&zc); 129228103Smm return (ret); 130228103Smm } 131228103Smm } 132228103Smm zcmd_free_nvlists(&zc); 133228103Smm return ((ret < 0) ? ret : 0); 134228103Smm} 135228103Smm 136228103Smm/* 137228103Smm * Iterate over all snapshots 138228103Smm */ 139228103Smmint 140228103Smmzfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 141228103Smm{ 142228103Smm zfs_cmd_t zc = { 0 }; 143228103Smm zfs_handle_t *nzhp; 144228103Smm int ret; 145228103Smm 146228103Smm if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 147228103Smm return (0); 148228103Smm 149228103Smm if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) 150228103Smm return (-1); 151228103Smm while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, 152228103Smm &zc)) == 0) { 153228103Smm 154228103Smm if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, 155228103Smm &zc)) == NULL) { 156228103Smm continue; 157228103Smm } 158228103Smm 159228103Smm if ((ret = func(nzhp, data)) != 0) { 160228103Smm zcmd_free_nvlists(&zc); 161228103Smm return (ret); 162228103Smm } 163228103Smm } 164228103Smm zcmd_free_nvlists(&zc); 165228103Smm return ((ret < 0) ? ret : 0); 166228103Smm} 167228103Smm 168228103Smm/* 169228103Smm * Routines for dealing with the sorted snapshot functionality 170228103Smm */ 171228103Smmtypedef struct zfs_node { 172228103Smm zfs_handle_t *zn_handle; 173228103Smm avl_node_t zn_avlnode; 174228103Smm} zfs_node_t; 175228103Smm 176228103Smmstatic int 177228103Smmzfs_sort_snaps(zfs_handle_t *zhp, void *data) 178228103Smm{ 179228103Smm avl_tree_t *avl = data; 180228103Smm zfs_node_t *node; 181228103Smm zfs_node_t search; 182228103Smm 183228103Smm search.zn_handle = zhp; 184228103Smm node = avl_find(avl, &search, NULL); 185228103Smm if (node) { 186228103Smm /* 187228103Smm * If this snapshot was renamed while we were creating the 188228103Smm * AVL tree, it's possible that we already inserted it under 189228103Smm * its old name. Remove the old handle before adding the new 190228103Smm * one. 191228103Smm */ 192228103Smm zfs_close(node->zn_handle); 193228103Smm avl_remove(avl, node); 194228103Smm free(node); 195228103Smm } 196228103Smm 197228103Smm node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t)); 198228103Smm node->zn_handle = zhp; 199228103Smm avl_add(avl, node); 200228103Smm 201228103Smm return (0); 202228103Smm} 203228103Smm 204228103Smmstatic int 205228103Smmzfs_snapshot_compare(const void *larg, const void *rarg) 206228103Smm{ 207228103Smm zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 208228103Smm zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 209228103Smm uint64_t lcreate, rcreate; 210228103Smm 211228103Smm /* 212228103Smm * Sort them according to creation time. We use the hidden 213228103Smm * CREATETXG property to get an absolute ordering of snapshots. 214228103Smm */ 215228103Smm lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 216228103Smm rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 217228103Smm 218228103Smm if (lcreate < rcreate) 219228103Smm return (-1); 220228103Smm else if (lcreate > rcreate) 221228103Smm return (+1); 222228103Smm else 223228103Smm return (0); 224228103Smm} 225228103Smm 226228103Smmint 227228103Smmzfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data) 228228103Smm{ 229228103Smm int ret = 0; 230228103Smm zfs_node_t *node; 231228103Smm avl_tree_t avl; 232228103Smm void *cookie = NULL; 233228103Smm 234228103Smm avl_create(&avl, zfs_snapshot_compare, 235228103Smm sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode)); 236228103Smm 237228103Smm ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl); 238228103Smm 239228103Smm for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node)) 240228103Smm ret |= callback(node->zn_handle, data); 241228103Smm 242228103Smm while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL) 243228103Smm free(node); 244228103Smm 245228103Smm avl_destroy(&avl); 246228103Smm 247228103Smm return (ret); 248228103Smm} 249228103Smm 250228103Smmtypedef struct { 251228103Smm char *ssa_first; 252228103Smm char *ssa_last; 253228103Smm boolean_t ssa_seenfirst; 254228103Smm boolean_t ssa_seenlast; 255228103Smm zfs_iter_f ssa_func; 256228103Smm void *ssa_arg; 257228103Smm} snapspec_arg_t; 258228103Smm 259228103Smmstatic int 260228103Smmsnapspec_cb(zfs_handle_t *zhp, void *arg) { 261228103Smm snapspec_arg_t *ssa = arg; 262228103Smm char *shortsnapname; 263228103Smm int err = 0; 264228103Smm 265228103Smm if (ssa->ssa_seenlast) 266228103Smm return (0); 267228103Smm shortsnapname = zfs_strdup(zhp->zfs_hdl, 268228103Smm strchr(zfs_get_name(zhp), '@') + 1); 269228103Smm 270228103Smm if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0) 271228103Smm ssa->ssa_seenfirst = B_TRUE; 272228103Smm 273228103Smm if (ssa->ssa_seenfirst) { 274228103Smm err = ssa->ssa_func(zhp, ssa->ssa_arg); 275228103Smm } else { 276228103Smm zfs_close(zhp); 277228103Smm } 278228103Smm 279228103Smm if (strcmp(shortsnapname, ssa->ssa_last) == 0) 280228103Smm ssa->ssa_seenlast = B_TRUE; 281228103Smm free(shortsnapname); 282228103Smm 283228103Smm return (err); 284228103Smm} 285228103Smm 286228103Smm/* 287228103Smm * spec is a string like "A,B%C,D" 288228103Smm * 289228103Smm * <snaps>, where <snaps> can be: 290228103Smm * <snap> (single snapshot) 291228103Smm * <snap>%<snap> (range of snapshots, inclusive) 292228103Smm * %<snap> (range of snapshots, starting with earliest) 293228103Smm * <snap>% (range of snapshots, ending with last) 294228103Smm * % (all snapshots) 295228103Smm * <snaps>[,...] (comma separated list of the above) 296228103Smm * 297228103Smm * If a snapshot can not be opened, continue trying to open the others, but 298228103Smm * return ENOENT at the end. 299228103Smm */ 300228103Smmint 301228103Smmzfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig, 302228103Smm zfs_iter_f func, void *arg) 303228103Smm{ 304228103Smm char buf[ZFS_MAXNAMELEN]; 305228103Smm char *comma_separated, *cp; 306228103Smm int err = 0; 307228103Smm int ret = 0; 308228103Smm 309228103Smm (void) strlcpy(buf, spec_orig, sizeof (buf)); 310228103Smm cp = buf; 311228103Smm 312228103Smm while ((comma_separated = strsep(&cp, ",")) != NULL) { 313228103Smm char *pct = strchr(comma_separated, '%'); 314228103Smm if (pct != NULL) { 315228103Smm snapspec_arg_t ssa = { 0 }; 316228103Smm ssa.ssa_func = func; 317228103Smm ssa.ssa_arg = arg; 318228103Smm 319228103Smm if (pct == comma_separated) 320228103Smm ssa.ssa_seenfirst = B_TRUE; 321228103Smm else 322228103Smm ssa.ssa_first = comma_separated; 323228103Smm *pct = '\0'; 324228103Smm ssa.ssa_last = pct + 1; 325228103Smm 326228103Smm /* 327228103Smm * If there is a lastname specified, make sure it 328228103Smm * exists. 329228103Smm */ 330228103Smm if (ssa.ssa_last[0] != '\0') { 331228103Smm char snapname[ZFS_MAXNAMELEN]; 332228103Smm (void) snprintf(snapname, sizeof (snapname), 333228103Smm "%s@%s", zfs_get_name(fs_zhp), 334228103Smm ssa.ssa_last); 335228103Smm if (!zfs_dataset_exists(fs_zhp->zfs_hdl, 336228103Smm snapname, ZFS_TYPE_SNAPSHOT)) { 337228103Smm ret = ENOENT; 338228103Smm continue; 339228103Smm } 340228103Smm } 341228103Smm 342228103Smm err = zfs_iter_snapshots_sorted(fs_zhp, 343228103Smm snapspec_cb, &ssa); 344228103Smm if (ret == 0) 345228103Smm ret = err; 346228103Smm if (ret == 0 && (!ssa.ssa_seenfirst || 347228103Smm (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) { 348228103Smm ret = ENOENT; 349228103Smm } 350228103Smm } else { 351228103Smm char snapname[ZFS_MAXNAMELEN]; 352228103Smm zfs_handle_t *snap_zhp; 353228103Smm (void) snprintf(snapname, sizeof (snapname), "%s@%s", 354228103Smm zfs_get_name(fs_zhp), comma_separated); 355228103Smm snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl, 356228103Smm snapname); 357228103Smm if (snap_zhp == NULL) { 358228103Smm ret = ENOENT; 359228103Smm continue; 360228103Smm } 361228103Smm err = func(snap_zhp, arg); 362228103Smm if (ret == 0) 363228103Smm ret = err; 364228103Smm } 365228103Smm } 366228103Smm 367228103Smm return (ret); 368228103Smm} 369228103Smm 370228103Smm/* 371228103Smm * Iterate over all children, snapshots and filesystems 372228103Smm */ 373228103Smmint 374228103Smmzfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) 375228103Smm{ 376228103Smm int ret; 377228103Smm 378228103Smm if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0) 379228103Smm return (ret); 380228103Smm 381228103Smm return (zfs_iter_snapshots(zhp, func, data)); 382228103Smm} 383228103Smm 384228103Smm 385228103Smmtypedef struct iter_stack_frame { 386228103Smm struct iter_stack_frame *next; 387228103Smm zfs_handle_t *zhp; 388228103Smm} iter_stack_frame_t; 389228103Smm 390228103Smmtypedef struct iter_dependents_arg { 391228103Smm boolean_t first; 392228103Smm boolean_t allowrecursion; 393228103Smm iter_stack_frame_t *stack; 394228103Smm zfs_iter_f func; 395228103Smm void *data; 396228103Smm} iter_dependents_arg_t; 397228103Smm 398228103Smmstatic int 399228103Smmiter_dependents_cb(zfs_handle_t *zhp, void *arg) 400228103Smm{ 401228103Smm iter_dependents_arg_t *ida = arg; 402228103Smm int err; 403228103Smm boolean_t first = ida->first; 404228103Smm ida->first = B_FALSE; 405228103Smm 406228103Smm if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { 407228103Smm err = zfs_iter_clones(zhp, iter_dependents_cb, ida); 408228103Smm } else { 409228103Smm iter_stack_frame_t isf; 410228103Smm iter_stack_frame_t *f; 411228103Smm 412228103Smm /* 413228103Smm * check if there is a cycle by seeing if this fs is already 414228103Smm * on the stack. 415228103Smm */ 416228103Smm for (f = ida->stack; f != NULL; f = f->next) { 417228103Smm if (f->zhp->zfs_dmustats.dds_guid == 418228103Smm zhp->zfs_dmustats.dds_guid) { 419228103Smm if (ida->allowrecursion) { 420228103Smm zfs_close(zhp); 421228103Smm return (0); 422228103Smm } else { 423228103Smm zfs_error_aux(zhp->zfs_hdl, 424228103Smm dgettext(TEXT_DOMAIN, 425228103Smm "recursive dependency at '%s'"), 426228103Smm zfs_get_name(zhp)); 427228103Smm err = zfs_error(zhp->zfs_hdl, 428228103Smm EZFS_RECURSIVE, 429228103Smm dgettext(TEXT_DOMAIN, 430228103Smm "cannot determine dependent " 431228103Smm "datasets")); 432228103Smm zfs_close(zhp); 433228103Smm return (err); 434228103Smm } 435228103Smm } 436228103Smm } 437228103Smm 438228103Smm isf.zhp = zhp; 439228103Smm isf.next = ida->stack; 440228103Smm ida->stack = &isf; 441228103Smm err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida); 442228103Smm if (err == 0) 443228103Smm err = zfs_iter_snapshots(zhp, iter_dependents_cb, ida); 444228103Smm ida->stack = isf.next; 445228103Smm } 446228103Smm if (!first && err == 0) 447228103Smm err = ida->func(zhp, ida->data); 448228103Smm return (err); 449228103Smm} 450228103Smm 451228103Smmint 452228103Smmzfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion, 453228103Smm zfs_iter_f func, void *data) 454228103Smm{ 455228103Smm iter_dependents_arg_t ida; 456228103Smm ida.allowrecursion = allowrecursion; 457228103Smm ida.stack = NULL; 458228103Smm ida.func = func; 459228103Smm ida.data = data; 460228103Smm ida.first = B_TRUE; 461228103Smm return (iter_dependents_cb(zfs_handle_dup(zhp), &ida)); 462228103Smm} 463