Path: blob/main/sys/contrib/openzfs/module/zfs/dmu_diff.c
48383 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.23* Copyright (c) 2012, 2018 by Delphix. All rights reserved.24* Copyright (c) 2019, loli10K <[email protected]>. All rights reserved.25*/2627#include <sys/dmu.h>28#include <sys/dmu_impl.h>29#include <sys/dmu_tx.h>30#include <sys/dbuf.h>31#include <sys/dnode.h>32#include <sys/zfs_context.h>33#include <sys/dmu_objset.h>34#include <sys/dmu_traverse.h>35#include <sys/dsl_dataset.h>36#include <sys/dsl_dir.h>37#include <sys/dsl_pool.h>38#include <sys/dsl_synctask.h>39#include <sys/zfs_ioctl.h>40#include <sys/zap.h>41#include <sys/zio_checksum.h>42#include <sys/zfs_znode.h>43#include <sys/zfs_file.h>444546typedef struct dmu_diffarg {47zfs_file_t *da_fp; /* file to which we are reporting */48offset_t *da_offp;49int da_err; /* error that stopped diff search */50dmu_diff_record_t da_ddr;51} dmu_diffarg_t;5253static int54write_record(dmu_diffarg_t *da)55{56zfs_file_t *fp;57ssize_t resid;5859if (da->da_ddr.ddr_type == DDR_NONE) {60da->da_err = 0;61return (0);62}6364fp = da->da_fp;65da->da_err = zfs_file_write(fp, (caddr_t)&da->da_ddr,66sizeof (da->da_ddr), &resid);67*da->da_offp += sizeof (da->da_ddr);68return (da->da_err);69}7071static int72report_free_dnode_range(dmu_diffarg_t *da, uint64_t first, uint64_t last)73{74ASSERT(first <= last);75if (da->da_ddr.ddr_type != DDR_FREE ||76first != da->da_ddr.ddr_last + 1) {77if (write_record(da) != 0)78return (da->da_err);79da->da_ddr.ddr_type = DDR_FREE;80da->da_ddr.ddr_first = first;81da->da_ddr.ddr_last = last;82return (0);83}84da->da_ddr.ddr_last = last;85return (0);86}8788static int89report_dnode(dmu_diffarg_t *da, uint64_t object, dnode_phys_t *dnp)90{91ASSERT(dnp != NULL);92if (dnp->dn_type == DMU_OT_NONE)93return (report_free_dnode_range(da, object, object));9495if (da->da_ddr.ddr_type != DDR_INUSE ||96object != da->da_ddr.ddr_last + 1) {97if (write_record(da) != 0)98return (da->da_err);99da->da_ddr.ddr_type = DDR_INUSE;100da->da_ddr.ddr_first = da->da_ddr.ddr_last = object;101return (0);102}103da->da_ddr.ddr_last = object;104return (0);105}106107#define DBP_SPAN(dnp, level) \108(((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \109(level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT)))110111static int112diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,113const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg)114{115(void) zilog;116dmu_diffarg_t *da = arg;117int err = 0;118119if (issig())120return (SET_ERROR(EINTR));121122if (zb->zb_level == ZB_DNODE_LEVEL ||123zb->zb_object != DMU_META_DNODE_OBJECT)124return (0);125126if (BP_IS_HOLE(bp)) {127uint64_t span = DBP_SPAN(dnp, zb->zb_level);128uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT;129130err = report_free_dnode_range(da, dnobj,131dnobj + (span >> DNODE_SHIFT) - 1);132if (err)133return (err);134} else if (zb->zb_level == 0) {135dnode_phys_t *blk;136arc_buf_t *abuf;137arc_flags_t aflags = ARC_FLAG_WAIT;138int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT;139int zio_flags = ZIO_FLAG_CANFAIL;140int i;141142if (BP_IS_PROTECTED(bp))143zio_flags |= ZIO_FLAG_RAW;144145if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf,146ZIO_PRIORITY_ASYNC_READ, zio_flags, &aflags, zb) != 0)147return (SET_ERROR(EIO));148149blk = abuf->b_data;150for (i = 0; i < epb; i += blk[i].dn_extra_slots + 1) {151uint64_t dnobj = (zb->zb_blkid <<152(DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i;153err = report_dnode(da, dnobj, blk+i);154if (err)155break;156}157arc_buf_destroy(abuf, &abuf);158if (err)159return (err);160/* Don't care about the data blocks */161return (TRAVERSE_VISIT_NO_CHILDREN);162}163return (0);164}165166int167dmu_diff(const char *tosnap_name, const char *fromsnap_name,168zfs_file_t *fp, offset_t *offp)169{170dmu_diffarg_t da;171dsl_dataset_t *fromsnap;172dsl_dataset_t *tosnap;173dsl_pool_t *dp;174int error;175uint64_t fromtxg;176177if (strchr(tosnap_name, '@') == NULL ||178strchr(fromsnap_name, '@') == NULL)179return (SET_ERROR(EINVAL));180181error = dsl_pool_hold(tosnap_name, FTAG, &dp);182if (error != 0)183return (error);184185error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap);186if (error != 0) {187dsl_pool_rele(dp, FTAG);188return (error);189}190191error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap);192if (error != 0) {193dsl_dataset_rele(tosnap, FTAG);194dsl_pool_rele(dp, FTAG);195return (error);196}197198if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) {199dsl_dataset_rele(fromsnap, FTAG);200dsl_dataset_rele(tosnap, FTAG);201dsl_pool_rele(dp, FTAG);202return (SET_ERROR(EXDEV));203}204205fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg;206dsl_dataset_rele(fromsnap, FTAG);207208dsl_dataset_long_hold(tosnap, FTAG);209dsl_pool_rele(dp, FTAG);210211da.da_fp = fp;212da.da_offp = offp;213da.da_ddr.ddr_type = DDR_NONE;214da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0;215da.da_err = 0;216217/*218* Since zfs diff only looks at dnodes which are stored in plaintext219* (other than bonus buffers), we don't technically need to decrypt220* the dataset to perform this operation. However, the command line221* utility will still fail if the keys are not loaded because the222* dataset isn't mounted and because it will fail when it attempts to223* call the ZFS_IOC_OBJ_TO_STATS ioctl.224*/225error = traverse_dataset(tosnap, fromtxg,226TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | TRAVERSE_NO_DECRYPT |227TRAVERSE_LOGICAL, diff_cb, &da);228229if (error != 0) {230da.da_err = error;231} else {232/* we set the da.da_err we return as side-effect */233(void) write_record(&da);234}235236dsl_dataset_long_rele(tosnap, FTAG);237dsl_dataset_rele(tosnap, FTAG);238239return (da.da_err);240}241242243