1219089Spjd/*
2219089Spjd * CDDL HEADER START
3219089Spjd *
4219089Spjd * The contents of this file are subject to the terms of the
5219089Spjd * Common Development and Distribution License (the "License").
6219089Spjd * You may not use this file except in compliance with the License.
7219089Spjd *
8219089Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9219089Spjd * or http://www.opensolaris.org/os/licensing.
10219089Spjd * See the License for the specific language governing permissions
11219089Spjd * and limitations under the License.
12219089Spjd *
13219089Spjd * When distributing Covered Code, include this CDDL HEADER in each
14219089Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15219089Spjd * If applicable, add the following below this CDDL HEADER, with the
16219089Spjd * fields enclosed by brackets "[]" replaced with your own identifying
17219089Spjd * information: Portions Copyright [yyyy] [name of copyright owner]
18219089Spjd *
19219089Spjd * CDDL HEADER END
20219089Spjd */
21219089Spjd/*
22219089Spjd * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23249643Smm * Copyright (c) 2013 by Delphix. All rights reserved.
24219089Spjd */
25219089Spjd
26219089Spjd#include <sys/dmu.h>
27219089Spjd#include <sys/dmu_impl.h>
28219089Spjd#include <sys/dmu_tx.h>
29219089Spjd#include <sys/dbuf.h>
30219089Spjd#include <sys/dnode.h>
31219089Spjd#include <sys/zfs_context.h>
32219089Spjd#include <sys/dmu_objset.h>
33219089Spjd#include <sys/dmu_traverse.h>
34219089Spjd#include <sys/dsl_dataset.h>
35219089Spjd#include <sys/dsl_dir.h>
36219089Spjd#include <sys/dsl_pool.h>
37219089Spjd#include <sys/dsl_synctask.h>
38219089Spjd#include <sys/zfs_ioctl.h>
39219089Spjd#include <sys/zap.h>
40219089Spjd#include <sys/zio_checksum.h>
41219089Spjd#include <sys/zfs_znode.h>
42219089Spjd
43219089Spjdstruct diffarg {
44219089Spjd	struct file *da_fp;		/* file to which we are reporting */
45219089Spjd	offset_t *da_offp;
46219089Spjd	int da_err;			/* error that stopped diff search */
47219089Spjd	dmu_diff_record_t da_ddr;
48219089Spjd	kthread_t *da_td;
49219089Spjd};
50219089Spjd
51219089Spjdstatic int
52219089Spjdwrite_bytes(struct diffarg *da)
53219089Spjd{
54219089Spjd	struct uio auio;
55219089Spjd	struct iovec aiov;
56219089Spjd
57219089Spjd	aiov.iov_base = (caddr_t)&da->da_ddr;
58219089Spjd	aiov.iov_len = sizeof (da->da_ddr);
59219089Spjd	auio.uio_iov = &aiov;
60219089Spjd	auio.uio_iovcnt = 1;
61219089Spjd	auio.uio_resid = aiov.iov_len;
62219089Spjd	auio.uio_segflg = UIO_SYSSPACE;
63219089Spjd	auio.uio_rw = UIO_WRITE;
64219089Spjd	auio.uio_offset = (off_t)-1;
65219089Spjd	auio.uio_td = da->da_td;
66219089Spjd#ifdef _KERNEL
67219089Spjd	if (da->da_fp->f_type == DTYPE_VNODE)
68219089Spjd		bwillwrite();
69219089Spjd	return (fo_write(da->da_fp, &auio, da->da_td->td_ucred, 0, da->da_td));
70219089Spjd#else
71219089Spjd	fprintf(stderr, "%s: returning EOPNOTSUPP\n", __func__);
72219089Spjd	return (EOPNOTSUPP);
73219089Spjd#endif
74219089Spjd}
75219089Spjd
76219089Spjdstatic int
77219089Spjdwrite_record(struct diffarg *da)
78219089Spjd{
79219089Spjd
80219089Spjd	if (da->da_ddr.ddr_type == DDR_NONE) {
81219089Spjd		da->da_err = 0;
82219089Spjd		return (0);
83219089Spjd	}
84219089Spjd
85219089Spjd	da->da_err = write_bytes(da);
86219089Spjd	*da->da_offp += sizeof (da->da_ddr);
87219089Spjd	return (da->da_err);
88219089Spjd}
89219089Spjd
90219089Spjdstatic int
91219089Spjdreport_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last)
92219089Spjd{
93219089Spjd	ASSERT(first <= last);
94219089Spjd	if (da->da_ddr.ddr_type != DDR_FREE ||
95219089Spjd	    first != da->da_ddr.ddr_last + 1) {
96219089Spjd		if (write_record(da) != 0)
97219089Spjd			return (da->da_err);
98219089Spjd		da->da_ddr.ddr_type = DDR_FREE;
99219089Spjd		da->da_ddr.ddr_first = first;
100219089Spjd		da->da_ddr.ddr_last = last;
101219089Spjd		return (0);
102219089Spjd	}
103219089Spjd	da->da_ddr.ddr_last = last;
104219089Spjd	return (0);
105219089Spjd}
106219089Spjd
107219089Spjdstatic int
108219089Spjdreport_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp)
109219089Spjd{
110219089Spjd	ASSERT(dnp != NULL);
111219089Spjd	if (dnp->dn_type == DMU_OT_NONE)
112219089Spjd		return (report_free_dnode_range(da, object, object));
113219089Spjd
114219089Spjd	if (da->da_ddr.ddr_type != DDR_INUSE ||
115219089Spjd	    object != da->da_ddr.ddr_last + 1) {
116219089Spjd		if (write_record(da) != 0)
117219089Spjd			return (da->da_err);
118219089Spjd		da->da_ddr.ddr_type = DDR_INUSE;
119219089Spjd		da->da_ddr.ddr_first = da->da_ddr.ddr_last = object;
120219089Spjd		return (0);
121219089Spjd	}
122219089Spjd	da->da_ddr.ddr_last = object;
123219089Spjd	return (0);
124219089Spjd}
125219089Spjd
126219089Spjd#define	DBP_SPAN(dnp, level)				  \
127219089Spjd	(((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \
128219089Spjd	(level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT)))
129219089Spjd
130219089Spjd/* ARGSUSED */
131219089Spjdstatic int
132247406Smmdiff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
133219089Spjd    const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg)
134219089Spjd{
135219089Spjd	struct diffarg *da = arg;
136219089Spjd	int err = 0;
137219089Spjd
138219089Spjd	if (issig(JUSTLOOKING) && issig(FORREAL))
139249643Smm		return (SET_ERROR(EINTR));
140219089Spjd
141219089Spjd	if (zb->zb_object != DMU_META_DNODE_OBJECT)
142219089Spjd		return (0);
143219089Spjd
144263398Sdelphij	if (BP_IS_HOLE(bp)) {
145219089Spjd		uint64_t span = DBP_SPAN(dnp, zb->zb_level);
146219089Spjd		uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT;
147219089Spjd
148219089Spjd		err = report_free_dnode_range(da, dnobj,
149219089Spjd		    dnobj + (span >> DNODE_SHIFT) - 1);
150219089Spjd		if (err)
151219089Spjd			return (err);
152219089Spjd	} else if (zb->zb_level == 0) {
153219089Spjd		dnode_phys_t *blk;
154219089Spjd		arc_buf_t *abuf;
155219089Spjd		uint32_t aflags = ARC_WAIT;
156219089Spjd		int blksz = BP_GET_LSIZE(bp);
157219089Spjd		int i;
158219089Spjd
159247406Smm		if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf,
160247406Smm		    ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL,
161247406Smm		    &aflags, zb) != 0)
162249643Smm			return (SET_ERROR(EIO));
163219089Spjd
164219089Spjd		blk = abuf->b_data;
165219089Spjd		for (i = 0; i < blksz >> DNODE_SHIFT; i++) {
166219089Spjd			uint64_t dnobj = (zb->zb_blkid <<
167219089Spjd			    (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i;
168219089Spjd			err = report_dnode(da, dnobj, blk+i);
169219089Spjd			if (err)
170219089Spjd				break;
171219089Spjd		}
172219089Spjd		(void) arc_buf_remove_ref(abuf, &abuf);
173219089Spjd		if (err)
174219089Spjd			return (err);
175219089Spjd		/* Don't care about the data blocks */
176219089Spjd		return (TRAVERSE_VISIT_NO_CHILDREN);
177219089Spjd	}
178219089Spjd	return (0);
179219089Spjd}
180219089Spjd
181219089Spjdint
182249643Smmdmu_diff(const char *tosnap_name, const char *fromsnap_name,
183249643Smm#ifdef illumos
184249643Smm    struct vnode *vp, offset_t *offp)
185249643Smm#else
186249643Smm    struct file *fp, offset_t *offp)
187249643Smm#endif
188219089Spjd{
189219089Spjd	struct diffarg da;
190249643Smm	dsl_dataset_t *fromsnap;
191249643Smm	dsl_dataset_t *tosnap;
192249643Smm	dsl_pool_t *dp;
193249643Smm	int error;
194249643Smm	uint64_t fromtxg;
195219089Spjd
196249643Smm	if (strchr(tosnap_name, '@') == NULL ||
197249643Smm	    strchr(fromsnap_name, '@') == NULL)
198249643Smm		return (SET_ERROR(EINVAL));
199219089Spjd
200249643Smm	error = dsl_pool_hold(tosnap_name, FTAG, &dp);
201249643Smm	if (error != 0)
202249643Smm		return (error);
203219089Spjd
204249643Smm	error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap);
205249643Smm	if (error != 0) {
206249643Smm		dsl_pool_rele(dp, FTAG);
207249643Smm		return (error);
208249643Smm	}
209219089Spjd
210249643Smm	error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap);
211249643Smm	if (error != 0) {
212249643Smm		dsl_dataset_rele(tosnap, FTAG);
213249643Smm		dsl_pool_rele(dp, FTAG);
214249643Smm		return (error);
215249643Smm	}
216219089Spjd
217263410Sdelphij	if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) {
218249643Smm		dsl_dataset_rele(fromsnap, FTAG);
219249643Smm		dsl_dataset_rele(tosnap, FTAG);
220249643Smm		dsl_pool_rele(dp, FTAG);
221249643Smm		return (SET_ERROR(EXDEV));
222249643Smm	}
223219089Spjd
224249643Smm	fromtxg = fromsnap->ds_phys->ds_creation_txg;
225249643Smm	dsl_dataset_rele(fromsnap, FTAG);
226219089Spjd
227249643Smm	dsl_dataset_long_hold(tosnap, FTAG);
228249643Smm	dsl_pool_rele(dp, FTAG);
229219089Spjd
230219089Spjd	da.da_fp = fp;
231219089Spjd	da.da_offp = offp;
232219089Spjd	da.da_ddr.ddr_type = DDR_NONE;
233219089Spjd	da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0;
234219089Spjd	da.da_err = 0;
235219089Spjd	da.da_td = curthread;
236219089Spjd
237249643Smm	error = traverse_dataset(tosnap, fromtxg,
238219089Spjd	    TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da);
239219089Spjd
240249643Smm	if (error != 0) {
241249643Smm		da.da_err = error;
242219089Spjd	} else {
243219089Spjd		/* we set the da.da_err we return as side-effect */
244219089Spjd		(void) write_record(&da);
245219089Spjd	}
246219089Spjd
247249643Smm	dsl_dataset_long_rele(tosnap, FTAG);
248249643Smm	dsl_dataset_rele(tosnap, FTAG);
249249643Smm
250219089Spjd	return (da.da_err);
251219089Spjd}
252