xref: /titanic_41/usr/src/lib/libzfs/common/libzfs_sendrecv.c (revision 675fc291908baceb17f92b0b6d961439aaddafc9)
13cb34c60Sahrens /*
23cb34c60Sahrens  * CDDL HEADER START
33cb34c60Sahrens  *
43cb34c60Sahrens  * The contents of this file are subject to the terms of the
53cb34c60Sahrens  * Common Development and Distribution License (the "License").
63cb34c60Sahrens  * You may not use this file except in compliance with the License.
73cb34c60Sahrens  *
83cb34c60Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93cb34c60Sahrens  * or http://www.opensolaris.org/os/licensing.
103cb34c60Sahrens  * See the License for the specific language governing permissions
113cb34c60Sahrens  * and limitations under the License.
123cb34c60Sahrens  *
133cb34c60Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
143cb34c60Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153cb34c60Sahrens  * If applicable, add the following below this CDDL HEADER, with the
163cb34c60Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
173cb34c60Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
183cb34c60Sahrens  *
193cb34c60Sahrens  * CDDL HEADER END
203cb34c60Sahrens  */
213cb34c60Sahrens 
223cb34c60Sahrens /*
23dc7cd546SMark Shellenbaum  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
248429b235SMatthew Ahrens  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
254e3c9f44SBill Pijewski  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
26a7a845e4SSteven Hartland  * Copyright (c) 2013 Steven Hartland. All rights reserved.
273cb34c60Sahrens  */
283cb34c60Sahrens 
293cb34c60Sahrens #include <assert.h>
303cb34c60Sahrens #include <ctype.h>
313cb34c60Sahrens #include <errno.h>
323cb34c60Sahrens #include <libintl.h>
333cb34c60Sahrens #include <stdio.h>
343cb34c60Sahrens #include <stdlib.h>
353cb34c60Sahrens #include <strings.h>
363cb34c60Sahrens #include <unistd.h>
373cb34c60Sahrens #include <stddef.h>
383cb34c60Sahrens #include <fcntl.h>
393cb34c60Sahrens #include <sys/mount.h>
409e69d7d0SLori Alt #include <pthread.h>
419e69d7d0SLori Alt #include <umem.h>
424e3c9f44SBill Pijewski #include <time.h>
433cb34c60Sahrens 
443cb34c60Sahrens #include <libzfs.h>
45e54e0be9SMatthew Ahrens #include <libzfs_core.h>
463cb34c60Sahrens 
473cb34c60Sahrens #include "zfs_namecheck.h"
483cb34c60Sahrens #include "zfs_prop.h"
49495db6fbSLori Alt #include "zfs_fletcher.h"
503cb34c60Sahrens #include "libzfs_impl.h"
518429b235SMatthew Ahrens #include <zlib.h>
529e69d7d0SLori Alt #include <sha2.h>
538e714474SLori Alt #include <sys/zio_checksum.h>
548e714474SLori Alt #include <sys/ddt.h>
553cb34c60Sahrens 
5692241e0bSTom Erickson /* in libzfs_dataset.c */
5792241e0bSTom Erickson extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
5892241e0bSTom Erickson 
595f9bb2f3SPaul Dagnelie static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
605f9bb2f3SPaul Dagnelie     recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
615f9bb2f3SPaul Dagnelie     uint64_t *);
628429b235SMatthew Ahrens static int guid_to_name(libzfs_handle_t *, const char *,
638429b235SMatthew Ahrens     uint64_t, boolean_t, char *);
640069fd67STim Haley 
659e69d7d0SLori Alt static const zio_cksum_t zero_cksum = { 0 };
669e69d7d0SLori Alt 
679e69d7d0SLori Alt typedef struct dedup_arg {
689e69d7d0SLori Alt 	int	inputfd;
699e69d7d0SLori Alt 	int	outputfd;
709e69d7d0SLori Alt 	libzfs_handle_t  *dedup_hdl;
719e69d7d0SLori Alt } dedup_arg_t;
729e69d7d0SLori Alt 
734e3c9f44SBill Pijewski typedef struct progress_arg {
744e3c9f44SBill Pijewski 	zfs_handle_t *pa_zhp;
754e3c9f44SBill Pijewski 	int pa_fd;
764e3c9f44SBill Pijewski 	boolean_t pa_parsable;
774e3c9f44SBill Pijewski } progress_arg_t;
784e3c9f44SBill Pijewski 
799e69d7d0SLori Alt typedef struct dataref {
809e69d7d0SLori Alt 	uint64_t ref_guid;
819e69d7d0SLori Alt 	uint64_t ref_object;
829e69d7d0SLori Alt 	uint64_t ref_offset;
839e69d7d0SLori Alt } dataref_t;
849e69d7d0SLori Alt 
859e69d7d0SLori Alt typedef struct dedup_entry {
869e69d7d0SLori Alt 	struct dedup_entry	*dde_next;
879e69d7d0SLori Alt 	zio_cksum_t dde_chksum;
888e714474SLori Alt 	uint64_t dde_prop;
899e69d7d0SLori Alt 	dataref_t dde_ref;
909e69d7d0SLori Alt } dedup_entry_t;
919e69d7d0SLori Alt 
929e69d7d0SLori Alt #define	MAX_DDT_PHYSMEM_PERCENT		20
939e69d7d0SLori Alt #define	SMALLEST_POSSIBLE_MAX_DDT_MB		128
949e69d7d0SLori Alt 
959e69d7d0SLori Alt typedef struct dedup_table {
969e69d7d0SLori Alt 	dedup_entry_t	**dedup_hash_array;
979e69d7d0SLori Alt 	umem_cache_t	*ddecache;
989e69d7d0SLori Alt 	uint64_t	max_ddt_size;  /* max dedup table size in bytes */
999e69d7d0SLori Alt 	uint64_t	cur_ddt_size;  /* current dedup table size in bytes */
1009e69d7d0SLori Alt 	uint64_t	ddt_count;
1019e69d7d0SLori Alt 	int		numhashbits;
1029e69d7d0SLori Alt 	boolean_t	ddt_full;
1039e69d7d0SLori Alt } dedup_table_t;
1049e69d7d0SLori Alt 
1059e69d7d0SLori Alt static int
high_order_bit(uint64_t n)1069e69d7d0SLori Alt high_order_bit(uint64_t n)
1079e69d7d0SLori Alt {
1089e69d7d0SLori Alt 	int count;
1099e69d7d0SLori Alt 
1109e69d7d0SLori Alt 	for (count = 0; n != 0; count++)
1119e69d7d0SLori Alt 		n >>= 1;
1129e69d7d0SLori Alt 	return (count);
1139e69d7d0SLori Alt }
1149e69d7d0SLori Alt 
1159e69d7d0SLori Alt static size_t
ssread(void * buf,size_t len,FILE * stream)1169e69d7d0SLori Alt ssread(void *buf, size_t len, FILE *stream)
1179e69d7d0SLori Alt {
1189e69d7d0SLori Alt 	size_t outlen;
1199e69d7d0SLori Alt 
1209e69d7d0SLori Alt 	if ((outlen = fread(buf, len, 1, stream)) == 0)
1219e69d7d0SLori Alt 		return (0);
1229e69d7d0SLori Alt 
1239e69d7d0SLori Alt 	return (outlen);
1249e69d7d0SLori Alt }
1259e69d7d0SLori Alt 
1269e69d7d0SLori Alt static void
ddt_hash_append(libzfs_handle_t * hdl,dedup_table_t * ddt,dedup_entry_t ** ddepp,zio_cksum_t * cs,uint64_t prop,dataref_t * dr)1279e69d7d0SLori Alt ddt_hash_append(libzfs_handle_t *hdl, dedup_table_t *ddt, dedup_entry_t **ddepp,
1288e714474SLori Alt     zio_cksum_t *cs, uint64_t prop, dataref_t *dr)
1299e69d7d0SLori Alt {
1309e69d7d0SLori Alt 	dedup_entry_t	*dde;
1319e69d7d0SLori Alt 
1329e69d7d0SLori Alt 	if (ddt->cur_ddt_size >= ddt->max_ddt_size) {
1339e69d7d0SLori Alt 		if (ddt->ddt_full == B_FALSE) {
1349e69d7d0SLori Alt 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1359e69d7d0SLori Alt 			    "Dedup table full.  Deduplication will continue "
1369e69d7d0SLori Alt 			    "with existing table entries"));
1379e69d7d0SLori Alt 			ddt->ddt_full = B_TRUE;
1389e69d7d0SLori Alt 		}
1399e69d7d0SLori Alt 		return;
1409e69d7d0SLori Alt 	}
1419e69d7d0SLori Alt 
1429e69d7d0SLori Alt 	if ((dde = umem_cache_alloc(ddt->ddecache, UMEM_DEFAULT))
1439e69d7d0SLori Alt 	    != NULL) {
1449e69d7d0SLori Alt 		assert(*ddepp == NULL);
1459e69d7d0SLori Alt 		dde->dde_next = NULL;
1469e69d7d0SLori Alt 		dde->dde_chksum = *cs;
1478e714474SLori Alt 		dde->dde_prop = prop;
1489e69d7d0SLori Alt 		dde->dde_ref = *dr;
1499e69d7d0SLori Alt 		*ddepp = dde;
1509e69d7d0SLori Alt 		ddt->cur_ddt_size += sizeof (dedup_entry_t);
1519e69d7d0SLori Alt 		ddt->ddt_count++;
1529e69d7d0SLori Alt 	}
1539e69d7d0SLori Alt }
1549e69d7d0SLori Alt 
1559e69d7d0SLori Alt /*
1569e69d7d0SLori Alt  * Using the specified dedup table, do a lookup for an entry with
1579e69d7d0SLori Alt  * the checksum cs.  If found, return the block's reference info
1589e69d7d0SLori Alt  * in *dr. Otherwise, insert a new entry in the dedup table, using
1599e69d7d0SLori Alt  * the reference information specified by *dr.
1609e69d7d0SLori Alt  *
1619e69d7d0SLori Alt  * return value:  true - entry was found
1629e69d7d0SLori Alt  *		  false - entry was not found
1639e69d7d0SLori Alt  */
1649e69d7d0SLori Alt static boolean_t
ddt_update(libzfs_handle_t * hdl,dedup_table_t * ddt,zio_cksum_t * cs,uint64_t prop,dataref_t * dr)1659e69d7d0SLori Alt ddt_update(libzfs_handle_t *hdl, dedup_table_t *ddt, zio_cksum_t *cs,
1668e714474SLori Alt     uint64_t prop, dataref_t *dr)
1679e69d7d0SLori Alt {
1689e69d7d0SLori Alt 	uint32_t hashcode;
1699e69d7d0SLori Alt 	dedup_entry_t **ddepp;
1709e69d7d0SLori Alt 
1719e69d7d0SLori Alt 	hashcode = BF64_GET(cs->zc_word[0], 0, ddt->numhashbits);
1729e69d7d0SLori Alt 
1739e69d7d0SLori Alt 	for (ddepp = &(ddt->dedup_hash_array[hashcode]); *ddepp != NULL;
1749e69d7d0SLori Alt 	    ddepp = &((*ddepp)->dde_next)) {
1758e714474SLori Alt 		if (ZIO_CHECKSUM_EQUAL(((*ddepp)->dde_chksum), *cs) &&
1768e714474SLori Alt 		    (*ddepp)->dde_prop == prop) {
1779e69d7d0SLori Alt 			*dr = (*ddepp)->dde_ref;
1789e69d7d0SLori Alt 			return (B_TRUE);
1799e69d7d0SLori Alt 		}
1809e69d7d0SLori Alt 	}
1818e714474SLori Alt 	ddt_hash_append(hdl, ddt, ddepp, cs, prop, dr);
1829e69d7d0SLori Alt 	return (B_FALSE);
1839e69d7d0SLori Alt }
1849e69d7d0SLori Alt 
1859e69d7d0SLori Alt static int
dump_record(dmu_replay_record_t * drr,void * payload,int payload_len,zio_cksum_t * zc,int outfd)18662e66153SMatthew Ahrens dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
18762e66153SMatthew Ahrens     zio_cksum_t *zc, int outfd)
1889e69d7d0SLori Alt {
18962e66153SMatthew Ahrens 	ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
19062e66153SMatthew Ahrens 	    ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
19162e66153SMatthew Ahrens 	fletcher_4_incremental_native(drr,
19262e66153SMatthew Ahrens 	    offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
19362e66153SMatthew Ahrens 	if (drr->drr_type != DRR_BEGIN) {
19462e66153SMatthew Ahrens 		ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
19562e66153SMatthew Ahrens 		    drr_checksum.drr_checksum));
19662e66153SMatthew Ahrens 		drr->drr_u.drr_checksum.drr_checksum = *zc;
19762e66153SMatthew Ahrens 	}
19862e66153SMatthew Ahrens 	fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum,
19962e66153SMatthew Ahrens 	    sizeof (zio_cksum_t), zc);
20062e66153SMatthew Ahrens 	if (write(outfd, drr, sizeof (*drr)) == -1)
20162e66153SMatthew Ahrens 		return (errno);
20262e66153SMatthew Ahrens 	if (payload_len != 0) {
20362e66153SMatthew Ahrens 		fletcher_4_incremental_native(payload, payload_len, zc);
20462e66153SMatthew Ahrens 		if (write(outfd, payload, payload_len) == -1)
20562e66153SMatthew Ahrens 			return (errno);
20662e66153SMatthew Ahrens 	}
20762e66153SMatthew Ahrens 	return (0);
2089e69d7d0SLori Alt }
2099e69d7d0SLori Alt 
210f295f440SMarcel Telka int
zfs_send_compoundstream_begin(zfs_handle_t * zhp,const char * tosnap,int featureflags,void * payload,int payload_len,int outfd)211f295f440SMarcel Telka zfs_send_compoundstream_begin(zfs_handle_t *zhp, const char *tosnap,
212f295f440SMarcel Telka     int featureflags, void *payload, int payload_len, int outfd)
213f295f440SMarcel Telka {
214f295f440SMarcel Telka 	dmu_replay_record_t drr = { 0 };
215f295f440SMarcel Telka 	zio_cksum_t zc = { 0 };
216f295f440SMarcel Telka 	int err;
217f295f440SMarcel Telka 
218f295f440SMarcel Telka 	if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
219f295f440SMarcel Telka 		uint64_t version;
220f295f440SMarcel Telka 		version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
221f295f440SMarcel Telka 		if (version >= ZPL_VERSION_SA) {
222f295f440SMarcel Telka 			featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
223f295f440SMarcel Telka 		}
224f295f440SMarcel Telka 	}
225f295f440SMarcel Telka 
226f295f440SMarcel Telka 	drr.drr_type = DRR_BEGIN;
227f295f440SMarcel Telka 	drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
228f295f440SMarcel Telka 	DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.drr_versioninfo,
229f295f440SMarcel Telka 	    DMU_COMPOUNDSTREAM);
230f295f440SMarcel Telka 	DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.drr_versioninfo,
231f295f440SMarcel Telka 	    featureflags);
232f295f440SMarcel Telka 	(void) snprintf(drr.drr_u.drr_begin.drr_toname,
233f295f440SMarcel Telka 	    sizeof (drr.drr_u.drr_begin.drr_toname), "%s@%s", zfs_get_name(zhp),
234f295f440SMarcel Telka 	    tosnap);
235f295f440SMarcel Telka 	drr.drr_payloadlen = payload_len;
236f295f440SMarcel Telka 
237f295f440SMarcel Telka 	err = dump_record(&drr, payload, payload_len, &zc, outfd);
238f295f440SMarcel Telka 	if (err != 0)
239f295f440SMarcel Telka 		return (err);
240f295f440SMarcel Telka 
241f295f440SMarcel Telka 	bzero(&drr, sizeof (drr));
242f295f440SMarcel Telka 	drr.drr_type = DRR_END;
243f295f440SMarcel Telka 	drr.drr_u.drr_end.drr_checksum = zc;
244f295f440SMarcel Telka 	err = write(outfd, &drr, sizeof (drr));
245f295f440SMarcel Telka 	if (err == -1) {
246f295f440SMarcel Telka 		err = errno;
247f295f440SMarcel Telka 		return (err);
248f295f440SMarcel Telka 	}
249f295f440SMarcel Telka 
250f295f440SMarcel Telka 	return (0);
251f295f440SMarcel Telka }
252f295f440SMarcel Telka 
253f295f440SMarcel Telka int
zfs_send_compoundstream_end(int outfd)254f295f440SMarcel Telka zfs_send_compoundstream_end(int outfd)
255f295f440SMarcel Telka {
256f295f440SMarcel Telka 	dmu_replay_record_t drr = { 0 };
257f295f440SMarcel Telka 
258f295f440SMarcel Telka 	drr.drr_type = DRR_END;
259f295f440SMarcel Telka 	if (write(outfd, &drr, sizeof (drr)) == -1)
260f295f440SMarcel Telka 		return (errno);
261f295f440SMarcel Telka 
262f295f440SMarcel Telka 	return (0);
263f295f440SMarcel Telka }
264f295f440SMarcel Telka 
2659e69d7d0SLori Alt /*
2669e69d7d0SLori Alt  * This function is started in a separate thread when the dedup option
2679e69d7d0SLori Alt  * has been requested.  The main send thread determines the list of
2689e69d7d0SLori Alt  * snapshots to be included in the send stream and makes the ioctl calls
2699e69d7d0SLori Alt  * for each one.  But instead of having the ioctl send the output to the
2709e69d7d0SLori Alt  * the output fd specified by the caller of zfs_send()), the
2719e69d7d0SLori Alt  * ioctl is told to direct the output to a pipe, which is read by the
2729e69d7d0SLori Alt  * alternate thread running THIS function.  This function does the
2739e69d7d0SLori Alt  * dedup'ing by:
2749e69d7d0SLori Alt  *  1. building a dedup table (the DDT)
2759e69d7d0SLori Alt  *  2. doing checksums on each data block and inserting a record in the DDT
2769e69d7d0SLori Alt  *  3. looking for matching checksums, and
2779e69d7d0SLori Alt  *  4.  sending a DRR_WRITE_BYREF record instead of a write record whenever
2789e69d7d0SLori Alt  *      a duplicate block is found.
2799e69d7d0SLori Alt  * The output of this function then goes to the output fd requested
2809e69d7d0SLori Alt  * by the caller of zfs_send().
2819e69d7d0SLori Alt  */
2829e69d7d0SLori Alt static void *
cksummer(void * arg)2839e69d7d0SLori Alt cksummer(void *arg)
2849e69d7d0SLori Alt {
2859e69d7d0SLori Alt 	dedup_arg_t *dda = arg;
286d1a98260SMatthew Ahrens 	char *buf = zfs_alloc(dda->dedup_hdl, SPA_MAXBLOCKSIZE);
2879e69d7d0SLori Alt 	dmu_replay_record_t thedrr;
2889e69d7d0SLori Alt 	dmu_replay_record_t *drr = &thedrr;
2899e69d7d0SLori Alt 	FILE *ofp;
2909e69d7d0SLori Alt 	int outfd;
2919e69d7d0SLori Alt 	dedup_table_t ddt;
2929e69d7d0SLori Alt 	zio_cksum_t stream_cksum;
2939e69d7d0SLori Alt 	uint64_t physmem = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
2949e69d7d0SLori Alt 	uint64_t numbuckets;
2959e69d7d0SLori Alt 
2969e69d7d0SLori Alt 	ddt.max_ddt_size =
2979e69d7d0SLori Alt 	    MAX((physmem * MAX_DDT_PHYSMEM_PERCENT) / 100,
2989e69d7d0SLori Alt 	    SMALLEST_POSSIBLE_MAX_DDT_MB << 20);
2999e69d7d0SLori Alt 
3009e69d7d0SLori Alt 	numbuckets = ddt.max_ddt_size / (sizeof (dedup_entry_t));
3019e69d7d0SLori Alt 
3029e69d7d0SLori Alt 	/*
3039e69d7d0SLori Alt 	 * numbuckets must be a power of 2.  Increase number to
3049e69d7d0SLori Alt 	 * a power of 2 if necessary.
3059e69d7d0SLori Alt 	 */
3069e69d7d0SLori Alt 	if (!ISP2(numbuckets))
3079e69d7d0SLori Alt 		numbuckets = 1 << high_order_bit(numbuckets);
3089e69d7d0SLori Alt 
3099e69d7d0SLori Alt 	ddt.dedup_hash_array = calloc(numbuckets, sizeof (dedup_entry_t *));
3109e69d7d0SLori Alt 	ddt.ddecache = umem_cache_create("dde", sizeof (dedup_entry_t), 0,
3119e69d7d0SLori Alt 	    NULL, NULL, NULL, NULL, NULL, 0);
3129e69d7d0SLori Alt 	ddt.cur_ddt_size = numbuckets * sizeof (dedup_entry_t *);
3139e69d7d0SLori Alt 	ddt.numhashbits = high_order_bit(numbuckets) - 1;
3149e69d7d0SLori Alt 	ddt.ddt_full = B_FALSE;
3159e69d7d0SLori Alt 
3169e69d7d0SLori Alt 	outfd = dda->outputfd;
3179e69d7d0SLori Alt 	ofp = fdopen(dda->inputfd, "r");
31862e66153SMatthew Ahrens 	while (ssread(drr, sizeof (*drr), ofp) != 0) {
3199e69d7d0SLori Alt 
3209e69d7d0SLori Alt 		switch (drr->drr_type) {
3219e69d7d0SLori Alt 		case DRR_BEGIN:
3229e69d7d0SLori Alt 		{
32362e66153SMatthew Ahrens 			struct drr_begin *drrb = &drr->drr_u.drr_begin;
3249e69d7d0SLori Alt 			int fflags;
32562e66153SMatthew Ahrens 			int sz = 0;
3269e69d7d0SLori Alt 			ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
3279e69d7d0SLori Alt 
32862e66153SMatthew Ahrens 			ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC);
32962e66153SMatthew Ahrens 
3309e69d7d0SLori Alt 			/* set the DEDUP feature flag for this stream */
3319e69d7d0SLori Alt 			fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
3328e714474SLori Alt 			fflags |= (DMU_BACKUP_FEATURE_DEDUP |
3338e714474SLori Alt 			    DMU_BACKUP_FEATURE_DEDUPPROPS);
3349e69d7d0SLori Alt 			DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags);
3359e69d7d0SLori Alt 
3368429b235SMatthew Ahrens 			if (drr->drr_payloadlen != 0) {
33762e66153SMatthew Ahrens 				sz = drr->drr_payloadlen;
3389e69d7d0SLori Alt 
339d1a98260SMatthew Ahrens 				if (sz > SPA_MAXBLOCKSIZE) {
340d1a98260SMatthew Ahrens 					buf = zfs_realloc(dda->dedup_hdl, buf,
341d1a98260SMatthew Ahrens 					    SPA_MAXBLOCKSIZE, sz);
3429e69d7d0SLori Alt 				}
3439e69d7d0SLori Alt 				(void) ssread(buf, sz, ofp);
3449e69d7d0SLori Alt 				if (ferror(stdin))
3459e69d7d0SLori Alt 					perror("fread");
3469e69d7d0SLori Alt 			}
34762e66153SMatthew Ahrens 			if (dump_record(drr, buf, sz, &stream_cksum,
34862e66153SMatthew Ahrens 			    outfd) != 0)
34962e66153SMatthew Ahrens 				goto out;
3509e69d7d0SLori Alt 			break;
3519e69d7d0SLori Alt 		}
3529e69d7d0SLori Alt 
3539e69d7d0SLori Alt 		case DRR_END:
3549e69d7d0SLori Alt 		{
35562e66153SMatthew Ahrens 			struct drr_end *drre = &drr->drr_u.drr_end;
3569e69d7d0SLori Alt 			/* use the recalculated checksum */
35762e66153SMatthew Ahrens 			drre->drr_checksum = stream_cksum;
35862e66153SMatthew Ahrens 			if (dump_record(drr, NULL, 0, &stream_cksum,
35962e66153SMatthew Ahrens 			    outfd) != 0)
3609e69d7d0SLori Alt 				goto out;
3619e69d7d0SLori Alt 			break;
3629e69d7d0SLori Alt 		}
3639e69d7d0SLori Alt 
3649e69d7d0SLori Alt 		case DRR_OBJECT:
3659e69d7d0SLori Alt 		{
36662e66153SMatthew Ahrens 			struct drr_object *drro = &drr->drr_u.drr_object;
3679e69d7d0SLori Alt 			if (drro->drr_bonuslen > 0) {
3689e69d7d0SLori Alt 				(void) ssread(buf,
3699e69d7d0SLori Alt 				    P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
3709e69d7d0SLori Alt 				    ofp);
3719e69d7d0SLori Alt 			}
37262e66153SMatthew Ahrens 			if (dump_record(drr, buf,
37362e66153SMatthew Ahrens 			    P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
37462e66153SMatthew Ahrens 			    &stream_cksum, outfd) != 0)
37562e66153SMatthew Ahrens 				goto out;
3769e69d7d0SLori Alt 			break;
3779e69d7d0SLori Alt 		}
3789e69d7d0SLori Alt 
3790a586ceaSMark Shellenbaum 		case DRR_SPILL:
3800a586ceaSMark Shellenbaum 		{
38162e66153SMatthew Ahrens 			struct drr_spill *drrs = &drr->drr_u.drr_spill;
3820a586ceaSMark Shellenbaum 			(void) ssread(buf, drrs->drr_length, ofp);
38362e66153SMatthew Ahrens 			if (dump_record(drr, buf, drrs->drr_length,
38462e66153SMatthew Ahrens 			    &stream_cksum, outfd) != 0)
3850a586ceaSMark Shellenbaum 				goto out;
3860a586ceaSMark Shellenbaum 			break;
3870a586ceaSMark Shellenbaum 		}
3880a586ceaSMark Shellenbaum 
3899e69d7d0SLori Alt 		case DRR_FREEOBJECTS:
3909e69d7d0SLori Alt 		{
39162e66153SMatthew Ahrens 			if (dump_record(drr, NULL, 0, &stream_cksum,
39262e66153SMatthew Ahrens 			    outfd) != 0)
3939e69d7d0SLori Alt 				goto out;
3949e69d7d0SLori Alt 			break;
3959e69d7d0SLori Alt 		}
3969e69d7d0SLori Alt 
3979e69d7d0SLori Alt 		case DRR_WRITE:
3989e69d7d0SLori Alt 		{
39962e66153SMatthew Ahrens 			struct drr_write *drrw = &drr->drr_u.drr_write;
4009e69d7d0SLori Alt 			dataref_t	dataref;
4019e69d7d0SLori Alt 
4029e69d7d0SLori Alt 			(void) ssread(buf, drrw->drr_length, ofp);
4038e714474SLori Alt 
4049e69d7d0SLori Alt 			/*
4058e714474SLori Alt 			 * Use the existing checksum if it's dedup-capable,
4068e714474SLori Alt 			 * else calculate a SHA256 checksum for it.
4079e69d7d0SLori Alt 			 */
4088e714474SLori Alt 
4098e714474SLori Alt 			if (ZIO_CHECKSUM_EQUAL(drrw->drr_key.ddk_cksum,
4108e714474SLori Alt 			    zero_cksum) ||
4118e714474SLori Alt 			    !DRR_IS_DEDUP_CAPABLE(drrw->drr_checksumflags)) {
4129e69d7d0SLori Alt 				SHA256_CTX	ctx;
4139e69d7d0SLori Alt 				zio_cksum_t	tmpsha256;
4149e69d7d0SLori Alt 
4159e69d7d0SLori Alt 				SHA256Init(&ctx);
4169e69d7d0SLori Alt 				SHA256Update(&ctx, buf, drrw->drr_length);
4179e69d7d0SLori Alt 				SHA256Final(&tmpsha256, &ctx);
4188e714474SLori Alt 				drrw->drr_key.ddk_cksum.zc_word[0] =
4199e69d7d0SLori Alt 				    BE_64(tmpsha256.zc_word[0]);
4208e714474SLori Alt 				drrw->drr_key.ddk_cksum.zc_word[1] =
4219e69d7d0SLori Alt 				    BE_64(tmpsha256.zc_word[1]);
4228e714474SLori Alt 				drrw->drr_key.ddk_cksum.zc_word[2] =
4239e69d7d0SLori Alt 				    BE_64(tmpsha256.zc_word[2]);
4248e714474SLori Alt 				drrw->drr_key.ddk_cksum.zc_word[3] =
4259e69d7d0SLori Alt 				    BE_64(tmpsha256.zc_word[3]);
4268e714474SLori Alt 				drrw->drr_checksumtype = ZIO_CHECKSUM_SHA256;
4278e714474SLori Alt 				drrw->drr_checksumflags = DRR_CHECKSUM_DEDUP;
4289e69d7d0SLori Alt 			}
4299e69d7d0SLori Alt 
4309e69d7d0SLori Alt 			dataref.ref_guid = drrw->drr_toguid;
4319e69d7d0SLori Alt 			dataref.ref_object = drrw->drr_object;
4329e69d7d0SLori Alt 			dataref.ref_offset = drrw->drr_offset;
4339e69d7d0SLori Alt 
4349e69d7d0SLori Alt 			if (ddt_update(dda->dedup_hdl, &ddt,
4358e714474SLori Alt 			    &drrw->drr_key.ddk_cksum, drrw->drr_key.ddk_prop,
4368e714474SLori Alt 			    &dataref)) {
43762e66153SMatthew Ahrens 				dmu_replay_record_t wbr_drr = {0};
43862e66153SMatthew Ahrens 				struct drr_write_byref *wbr_drrr =
43962e66153SMatthew Ahrens 				    &wbr_drr.drr_u.drr_write_byref;
44062e66153SMatthew Ahrens 
4419e69d7d0SLori Alt 				/* block already present in stream */
44262e66153SMatthew Ahrens 				wbr_drr.drr_type = DRR_WRITE_BYREF;
44362e66153SMatthew Ahrens 
4449e69d7d0SLori Alt 				wbr_drrr->drr_object = drrw->drr_object;
4459e69d7d0SLori Alt 				wbr_drrr->drr_offset = drrw->drr_offset;
4469e69d7d0SLori Alt 				wbr_drrr->drr_length = drrw->drr_length;
4479e69d7d0SLori Alt 				wbr_drrr->drr_toguid = drrw->drr_toguid;
4489e69d7d0SLori Alt 				wbr_drrr->drr_refguid = dataref.ref_guid;
4499e69d7d0SLori Alt 				wbr_drrr->drr_refobject =
4509e69d7d0SLori Alt 				    dataref.ref_object;
4519e69d7d0SLori Alt 				wbr_drrr->drr_refoffset =
4529e69d7d0SLori Alt 				    dataref.ref_offset;
4539e69d7d0SLori Alt 
4548e714474SLori Alt 				wbr_drrr->drr_checksumtype =
4558e714474SLori Alt 				    drrw->drr_checksumtype;
4568e714474SLori Alt 				wbr_drrr->drr_checksumflags =
4578e714474SLori Alt 				    drrw->drr_checksumtype;
4588e714474SLori Alt 				wbr_drrr->drr_key.ddk_cksum =
4598e714474SLori Alt 				    drrw->drr_key.ddk_cksum;
4608e714474SLori Alt 				wbr_drrr->drr_key.ddk_prop =
4618e714474SLori Alt 				    drrw->drr_key.ddk_prop;
4629e69d7d0SLori Alt 
46362e66153SMatthew Ahrens 				if (dump_record(&wbr_drr, NULL, 0,
46462e66153SMatthew Ahrens 				    &stream_cksum, outfd) != 0)
4659e69d7d0SLori Alt 					goto out;
4669e69d7d0SLori Alt 			} else {
4679e69d7d0SLori Alt 				/* block not previously seen */
46862e66153SMatthew Ahrens 				if (dump_record(drr, buf, drrw->drr_length,
46962e66153SMatthew Ahrens 				    &stream_cksum, outfd) != 0)
4709e69d7d0SLori Alt 					goto out;
4719e69d7d0SLori Alt 			}
4729e69d7d0SLori Alt 			break;
4739e69d7d0SLori Alt 		}
4749e69d7d0SLori Alt 
475e54e0be9SMatthew Ahrens 		case DRR_WRITE_EMBEDDED:
476e54e0be9SMatthew Ahrens 		{
47762e66153SMatthew Ahrens 			struct drr_write_embedded *drrwe =
47862e66153SMatthew Ahrens 			    &drr->drr_u.drr_write_embedded;
479e54e0be9SMatthew Ahrens 			(void) ssread(buf,
480e54e0be9SMatthew Ahrens 			    P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp);
48162e66153SMatthew Ahrens 			if (dump_record(drr, buf,
482e54e0be9SMatthew Ahrens 			    P2ROUNDUP((uint64_t)drrwe->drr_psize, 8),
48362e66153SMatthew Ahrens 			    &stream_cksum, outfd) != 0)
484e54e0be9SMatthew Ahrens 				goto out;
485e54e0be9SMatthew Ahrens 			break;
486e54e0be9SMatthew Ahrens 		}
487e54e0be9SMatthew Ahrens 
4889e69d7d0SLori Alt 		case DRR_FREE:
4899e69d7d0SLori Alt 		{
49062e66153SMatthew Ahrens 			if (dump_record(drr, NULL, 0, &stream_cksum,
49162e66153SMatthew Ahrens 			    outfd) != 0)
4929e69d7d0SLori Alt 				goto out;
4939e69d7d0SLori Alt 			break;
4949e69d7d0SLori Alt 		}
4959e69d7d0SLori Alt 
4969e69d7d0SLori Alt 		default:
49762e66153SMatthew Ahrens 			(void) fprintf(stderr, "INVALID record type 0x%x\n",
4989e69d7d0SLori Alt 			    drr->drr_type);
4999e69d7d0SLori Alt 			/* should never happen, so assert */
5009e69d7d0SLori Alt 			assert(B_FALSE);
5019e69d7d0SLori Alt 		}
5029e69d7d0SLori Alt 	}
5039e69d7d0SLori Alt out:
5049e69d7d0SLori Alt 	umem_cache_destroy(ddt.ddecache);
5059e69d7d0SLori Alt 	free(ddt.dedup_hash_array);
5069e69d7d0SLori Alt 	free(buf);
5079e69d7d0SLori Alt 	(void) fclose(ofp);
5089e69d7d0SLori Alt 
5099e69d7d0SLori Alt 	return (NULL);
5109e69d7d0SLori Alt }
5119e69d7d0SLori Alt 
5123cb34c60Sahrens /*
5133cb34c60Sahrens  * Routines for dealing with the AVL tree of fs-nvlists
5143cb34c60Sahrens  */
5153cb34c60Sahrens typedef struct fsavl_node {
5163cb34c60Sahrens 	avl_node_t fn_node;
5173cb34c60Sahrens 	nvlist_t *fn_nvfs;
5183cb34c60Sahrens 	char *fn_snapname;
5193cb34c60Sahrens 	uint64_t fn_guid;
5203cb34c60Sahrens } fsavl_node_t;
5213cb34c60Sahrens 
5223cb34c60Sahrens static int
fsavl_compare(const void * arg1,const void * arg2)5233cb34c60Sahrens fsavl_compare(const void *arg1, const void *arg2)
5243cb34c60Sahrens {
5253cb34c60Sahrens 	const fsavl_node_t *fn1 = arg1;
5263cb34c60Sahrens 	const fsavl_node_t *fn2 = arg2;
5273cb34c60Sahrens 
5283cb34c60Sahrens 	if (fn1->fn_guid > fn2->fn_guid)
5293cb34c60Sahrens 		return (+1);
5303cb34c60Sahrens 	else if (fn1->fn_guid < fn2->fn_guid)
5313cb34c60Sahrens 		return (-1);
5323cb34c60Sahrens 	else
5333cb34c60Sahrens 		return (0);
5343cb34c60Sahrens }
5353cb34c60Sahrens 
5363cb34c60Sahrens /*
5373cb34c60Sahrens  * Given the GUID of a snapshot, find its containing filesystem and
5383cb34c60Sahrens  * (optionally) name.
5393cb34c60Sahrens  */
5403cb34c60Sahrens static nvlist_t *
fsavl_find(avl_tree_t * avl,uint64_t snapguid,char ** snapname)5413cb34c60Sahrens fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
5423cb34c60Sahrens {
5433cb34c60Sahrens 	fsavl_node_t fn_find;
5443cb34c60Sahrens 	fsavl_node_t *fn;
5453cb34c60Sahrens 
5463cb34c60Sahrens 	fn_find.fn_guid = snapguid;
5473cb34c60Sahrens 
5483cb34c60Sahrens 	fn = avl_find(avl, &fn_find, NULL);
5493cb34c60Sahrens 	if (fn) {
5503cb34c60Sahrens 		if (snapname)
5513cb34c60Sahrens 			*snapname = fn->fn_snapname;
5523cb34c60Sahrens 		return (fn->fn_nvfs);
5533cb34c60Sahrens 	}
5543cb34c60Sahrens 	return (NULL);
5553cb34c60Sahrens }
5563cb34c60Sahrens 
55700954d7bSahl static void
fsavl_destroy(avl_tree_t * avl)55800954d7bSahl fsavl_destroy(avl_tree_t *avl)
55900954d7bSahl {
56000954d7bSahl 	fsavl_node_t *fn;
56100954d7bSahl 	void *cookie;
56200954d7bSahl 
56300954d7bSahl 	if (avl == NULL)
56400954d7bSahl 		return;
56500954d7bSahl 
56600954d7bSahl 	cookie = NULL;
56700954d7bSahl 	while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
56800954d7bSahl 		free(fn);
56900954d7bSahl 	avl_destroy(avl);
57000954d7bSahl 	free(avl);
57100954d7bSahl }
57200954d7bSahl 
57388bb18d2SLori Alt /*
57488bb18d2SLori Alt  * Given an nvlist, produce an avl tree of snapshots, ordered by guid
57588bb18d2SLori Alt  */
5763cb34c60Sahrens static avl_tree_t *
fsavl_create(nvlist_t * fss)5773cb34c60Sahrens fsavl_create(nvlist_t *fss)
5783cb34c60Sahrens {
5793cb34c60Sahrens 	avl_tree_t *fsavl;
5803cb34c60Sahrens 	nvpair_t *fselem = NULL;
5813cb34c60Sahrens 
58200954d7bSahl 	if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL)
58300954d7bSahl 		return (NULL);
58400954d7bSahl 
5853cb34c60Sahrens 	avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
5863cb34c60Sahrens 	    offsetof(fsavl_node_t, fn_node));
5873cb34c60Sahrens 
5883cb34c60Sahrens 	while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
5893cb34c60Sahrens 		nvlist_t *nvfs, *snaps;
5903cb34c60Sahrens 		nvpair_t *snapelem = NULL;
5913cb34c60Sahrens 
5923cb34c60Sahrens 		VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
5933cb34c60Sahrens 		VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
5943cb34c60Sahrens 
5953cb34c60Sahrens 		while ((snapelem =
5963cb34c60Sahrens 		    nvlist_next_nvpair(snaps, snapelem)) != NULL) {
5973cb34c60Sahrens 			fsavl_node_t *fn;
5983cb34c60Sahrens 			uint64_t guid;
5993cb34c60Sahrens 
6003cb34c60Sahrens 			VERIFY(0 == nvpair_value_uint64(snapelem, &guid));
60100954d7bSahl 			if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) {
60200954d7bSahl 				fsavl_destroy(fsavl);
60300954d7bSahl 				return (NULL);
60400954d7bSahl 			}
6053cb34c60Sahrens 			fn->fn_nvfs = nvfs;
6063cb34c60Sahrens 			fn->fn_snapname = nvpair_name(snapelem);
6073cb34c60Sahrens 			fn->fn_guid = guid;
6083cb34c60Sahrens 
6093cb34c60Sahrens 			/*
6103cb34c60Sahrens 			 * Note: if there are multiple snaps with the
6113cb34c60Sahrens 			 * same GUID, we ignore all but one.
6123cb34c60Sahrens 			 */
6133cb34c60Sahrens 			if (avl_find(fsavl, fn, NULL) == NULL)
6143cb34c60Sahrens 				avl_add(fsavl, fn);
6153cb34c60Sahrens 			else
6163cb34c60Sahrens 				free(fn);
6173cb34c60Sahrens 		}
6183cb34c60Sahrens 	}
6193cb34c60Sahrens 
6203cb34c60Sahrens 	return (fsavl);
6213cb34c60Sahrens }
6223cb34c60Sahrens 
6233cb34c60Sahrens /*
6243cb34c60Sahrens  * Routines for dealing with the giant nvlist of fs-nvlists, etc.
6253cb34c60Sahrens  */
6263cb34c60Sahrens typedef struct send_data {
6273cb34c60Sahrens 	uint64_t parent_fromsnap_guid;
6283cb34c60Sahrens 	nvlist_t *parent_snaps;
6293cb34c60Sahrens 	nvlist_t *fss;
630bb0ade09Sahrens 	nvlist_t *snapprops;
6313cb34c60Sahrens 	const char *fromsnap;
6323cb34c60Sahrens 	const char *tosnap;
63392241e0bSTom Erickson 	boolean_t recursive;
6343cb34c60Sahrens 
6353cb34c60Sahrens 	/*
6363cb34c60Sahrens 	 * The header nvlist is of the following format:
6373cb34c60Sahrens 	 * {
6383cb34c60Sahrens 	 *   "tosnap" -> string
6393cb34c60Sahrens 	 *   "fromsnap" -> string (if incremental)
6403cb34c60Sahrens 	 *   "fss" -> {
6413cb34c60Sahrens 	 *	id -> {
6423cb34c60Sahrens 	 *
6433cb34c60Sahrens 	 *	 "name" -> string (full name; for debugging)
6443cb34c60Sahrens 	 *	 "parentfromsnap" -> number (guid of fromsnap in parent)
6453cb34c60Sahrens 	 *
6463cb34c60Sahrens 	 *	 "props" -> { name -> value (only if set here) }
6473cb34c60Sahrens 	 *	 "snaps" -> { name (lastname) -> number (guid) }
648bb0ade09Sahrens 	 *	 "snapprops" -> { name (lastname) -> { name -> value } }
6493cb34c60Sahrens 	 *
6503cb34c60Sahrens 	 *	 "origin" -> number (guid) (if clone)
6513cb34c60Sahrens 	 *	 "sent" -> boolean (not on-disk)
6523cb34c60Sahrens 	 *	}
6533cb34c60Sahrens 	 *   }
6543cb34c60Sahrens 	 * }
6553cb34c60Sahrens 	 *
6563cb34c60Sahrens 	 */
6573cb34c60Sahrens } send_data_t;
6583cb34c60Sahrens 
659bb0ade09Sahrens static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
660bb0ade09Sahrens 
6613cb34c60Sahrens static int
send_iterate_snap(zfs_handle_t * zhp,void * arg)6623cb34c60Sahrens send_iterate_snap(zfs_handle_t *zhp, void *arg)
6633cb34c60Sahrens {
6643cb34c60Sahrens 	send_data_t *sd = arg;
6653cb34c60Sahrens 	uint64_t guid = zhp->zfs_dmustats.dds_guid;
6663cb34c60Sahrens 	char *snapname;
667bb0ade09Sahrens 	nvlist_t *nv;
6683cb34c60Sahrens 
6693cb34c60Sahrens 	snapname = strrchr(zhp->zfs_name, '@')+1;
6703cb34c60Sahrens 
6713cb34c60Sahrens 	VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
6723cb34c60Sahrens 	/*
6733cb34c60Sahrens 	 * NB: if there is no fromsnap here (it's a newly created fs in
6743cb34c60Sahrens 	 * an incremental replication), we will substitute the tosnap.
6753cb34c60Sahrens 	 */
6763cb34c60Sahrens 	if ((sd->fromsnap && strcmp(snapname, sd->fromsnap) == 0) ||
6773cb34c60Sahrens 	    (sd->parent_fromsnap_guid == 0 && sd->tosnap &&
6783cb34c60Sahrens 	    strcmp(snapname, sd->tosnap) == 0)) {
6793cb34c60Sahrens 		sd->parent_fromsnap_guid = guid;
6803cb34c60Sahrens 	}
6813cb34c60Sahrens 
682bb0ade09Sahrens 	VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
683bb0ade09Sahrens 	send_iterate_prop(zhp, nv);
684bb0ade09Sahrens 	VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
685bb0ade09Sahrens 	nvlist_free(nv);
686bb0ade09Sahrens 
6873cb34c60Sahrens 	zfs_close(zhp);
6883cb34c60Sahrens 	return (0);
6893cb34c60Sahrens }
6903cb34c60Sahrens 
6913cb34c60Sahrens static void
send_iterate_prop(zfs_handle_t * zhp,nvlist_t * nv)6923cb34c60Sahrens send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
6933cb34c60Sahrens {
6943cb34c60Sahrens 	nvpair_t *elem = NULL;
6953cb34c60Sahrens 
6963cb34c60Sahrens 	while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
6973cb34c60Sahrens 		char *propname = nvpair_name(elem);
6983cb34c60Sahrens 		zfs_prop_t prop = zfs_name_to_prop(propname);
6993cb34c60Sahrens 		nvlist_t *propnv;
7003cb34c60Sahrens 
701faaa6415SEric Schrock 		if (!zfs_prop_user(propname)) {
702faaa6415SEric Schrock 			/*
703faaa6415SEric Schrock 			 * Realistically, this should never happen.  However,
704faaa6415SEric Schrock 			 * we want the ability to add DSL properties without
705faaa6415SEric Schrock 			 * needing to make incompatible version changes.  We
706faaa6415SEric Schrock 			 * need to ignore unknown properties to allow older
707faaa6415SEric Schrock 			 * software to still send datasets containing these
708faaa6415SEric Schrock 			 * properties, with the unknown properties elided.
709faaa6415SEric Schrock 			 */
710faaa6415SEric Schrock 			if (prop == ZPROP_INVAL)
7113cb34c60Sahrens 				continue;
7123cb34c60Sahrens 
713faaa6415SEric Schrock 			if (zfs_prop_readonly(prop))
714faaa6415SEric Schrock 				continue;
715faaa6415SEric Schrock 		}
716faaa6415SEric Schrock 
7173cb34c60Sahrens 		verify(nvpair_value_nvlist(elem, &propnv) == 0);
718166773ebSSanjeev Bagewadi 		if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION ||
719166773ebSSanjeev Bagewadi 		    prop == ZFS_PROP_REFQUOTA ||
720166773ebSSanjeev Bagewadi 		    prop == ZFS_PROP_REFRESERVATION) {
72192241e0bSTom Erickson 			char *source;
7223cb34c60Sahrens 			uint64_t value;
7233cb34c60Sahrens 			verify(nvlist_lookup_uint64(propnv,
7243cb34c60Sahrens 			    ZPROP_VALUE, &value) == 0);
725bb0ade09Sahrens 			if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
726bb0ade09Sahrens 				continue;
72792241e0bSTom Erickson 			/*
72892241e0bSTom Erickson 			 * May have no source before SPA_VERSION_RECVD_PROPS,
72992241e0bSTom Erickson 			 * but is still modifiable.
73092241e0bSTom Erickson 			 */
73192241e0bSTom Erickson 			if (nvlist_lookup_string(propnv,
73292241e0bSTom Erickson 			    ZPROP_SOURCE, &source) == 0) {
73392241e0bSTom Erickson 				if ((strcmp(source, zhp->zfs_name) != 0) &&
73492241e0bSTom Erickson 				    (strcmp(source,
73592241e0bSTom Erickson 				    ZPROP_SOURCE_VAL_RECVD) != 0))
73692241e0bSTom Erickson 					continue;
73792241e0bSTom Erickson 			}
7383cb34c60Sahrens 		} else {
7393cb34c60Sahrens 			char *source;
7403cb34c60Sahrens 			if (nvlist_lookup_string(propnv,
7413cb34c60Sahrens 			    ZPROP_SOURCE, &source) != 0)
7423cb34c60Sahrens 				continue;
74392241e0bSTom Erickson 			if ((strcmp(source, zhp->zfs_name) != 0) &&
74492241e0bSTom Erickson 			    (strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0))
7453cb34c60Sahrens 				continue;
7463cb34c60Sahrens 		}
7473cb34c60Sahrens 
7483cb34c60Sahrens 		if (zfs_prop_user(propname) ||
7493cb34c60Sahrens 		    zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
7503cb34c60Sahrens 			char *value;
7513cb34c60Sahrens 			verify(nvlist_lookup_string(propnv,
7523cb34c60Sahrens 			    ZPROP_VALUE, &value) == 0);
7533cb34c60Sahrens 			VERIFY(0 == nvlist_add_string(nv, propname, value));
7543cb34c60Sahrens 		} else {
7553cb34c60Sahrens 			uint64_t value;
7563cb34c60Sahrens 			verify(nvlist_lookup_uint64(propnv,
7573cb34c60Sahrens 			    ZPROP_VALUE, &value) == 0);
7583cb34c60Sahrens 			VERIFY(0 == nvlist_add_uint64(nv, propname, value));
7593cb34c60Sahrens 		}
7603cb34c60Sahrens 	}
7613cb34c60Sahrens }
7623cb34c60Sahrens 
76388bb18d2SLori Alt /*
76488bb18d2SLori Alt  * recursively generate nvlists describing datasets.  See comment
76588bb18d2SLori Alt  * for the data structure send_data_t above for description of contents
76688bb18d2SLori Alt  * of the nvlist.
76788bb18d2SLori Alt  */
7683cb34c60Sahrens static int
send_iterate_fs(zfs_handle_t * zhp,void * arg)7693cb34c60Sahrens send_iterate_fs(zfs_handle_t *zhp, void *arg)
7703cb34c60Sahrens {
7713cb34c60Sahrens 	send_data_t *sd = arg;
7723cb34c60Sahrens 	nvlist_t *nvfs, *nv;
77392241e0bSTom Erickson 	int rv = 0;
7743cb34c60Sahrens 	uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
7753cb34c60Sahrens 	uint64_t guid = zhp->zfs_dmustats.dds_guid;
7763cb34c60Sahrens 	char guidstring[64];
7773cb34c60Sahrens 
7783cb34c60Sahrens 	VERIFY(0 == nvlist_alloc(&nvfs, NV_UNIQUE_NAME, 0));
7793cb34c60Sahrens 	VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
7803cb34c60Sahrens 	VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap",
7813cb34c60Sahrens 	    sd->parent_fromsnap_guid));
7823cb34c60Sahrens 
7833cb34c60Sahrens 	if (zhp->zfs_dmustats.dds_origin[0]) {
7843cb34c60Sahrens 		zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
7853cb34c60Sahrens 		    zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
7863cb34c60Sahrens 		if (origin == NULL)
7873cb34c60Sahrens 			return (-1);
7883cb34c60Sahrens 		VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
7893cb34c60Sahrens 		    origin->zfs_dmustats.dds_guid));
7903cb34c60Sahrens 	}
7913cb34c60Sahrens 
7923cb34c60Sahrens 	/* iterate over props */
7933cb34c60Sahrens 	VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
7943cb34c60Sahrens 	send_iterate_prop(zhp, nv);
7953cb34c60Sahrens 	VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv));
7963cb34c60Sahrens 	nvlist_free(nv);
7973cb34c60Sahrens 
7983cb34c60Sahrens 	/* iterate over snaps, and set sd->parent_fromsnap_guid */
7993cb34c60Sahrens 	sd->parent_fromsnap_guid = 0;
8003cb34c60Sahrens 	VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
801bb0ade09Sahrens 	VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
8023cb34c60Sahrens 	(void) zfs_iter_snapshots(zhp, send_iterate_snap, sd);
8033cb34c60Sahrens 	VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
804bb0ade09Sahrens 	VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
8053cb34c60Sahrens 	nvlist_free(sd->parent_snaps);
806bb0ade09Sahrens 	nvlist_free(sd->snapprops);
8073cb34c60Sahrens 
8083cb34c60Sahrens 	/* add this fs to nvlist */
8093cb34c60Sahrens 	(void) snprintf(guidstring, sizeof (guidstring),
8103cb34c60Sahrens 	    "0x%llx", (longlong_t)guid);
8113cb34c60Sahrens 	VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs));
8123cb34c60Sahrens 	nvlist_free(nvfs);
8133cb34c60Sahrens 
8143cb34c60Sahrens 	/* iterate over children */
81592241e0bSTom Erickson 	if (sd->recursive)
8163cb34c60Sahrens 		rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
8173cb34c60Sahrens 
8183cb34c60Sahrens 	sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
8193cb34c60Sahrens 
8203cb34c60Sahrens 	zfs_close(zhp);
8213cb34c60Sahrens 	return (rv);
8223cb34c60Sahrens }
8233cb34c60Sahrens 
8243cb34c60Sahrens static int
gather_nvlist(libzfs_handle_t * hdl,const char * fsname,const char * fromsnap,const char * tosnap,boolean_t recursive,nvlist_t ** nvlp,avl_tree_t ** avlp)8253cb34c60Sahrens gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
82692241e0bSTom Erickson     const char *tosnap, boolean_t recursive, nvlist_t **nvlp, avl_tree_t **avlp)
8273cb34c60Sahrens {
8283cb34c60Sahrens 	zfs_handle_t *zhp;
8293cb34c60Sahrens 	send_data_t sd = { 0 };
8303cb34c60Sahrens 	int error;
8313cb34c60Sahrens 
8323cb34c60Sahrens 	zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
8333cb34c60Sahrens 	if (zhp == NULL)
8343cb34c60Sahrens 		return (EZFS_BADTYPE);
8353cb34c60Sahrens 
8363cb34c60Sahrens 	VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
8373cb34c60Sahrens 	sd.fromsnap = fromsnap;
8383cb34c60Sahrens 	sd.tosnap = tosnap;
83992241e0bSTom Erickson 	sd.recursive = recursive;
84000954d7bSahl 
84100954d7bSahl 	if ((error = send_iterate_fs(zhp, &sd)) != 0) {
84200954d7bSahl 		nvlist_free(sd.fss);
84300954d7bSahl 		if (avlp != NULL)
84400954d7bSahl 			*avlp = NULL;
84500954d7bSahl 		*nvlp = NULL;
84600954d7bSahl 		return (error);
84700954d7bSahl 	}
84800954d7bSahl 
84900954d7bSahl 	if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) {
85000954d7bSahl 		nvlist_free(sd.fss);
85100954d7bSahl 		*nvlp = NULL;
85200954d7bSahl 		return (EZFS_NOMEM);
85300954d7bSahl 	}
8543cb34c60Sahrens 
8553cb34c60Sahrens 	*nvlp = sd.fss;
85600954d7bSahl 	return (0);
8573cb34c60Sahrens }
8583cb34c60Sahrens 
8593cb34c60Sahrens /*
8603cb34c60Sahrens  * Routines specific to "zfs send"
8613cb34c60Sahrens  */
8623cb34c60Sahrens typedef struct send_dump_data {
8633cb34c60Sahrens 	/* these are all just the short snapname (the part after the @) */
8643cb34c60Sahrens 	const char *fromsnap;
8653cb34c60Sahrens 	const char *tosnap;
866*675fc291SMatthew Ahrens 	char prevsnap[ZFS_MAX_DATASET_NAME_LEN];
867a7f53a56SChris Kirby 	uint64_t prevsnap_obj;
8683cb34c60Sahrens 	boolean_t seenfrom, seento, replicate, doall, fromorigin;
869e00f47baSManoj Joseph 	boolean_t verbose, dryrun, parsable, progress, embed_data, std_out;
870e00f47baSManoj Joseph 	boolean_t large_block;
8713cb34c60Sahrens 	int outfd;
8723cb34c60Sahrens 	boolean_t err;
8733cb34c60Sahrens 	nvlist_t *fss;
874a7a845e4SSteven Hartland 	nvlist_t *snapholds;
8753cb34c60Sahrens 	avl_tree_t *fsavl;
8769e69d7d0SLori Alt 	snapfilter_cb_t *filter_cb;
8779e69d7d0SLori Alt 	void *filter_cb_arg;
8783f9d6ad7SLin Ling 	nvlist_t *debugnv;
879*675fc291SMatthew Ahrens 	char holdtag[ZFS_MAX_DATASET_NAME_LEN];
880a7f53a56SChris Kirby 	int cleanup_fd;
88119b94df9SMatthew Ahrens 	uint64_t size;
8823cb34c60Sahrens } send_dump_data_t;
8833cb34c60Sahrens 
88419b94df9SMatthew Ahrens static int
estimate_ioctl(zfs_handle_t * zhp,uint64_t fromsnap_obj,boolean_t fromorigin,uint64_t * sizep)88519b94df9SMatthew Ahrens estimate_ioctl(zfs_handle_t *zhp, uint64_t fromsnap_obj,
88619b94df9SMatthew Ahrens     boolean_t fromorigin, uint64_t *sizep)
88719b94df9SMatthew Ahrens {
88819b94df9SMatthew Ahrens 	zfs_cmd_t zc = { 0 };
88919b94df9SMatthew Ahrens 	libzfs_handle_t *hdl = zhp->zfs_hdl;
89019b94df9SMatthew Ahrens 
89119b94df9SMatthew Ahrens 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
89219b94df9SMatthew Ahrens 	assert(fromsnap_obj == 0 || !fromorigin);
89319b94df9SMatthew Ahrens 
89419b94df9SMatthew Ahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
89519b94df9SMatthew Ahrens 	zc.zc_obj = fromorigin;
89619b94df9SMatthew Ahrens 	zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
89719b94df9SMatthew Ahrens 	zc.zc_fromobj = fromsnap_obj;
89819b94df9SMatthew Ahrens 	zc.zc_guid = 1;  /* estimate flag */
89919b94df9SMatthew Ahrens 
90019b94df9SMatthew Ahrens 	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
90119b94df9SMatthew Ahrens 		char errbuf[1024];
90219b94df9SMatthew Ahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
90319b94df9SMatthew Ahrens 		    "warning: cannot estimate space for '%s'"), zhp->zfs_name);
90419b94df9SMatthew Ahrens 
90519b94df9SMatthew Ahrens 		switch (errno) {
90619b94df9SMatthew Ahrens 		case EXDEV:
90719b94df9SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
90819b94df9SMatthew Ahrens 			    "not an earlier snapshot from the same fs"));
90919b94df9SMatthew Ahrens 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
91019b94df9SMatthew Ahrens 
91119b94df9SMatthew Ahrens 		case ENOENT:
91219b94df9SMatthew Ahrens 			if (zfs_dataset_exists(hdl, zc.zc_name,
91319b94df9SMatthew Ahrens 			    ZFS_TYPE_SNAPSHOT)) {
91419b94df9SMatthew Ahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
91519b94df9SMatthew Ahrens 				    "incremental source (@%s) does not exist"),
91619b94df9SMatthew Ahrens 				    zc.zc_value);
91719b94df9SMatthew Ahrens 			}
91819b94df9SMatthew Ahrens 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
91919b94df9SMatthew Ahrens 
92019b94df9SMatthew Ahrens 		case EDQUOT:
92119b94df9SMatthew Ahrens 		case EFBIG:
92219b94df9SMatthew Ahrens 		case EIO:
92319b94df9SMatthew Ahrens 		case ENOLINK:
92419b94df9SMatthew Ahrens 		case ENOSPC:
92519b94df9SMatthew Ahrens 		case ENOSTR:
92619b94df9SMatthew Ahrens 		case ENXIO:
92719b94df9SMatthew Ahrens 		case EPIPE:
92819b94df9SMatthew Ahrens 		case ERANGE:
92919b94df9SMatthew Ahrens 		case EFAULT:
93019b94df9SMatthew Ahrens 		case EROFS:
93119b94df9SMatthew Ahrens 			zfs_error_aux(hdl, strerror(errno));
93219b94df9SMatthew Ahrens 			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
93319b94df9SMatthew Ahrens 
93419b94df9SMatthew Ahrens 		default:
93519b94df9SMatthew Ahrens 			return (zfs_standard_error(hdl, errno, errbuf));
93619b94df9SMatthew Ahrens 		}
93719b94df9SMatthew Ahrens 	}
93819b94df9SMatthew Ahrens 
93919b94df9SMatthew Ahrens 	*sizep = zc.zc_objset_type;
94019b94df9SMatthew Ahrens 
94119b94df9SMatthew Ahrens 	return (0);
94219b94df9SMatthew Ahrens }
94319b94df9SMatthew Ahrens 
9443cb34c60Sahrens /*
9453cb34c60Sahrens  * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
9463cb34c60Sahrens  * NULL) to the file descriptor specified by outfd.
9473cb34c60Sahrens  */
9483cb34c60Sahrens static int
dump_ioctl(zfs_handle_t * zhp,const char * fromsnap,uint64_t fromsnap_obj,boolean_t fromorigin,int outfd,enum lzc_send_flags flags,nvlist_t * debugnv)949a7f53a56SChris Kirby dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
950e54e0be9SMatthew Ahrens     boolean_t fromorigin, int outfd, enum lzc_send_flags flags,
951e54e0be9SMatthew Ahrens     nvlist_t *debugnv)
9523cb34c60Sahrens {
9533cb34c60Sahrens 	zfs_cmd_t zc = { 0 };
9543cb34c60Sahrens 	libzfs_handle_t *hdl = zhp->zfs_hdl;
9553f9d6ad7SLin Ling 	nvlist_t *thisdbg;
9563cb34c60Sahrens 
9573cb34c60Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
958a7f53a56SChris Kirby 	assert(fromsnap_obj == 0 || !fromorigin);
9593cb34c60Sahrens 
9603cb34c60Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
9613cb34c60Sahrens 	zc.zc_cookie = outfd;
9623cb34c60Sahrens 	zc.zc_obj = fromorigin;
963a7f53a56SChris Kirby 	zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
964a7f53a56SChris Kirby 	zc.zc_fromobj = fromsnap_obj;
965e54e0be9SMatthew Ahrens 	zc.zc_flags = flags;
966c6fd73aeSChris Kirby 
9673f9d6ad7SLin Ling 	VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0));
9683f9d6ad7SLin Ling 	if (fromsnap && fromsnap[0] != '\0') {
9693f9d6ad7SLin Ling 		VERIFY(0 == nvlist_add_string(thisdbg,
9703f9d6ad7SLin Ling 		    "fromsnap", fromsnap));
9713f9d6ad7SLin Ling 	}
9723f9d6ad7SLin Ling 
97319b94df9SMatthew Ahrens 	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
9743cb34c60Sahrens 		char errbuf[1024];
9753cb34c60Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
9763cb34c60Sahrens 		    "warning: cannot send '%s'"), zhp->zfs_name);
9773cb34c60Sahrens 
9783f9d6ad7SLin Ling 		VERIFY(0 == nvlist_add_uint64(thisdbg, "error", errno));
9793f9d6ad7SLin Ling 		if (debugnv) {
9803f9d6ad7SLin Ling 			VERIFY(0 == nvlist_add_nvlist(debugnv,
9813f9d6ad7SLin Ling 			    zhp->zfs_name, thisdbg));
9823f9d6ad7SLin Ling 		}
9833f9d6ad7SLin Ling 		nvlist_free(thisdbg);
9843f9d6ad7SLin Ling 
9853cb34c60Sahrens 		switch (errno) {
9863cb34c60Sahrens 		case EXDEV:
9873cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
9883cb34c60Sahrens 			    "not an earlier snapshot from the same fs"));
9893cb34c60Sahrens 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
9903cb34c60Sahrens 
9913cb34c60Sahrens 		case ENOENT:
9923cb34c60Sahrens 			if (zfs_dataset_exists(hdl, zc.zc_name,
9933cb34c60Sahrens 			    ZFS_TYPE_SNAPSHOT)) {
9943cb34c60Sahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
9953cb34c60Sahrens 				    "incremental source (@%s) does not exist"),
9963cb34c60Sahrens 				    zc.zc_value);
9973cb34c60Sahrens 			}
9983cb34c60Sahrens 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
9993cb34c60Sahrens 
10003cb34c60Sahrens 		case EDQUOT:
10013cb34c60Sahrens 		case EFBIG:
10023cb34c60Sahrens 		case EIO:
10033cb34c60Sahrens 		case ENOLINK:
10043cb34c60Sahrens 		case ENOSPC:
10053cb34c60Sahrens 		case ENOSTR:
10063cb34c60Sahrens 		case ENXIO:
10073cb34c60Sahrens 		case EPIPE:
10083cb34c60Sahrens 		case ERANGE:
10093cb34c60Sahrens 		case EFAULT:
10103cb34c60Sahrens 		case EROFS:
10113cb34c60Sahrens 			zfs_error_aux(hdl, strerror(errno));
10123cb34c60Sahrens 			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
10133cb34c60Sahrens 
10143cb34c60Sahrens 		default:
10153cb34c60Sahrens 			return (zfs_standard_error(hdl, errno, errbuf));
10163cb34c60Sahrens 		}
10173cb34c60Sahrens 	}
10183cb34c60Sahrens 
10193f9d6ad7SLin Ling 	if (debugnv)
10203f9d6ad7SLin Ling 		VERIFY(0 == nvlist_add_nvlist(debugnv, zhp->zfs_name, thisdbg));
10213f9d6ad7SLin Ling 	nvlist_free(thisdbg);
10223f9d6ad7SLin Ling 
10233cb34c60Sahrens 	return (0);
10243cb34c60Sahrens }
10253cb34c60Sahrens 
1026a7a845e4SSteven Hartland static void
gather_holds(zfs_handle_t * zhp,send_dump_data_t * sdd)1027a7a845e4SSteven Hartland gather_holds(zfs_handle_t *zhp, send_dump_data_t *sdd)
1028a7f53a56SChris Kirby {
1029a7f53a56SChris Kirby 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
1030a7f53a56SChris Kirby 
1031a7f53a56SChris Kirby 	/*
1032a7a845e4SSteven Hartland 	 * zfs_send() only sets snapholds for sends that need them,
1033a7f53a56SChris Kirby 	 * e.g. replication and doall.
1034a7f53a56SChris Kirby 	 */
1035a7a845e4SSteven Hartland 	if (sdd->snapholds == NULL)
1036a7a845e4SSteven Hartland 		return;
1037a7f53a56SChris Kirby 
1038a7a845e4SSteven Hartland 	fnvlist_add_string(sdd->snapholds, zhp->zfs_name, sdd->holdtag);
1039a7f53a56SChris Kirby }
1040a7f53a56SChris Kirby 
10414e3c9f44SBill Pijewski static void *
send_progress_thread(void * arg)10424e3c9f44SBill Pijewski send_progress_thread(void *arg)
10434e3c9f44SBill Pijewski {
10444e3c9f44SBill Pijewski 	progress_arg_t *pa = arg;
10454e3c9f44SBill Pijewski 	zfs_cmd_t zc = { 0 };
10464e3c9f44SBill Pijewski 	zfs_handle_t *zhp = pa->pa_zhp;
10474e3c9f44SBill Pijewski 	libzfs_handle_t *hdl = zhp->zfs_hdl;
10484e3c9f44SBill Pijewski 	unsigned long long bytes;
10494e3c9f44SBill Pijewski 	char buf[16];
10504e3c9f44SBill Pijewski 	time_t t;
10514e3c9f44SBill Pijewski 	struct tm *tm;
10524e3c9f44SBill Pijewski 
10534e3c9f44SBill Pijewski 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
10544e3c9f44SBill Pijewski 
10554e3c9f44SBill Pijewski 	if (!pa->pa_parsable)
10564e3c9f44SBill Pijewski 		(void) fprintf(stderr, "TIME        SENT   SNAPSHOT\n");
10574e3c9f44SBill Pijewski 
10584e3c9f44SBill Pijewski 	/*
10594e3c9f44SBill Pijewski 	 * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
10604e3c9f44SBill Pijewski 	 */
10614e3c9f44SBill Pijewski 	for (;;) {
10624e3c9f44SBill Pijewski 		(void) sleep(1);
10634e3c9f44SBill Pijewski 
10644e3c9f44SBill Pijewski 		zc.zc_cookie = pa->pa_fd;
10654e3c9f44SBill Pijewski 		if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0)
10664e3c9f44SBill Pijewski 			return ((void *)-1);
10674e3c9f44SBill Pijewski 
10684e3c9f44SBill Pijewski 		(void) time(&t);
10694e3c9f44SBill Pijewski 		tm = localtime(&t);
10704e3c9f44SBill Pijewski 		bytes = zc.zc_cookie;
10714e3c9f44SBill Pijewski 
10724e3c9f44SBill Pijewski 		if (pa->pa_parsable) {
10734e3c9f44SBill Pijewski 			(void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
10744e3c9f44SBill Pijewski 			    tm->tm_hour, tm->tm_min, tm->tm_sec,
10754e3c9f44SBill Pijewski 			    bytes, zhp->zfs_name);
10764e3c9f44SBill Pijewski 		} else {
10774e3c9f44SBill Pijewski 			zfs_nicenum(bytes, buf, sizeof (buf));
10784e3c9f44SBill Pijewski 			(void) fprintf(stderr, "%02d:%02d:%02d   %5s   %s\n",
10794e3c9f44SBill Pijewski 			    tm->tm_hour, tm->tm_min, tm->tm_sec,
10804e3c9f44SBill Pijewski 			    buf, zhp->zfs_name);
10814e3c9f44SBill Pijewski 		}
10824e3c9f44SBill Pijewski 	}
10834e3c9f44SBill Pijewski }
10844e3c9f44SBill Pijewski 
10858429b235SMatthew Ahrens static void
send_print_verbose(FILE * fout,const char * tosnap,const char * fromsnap,uint64_t size,boolean_t parsable)10868429b235SMatthew Ahrens send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap,
10878429b235SMatthew Ahrens     uint64_t size, boolean_t parsable)
10888429b235SMatthew Ahrens {
10898429b235SMatthew Ahrens 	if (parsable) {
10908429b235SMatthew Ahrens 		if (fromsnap != NULL) {
10918429b235SMatthew Ahrens 			(void) fprintf(fout, "incremental\t%s\t%s",
10928429b235SMatthew Ahrens 			    fromsnap, tosnap);
10938429b235SMatthew Ahrens 		} else {
10948429b235SMatthew Ahrens 			(void) fprintf(fout, "full\t%s",
10958429b235SMatthew Ahrens 			    tosnap);
10968429b235SMatthew Ahrens 		}
10978429b235SMatthew Ahrens 	} else {
10988429b235SMatthew Ahrens 		if (fromsnap != NULL) {
10998429b235SMatthew Ahrens 			if (strchr(fromsnap, '@') == NULL &&
11008429b235SMatthew Ahrens 			    strchr(fromsnap, '#') == NULL) {
11018429b235SMatthew Ahrens 				(void) fprintf(fout, dgettext(TEXT_DOMAIN,
11028429b235SMatthew Ahrens 				    "send from @%s to %s"),
11038429b235SMatthew Ahrens 				    fromsnap, tosnap);
11048429b235SMatthew Ahrens 			} else {
11058429b235SMatthew Ahrens 				(void) fprintf(fout, dgettext(TEXT_DOMAIN,
11068429b235SMatthew Ahrens 				    "send from %s to %s"),
11078429b235SMatthew Ahrens 				    fromsnap, tosnap);
11088429b235SMatthew Ahrens 			}
11098429b235SMatthew Ahrens 		} else {
11108429b235SMatthew Ahrens 			(void) fprintf(fout, dgettext(TEXT_DOMAIN,
11118429b235SMatthew Ahrens 			    "full send of %s"),
11128429b235SMatthew Ahrens 			    tosnap);
11138429b235SMatthew Ahrens 		}
11148429b235SMatthew Ahrens 	}
11158429b235SMatthew Ahrens 
11168429b235SMatthew Ahrens 	if (size != 0) {
11178429b235SMatthew Ahrens 		if (parsable) {
11188429b235SMatthew Ahrens 			(void) fprintf(fout, "\t%llu",
11198429b235SMatthew Ahrens 			    (longlong_t)size);
11208429b235SMatthew Ahrens 		} else {
11218429b235SMatthew Ahrens 			char buf[16];
11228429b235SMatthew Ahrens 			zfs_nicenum(size, buf, sizeof (buf));
11238429b235SMatthew Ahrens 			(void) fprintf(fout, dgettext(TEXT_DOMAIN,
11248429b235SMatthew Ahrens 			    " estimated size is %s"), buf);
11258429b235SMatthew Ahrens 		}
11268429b235SMatthew Ahrens 	}
11278429b235SMatthew Ahrens 	(void) fprintf(fout, "\n");
11288429b235SMatthew Ahrens }
11298429b235SMatthew Ahrens 
1130a7f53a56SChris Kirby static int
dump_snapshot(zfs_handle_t * zhp,void * arg)11313cb34c60Sahrens dump_snapshot(zfs_handle_t *zhp, void *arg)
11323cb34c60Sahrens {
11333cb34c60Sahrens 	send_dump_data_t *sdd = arg;
11344e3c9f44SBill Pijewski 	progress_arg_t pa = { 0 };
11354e3c9f44SBill Pijewski 	pthread_t tid;
1136a7f53a56SChris Kirby 	char *thissnap;
11373cb34c60Sahrens 	int err;
113819b94df9SMatthew Ahrens 	boolean_t isfromsnap, istosnap, fromorigin;
1139468db2f4STom Erickson 	boolean_t exclude = B_FALSE;
1140e00f47baSManoj Joseph 	FILE *fout = sdd->std_out ? stdout : stderr;
11413cb34c60Sahrens 
1142a7a845e4SSteven Hartland 	err = 0;
11433cb34c60Sahrens 	thissnap = strchr(zhp->zfs_name, '@') + 1;
1144468db2f4STom Erickson 	isfromsnap = (sdd->fromsnap != NULL &&
1145468db2f4STom Erickson 	    strcmp(sdd->fromsnap, thissnap) == 0);
11463cb34c60Sahrens 
1147468db2f4STom Erickson 	if (!sdd->seenfrom && isfromsnap) {
1148a7a845e4SSteven Hartland 		gather_holds(zhp, sdd);
11493cb34c60Sahrens 		sdd->seenfrom = B_TRUE;
11509e69d7d0SLori Alt 		(void) strcpy(sdd->prevsnap, thissnap);
1151a7a845e4SSteven Hartland 		sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
11523cb34c60Sahrens 		zfs_close(zhp);
1153a7a845e4SSteven Hartland 		return (0);
11543cb34c60Sahrens 	}
11553cb34c60Sahrens 
11563cb34c60Sahrens 	if (sdd->seento || !sdd->seenfrom) {
11573cb34c60Sahrens 		zfs_close(zhp);
11583cb34c60Sahrens 		return (0);
11593cb34c60Sahrens 	}
11603cb34c60Sahrens 
1161468db2f4STom Erickson 	istosnap = (strcmp(sdd->tosnap, thissnap) == 0);
1162468db2f4STom Erickson 	if (istosnap)
11639e69d7d0SLori Alt 		sdd->seento = B_TRUE;
11649e69d7d0SLori Alt 
1165468db2f4STom Erickson 	if (!sdd->doall && !isfromsnap && !istosnap) {
1166468db2f4STom Erickson 		if (sdd->replicate) {
1167468db2f4STom Erickson 			char *snapname;
1168468db2f4STom Erickson 			nvlist_t *snapprops;
1169468db2f4STom Erickson 			/*
1170468db2f4STom Erickson 			 * Filter out all intermediate snapshots except origin
1171468db2f4STom Erickson 			 * snapshots needed to replicate clones.
1172468db2f4STom Erickson 			 */
1173468db2f4STom Erickson 			nvlist_t *nvfs = fsavl_find(sdd->fsavl,
1174468db2f4STom Erickson 			    zhp->zfs_dmustats.dds_guid, &snapname);
1175468db2f4STom Erickson 
1176468db2f4STom Erickson 			VERIFY(0 == nvlist_lookup_nvlist(nvfs,
1177468db2f4STom Erickson 			    "snapprops", &snapprops));
1178468db2f4STom Erickson 			VERIFY(0 == nvlist_lookup_nvlist(snapprops,
1179468db2f4STom Erickson 			    thissnap, &snapprops));
1180468db2f4STom Erickson 			exclude = !nvlist_exists(snapprops, "is_clone_origin");
1181468db2f4STom Erickson 		} else {
1182468db2f4STom Erickson 			exclude = B_TRUE;
1183468db2f4STom Erickson 		}
1184468db2f4STom Erickson 	}
1185468db2f4STom Erickson 
11869e69d7d0SLori Alt 	/*
11879e69d7d0SLori Alt 	 * If a filter function exists, call it to determine whether
11889e69d7d0SLori Alt 	 * this snapshot will be sent.
11899e69d7d0SLori Alt 	 */
1190468db2f4STom Erickson 	if (exclude || (sdd->filter_cb != NULL &&
1191468db2f4STom Erickson 	    sdd->filter_cb(zhp, sdd->filter_cb_arg) == B_FALSE)) {
11929e69d7d0SLori Alt 		/*
11939e69d7d0SLori Alt 		 * This snapshot is filtered out.  Don't send it, and don't
1194a7f53a56SChris Kirby 		 * set prevsnap_obj, so it will be as if this snapshot didn't
11959e69d7d0SLori Alt 		 * exist, and the next accepted snapshot will be sent as
11969e69d7d0SLori Alt 		 * an incremental from the last accepted one, or as the
11979e69d7d0SLori Alt 		 * first (and full) snapshot in the case of a replication,
11989e69d7d0SLori Alt 		 * non-incremental send.
11999e69d7d0SLori Alt 		 */
12009e69d7d0SLori Alt 		zfs_close(zhp);
12019e69d7d0SLori Alt 		return (0);
12029e69d7d0SLori Alt 	}
12039e69d7d0SLori Alt 
1204a7a845e4SSteven Hartland 	gather_holds(zhp, sdd);
120519b94df9SMatthew Ahrens 	fromorigin = sdd->prevsnap[0] == '\0' &&
120619b94df9SMatthew Ahrens 	    (sdd->fromorigin || sdd->replicate);
120719b94df9SMatthew Ahrens 
12083cb34c60Sahrens 	if (sdd->verbose) {
12098429b235SMatthew Ahrens 		uint64_t size = 0;
12108429b235SMatthew Ahrens 		(void) estimate_ioctl(zhp, sdd->prevsnap_obj,
121119b94df9SMatthew Ahrens 		    fromorigin, &size);
121219b94df9SMatthew Ahrens 
12138429b235SMatthew Ahrens 		send_print_verbose(fout, zhp->zfs_name,
12148429b235SMatthew Ahrens 		    sdd->prevsnap[0] ? sdd->prevsnap : NULL,
12158429b235SMatthew Ahrens 		    size, sdd->parsable);
121619b94df9SMatthew Ahrens 		sdd->size += size;
121719b94df9SMatthew Ahrens 	}
12183cb34c60Sahrens 
121919b94df9SMatthew Ahrens 	if (!sdd->dryrun) {
12204e3c9f44SBill Pijewski 		/*
12214e3c9f44SBill Pijewski 		 * If progress reporting is requested, spawn a new thread to
12224e3c9f44SBill Pijewski 		 * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
12234e3c9f44SBill Pijewski 		 */
12244e3c9f44SBill Pijewski 		if (sdd->progress) {
12254e3c9f44SBill Pijewski 			pa.pa_zhp = zhp;
12264e3c9f44SBill Pijewski 			pa.pa_fd = sdd->outfd;
12274e3c9f44SBill Pijewski 			pa.pa_parsable = sdd->parsable;
12284e3c9f44SBill Pijewski 
12294e3c9f44SBill Pijewski 			if (err = pthread_create(&tid, NULL,
12304e3c9f44SBill Pijewski 			    send_progress_thread, &pa)) {
12314e3c9f44SBill Pijewski 				zfs_close(zhp);
12324e3c9f44SBill Pijewski 				return (err);
12334e3c9f44SBill Pijewski 			}
12344e3c9f44SBill Pijewski 		}
12354e3c9f44SBill Pijewski 
1236e54e0be9SMatthew Ahrens 		enum lzc_send_flags flags = 0;
1237d1a98260SMatthew Ahrens 		if (sdd->large_block)
1238d1a98260SMatthew Ahrens 			flags |= LZC_SEND_FLAG_LARGE_BLOCK;
1239e54e0be9SMatthew Ahrens 		if (sdd->embed_data)
1240e54e0be9SMatthew Ahrens 			flags |= LZC_SEND_FLAG_EMBED_DATA;
1241e54e0be9SMatthew Ahrens 
1242a7f53a56SChris Kirby 		err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
1243e54e0be9SMatthew Ahrens 		    fromorigin, sdd->outfd, flags, sdd->debugnv);
12444e3c9f44SBill Pijewski 
12454e3c9f44SBill Pijewski 		if (sdd->progress) {
12464e3c9f44SBill Pijewski 			(void) pthread_cancel(tid);
12474e3c9f44SBill Pijewski 			(void) pthread_join(tid, NULL);
12484e3c9f44SBill Pijewski 		}
124919b94df9SMatthew Ahrens 	}
12503cb34c60Sahrens 
12519e69d7d0SLori Alt 	(void) strcpy(sdd->prevsnap, thissnap);
1252a7f53a56SChris Kirby 	sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
12533cb34c60Sahrens 	zfs_close(zhp);
12543cb34c60Sahrens 	return (err);
12553cb34c60Sahrens }
12563cb34c60Sahrens 
12573cb34c60Sahrens static int
dump_filesystem(zfs_handle_t * zhp,void * arg)12583cb34c60Sahrens dump_filesystem(zfs_handle_t *zhp, void *arg)
12593cb34c60Sahrens {
12603cb34c60Sahrens 	int rv = 0;
12613cb34c60Sahrens 	send_dump_data_t *sdd = arg;
12623cb34c60Sahrens 	boolean_t missingfrom = B_FALSE;
12633cb34c60Sahrens 	zfs_cmd_t zc = { 0 };
12643cb34c60Sahrens 
12653cb34c60Sahrens 	(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
12663cb34c60Sahrens 	    zhp->zfs_name, sdd->tosnap);
12673cb34c60Sahrens 	if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
126819b94df9SMatthew Ahrens 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
126919b94df9SMatthew Ahrens 		    "WARNING: could not send %s@%s: does not exist\n"),
12703cb34c60Sahrens 		    zhp->zfs_name, sdd->tosnap);
12713cb34c60Sahrens 		sdd->err = B_TRUE;
12723cb34c60Sahrens 		return (0);
12733cb34c60Sahrens 	}
12743cb34c60Sahrens 
12753cb34c60Sahrens 	if (sdd->replicate && sdd->fromsnap) {
12763cb34c60Sahrens 		/*
12773cb34c60Sahrens 		 * If this fs does not have fromsnap, and we're doing
12783cb34c60Sahrens 		 * recursive, we need to send a full stream from the
12793cb34c60Sahrens 		 * beginning (or an incremental from the origin if this
12803cb34c60Sahrens 		 * is a clone).  If we're doing non-recursive, then let
12813cb34c60Sahrens 		 * them get the error.
12823cb34c60Sahrens 		 */
12833cb34c60Sahrens 		(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
12843cb34c60Sahrens 		    zhp->zfs_name, sdd->fromsnap);
12853cb34c60Sahrens 		if (ioctl(zhp->zfs_hdl->libzfs_fd,
12863cb34c60Sahrens 		    ZFS_IOC_OBJSET_STATS, &zc) != 0) {
12873cb34c60Sahrens 			missingfrom = B_TRUE;
12883cb34c60Sahrens 		}
12893cb34c60Sahrens 	}
12903cb34c60Sahrens 
12919e69d7d0SLori Alt 	sdd->seenfrom = sdd->seento = sdd->prevsnap[0] = 0;
1292a7f53a56SChris Kirby 	sdd->prevsnap_obj = 0;
12933cb34c60Sahrens 	if (sdd->fromsnap == NULL || missingfrom)
12943cb34c60Sahrens 		sdd->seenfrom = B_TRUE;
12953cb34c60Sahrens 
12963cb34c60Sahrens 	rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
12973cb34c60Sahrens 	if (!sdd->seenfrom) {
129819b94df9SMatthew Ahrens 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
12993cb34c60Sahrens 		    "WARNING: could not send %s@%s:\n"
130019b94df9SMatthew Ahrens 		    "incremental source (%s@%s) does not exist\n"),
13013cb34c60Sahrens 		    zhp->zfs_name, sdd->tosnap,
13023cb34c60Sahrens 		    zhp->zfs_name, sdd->fromsnap);
13033cb34c60Sahrens 		sdd->err = B_TRUE;
13043cb34c60Sahrens 	} else if (!sdd->seento) {
130514843421SMatthew Ahrens 		if (sdd->fromsnap) {
130619b94df9SMatthew Ahrens 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
13073cb34c60Sahrens 			    "WARNING: could not send %s@%s:\n"
13083cb34c60Sahrens 			    "incremental source (%s@%s) "
130919b94df9SMatthew Ahrens 			    "is not earlier than it\n"),
13103cb34c60Sahrens 			    zhp->zfs_name, sdd->tosnap,
13113cb34c60Sahrens 			    zhp->zfs_name, sdd->fromsnap);
131214843421SMatthew Ahrens 		} else {
131319b94df9SMatthew Ahrens 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
131419b94df9SMatthew Ahrens 			    "WARNING: "
131519b94df9SMatthew Ahrens 			    "could not send %s@%s: does not exist\n"),
131614843421SMatthew Ahrens 			    zhp->zfs_name, sdd->tosnap);
131714843421SMatthew Ahrens 		}
13183cb34c60Sahrens 		sdd->err = B_TRUE;
13193cb34c60Sahrens 	}
13203cb34c60Sahrens 
13213cb34c60Sahrens 	return (rv);
13223cb34c60Sahrens }
13233cb34c60Sahrens 
13243cb34c60Sahrens static int
dump_filesystems(zfs_handle_t * rzhp,void * arg)13253cb34c60Sahrens dump_filesystems(zfs_handle_t *rzhp, void *arg)
13263cb34c60Sahrens {
13273cb34c60Sahrens 	send_dump_data_t *sdd = arg;
13283cb34c60Sahrens 	nvpair_t *fspair;
13293cb34c60Sahrens 	boolean_t needagain, progress;
13303cb34c60Sahrens 
13313cb34c60Sahrens 	if (!sdd->replicate)
13323cb34c60Sahrens 		return (dump_filesystem(rzhp, sdd));
13333cb34c60Sahrens 
1334468db2f4STom Erickson 	/* Mark the clone origin snapshots. */
1335468db2f4STom Erickson 	for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
1336468db2f4STom Erickson 	    fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
1337468db2f4STom Erickson 		nvlist_t *nvfs;
1338468db2f4STom Erickson 		uint64_t origin_guid = 0;
1339468db2f4STom Erickson 
1340468db2f4STom Erickson 		VERIFY(0 == nvpair_value_nvlist(fspair, &nvfs));
1341468db2f4STom Erickson 		(void) nvlist_lookup_uint64(nvfs, "origin", &origin_guid);
1342468db2f4STom Erickson 		if (origin_guid != 0) {
1343468db2f4STom Erickson 			char *snapname;
1344468db2f4STom Erickson 			nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
1345468db2f4STom Erickson 			    origin_guid, &snapname);
1346468db2f4STom Erickson 			if (origin_nv != NULL) {
1347468db2f4STom Erickson 				nvlist_t *snapprops;
1348468db2f4STom Erickson 				VERIFY(0 == nvlist_lookup_nvlist(origin_nv,
1349468db2f4STom Erickson 				    "snapprops", &snapprops));
1350468db2f4STom Erickson 				VERIFY(0 == nvlist_lookup_nvlist(snapprops,
1351468db2f4STom Erickson 				    snapname, &snapprops));
1352468db2f4STom Erickson 				VERIFY(0 == nvlist_add_boolean(
1353468db2f4STom Erickson 				    snapprops, "is_clone_origin"));
1354468db2f4STom Erickson 			}
1355468db2f4STom Erickson 		}
1356468db2f4STom Erickson 	}
13573cb34c60Sahrens again:
13583cb34c60Sahrens 	needagain = progress = B_FALSE;
13593cb34c60Sahrens 	for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
13603cb34c60Sahrens 	    fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
136119b94df9SMatthew Ahrens 		nvlist_t *fslist, *parent_nv;
13623cb34c60Sahrens 		char *fsname;
13633cb34c60Sahrens 		zfs_handle_t *zhp;
13643cb34c60Sahrens 		int err;
13653cb34c60Sahrens 		uint64_t origin_guid = 0;
136619b94df9SMatthew Ahrens 		uint64_t parent_guid = 0;
13673cb34c60Sahrens 
13683cb34c60Sahrens 		VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
13693cb34c60Sahrens 		if (nvlist_lookup_boolean(fslist, "sent") == 0)
13703cb34c60Sahrens 			continue;
13713cb34c60Sahrens 
13723cb34c60Sahrens 		VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
13733cb34c60Sahrens 		(void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
137419b94df9SMatthew Ahrens 		(void) nvlist_lookup_uint64(fslist, "parentfromsnap",
137519b94df9SMatthew Ahrens 		    &parent_guid);
137619b94df9SMatthew Ahrens 
137719b94df9SMatthew Ahrens 		if (parent_guid != 0) {
137819b94df9SMatthew Ahrens 			parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
137919b94df9SMatthew Ahrens 			if (!nvlist_exists(parent_nv, "sent")) {
138019b94df9SMatthew Ahrens 				/* parent has not been sent; skip this one */
138119b94df9SMatthew Ahrens 				needagain = B_TRUE;
138219b94df9SMatthew Ahrens 				continue;
138319b94df9SMatthew Ahrens 			}
138419b94df9SMatthew Ahrens 		}
13853cb34c60Sahrens 
1386468db2f4STom Erickson 		if (origin_guid != 0) {
1387468db2f4STom Erickson 			nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
1388468db2f4STom Erickson 			    origin_guid, NULL);
1389468db2f4STom Erickson 			if (origin_nv != NULL &&
139019b94df9SMatthew Ahrens 			    !nvlist_exists(origin_nv, "sent")) {
13913cb34c60Sahrens 				/*
13923cb34c60Sahrens 				 * origin has not been sent yet;
13933cb34c60Sahrens 				 * skip this clone.
13943cb34c60Sahrens 				 */
13953cb34c60Sahrens 				needagain = B_TRUE;
13963cb34c60Sahrens 				continue;
13973cb34c60Sahrens 			}
1398468db2f4STom Erickson 		}
13993cb34c60Sahrens 
14003cb34c60Sahrens 		zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET);
140104bb726eSahl 		if (zhp == NULL)
140204bb726eSahl 			return (-1);
14033cb34c60Sahrens 		err = dump_filesystem(zhp, sdd);
14043cb34c60Sahrens 		VERIFY(nvlist_add_boolean(fslist, "sent") == 0);
14053cb34c60Sahrens 		progress = B_TRUE;
14063cb34c60Sahrens 		zfs_close(zhp);
14073cb34c60Sahrens 		if (err)
14083cb34c60Sahrens 			return (err);
14093cb34c60Sahrens 	}
14103cb34c60Sahrens 	if (needagain) {
14113cb34c60Sahrens 		assert(progress);
14123cb34c60Sahrens 		goto again;
14133cb34c60Sahrens 	}
141419b94df9SMatthew Ahrens 
141519b94df9SMatthew Ahrens 	/* clean out the sent flags in case we reuse this fss */
141619b94df9SMatthew Ahrens 	for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
141719b94df9SMatthew Ahrens 	    fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
141819b94df9SMatthew Ahrens 		nvlist_t *fslist;
141919b94df9SMatthew Ahrens 
142019b94df9SMatthew Ahrens 		VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
142119b94df9SMatthew Ahrens 		(void) nvlist_remove_all(fslist, "sent");
142219b94df9SMatthew Ahrens 	}
142319b94df9SMatthew Ahrens 
14243cb34c60Sahrens 	return (0);
14253cb34c60Sahrens }
14263cb34c60Sahrens 
14278429b235SMatthew Ahrens nvlist_t *
zfs_send_resume_token_to_nvlist(libzfs_handle_t * hdl,const char * token)14288429b235SMatthew Ahrens zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
14298429b235SMatthew Ahrens {
14308429b235SMatthew Ahrens 	unsigned int version;
14318429b235SMatthew Ahrens 	int nread;
14328429b235SMatthew Ahrens 	unsigned long long checksum, packed_len;
14338429b235SMatthew Ahrens 
14348429b235SMatthew Ahrens 	/*
14358429b235SMatthew Ahrens 	 * Decode token header, which is:
14368429b235SMatthew Ahrens 	 *   <token version>-<checksum of payload>-<uncompressed payload length>
14378429b235SMatthew Ahrens 	 * Note that the only supported token version is 1.
14388429b235SMatthew Ahrens 	 */
14398429b235SMatthew Ahrens 	nread = sscanf(token, "%u-%llx-%llx-",
14408429b235SMatthew Ahrens 	    &version, &checksum, &packed_len);
14418429b235SMatthew Ahrens 	if (nread != 3) {
14428429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14438429b235SMatthew Ahrens 		    "resume token is corrupt (invalid format)"));
14448429b235SMatthew Ahrens 		return (NULL);
14458429b235SMatthew Ahrens 	}
14468429b235SMatthew Ahrens 
14478429b235SMatthew Ahrens 	if (version != ZFS_SEND_RESUME_TOKEN_VERSION) {
14488429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14498429b235SMatthew Ahrens 		    "resume token is corrupt (invalid version %u)"),
14508429b235SMatthew Ahrens 		    version);
14518429b235SMatthew Ahrens 		return (NULL);
14528429b235SMatthew Ahrens 	}
14538429b235SMatthew Ahrens 
14548429b235SMatthew Ahrens 	/* convert hexadecimal representation to binary */
14558429b235SMatthew Ahrens 	token = strrchr(token, '-') + 1;
14568429b235SMatthew Ahrens 	int len = strlen(token) / 2;
14578429b235SMatthew Ahrens 	unsigned char *compressed = zfs_alloc(hdl, len);
14588429b235SMatthew Ahrens 	for (int i = 0; i < len; i++) {
14598429b235SMatthew Ahrens 		nread = sscanf(token + i * 2, "%2hhx", compressed + i);
14608429b235SMatthew Ahrens 		if (nread != 1) {
14618429b235SMatthew Ahrens 			free(compressed);
14628429b235SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14638429b235SMatthew Ahrens 			    "resume token is corrupt "
14648429b235SMatthew Ahrens 			    "(payload is not hex-encoded)"));
14658429b235SMatthew Ahrens 			return (NULL);
14668429b235SMatthew Ahrens 		}
14678429b235SMatthew Ahrens 	}
14688429b235SMatthew Ahrens 
14698429b235SMatthew Ahrens 	/* verify checksum */
14708429b235SMatthew Ahrens 	zio_cksum_t cksum;
14718429b235SMatthew Ahrens 	fletcher_4_native(compressed, len, &cksum);
14728429b235SMatthew Ahrens 	if (cksum.zc_word[0] != checksum) {
14738429b235SMatthew Ahrens 		free(compressed);
14748429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14758429b235SMatthew Ahrens 		    "resume token is corrupt (incorrect checksum)"));
14768429b235SMatthew Ahrens 		return (NULL);
14778429b235SMatthew Ahrens 	}
14788429b235SMatthew Ahrens 
14798429b235SMatthew Ahrens 	/* uncompress */
14808429b235SMatthew Ahrens 	void *packed = zfs_alloc(hdl, packed_len);
14818429b235SMatthew Ahrens 	uLongf packed_len_long = packed_len;
14828429b235SMatthew Ahrens 	if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK ||
14838429b235SMatthew Ahrens 	    packed_len_long != packed_len) {
14848429b235SMatthew Ahrens 		free(packed);
14858429b235SMatthew Ahrens 		free(compressed);
14868429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14878429b235SMatthew Ahrens 		    "resume token is corrupt (decompression failed)"));
14888429b235SMatthew Ahrens 		return (NULL);
14898429b235SMatthew Ahrens 	}
14908429b235SMatthew Ahrens 
14918429b235SMatthew Ahrens 	/* unpack nvlist */
14928429b235SMatthew Ahrens 	nvlist_t *nv;
14938429b235SMatthew Ahrens 	int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP);
14948429b235SMatthew Ahrens 	free(packed);
14958429b235SMatthew Ahrens 	free(compressed);
14968429b235SMatthew Ahrens 	if (error != 0) {
14978429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14988429b235SMatthew Ahrens 		    "resume token is corrupt (nvlist_unpack failed)"));
14998429b235SMatthew Ahrens 		return (NULL);
15008429b235SMatthew Ahrens 	}
15018429b235SMatthew Ahrens 	return (nv);
15028429b235SMatthew Ahrens }
15038429b235SMatthew Ahrens 
15048429b235SMatthew Ahrens int
zfs_send_resume(libzfs_handle_t * hdl,sendflags_t * flags,int outfd,const char * resume_token)15058429b235SMatthew Ahrens zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
15068429b235SMatthew Ahrens     const char *resume_token)
15078429b235SMatthew Ahrens {
15088429b235SMatthew Ahrens 	char errbuf[1024];
15098429b235SMatthew Ahrens 	char *toname;
15108429b235SMatthew Ahrens 	char *fromname = NULL;
15118429b235SMatthew Ahrens 	uint64_t resumeobj, resumeoff, toguid, fromguid, bytes;
15128429b235SMatthew Ahrens 	zfs_handle_t *zhp;
15138429b235SMatthew Ahrens 	int error = 0;
1514*675fc291SMatthew Ahrens 	char name[ZFS_MAX_DATASET_NAME_LEN];
15158429b235SMatthew Ahrens 	enum lzc_send_flags lzc_flags = 0;
15168429b235SMatthew Ahrens 
15178429b235SMatthew Ahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
15188429b235SMatthew Ahrens 	    "cannot resume send"));
15198429b235SMatthew Ahrens 
15208429b235SMatthew Ahrens 	nvlist_t *resume_nvl =
15218429b235SMatthew Ahrens 	    zfs_send_resume_token_to_nvlist(hdl, resume_token);
15228429b235SMatthew Ahrens 	if (resume_nvl == NULL) {
15238429b235SMatthew Ahrens 		/*
15248429b235SMatthew Ahrens 		 * zfs_error_aux has already been set by
15258429b235SMatthew Ahrens 		 * zfs_send_resume_token_to_nvlist
15268429b235SMatthew Ahrens 		 */
15278429b235SMatthew Ahrens 		return (zfs_error(hdl, EZFS_FAULT, errbuf));
15288429b235SMatthew Ahrens 	}
15298429b235SMatthew Ahrens 	if (flags->verbose) {
15308429b235SMatthew Ahrens 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
15318429b235SMatthew Ahrens 		    "resume token contents:\n"));
15328429b235SMatthew Ahrens 		nvlist_print(stderr, resume_nvl);
15338429b235SMatthew Ahrens 	}
15348429b235SMatthew Ahrens 
15358429b235SMatthew Ahrens 	if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
15368429b235SMatthew Ahrens 	    nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 ||
15378429b235SMatthew Ahrens 	    nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 ||
15388429b235SMatthew Ahrens 	    nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 ||
15398429b235SMatthew Ahrens 	    nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) {
15408429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15418429b235SMatthew Ahrens 		    "resume token is corrupt"));
15428429b235SMatthew Ahrens 		return (zfs_error(hdl, EZFS_FAULT, errbuf));
15438429b235SMatthew Ahrens 	}
15448429b235SMatthew Ahrens 	fromguid = 0;
15458429b235SMatthew Ahrens 	(void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid);
15468429b235SMatthew Ahrens 
15478429b235SMatthew Ahrens 	if (flags->embed_data || nvlist_exists(resume_nvl, "embedok"))
15488429b235SMatthew Ahrens 		lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
15498429b235SMatthew Ahrens 
15508429b235SMatthew Ahrens 	if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) {
15518429b235SMatthew Ahrens 		if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) {
15528429b235SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15538429b235SMatthew Ahrens 			    "'%s' is no longer the same snapshot used in "
15548429b235SMatthew Ahrens 			    "the initial send"), toname);
15558429b235SMatthew Ahrens 		} else {
15568429b235SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15578429b235SMatthew Ahrens 			    "'%s' used in the initial send no longer exists"),
15588429b235SMatthew Ahrens 			    toname);
15598429b235SMatthew Ahrens 		}
15608429b235SMatthew Ahrens 		return (zfs_error(hdl, EZFS_BADPATH, errbuf));
15618429b235SMatthew Ahrens 	}
15628429b235SMatthew Ahrens 	zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
15638429b235SMatthew Ahrens 	if (zhp == NULL) {
15648429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15658429b235SMatthew Ahrens 		    "unable to access '%s'"), name);
15668429b235SMatthew Ahrens 		return (zfs_error(hdl, EZFS_BADPATH, errbuf));
15678429b235SMatthew Ahrens 	}
15688429b235SMatthew Ahrens 
15698429b235SMatthew Ahrens 	if (fromguid != 0) {
15708429b235SMatthew Ahrens 		if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) {
15718429b235SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15728429b235SMatthew Ahrens 			    "incremental source %#llx no longer exists"),
15738429b235SMatthew Ahrens 			    (longlong_t)fromguid);
15748429b235SMatthew Ahrens 			return (zfs_error(hdl, EZFS_BADPATH, errbuf));
15758429b235SMatthew Ahrens 		}
15768429b235SMatthew Ahrens 		fromname = name;
15778429b235SMatthew Ahrens 	}
15788429b235SMatthew Ahrens 
15798429b235SMatthew Ahrens 	if (flags->verbose) {
15808429b235SMatthew Ahrens 		uint64_t size = 0;
15818429b235SMatthew Ahrens 		error = lzc_send_space(zhp->zfs_name, fromname, &size);
15828429b235SMatthew Ahrens 		if (error == 0)
15838429b235SMatthew Ahrens 			size = MAX(0, (int64_t)(size - bytes));
15848429b235SMatthew Ahrens 		send_print_verbose(stderr, zhp->zfs_name, fromname,
15858429b235SMatthew Ahrens 		    size, flags->parsable);
15868429b235SMatthew Ahrens 	}
15878429b235SMatthew Ahrens 
15888429b235SMatthew Ahrens 	if (!flags->dryrun) {
15898429b235SMatthew Ahrens 		progress_arg_t pa = { 0 };
15908429b235SMatthew Ahrens 		pthread_t tid;
15918429b235SMatthew Ahrens 		/*
15928429b235SMatthew Ahrens 		 * If progress reporting is requested, spawn a new thread to
15938429b235SMatthew Ahrens 		 * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
15948429b235SMatthew Ahrens 		 */
15958429b235SMatthew Ahrens 		if (flags->progress) {
15968429b235SMatthew Ahrens 			pa.pa_zhp = zhp;
15978429b235SMatthew Ahrens 			pa.pa_fd = outfd;
15988429b235SMatthew Ahrens 			pa.pa_parsable = flags->parsable;
15998429b235SMatthew Ahrens 
16008429b235SMatthew Ahrens 			error = pthread_create(&tid, NULL,
16018429b235SMatthew Ahrens 			    send_progress_thread, &pa);
16028429b235SMatthew Ahrens 			if (error != 0) {
16038429b235SMatthew Ahrens 				zfs_close(zhp);
16048429b235SMatthew Ahrens 				return (error);
16058429b235SMatthew Ahrens 			}
16068429b235SMatthew Ahrens 		}
16078429b235SMatthew Ahrens 
16088429b235SMatthew Ahrens 		error = lzc_send_resume(zhp->zfs_name, fromname, outfd,
16098429b235SMatthew Ahrens 		    lzc_flags, resumeobj, resumeoff);
16108429b235SMatthew Ahrens 
16118429b235SMatthew Ahrens 		if (flags->progress) {
16128429b235SMatthew Ahrens 			(void) pthread_cancel(tid);
16138429b235SMatthew Ahrens 			(void) pthread_join(tid, NULL);
16148429b235SMatthew Ahrens 		}
16158429b235SMatthew Ahrens 
16168429b235SMatthew Ahrens 		char errbuf[1024];
16178429b235SMatthew Ahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
16188429b235SMatthew Ahrens 		    "warning: cannot send '%s'"), zhp->zfs_name);
16198429b235SMatthew Ahrens 
16208429b235SMatthew Ahrens 		zfs_close(zhp);
16218429b235SMatthew Ahrens 
16228429b235SMatthew Ahrens 		switch (error) {
16238429b235SMatthew Ahrens 		case 0:
16248429b235SMatthew Ahrens 			return (0);
16258429b235SMatthew Ahrens 		case EXDEV:
16268429b235SMatthew Ahrens 		case ENOENT:
16278429b235SMatthew Ahrens 		case EDQUOT:
16288429b235SMatthew Ahrens 		case EFBIG:
16298429b235SMatthew Ahrens 		case EIO:
16308429b235SMatthew Ahrens 		case ENOLINK:
16318429b235SMatthew Ahrens 		case ENOSPC:
16328429b235SMatthew Ahrens 		case ENOSTR:
16338429b235SMatthew Ahrens 		case ENXIO:
16348429b235SMatthew Ahrens 		case EPIPE:
16358429b235SMatthew Ahrens 		case ERANGE:
16368429b235SMatthew Ahrens 		case EFAULT:
16378429b235SMatthew Ahrens 		case EROFS:
16388429b235SMatthew Ahrens 			zfs_error_aux(hdl, strerror(errno));
16398429b235SMatthew Ahrens 			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
16408429b235SMatthew Ahrens 
16418429b235SMatthew Ahrens 		default:
16428429b235SMatthew Ahrens 			return (zfs_standard_error(hdl, errno, errbuf));
16438429b235SMatthew Ahrens 		}
16448429b235SMatthew Ahrens 	}
16458429b235SMatthew Ahrens 
16468429b235SMatthew Ahrens 
16478429b235SMatthew Ahrens 	zfs_close(zhp);
16488429b235SMatthew Ahrens 
16498429b235SMatthew Ahrens 	return (error);
16508429b235SMatthew Ahrens }
16518429b235SMatthew Ahrens 
16523cb34c60Sahrens /*
165388bb18d2SLori Alt  * Generate a send stream for the dataset identified by the argument zhp.
165488bb18d2SLori Alt  *
165588bb18d2SLori Alt  * The content of the send stream is the snapshot identified by
165688bb18d2SLori Alt  * 'tosnap'.  Incremental streams are requested in two ways:
165788bb18d2SLori Alt  *     - from the snapshot identified by "fromsnap" (if non-null) or
165888bb18d2SLori Alt  *     - from the origin of the dataset identified by zhp, which must
165988bb18d2SLori Alt  *	 be a clone.  In this case, "fromsnap" is null and "fromorigin"
166088bb18d2SLori Alt  *	 is TRUE.
166188bb18d2SLori Alt  *
166288bb18d2SLori Alt  * The send stream is recursive (i.e. dumps a hierarchy of snapshots) and
16639e69d7d0SLori Alt  * uses a special header (with a hdrtype field of DMU_COMPOUNDSTREAM)
166488bb18d2SLori Alt  * if "replicate" is set.  If "doall" is set, dump all the intermediate
16659e69d7d0SLori Alt  * snapshots. The DMU_COMPOUNDSTREAM header is used in the "doall"
166692241e0bSTom Erickson  * case too. If "props" is set, send properties.
16673cb34c60Sahrens  */
16683cb34c60Sahrens int
zfs_send(zfs_handle_t * zhp,const char * fromsnap,const char * tosnap,sendflags_t * flags,int outfd,snapfilter_cb_t filter_func,void * cb_arg,nvlist_t ** debugnvp)16693cb34c60Sahrens zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
167019b94df9SMatthew Ahrens     sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
16713f9d6ad7SLin Ling     void *cb_arg, nvlist_t **debugnvp)
16723cb34c60Sahrens {
16733cb34c60Sahrens 	char errbuf[1024];
16743cb34c60Sahrens 	send_dump_data_t sdd = { 0 };
167519b94df9SMatthew Ahrens 	int err = 0;
16763cb34c60Sahrens 	nvlist_t *fss = NULL;
16773cb34c60Sahrens 	avl_tree_t *fsavl = NULL;
1678ca45db41SChris Kirby 	static uint64_t holdseq;
167954809200SChris Kirby 	int spa_version;
1680a7a845e4SSteven Hartland 	pthread_t tid = 0;
16819e69d7d0SLori Alt 	int pipefd[2];
16829e69d7d0SLori Alt 	dedup_arg_t dda = { 0 };
16839e69d7d0SLori Alt 	int featureflags = 0;
1684e00f47baSManoj Joseph 	FILE *fout;
16850a586ceaSMark Shellenbaum 
16863cb34c60Sahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
16873cb34c60Sahrens 	    "cannot send '%s'"), zhp->zfs_name);
16883cb34c60Sahrens 
16893cb34c60Sahrens 	if (fromsnap && fromsnap[0] == '\0') {
16903cb34c60Sahrens 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
16913cb34c60Sahrens 		    "zero-length incremental source"));
16923cb34c60Sahrens 		return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
16933cb34c60Sahrens 	}
16943cb34c60Sahrens 
1695a7f53a56SChris Kirby 	if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM) {
1696a7f53a56SChris Kirby 		uint64_t version;
1697a7f53a56SChris Kirby 		version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
1698a7f53a56SChris Kirby 		if (version >= ZPL_VERSION_SA) {
1699a7f53a56SChris Kirby 			featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
1700a7f53a56SChris Kirby 		}
1701a7f53a56SChris Kirby 	}
1702a7f53a56SChris Kirby 
170319b94df9SMatthew Ahrens 	if (flags->dedup && !flags->dryrun) {
17048e714474SLori Alt 		featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
17058e714474SLori Alt 		    DMU_BACKUP_FEATURE_DEDUPPROPS);
17069e69d7d0SLori Alt 		if (err = pipe(pipefd)) {
17079e69d7d0SLori Alt 			zfs_error_aux(zhp->zfs_hdl, strerror(errno));
17089e69d7d0SLori Alt 			return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED,
17099e69d7d0SLori Alt 			    errbuf));
17109e69d7d0SLori Alt 		}
17119e69d7d0SLori Alt 		dda.outputfd = outfd;
17129e69d7d0SLori Alt 		dda.inputfd = pipefd[1];
17139e69d7d0SLori Alt 		dda.dedup_hdl = zhp->zfs_hdl;
17149e69d7d0SLori Alt 		if (err = pthread_create(&tid, NULL, cksummer, &dda)) {
17159e69d7d0SLori Alt 			(void) close(pipefd[0]);
17169e69d7d0SLori Alt 			(void) close(pipefd[1]);
17179e69d7d0SLori Alt 			zfs_error_aux(zhp->zfs_hdl, strerror(errno));
17189e69d7d0SLori Alt 			return (zfs_error(zhp->zfs_hdl,
17199e69d7d0SLori Alt 			    EZFS_THREADCREATEFAILED, errbuf));
17209e69d7d0SLori Alt 		}
17219e69d7d0SLori Alt 	}
17229e69d7d0SLori Alt 
172319b94df9SMatthew Ahrens 	if (flags->replicate || flags->doall || flags->props) {
17243cb34c60Sahrens 		dmu_replay_record_t drr = { 0 };
17253cb34c60Sahrens 		char *packbuf = NULL;
17263cb34c60Sahrens 		size_t buflen = 0;
17273cb34c60Sahrens 		zio_cksum_t zc = { 0 };
17283cb34c60Sahrens 
172919b94df9SMatthew Ahrens 		if (flags->replicate || flags->props) {
17303cb34c60Sahrens 			nvlist_t *hdrnv;
17313cb34c60Sahrens 
17323cb34c60Sahrens 			VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
17333cb34c60Sahrens 			if (fromsnap) {
17343cb34c60Sahrens 				VERIFY(0 == nvlist_add_string(hdrnv,
17353cb34c60Sahrens 				    "fromsnap", fromsnap));
17363cb34c60Sahrens 			}
17373cb34c60Sahrens 			VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
173819b94df9SMatthew Ahrens 			if (!flags->replicate) {
173992241e0bSTom Erickson 				VERIFY(0 == nvlist_add_boolean(hdrnv,
174092241e0bSTom Erickson 				    "not_recursive"));
174192241e0bSTom Erickson 			}
17423cb34c60Sahrens 
17433cb34c60Sahrens 			err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
174419b94df9SMatthew Ahrens 			    fromsnap, tosnap, flags->replicate, &fss, &fsavl);
1745c99e4bdcSChris Kirby 			if (err)
17469e69d7d0SLori Alt 				goto err_out;
17473cb34c60Sahrens 			VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
17483cb34c60Sahrens 			err = nvlist_pack(hdrnv, &packbuf, &buflen,
17493cb34c60Sahrens 			    NV_ENCODE_XDR, 0);
17503f9d6ad7SLin Ling 			if (debugnvp)
17513f9d6ad7SLin Ling 				*debugnvp = hdrnv;
17523f9d6ad7SLin Ling 			else
17533cb34c60Sahrens 				nvlist_free(hdrnv);
1754a7a845e4SSteven Hartland 			if (err)
17559e69d7d0SLori Alt 				goto stderr_out;
17563cb34c60Sahrens 		}
17573cb34c60Sahrens 
175819b94df9SMatthew Ahrens 		if (!flags->dryrun) {
17593cb34c60Sahrens 			/* write first begin record */
17603cb34c60Sahrens 			drr.drr_type = DRR_BEGIN;
17613cb34c60Sahrens 			drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
176219b94df9SMatthew Ahrens 			DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
176319b94df9SMatthew Ahrens 			    drr_versioninfo, DMU_COMPOUNDSTREAM);
176419b94df9SMatthew Ahrens 			DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
176519b94df9SMatthew Ahrens 			    drr_versioninfo, featureflags);
17663cb34c60Sahrens 			(void) snprintf(drr.drr_u.drr_begin.drr_toname,
17673cb34c60Sahrens 			    sizeof (drr.drr_u.drr_begin.drr_toname),
17683cb34c60Sahrens 			    "%s@%s", zhp->zfs_name, tosnap);
17693cb34c60Sahrens 			drr.drr_payloadlen = buflen;
17703cb34c60Sahrens 
177162e66153SMatthew Ahrens 			err = dump_record(&drr, packbuf, buflen, &zc, outfd);
17723cb34c60Sahrens 			free(packbuf);
177362e66153SMatthew Ahrens 			if (err != 0)
17749e69d7d0SLori Alt 				goto stderr_out;
17753cb34c60Sahrens 
17763cb34c60Sahrens 			/* write end record */
17773cb34c60Sahrens 			bzero(&drr, sizeof (drr));
17783cb34c60Sahrens 			drr.drr_type = DRR_END;
17793cb34c60Sahrens 			drr.drr_u.drr_end.drr_checksum = zc;
17803cb34c60Sahrens 			err = write(outfd, &drr, sizeof (drr));
17813cb34c60Sahrens 			if (err == -1) {
1782c6fd73aeSChris Kirby 				err = errno;
17839e69d7d0SLori Alt 				goto stderr_out;
17843cb34c60Sahrens 			}
178519b94df9SMatthew Ahrens 
178619b94df9SMatthew Ahrens 			err = 0;
17873cb34c60Sahrens 		}
17883cb34c60Sahrens 	}
17893cb34c60Sahrens 
17903cb34c60Sahrens 	/* dump each stream */
17913cb34c60Sahrens 	sdd.fromsnap = fromsnap;
17923cb34c60Sahrens 	sdd.tosnap = tosnap;
1793a7a845e4SSteven Hartland 	if (tid != 0)
17949e69d7d0SLori Alt 		sdd.outfd = pipefd[0];
17959e69d7d0SLori Alt 	else
17963cb34c60Sahrens 		sdd.outfd = outfd;
179719b94df9SMatthew Ahrens 	sdd.replicate = flags->replicate;
179819b94df9SMatthew Ahrens 	sdd.doall = flags->doall;
179919b94df9SMatthew Ahrens 	sdd.fromorigin = flags->fromorigin;
18003cb34c60Sahrens 	sdd.fss = fss;
18013cb34c60Sahrens 	sdd.fsavl = fsavl;
180219b94df9SMatthew Ahrens 	sdd.verbose = flags->verbose;
180319b94df9SMatthew Ahrens 	sdd.parsable = flags->parsable;
18044e3c9f44SBill Pijewski 	sdd.progress = flags->progress;
180519b94df9SMatthew Ahrens 	sdd.dryrun = flags->dryrun;
1806d1a98260SMatthew Ahrens 	sdd.large_block = flags->largeblock;
1807e54e0be9SMatthew Ahrens 	sdd.embed_data = flags->embed_data;
18089e69d7d0SLori Alt 	sdd.filter_cb = filter_func;
18099e69d7d0SLori Alt 	sdd.filter_cb_arg = cb_arg;
18103f9d6ad7SLin Ling 	if (debugnvp)
18113f9d6ad7SLin Ling 		sdd.debugnv = *debugnvp;
1812e00f47baSManoj Joseph 	if (sdd.verbose && sdd.dryrun)
1813e00f47baSManoj Joseph 		sdd.std_out = B_TRUE;
1814e00f47baSManoj Joseph 	fout = sdd.std_out ? stdout : stderr;
181565fec9f6SChristopher Siden 
181665fec9f6SChristopher Siden 	/*
181765fec9f6SChristopher Siden 	 * Some flags require that we place user holds on the datasets that are
181865fec9f6SChristopher Siden 	 * being sent so they don't get destroyed during the send. We can skip
181965fec9f6SChristopher Siden 	 * this step if the pool is imported read-only since the datasets cannot
182065fec9f6SChristopher Siden 	 * be destroyed.
182165fec9f6SChristopher Siden 	 */
182265fec9f6SChristopher Siden 	if (!flags->dryrun && !zpool_get_prop_int(zfs_get_pool_handle(zhp),
182365fec9f6SChristopher Siden 	    ZPOOL_PROP_READONLY, NULL) &&
182465fec9f6SChristopher Siden 	    zfs_spa_version(zhp, &spa_version) == 0 &&
182565fec9f6SChristopher Siden 	    spa_version >= SPA_VERSION_USERREFS &&
182665fec9f6SChristopher Siden 	    (flags->doall || flags->replicate)) {
1827a7f53a56SChris Kirby 		++holdseq;
1828a7f53a56SChris Kirby 		(void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
1829a7f53a56SChris Kirby 		    ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
1830a7f53a56SChris Kirby 		sdd.cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
1831a7f53a56SChris Kirby 		if (sdd.cleanup_fd < 0) {
1832a7f53a56SChris Kirby 			err = errno;
1833a7f53a56SChris Kirby 			goto stderr_out;
1834a7f53a56SChris Kirby 		}
1835a7a845e4SSteven Hartland 		sdd.snapholds = fnvlist_alloc();
1836a7f53a56SChris Kirby 	} else {
1837a7f53a56SChris Kirby 		sdd.cleanup_fd = -1;
1838a7a845e4SSteven Hartland 		sdd.snapholds = NULL;
1839a7f53a56SChris Kirby 	}
1840a7a845e4SSteven Hartland 	if (flags->verbose || sdd.snapholds != NULL) {
184119b94df9SMatthew Ahrens 		/*
184219b94df9SMatthew Ahrens 		 * Do a verbose no-op dry run to get all the verbose output
1843a7a845e4SSteven Hartland 		 * or to gather snapshot hold's before generating any data,
1844a7a845e4SSteven Hartland 		 * then do a non-verbose real run to generate the streams.
184519b94df9SMatthew Ahrens 		 */
184619b94df9SMatthew Ahrens 		sdd.dryrun = B_TRUE;
184719b94df9SMatthew Ahrens 		err = dump_filesystems(zhp, &sdd);
1848a7a845e4SSteven Hartland 
1849a7a845e4SSteven Hartland 		if (err != 0)
1850a7a845e4SSteven Hartland 			goto stderr_out;
1851a7a845e4SSteven Hartland 
1852a7a845e4SSteven Hartland 		if (flags->verbose) {
185319b94df9SMatthew Ahrens 			if (flags->parsable) {
1854e00f47baSManoj Joseph 				(void) fprintf(fout, "size\t%llu\n",
185519b94df9SMatthew Ahrens 				    (longlong_t)sdd.size);
185619b94df9SMatthew Ahrens 			} else {
185719b94df9SMatthew Ahrens 				char buf[16];
185819b94df9SMatthew Ahrens 				zfs_nicenum(sdd.size, buf, sizeof (buf));
1859e00f47baSManoj Joseph 				(void) fprintf(fout, dgettext(TEXT_DOMAIN,
186019b94df9SMatthew Ahrens 				    "total estimated size is %s\n"), buf);
186119b94df9SMatthew Ahrens 			}
186219b94df9SMatthew Ahrens 		}
1863a7a845e4SSteven Hartland 
1864a7a845e4SSteven Hartland 		/* Ensure no snaps found is treated as an error. */
1865a7a845e4SSteven Hartland 		if (!sdd.seento) {
1866a7a845e4SSteven Hartland 			err = ENOENT;
1867a7a845e4SSteven Hartland 			goto err_out;
1868a7a845e4SSteven Hartland 		}
1869a7a845e4SSteven Hartland 
1870a7a845e4SSteven Hartland 		/* Skip the second run if dryrun was requested. */
1871a7a845e4SSteven Hartland 		if (flags->dryrun)
1872a7a845e4SSteven Hartland 			goto err_out;
1873a7a845e4SSteven Hartland 
1874a7a845e4SSteven Hartland 		if (sdd.snapholds != NULL) {
1875a7a845e4SSteven Hartland 			err = zfs_hold_nvl(zhp, sdd.cleanup_fd, sdd.snapholds);
1876a7a845e4SSteven Hartland 			if (err != 0)
1877a7a845e4SSteven Hartland 				goto stderr_out;
1878a7a845e4SSteven Hartland 
1879a7a845e4SSteven Hartland 			fnvlist_free(sdd.snapholds);
1880a7a845e4SSteven Hartland 			sdd.snapholds = NULL;
1881a7a845e4SSteven Hartland 		}
1882a7a845e4SSteven Hartland 
1883a7a845e4SSteven Hartland 		sdd.dryrun = B_FALSE;
1884a7a845e4SSteven Hartland 		sdd.verbose = B_FALSE;
1885a7a845e4SSteven Hartland 	}
1886a7a845e4SSteven Hartland 
18873cb34c60Sahrens 	err = dump_filesystems(zhp, &sdd);
18883cb34c60Sahrens 	fsavl_destroy(fsavl);
18893cb34c60Sahrens 	nvlist_free(fss);
18903cb34c60Sahrens 
1891a7a845e4SSteven Hartland 	/* Ensure no snaps found is treated as an error. */
1892a7a845e4SSteven Hartland 	if (err == 0 && !sdd.seento)
1893a7a845e4SSteven Hartland 		err = ENOENT;
1894a7a845e4SSteven Hartland 
1895a7a845e4SSteven Hartland 	if (tid != 0) {
1896a7a845e4SSteven Hartland 		if (err != 0)
1897a7a845e4SSteven Hartland 			(void) pthread_cancel(tid);
1898a7a845e4SSteven Hartland 		(void) close(pipefd[0]);
189936f7455dSSteven Hartland 		(void) pthread_join(tid, NULL);
19009e69d7d0SLori Alt 	}
190192241e0bSTom Erickson 
1902a7f53a56SChris Kirby 	if (sdd.cleanup_fd != -1) {
1903a7f53a56SChris Kirby 		VERIFY(0 == close(sdd.cleanup_fd));
1904a7f53a56SChris Kirby 		sdd.cleanup_fd = -1;
1905c99e4bdcSChris Kirby 	}
1906c99e4bdcSChris Kirby 
190719b94df9SMatthew Ahrens 	if (!flags->dryrun && (flags->replicate || flags->doall ||
190819b94df9SMatthew Ahrens 	    flags->props)) {
19093cb34c60Sahrens 		/*
19103cb34c60Sahrens 		 * write final end record.  NB: want to do this even if
19113cb34c60Sahrens 		 * there was some error, because it might not be totally
19123cb34c60Sahrens 		 * failed.
19133cb34c60Sahrens 		 */
19143cb34c60Sahrens 		dmu_replay_record_t drr = { 0 };
19153cb34c60Sahrens 		drr.drr_type = DRR_END;
19163cb34c60Sahrens 		if (write(outfd, &drr, sizeof (drr)) == -1) {
19173cb34c60Sahrens 			return (zfs_standard_error(zhp->zfs_hdl,
19183cb34c60Sahrens 			    errno, errbuf));
19193cb34c60Sahrens 		}
19203cb34c60Sahrens 	}
19213cb34c60Sahrens 
19223cb34c60Sahrens 	return (err || sdd.err);
19239e69d7d0SLori Alt 
19249e69d7d0SLori Alt stderr_out:
19259e69d7d0SLori Alt 	err = zfs_standard_error(zhp->zfs_hdl, err, errbuf);
19269e69d7d0SLori Alt err_out:
1927a7a845e4SSteven Hartland 	fsavl_destroy(fsavl);
1928a7a845e4SSteven Hartland 	nvlist_free(fss);
1929a7a845e4SSteven Hartland 	fnvlist_free(sdd.snapholds);
1930a7a845e4SSteven Hartland 
1931a7f53a56SChris Kirby 	if (sdd.cleanup_fd != -1)
1932a7f53a56SChris Kirby 		VERIFY(0 == close(sdd.cleanup_fd));
1933a7a845e4SSteven Hartland 	if (tid != 0) {
19349e69d7d0SLori Alt 		(void) pthread_cancel(tid);
19359e69d7d0SLori Alt 		(void) close(pipefd[0]);
193636f7455dSSteven Hartland 		(void) pthread_join(tid, NULL);
19379e69d7d0SLori Alt 	}
19389e69d7d0SLori Alt 	return (err);
19393cb34c60Sahrens }
19403cb34c60Sahrens 
1941e69d9941SMatthew Ahrens int
zfs_send_one(zfs_handle_t * zhp,const char * from,int fd,enum lzc_send_flags flags)1942e54e0be9SMatthew Ahrens zfs_send_one(zfs_handle_t *zhp, const char *from, int fd,
1943e54e0be9SMatthew Ahrens     enum lzc_send_flags flags)
1944e69d9941SMatthew Ahrens {
1945e69d9941SMatthew Ahrens 	int err;
1946e69d9941SMatthew Ahrens 	libzfs_handle_t *hdl = zhp->zfs_hdl;
1947e69d9941SMatthew Ahrens 
1948e69d9941SMatthew Ahrens 	char errbuf[1024];
1949e69d9941SMatthew Ahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
1950e69d9941SMatthew Ahrens 	    "warning: cannot send '%s'"), zhp->zfs_name);
1951e69d9941SMatthew Ahrens 
1952e54e0be9SMatthew Ahrens 	err = lzc_send(zhp->zfs_name, from, fd, flags);
1953e69d9941SMatthew Ahrens 	if (err != 0) {
1954e69d9941SMatthew Ahrens 		switch (errno) {
1955e69d9941SMatthew Ahrens 		case EXDEV:
1956e69d9941SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1957e69d9941SMatthew Ahrens 			    "not an earlier snapshot from the same fs"));
1958e69d9941SMatthew Ahrens 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
1959e69d9941SMatthew Ahrens 
1960e69d9941SMatthew Ahrens 		case ENOENT:
1961e69d9941SMatthew Ahrens 		case ESRCH:
1962e69d9941SMatthew Ahrens 			if (lzc_exists(zhp->zfs_name)) {
1963e69d9941SMatthew Ahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1964e69d9941SMatthew Ahrens 				    "incremental source (%s) does not exist"),
1965e69d9941SMatthew Ahrens 				    from);
1966e69d9941SMatthew Ahrens 			}
1967e69d9941SMatthew Ahrens 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
1968e69d9941SMatthew Ahrens 
1969e69d9941SMatthew Ahrens 		case EBUSY:
1970e69d9941SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1971e69d9941SMatthew Ahrens 			    "target is busy; if a filesystem, "
1972e69d9941SMatthew Ahrens 			    "it must not be mounted"));
1973e69d9941SMatthew Ahrens 			return (zfs_error(hdl, EZFS_BUSY, errbuf));
1974e69d9941SMatthew Ahrens 
1975e69d9941SMatthew Ahrens 		case EDQUOT:
1976e69d9941SMatthew Ahrens 		case EFBIG:
1977e69d9941SMatthew Ahrens 		case EIO:
1978e69d9941SMatthew Ahrens 		case ENOLINK:
1979e69d9941SMatthew Ahrens 		case ENOSPC:
1980e69d9941SMatthew Ahrens 		case ENOSTR:
1981e69d9941SMatthew Ahrens 		case ENXIO:
1982e69d9941SMatthew Ahrens 		case EPIPE:
1983e69d9941SMatthew Ahrens 		case ERANGE:
1984e69d9941SMatthew Ahrens 		case EFAULT:
1985e69d9941SMatthew Ahrens 		case EROFS:
1986e69d9941SMatthew Ahrens 			zfs_error_aux(hdl, strerror(errno));
1987e69d9941SMatthew Ahrens 			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
1988e69d9941SMatthew Ahrens 
1989e69d9941SMatthew Ahrens 		default:
1990e69d9941SMatthew Ahrens 			return (zfs_standard_error(hdl, errno, errbuf));
1991e69d9941SMatthew Ahrens 		}
1992e69d9941SMatthew Ahrens 	}
1993e69d9941SMatthew Ahrens 	return (err != 0);
1994e69d9941SMatthew Ahrens }
1995e69d9941SMatthew Ahrens 
19963cb34c60Sahrens /*
19973cb34c60Sahrens  * Routines specific to "zfs recv"
19983cb34c60Sahrens  */
19993cb34c60Sahrens 
20003cb34c60Sahrens static int
recv_read(libzfs_handle_t * hdl,int fd,void * buf,int ilen,boolean_t byteswap,zio_cksum_t * zc)20013cb34c60Sahrens recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
20023cb34c60Sahrens     boolean_t byteswap, zio_cksum_t *zc)
20033cb34c60Sahrens {
20043cb34c60Sahrens 	char *cp = buf;
20053cb34c60Sahrens 	int rv;
20063cb34c60Sahrens 	int len = ilen;
20073cb34c60Sahrens 
200862e66153SMatthew Ahrens 	assert(ilen <= SPA_MAXBLOCKSIZE);
200962e66153SMatthew Ahrens 
20103cb34c60Sahrens 	do {
20113cb34c60Sahrens 		rv = read(fd, cp, len);
20123cb34c60Sahrens 		cp += rv;
20133cb34c60Sahrens 		len -= rv;
20143cb34c60Sahrens 	} while (rv > 0);
20153cb34c60Sahrens 
20163cb34c60Sahrens 	if (rv < 0 || len != 0) {
20173cb34c60Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
20183cb34c60Sahrens 		    "failed to read from stream"));
20193cb34c60Sahrens 		return (zfs_error(hdl, EZFS_BADSTREAM, dgettext(TEXT_DOMAIN,
20203cb34c60Sahrens 		    "cannot receive")));
20213cb34c60Sahrens 	}
20223cb34c60Sahrens 
20233cb34c60Sahrens 	if (zc) {
20243cb34c60Sahrens 		if (byteswap)
20253cb34c60Sahrens 			fletcher_4_incremental_byteswap(buf, ilen, zc);
20263cb34c60Sahrens 		else
20273cb34c60Sahrens 			fletcher_4_incremental_native(buf, ilen, zc);
20283cb34c60Sahrens 	}
20293cb34c60Sahrens 	return (0);
20303cb34c60Sahrens }
20313cb34c60Sahrens 
20323cb34c60Sahrens static int
recv_read_nvlist(libzfs_handle_t * hdl,int fd,int len,nvlist_t ** nvp,boolean_t byteswap,zio_cksum_t * zc)20333cb34c60Sahrens recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
20343cb34c60Sahrens     boolean_t byteswap, zio_cksum_t *zc)
20353cb34c60Sahrens {
20363cb34c60Sahrens 	char *buf;
20373cb34c60Sahrens 	int err;
20383cb34c60Sahrens 
20393cb34c60Sahrens 	buf = zfs_alloc(hdl, len);
20403cb34c60Sahrens 	if (buf == NULL)
20413cb34c60Sahrens 		return (ENOMEM);
20423cb34c60Sahrens 
20433cb34c60Sahrens 	err = recv_read(hdl, fd, buf, len, byteswap, zc);
20443cb34c60Sahrens 	if (err != 0) {
20453cb34c60Sahrens 		free(buf);
20463cb34c60Sahrens 		return (err);
20473cb34c60Sahrens 	}
20483cb34c60Sahrens 
20493cb34c60Sahrens 	err = nvlist_unpack(buf, len, nvp, 0);
20503cb34c60Sahrens 	free(buf);
20513cb34c60Sahrens 	if (err != 0) {
20523cb34c60Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
20533cb34c60Sahrens 		    "stream (malformed nvlist)"));
20543cb34c60Sahrens 		return (EINVAL);
20553cb34c60Sahrens 	}
20563cb34c60Sahrens 	return (0);
20573cb34c60Sahrens }
20583cb34c60Sahrens 
20593cb34c60Sahrens static int
recv_rename(libzfs_handle_t * hdl,const char * name,const char * tryname,int baselen,char * newname,recvflags_t * flags)20603cb34c60Sahrens recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
206119b94df9SMatthew Ahrens     int baselen, char *newname, recvflags_t *flags)
20623cb34c60Sahrens {
20633cb34c60Sahrens 	static int seq;
20643cb34c60Sahrens 	zfs_cmd_t zc = { 0 };
20653cb34c60Sahrens 	int err;
2066d8d77200Sahrens 	prop_changelist_t *clp;
2067d8d77200Sahrens 	zfs_handle_t *zhp;
20683cb34c60Sahrens 
2069d8d77200Sahrens 	zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
20703cb34c60Sahrens 	if (zhp == NULL)
20713cb34c60Sahrens 		return (-1);
20720069fd67STim Haley 	clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
207319b94df9SMatthew Ahrens 	    flags->force ? MS_FORCE : 0);
20743cb34c60Sahrens 	zfs_close(zhp);
20753cb34c60Sahrens 	if (clp == NULL)
20763cb34c60Sahrens 		return (-1);
20773cb34c60Sahrens 	err = changelist_prefix(clp);
20783cb34c60Sahrens 	if (err)
20793cb34c60Sahrens 		return (err);
20803cb34c60Sahrens 
2081842727c2SChris Kirby 	zc.zc_objset_type = DMU_OST_ZFS;
2082842727c2SChris Kirby 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
2083842727c2SChris Kirby 
20843cb34c60Sahrens 	if (tryname) {
20853cb34c60Sahrens 		(void) strcpy(newname, tryname);
20863cb34c60Sahrens 
20873cb34c60Sahrens 		(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
20883cb34c60Sahrens 
208919b94df9SMatthew Ahrens 		if (flags->verbose) {
20903cb34c60Sahrens 			(void) printf("attempting rename %s to %s\n",
20913cb34c60Sahrens 			    zc.zc_name, zc.zc_value);
20923cb34c60Sahrens 		}
20933cb34c60Sahrens 		err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
2094d8d77200Sahrens 		if (err == 0)
20953cb34c60Sahrens 			changelist_rename(clp, name, tryname);
20963cb34c60Sahrens 	} else {
20973cb34c60Sahrens 		err = ENOENT;
20983cb34c60Sahrens 	}
20993cb34c60Sahrens 
21003cb34c60Sahrens 	if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) {
21013cb34c60Sahrens 		seq++;
21023cb34c60Sahrens 
2103*675fc291SMatthew Ahrens 		(void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN,
2104*675fc291SMatthew Ahrens 		    "%.*srecv-%u-%u", baselen, name, getpid(), seq);
21053cb34c60Sahrens 		(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
21063cb34c60Sahrens 
210719b94df9SMatthew Ahrens 		if (flags->verbose) {
21083cb34c60Sahrens 			(void) printf("failed - trying rename %s to %s\n",
21093cb34c60Sahrens 			    zc.zc_name, zc.zc_value);
21103cb34c60Sahrens 		}
21113cb34c60Sahrens 		err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
2112d8d77200Sahrens 		if (err == 0)
21133cb34c60Sahrens 			changelist_rename(clp, name, newname);
211419b94df9SMatthew Ahrens 		if (err && flags->verbose) {
21153cb34c60Sahrens 			(void) printf("failed (%u) - "
21163cb34c60Sahrens 			    "will try again on next pass\n", errno);
21173cb34c60Sahrens 		}
21183cb34c60Sahrens 		err = EAGAIN;
211919b94df9SMatthew Ahrens 	} else if (flags->verbose) {
21203cb34c60Sahrens 		if (err == 0)
21213cb34c60Sahrens 			(void) printf("success\n");
21223cb34c60Sahrens 		else
21233cb34c60Sahrens 			(void) printf("failed (%u)\n", errno);
21243cb34c60Sahrens 	}
21253cb34c60Sahrens 
21263cb34c60Sahrens 	(void) changelist_postfix(clp);
21273cb34c60Sahrens 	changelist_free(clp);
21283cb34c60Sahrens 
21293cb34c60Sahrens 	return (err);
21303cb34c60Sahrens }
21313cb34c60Sahrens 
21323cb34c60Sahrens static int
recv_destroy(libzfs_handle_t * hdl,const char * name,int baselen,char * newname,recvflags_t * flags)21333cb34c60Sahrens recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
213419b94df9SMatthew Ahrens     char *newname, recvflags_t *flags)
21353cb34c60Sahrens {
21363cb34c60Sahrens 	zfs_cmd_t zc = { 0 };
2137d8d77200Sahrens 	int err = 0;
2138d8d77200Sahrens 	prop_changelist_t *clp;
2139d8d77200Sahrens 	zfs_handle_t *zhp;
2140842727c2SChris Kirby 	boolean_t defer = B_FALSE;
2141842727c2SChris Kirby 	int spa_version;
2142d8d77200Sahrens 
2143d8d77200Sahrens 	zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
2144d8d77200Sahrens 	if (zhp == NULL)
2145d8d77200Sahrens 		return (-1);
21460069fd67STim Haley 	clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
214719b94df9SMatthew Ahrens 	    flags->force ? MS_FORCE : 0);
2148842727c2SChris Kirby 	if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
2149842727c2SChris Kirby 	    zfs_spa_version(zhp, &spa_version) == 0 &&
2150842727c2SChris Kirby 	    spa_version >= SPA_VERSION_USERREFS)
2151842727c2SChris Kirby 		defer = B_TRUE;
2152d8d77200Sahrens 	zfs_close(zhp);
2153d8d77200Sahrens 	if (clp == NULL)
2154d8d77200Sahrens 		return (-1);
2155d8d77200Sahrens 	err = changelist_prefix(clp);
2156d8d77200Sahrens 	if (err)
2157d8d77200Sahrens 		return (err);
21583cb34c60Sahrens 
21593cb34c60Sahrens 	zc.zc_objset_type = DMU_OST_ZFS;
2160842727c2SChris Kirby 	zc.zc_defer_destroy = defer;
21613cb34c60Sahrens 	(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
21623cb34c60Sahrens 
216319b94df9SMatthew Ahrens 	if (flags->verbose)
21643cb34c60Sahrens 		(void) printf("attempting destroy %s\n", zc.zc_name);
21653cb34c60Sahrens 	err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
2166d8d77200Sahrens 	if (err == 0) {
216719b94df9SMatthew Ahrens 		if (flags->verbose)
21683cb34c60Sahrens 			(void) printf("success\n");
2169d8d77200Sahrens 		changelist_remove(clp, zc.zc_name);
21703cb34c60Sahrens 	}
21713cb34c60Sahrens 
2172d8d77200Sahrens 	(void) changelist_postfix(clp);
2173d8d77200Sahrens 	changelist_free(clp);
2174d8d77200Sahrens 
2175842727c2SChris Kirby 	/*
217692241e0bSTom Erickson 	 * Deferred destroy might destroy the snapshot or only mark it to be
217792241e0bSTom Erickson 	 * destroyed later, and it returns success in either case.
2178842727c2SChris Kirby 	 */
217992241e0bSTom Erickson 	if (err != 0 || (defer && zfs_dataset_exists(hdl, name,
218092241e0bSTom Erickson 	    ZFS_TYPE_SNAPSHOT))) {
2181d8d77200Sahrens 		err = recv_rename(hdl, name, NULL, baselen, newname, flags);
218292241e0bSTom Erickson 	}
2183d8d77200Sahrens 
21843cb34c60Sahrens 	return (err);
21853cb34c60Sahrens }
21863cb34c60Sahrens 
21873cb34c60Sahrens typedef struct guid_to_name_data {
21883cb34c60Sahrens 	uint64_t guid;
21898429b235SMatthew Ahrens 	boolean_t bookmark_ok;
21903cb34c60Sahrens 	char *name;
219119b94df9SMatthew Ahrens 	char *skip;
21923cb34c60Sahrens } guid_to_name_data_t;
21933cb34c60Sahrens 
21943cb34c60Sahrens static int
guid_to_name_cb(zfs_handle_t * zhp,void * arg)21953cb34c60Sahrens guid_to_name_cb(zfs_handle_t *zhp, void *arg)
21963cb34c60Sahrens {
21973cb34c60Sahrens 	guid_to_name_data_t *gtnd = arg;
21988429b235SMatthew Ahrens 	const char *slash;
21993cb34c60Sahrens 	int err;
22003cb34c60Sahrens 
220119b94df9SMatthew Ahrens 	if (gtnd->skip != NULL &&
22028429b235SMatthew Ahrens 	    (slash = strrchr(zhp->zfs_name, '/')) != NULL &&
22038429b235SMatthew Ahrens 	    strcmp(slash + 1, gtnd->skip) == 0) {
22048429b235SMatthew Ahrens 		zfs_close(zhp);
220519b94df9SMatthew Ahrens 		return (0);
220619b94df9SMatthew Ahrens 	}
220719b94df9SMatthew Ahrens 
22088429b235SMatthew Ahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_GUID) == gtnd->guid) {
22093cb34c60Sahrens 		(void) strcpy(gtnd->name, zhp->zfs_name);
2210a79992aaSTom Erickson 		zfs_close(zhp);
22113cb34c60Sahrens 		return (EEXIST);
22123cb34c60Sahrens 	}
221319b94df9SMatthew Ahrens 
22143cb34c60Sahrens 	err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
22158429b235SMatthew Ahrens 	if (err != EEXIST && gtnd->bookmark_ok)
22168429b235SMatthew Ahrens 		err = zfs_iter_bookmarks(zhp, guid_to_name_cb, gtnd);
22173cb34c60Sahrens 	zfs_close(zhp);
22183cb34c60Sahrens 	return (err);
22193cb34c60Sahrens }
22203cb34c60Sahrens 
222119b94df9SMatthew Ahrens /*
222219b94df9SMatthew Ahrens  * Attempt to find the local dataset associated with this guid.  In the case of
222319b94df9SMatthew Ahrens  * multiple matches, we attempt to find the "best" match by searching
222419b94df9SMatthew Ahrens  * progressively larger portions of the hierarchy.  This allows one to send a
222519b94df9SMatthew Ahrens  * tree of datasets individually and guarantee that we will find the source
222619b94df9SMatthew Ahrens  * guid within that hierarchy, even if there are multiple matches elsewhere.
222719b94df9SMatthew Ahrens  */
22283cb34c60Sahrens static int
guid_to_name(libzfs_handle_t * hdl,const char * parent,uint64_t guid,boolean_t bookmark_ok,char * name)22293cb34c60Sahrens guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
22308429b235SMatthew Ahrens     boolean_t bookmark_ok, char *name)
22313cb34c60Sahrens {
2232*675fc291SMatthew Ahrens 	char pname[ZFS_MAX_DATASET_NAME_LEN];
22333cb34c60Sahrens 	guid_to_name_data_t gtnd;
22343cb34c60Sahrens 
22353cb34c60Sahrens 	gtnd.guid = guid;
22368429b235SMatthew Ahrens 	gtnd.bookmark_ok = bookmark_ok;
22373cb34c60Sahrens 	gtnd.name = name;
223819b94df9SMatthew Ahrens 	gtnd.skip = NULL;
22393cb34c60Sahrens 
224019b94df9SMatthew Ahrens 	/*
22418429b235SMatthew Ahrens 	 * Search progressively larger portions of the hierarchy, starting
22428429b235SMatthew Ahrens 	 * with the filesystem specified by 'parent'.  This will
224319b94df9SMatthew Ahrens 	 * select the "most local" version of the origin snapshot in the case
224419b94df9SMatthew Ahrens 	 * that there are multiple matching snapshots in the system.
224519b94df9SMatthew Ahrens 	 */
22468429b235SMatthew Ahrens 	(void) strlcpy(pname, parent, sizeof (pname));
22478429b235SMatthew Ahrens 	char *cp = strrchr(pname, '@');
22488429b235SMatthew Ahrens 	if (cp == NULL)
22498429b235SMatthew Ahrens 		cp = strchr(pname, '\0');
22508429b235SMatthew Ahrens 	for (; cp != NULL; cp = strrchr(pname, '/')) {
225119b94df9SMatthew Ahrens 		/* Chop off the last component and open the parent */
225219b94df9SMatthew Ahrens 		*cp = '\0';
22538429b235SMatthew Ahrens 		zfs_handle_t *zhp = make_dataset_handle(hdl, pname);
225419b94df9SMatthew Ahrens 
225519b94df9SMatthew Ahrens 		if (zhp == NULL)
225619b94df9SMatthew Ahrens 			continue;
22578429b235SMatthew Ahrens 		int err = guid_to_name_cb(zfs_handle_dup(zhp), &gtnd);
22588429b235SMatthew Ahrens 		if (err != EEXIST)
22593cb34c60Sahrens 			err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
22608429b235SMatthew Ahrens 		if (err != EEXIST && bookmark_ok)
22618429b235SMatthew Ahrens 			err = zfs_iter_bookmarks(zhp, guid_to_name_cb, &gtnd);
22623cb34c60Sahrens 		zfs_close(zhp);
22633cb34c60Sahrens 		if (err == EEXIST)
22643cb34c60Sahrens 			return (0);
226519b94df9SMatthew Ahrens 
226619b94df9SMatthew Ahrens 		/*
22678429b235SMatthew Ahrens 		 * Remember the last portion of the dataset so we skip it next
22688429b235SMatthew Ahrens 		 * time through (as we've already searched that portion of the
22698429b235SMatthew Ahrens 		 * hierarchy).
227019b94df9SMatthew Ahrens 		 */
22718429b235SMatthew Ahrens 		gtnd.skip = strrchr(pname, '/') + 1;
22723cb34c60Sahrens 	}
22733cb34c60Sahrens 
227419b94df9SMatthew Ahrens 	return (ENOENT);
22753cb34c60Sahrens }
22763cb34c60Sahrens 
22773cb34c60Sahrens /*
227819b94df9SMatthew Ahrens  * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
227919b94df9SMatthew Ahrens  * guid1 is after guid2.
22803cb34c60Sahrens  */
228104bb726eSahl static int
created_before(libzfs_handle_t * hdl,avl_tree_t * avl,uint64_t guid1,uint64_t guid2)22823cb34c60Sahrens created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
22833cb34c60Sahrens     uint64_t guid1, uint64_t guid2)
22843cb34c60Sahrens {
22853cb34c60Sahrens 	nvlist_t *nvfs;
22863cb34c60Sahrens 	char *fsname, *snapname;
2287*675fc291SMatthew Ahrens 	char buf[ZFS_MAX_DATASET_NAME_LEN];
228804bb726eSahl 	int rv;
228919b94df9SMatthew Ahrens 	zfs_handle_t *guid1hdl, *guid2hdl;
229019b94df9SMatthew Ahrens 	uint64_t create1, create2;
22913cb34c60Sahrens 
22923cb34c60Sahrens 	if (guid2 == 0)
229304bb726eSahl 		return (0);
22943cb34c60Sahrens 	if (guid1 == 0)
229504bb726eSahl 		return (1);
22963cb34c60Sahrens 
22973cb34c60Sahrens 	nvfs = fsavl_find(avl, guid1, &snapname);
22983cb34c60Sahrens 	VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
22993cb34c60Sahrens 	(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
230019b94df9SMatthew Ahrens 	guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
230119b94df9SMatthew Ahrens 	if (guid1hdl == NULL)
230204bb726eSahl 		return (-1);
23033cb34c60Sahrens 
23043cb34c60Sahrens 	nvfs = fsavl_find(avl, guid2, &snapname);
23053cb34c60Sahrens 	VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
23063cb34c60Sahrens 	(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
230719b94df9SMatthew Ahrens 	guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
230819b94df9SMatthew Ahrens 	if (guid2hdl == NULL) {
230919b94df9SMatthew Ahrens 		zfs_close(guid1hdl);
231004bb726eSahl 		return (-1);
231104bb726eSahl 	}
23123cb34c60Sahrens 
231319b94df9SMatthew Ahrens 	create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
231419b94df9SMatthew Ahrens 	create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
23153cb34c60Sahrens 
231619b94df9SMatthew Ahrens 	if (create1 < create2)
231719b94df9SMatthew Ahrens 		rv = -1;
231819b94df9SMatthew Ahrens 	else if (create1 > create2)
231919b94df9SMatthew Ahrens 		rv = +1;
232019b94df9SMatthew Ahrens 	else
232119b94df9SMatthew Ahrens 		rv = 0;
232219b94df9SMatthew Ahrens 
232319b94df9SMatthew Ahrens 	zfs_close(guid1hdl);
232419b94df9SMatthew Ahrens 	zfs_close(guid2hdl);
23253cb34c60Sahrens 
23263cb34c60Sahrens 	return (rv);
23273cb34c60Sahrens }
23283cb34c60Sahrens 
23293cb34c60Sahrens static int
recv_incremental_replication(libzfs_handle_t * hdl,const char * tofs,recvflags_t * flags,nvlist_t * stream_nv,avl_tree_t * stream_avl,nvlist_t * renamed)23303cb34c60Sahrens recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
233119b94df9SMatthew Ahrens     recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
2332bd6a198fSTom Erickson     nvlist_t *renamed)
23333cb34c60Sahrens {
23343cb34c60Sahrens 	nvlist_t *local_nv;
23353cb34c60Sahrens 	avl_tree_t *local_avl;
23363cb34c60Sahrens 	nvpair_t *fselem, *nextfselem;
2337bd6a198fSTom Erickson 	char *fromsnap;
2338*675fc291SMatthew Ahrens 	char newname[ZFS_MAX_DATASET_NAME_LEN];
23393cb34c60Sahrens 	int error;
234092241e0bSTom Erickson 	boolean_t needagain, progress, recursive;
2341fb2d63c6SPrabahar Jeyaram 	char *s1, *s2;
23423cb34c60Sahrens 
23433cb34c60Sahrens 	VERIFY(0 == nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap));
23443cb34c60Sahrens 
234592241e0bSTom Erickson 	recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
234692241e0bSTom Erickson 	    ENOENT);
234792241e0bSTom Erickson 
234819b94df9SMatthew Ahrens 	if (flags->dryrun)
23493cb34c60Sahrens 		return (0);
23503cb34c60Sahrens 
23513cb34c60Sahrens again:
23523cb34c60Sahrens 	needagain = progress = B_FALSE;
23533cb34c60Sahrens 
23543cb34c60Sahrens 	if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
235592241e0bSTom Erickson 	    recursive, &local_nv, &local_avl)) != 0)
23563cb34c60Sahrens 		return (error);
23573cb34c60Sahrens 
23583cb34c60Sahrens 	/*
23593cb34c60Sahrens 	 * Process deletes and renames
23603cb34c60Sahrens 	 */
23613cb34c60Sahrens 	for (fselem = nvlist_next_nvpair(local_nv, NULL);
23623cb34c60Sahrens 	    fselem; fselem = nextfselem) {
23633cb34c60Sahrens 		nvlist_t *nvfs, *snaps;
23643cb34c60Sahrens 		nvlist_t *stream_nvfs = NULL;
23653cb34c60Sahrens 		nvpair_t *snapelem, *nextsnapelem;
23663cb34c60Sahrens 		uint64_t fromguid = 0;
23673cb34c60Sahrens 		uint64_t originguid = 0;
23683cb34c60Sahrens 		uint64_t stream_originguid = 0;
23693cb34c60Sahrens 		uint64_t parent_fromsnap_guid, stream_parent_fromsnap_guid;
23703cb34c60Sahrens 		char *fsname, *stream_fsname;
23713cb34c60Sahrens 
23723cb34c60Sahrens 		nextfselem = nvlist_next_nvpair(local_nv, fselem);
23733cb34c60Sahrens 
23743cb34c60Sahrens 		VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
23753cb34c60Sahrens 		VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
23763cb34c60Sahrens 		VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
23773cb34c60Sahrens 		VERIFY(0 == nvlist_lookup_uint64(nvfs, "parentfromsnap",
23783cb34c60Sahrens 		    &parent_fromsnap_guid));
23793cb34c60Sahrens 		(void) nvlist_lookup_uint64(nvfs, "origin", &originguid);
23803cb34c60Sahrens 
23813cb34c60Sahrens 		/*
23823cb34c60Sahrens 		 * First find the stream's fs, so we can check for
23833cb34c60Sahrens 		 * a different origin (due to "zfs promote")
23843cb34c60Sahrens 		 */
23853cb34c60Sahrens 		for (snapelem = nvlist_next_nvpair(snaps, NULL);
23863cb34c60Sahrens 		    snapelem; snapelem = nvlist_next_nvpair(snaps, snapelem)) {
23873cb34c60Sahrens 			uint64_t thisguid;
23883cb34c60Sahrens 
23893cb34c60Sahrens 			VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
23903cb34c60Sahrens 			stream_nvfs = fsavl_find(stream_avl, thisguid, NULL);
23913cb34c60Sahrens 
23923cb34c60Sahrens 			if (stream_nvfs != NULL)
23933cb34c60Sahrens 				break;
23943cb34c60Sahrens 		}
23953cb34c60Sahrens 
23963cb34c60Sahrens 		/* check for promote */
23973cb34c60Sahrens 		(void) nvlist_lookup_uint64(stream_nvfs, "origin",
23983cb34c60Sahrens 		    &stream_originguid);
23993cb34c60Sahrens 		if (stream_nvfs && originguid != stream_originguid) {
240004bb726eSahl 			switch (created_before(hdl, local_avl,
240104bb726eSahl 			    stream_originguid, originguid)) {
240204bb726eSahl 			case 1: {
24033cb34c60Sahrens 				/* promote it! */
24043cb34c60Sahrens 				zfs_cmd_t zc = { 0 };
24053cb34c60Sahrens 				nvlist_t *origin_nvfs;
24063cb34c60Sahrens 				char *origin_fsname;
24073cb34c60Sahrens 
240819b94df9SMatthew Ahrens 				if (flags->verbose)
24093cb34c60Sahrens 					(void) printf("promoting %s\n", fsname);
24103cb34c60Sahrens 
24113cb34c60Sahrens 				origin_nvfs = fsavl_find(local_avl, originguid,
24123cb34c60Sahrens 				    NULL);
24133cb34c60Sahrens 				VERIFY(0 == nvlist_lookup_string(origin_nvfs,
24143cb34c60Sahrens 				    "name", &origin_fsname));
24153cb34c60Sahrens 				(void) strlcpy(zc.zc_value, origin_fsname,
24163cb34c60Sahrens 				    sizeof (zc.zc_value));
24173cb34c60Sahrens 				(void) strlcpy(zc.zc_name, fsname,
24183cb34c60Sahrens 				    sizeof (zc.zc_name));
24193cb34c60Sahrens 				error = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
24203cb34c60Sahrens 				if (error == 0)
24213cb34c60Sahrens 					progress = B_TRUE;
242204bb726eSahl 				break;
242304bb726eSahl 			}
242404bb726eSahl 			default:
242504bb726eSahl 				break;
242604bb726eSahl 			case -1:
242704bb726eSahl 				fsavl_destroy(local_avl);
242804bb726eSahl 				nvlist_free(local_nv);
242904bb726eSahl 				return (-1);
24303cb34c60Sahrens 			}
24313cb34c60Sahrens 			/*
24323cb34c60Sahrens 			 * We had/have the wrong origin, therefore our
24333cb34c60Sahrens 			 * list of snapshots is wrong.  Need to handle
24343cb34c60Sahrens 			 * them on the next pass.
24353cb34c60Sahrens 			 */
24363cb34c60Sahrens 			needagain = B_TRUE;
24373cb34c60Sahrens 			continue;
24383cb34c60Sahrens 		}
24393cb34c60Sahrens 
24403cb34c60Sahrens 		for (snapelem = nvlist_next_nvpair(snaps, NULL);
24413cb34c60Sahrens 		    snapelem; snapelem = nextsnapelem) {
24423cb34c60Sahrens 			uint64_t thisguid;
24433cb34c60Sahrens 			char *stream_snapname;
2444bb0ade09Sahrens 			nvlist_t *found, *props;
24453cb34c60Sahrens 
24463cb34c60Sahrens 			nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
24473cb34c60Sahrens 
24483cb34c60Sahrens 			VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
24493cb34c60Sahrens 			found = fsavl_find(stream_avl, thisguid,
24503cb34c60Sahrens 			    &stream_snapname);
24513cb34c60Sahrens 
24523cb34c60Sahrens 			/* check for delete */
24533cb34c60Sahrens 			if (found == NULL) {
2454*675fc291SMatthew Ahrens 				char name[ZFS_MAX_DATASET_NAME_LEN];
24553cb34c60Sahrens 
245619b94df9SMatthew Ahrens 				if (!flags->force)
24573cb34c60Sahrens 					continue;
24583cb34c60Sahrens 
24593cb34c60Sahrens 				(void) snprintf(name, sizeof (name), "%s@%s",
24603cb34c60Sahrens 				    fsname, nvpair_name(snapelem));
24613cb34c60Sahrens 
24623cb34c60Sahrens 				error = recv_destroy(hdl, name,
24633cb34c60Sahrens 				    strlen(fsname)+1, newname, flags);
24643cb34c60Sahrens 				if (error)
24653cb34c60Sahrens 					needagain = B_TRUE;
24663cb34c60Sahrens 				else
24673cb34c60Sahrens 					progress = B_TRUE;
24683cb34c60Sahrens 				continue;
24693cb34c60Sahrens 			}
24703cb34c60Sahrens 
24713cb34c60Sahrens 			stream_nvfs = found;
24723cb34c60Sahrens 
2473bb0ade09Sahrens 			if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
2474bb0ade09Sahrens 			    &props) && 0 == nvlist_lookup_nvlist(props,
2475bb0ade09Sahrens 			    stream_snapname, &props)) {
2476bb0ade09Sahrens 				zfs_cmd_t zc = { 0 };
2477bb0ade09Sahrens 
247892241e0bSTom Erickson 				zc.zc_cookie = B_TRUE; /* received */
2479b6c10d80Sahrens 				(void) snprintf(zc.zc_name, sizeof (zc.zc_name),
2480bb0ade09Sahrens 				    "%s@%s", fsname, nvpair_name(snapelem));
2481bb0ade09Sahrens 				if (zcmd_write_src_nvlist(hdl, &zc,
2482bb0ade09Sahrens 				    props) == 0) {
2483bb0ade09Sahrens 					(void) zfs_ioctl(hdl,
2484bb0ade09Sahrens 					    ZFS_IOC_SET_PROP, &zc);
2485bb0ade09Sahrens 					zcmd_free_nvlists(&zc);
2486bb0ade09Sahrens 				}
2487bb0ade09Sahrens 			}
2488bb0ade09Sahrens 
24893cb34c60Sahrens 			/* check for different snapname */
24903cb34c60Sahrens 			if (strcmp(nvpair_name(snapelem),
24913cb34c60Sahrens 			    stream_snapname) != 0) {
2492*675fc291SMatthew Ahrens 				char name[ZFS_MAX_DATASET_NAME_LEN];
2493*675fc291SMatthew Ahrens 				char tryname[ZFS_MAX_DATASET_NAME_LEN];
24943cb34c60Sahrens 
24953cb34c60Sahrens 				(void) snprintf(name, sizeof (name), "%s@%s",
24963cb34c60Sahrens 				    fsname, nvpair_name(snapelem));
24973cb34c60Sahrens 				(void) snprintf(tryname, sizeof (name), "%s@%s",
24983cb34c60Sahrens 				    fsname, stream_snapname);
24993cb34c60Sahrens 
25003cb34c60Sahrens 				error = recv_rename(hdl, name, tryname,
25013cb34c60Sahrens 				    strlen(fsname)+1, newname, flags);
25023cb34c60Sahrens 				if (error)
25033cb34c60Sahrens 					needagain = B_TRUE;
25043cb34c60Sahrens 				else
25053cb34c60Sahrens 					progress = B_TRUE;
25063cb34c60Sahrens 			}
25073cb34c60Sahrens 
25083cb34c60Sahrens 			if (strcmp(stream_snapname, fromsnap) == 0)
25093cb34c60Sahrens 				fromguid = thisguid;
25103cb34c60Sahrens 		}
25113cb34c60Sahrens 
25123cb34c60Sahrens 		/* check for delete */
25133cb34c60Sahrens 		if (stream_nvfs == NULL) {
251419b94df9SMatthew Ahrens 			if (!flags->force)
25153cb34c60Sahrens 				continue;
25163cb34c60Sahrens 
25173cb34c60Sahrens 			error = recv_destroy(hdl, fsname, strlen(tofs)+1,
25183cb34c60Sahrens 			    newname, flags);
25193cb34c60Sahrens 			if (error)
25203cb34c60Sahrens 				needagain = B_TRUE;
25213cb34c60Sahrens 			else
25223cb34c60Sahrens 				progress = B_TRUE;
25233cb34c60Sahrens 			continue;
25243cb34c60Sahrens 		}
25253cb34c60Sahrens 
2526bd6a198fSTom Erickson 		if (fromguid == 0) {
252719b94df9SMatthew Ahrens 			if (flags->verbose) {
2528bd6a198fSTom Erickson 				(void) printf("local fs %s does not have "
2529bd6a198fSTom Erickson 				    "fromsnap (%s in stream); must have "
2530bd6a198fSTom Erickson 				    "been deleted locally; ignoring\n",
2531bd6a198fSTom Erickson 				    fsname, fromsnap);
2532bd6a198fSTom Erickson 			}
25333cb34c60Sahrens 			continue;
25343cb34c60Sahrens 		}
25353cb34c60Sahrens 
25363cb34c60Sahrens 		VERIFY(0 == nvlist_lookup_string(stream_nvfs,
25373cb34c60Sahrens 		    "name", &stream_fsname));
25383cb34c60Sahrens 		VERIFY(0 == nvlist_lookup_uint64(stream_nvfs,
25393cb34c60Sahrens 		    "parentfromsnap", &stream_parent_fromsnap_guid));
25403cb34c60Sahrens 
254194f5c1a2SPrabahar Jeyaram 		s1 = strrchr(fsname, '/');
254294f5c1a2SPrabahar Jeyaram 		s2 = strrchr(stream_fsname, '/');
254394f5c1a2SPrabahar Jeyaram 
2544bd6a198fSTom Erickson 		/*
2545bd6a198fSTom Erickson 		 * Check for rename. If the exact receive path is specified, it
2546bd6a198fSTom Erickson 		 * does not count as a rename, but we still need to check the
2547bd6a198fSTom Erickson 		 * datasets beneath it.
2548bd6a198fSTom Erickson 		 */
25493cb34c60Sahrens 		if ((stream_parent_fromsnap_guid != 0 &&
2550bd6a198fSTom Erickson 		    parent_fromsnap_guid != 0 &&
25513cb34c60Sahrens 		    stream_parent_fromsnap_guid != parent_fromsnap_guid) ||
255219b94df9SMatthew Ahrens 		    ((flags->isprefix || strcmp(tofs, fsname) != 0) &&
2553bd6a198fSTom Erickson 		    (s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) {
25543cb34c60Sahrens 			nvlist_t *parent;
2555*675fc291SMatthew Ahrens 			char tryname[ZFS_MAX_DATASET_NAME_LEN];
25563cb34c60Sahrens 
25573cb34c60Sahrens 			parent = fsavl_find(local_avl,
25583cb34c60Sahrens 			    stream_parent_fromsnap_guid, NULL);
25593cb34c60Sahrens 			/*
25603cb34c60Sahrens 			 * NB: parent might not be found if we used the
25613cb34c60Sahrens 			 * tosnap for stream_parent_fromsnap_guid,
25623cb34c60Sahrens 			 * because the parent is a newly-created fs;
25633cb34c60Sahrens 			 * we'll be able to rename it after we recv the
25643cb34c60Sahrens 			 * new fs.
25653cb34c60Sahrens 			 */
25663cb34c60Sahrens 			if (parent != NULL) {
25673cb34c60Sahrens 				char *pname;
25683cb34c60Sahrens 
25693cb34c60Sahrens 				VERIFY(0 == nvlist_lookup_string(parent, "name",
25703cb34c60Sahrens 				    &pname));
25713cb34c60Sahrens 				(void) snprintf(tryname, sizeof (tryname),
25723cb34c60Sahrens 				    "%s%s", pname, strrchr(stream_fsname, '/'));
25733cb34c60Sahrens 			} else {
25743cb34c60Sahrens 				tryname[0] = '\0';
257519b94df9SMatthew Ahrens 				if (flags->verbose) {
25763cb34c60Sahrens 					(void) printf("local fs %s new parent "
25773cb34c60Sahrens 					    "not found\n", fsname);
25783cb34c60Sahrens 				}
25793cb34c60Sahrens 			}
25803cb34c60Sahrens 
2581bd6a198fSTom Erickson 			newname[0] = '\0';
2582bd6a198fSTom Erickson 
25833cb34c60Sahrens 			error = recv_rename(hdl, fsname, tryname,
25843cb34c60Sahrens 			    strlen(tofs)+1, newname, flags);
2585bd6a198fSTom Erickson 
2586bd6a198fSTom Erickson 			if (renamed != NULL && newname[0] != '\0') {
2587bd6a198fSTom Erickson 				VERIFY(0 == nvlist_add_boolean(renamed,
2588bd6a198fSTom Erickson 				    newname));
2589bd6a198fSTom Erickson 			}
2590bd6a198fSTom Erickson 
25913cb34c60Sahrens 			if (error)
25923cb34c60Sahrens 				needagain = B_TRUE;
25933cb34c60Sahrens 			else
25943cb34c60Sahrens 				progress = B_TRUE;
25953cb34c60Sahrens 		}
25963cb34c60Sahrens 	}
25973cb34c60Sahrens 
25983cb34c60Sahrens 	fsavl_destroy(local_avl);
25993cb34c60Sahrens 	nvlist_free(local_nv);
26003cb34c60Sahrens 
26013cb34c60Sahrens 	if (needagain && progress) {
26023cb34c60Sahrens 		/* do another pass to fix up temporary names */
260319b94df9SMatthew Ahrens 		if (flags->verbose)
26043cb34c60Sahrens 			(void) printf("another pass:\n");
26053cb34c60Sahrens 		goto again;
26063cb34c60Sahrens 	}
26073cb34c60Sahrens 
26083cb34c60Sahrens 	return (needagain);
26093cb34c60Sahrens }
26103cb34c60Sahrens 
26113cb34c60Sahrens static int
zfs_receive_package(libzfs_handle_t * hdl,int fd,const char * destname,recvflags_t * flags,dmu_replay_record_t * drr,zio_cksum_t * zc,char ** top_zfs,int cleanup_fd,uint64_t * action_handlep)26123cb34c60Sahrens zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
261319b94df9SMatthew Ahrens     recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
2614c99e4bdcSChris Kirby     char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
26153cb34c60Sahrens {
26163cb34c60Sahrens 	nvlist_t *stream_nv = NULL;
26173cb34c60Sahrens 	avl_tree_t *stream_avl = NULL;
26183cb34c60Sahrens 	char *fromsnap = NULL;
2619bd6a198fSTom Erickson 	char *cp;
2620*675fc291SMatthew Ahrens 	char tofs[ZFS_MAX_DATASET_NAME_LEN];
2621*675fc291SMatthew Ahrens 	char sendfs[ZFS_MAX_DATASET_NAME_LEN];
26223cb34c60Sahrens 	char errbuf[1024];
26233cb34c60Sahrens 	dmu_replay_record_t drre;
26243cb34c60Sahrens 	int error;
26253cb34c60Sahrens 	boolean_t anyerr = B_FALSE;
2626137fa067Sahrens 	boolean_t softerr = B_FALSE;
2627bd6a198fSTom Erickson 	boolean_t recursive;
26283cb34c60Sahrens 
26293cb34c60Sahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
26303cb34c60Sahrens 	    "cannot receive"));
26313cb34c60Sahrens 
26323cb34c60Sahrens 	assert(drr->drr_type == DRR_BEGIN);
26333cb34c60Sahrens 	assert(drr->drr_u.drr_begin.drr_magic == DMU_BACKUP_MAGIC);
26349e69d7d0SLori Alt 	assert(DMU_GET_STREAM_HDRTYPE(drr->drr_u.drr_begin.drr_versioninfo) ==
26359e69d7d0SLori Alt 	    DMU_COMPOUNDSTREAM);
26363cb34c60Sahrens 
26373cb34c60Sahrens 	/*
26383cb34c60Sahrens 	 * Read in the nvlist from the stream.
26393cb34c60Sahrens 	 */
26403cb34c60Sahrens 	if (drr->drr_payloadlen != 0) {
26413cb34c60Sahrens 		error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen,
264219b94df9SMatthew Ahrens 		    &stream_nv, flags->byteswap, zc);
26433cb34c60Sahrens 		if (error) {
26443cb34c60Sahrens 			error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
26453cb34c60Sahrens 			goto out;
26463cb34c60Sahrens 		}
26473cb34c60Sahrens 	}
26483cb34c60Sahrens 
2649bd6a198fSTom Erickson 	recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
2650bd6a198fSTom Erickson 	    ENOENT);
2651bd6a198fSTom Erickson 
2652bd6a198fSTom Erickson 	if (recursive && strchr(destname, '@')) {
2653bd6a198fSTom Erickson 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2654bd6a198fSTom Erickson 		    "cannot specify snapshot name for multi-snapshot stream"));
2655bd6a198fSTom Erickson 		error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
2656bd6a198fSTom Erickson 		goto out;
2657bd6a198fSTom Erickson 	}
2658bd6a198fSTom Erickson 
26593cb34c60Sahrens 	/*
26603cb34c60Sahrens 	 * Read in the end record and verify checksum.
26613cb34c60Sahrens 	 */
26623cb34c60Sahrens 	if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre),
266319b94df9SMatthew Ahrens 	    flags->byteswap, NULL)))
26643cb34c60Sahrens 		goto out;
266519b94df9SMatthew Ahrens 	if (flags->byteswap) {
26663cb34c60Sahrens 		drre.drr_type = BSWAP_32(drre.drr_type);
26673cb34c60Sahrens 		drre.drr_u.drr_end.drr_checksum.zc_word[0] =
26683cb34c60Sahrens 		    BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]);
26693cb34c60Sahrens 		drre.drr_u.drr_end.drr_checksum.zc_word[1] =
26703cb34c60Sahrens 		    BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[1]);
26713cb34c60Sahrens 		drre.drr_u.drr_end.drr_checksum.zc_word[2] =
26723cb34c60Sahrens 		    BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[2]);
26733cb34c60Sahrens 		drre.drr_u.drr_end.drr_checksum.zc_word[3] =
26743cb34c60Sahrens 		    BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[3]);
26753cb34c60Sahrens 	}
26763cb34c60Sahrens 	if (drre.drr_type != DRR_END) {
26773cb34c60Sahrens 		error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
26783cb34c60Sahrens 		goto out;
26793cb34c60Sahrens 	}
26803cb34c60Sahrens 	if (!ZIO_CHECKSUM_EQUAL(drre.drr_u.drr_end.drr_checksum, *zc)) {
26813cb34c60Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
26823cb34c60Sahrens 		    "incorrect header checksum"));
26833cb34c60Sahrens 		error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
26843cb34c60Sahrens 		goto out;
26853cb34c60Sahrens 	}
26863cb34c60Sahrens 
26873cb34c60Sahrens 	(void) nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap);
26883cb34c60Sahrens 
26893cb34c60Sahrens 	if (drr->drr_payloadlen != 0) {
26903cb34c60Sahrens 		nvlist_t *stream_fss;
26913cb34c60Sahrens 
26923cb34c60Sahrens 		VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss",
26933cb34c60Sahrens 		    &stream_fss));
269400954d7bSahl 		if ((stream_avl = fsavl_create(stream_fss)) == NULL) {
269500954d7bSahl 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
269600954d7bSahl 			    "couldn't allocate avl tree"));
269700954d7bSahl 			error = zfs_error(hdl, EZFS_NOMEM, errbuf);
269800954d7bSahl 			goto out;
269900954d7bSahl 		}
27003cb34c60Sahrens 
27013cb34c60Sahrens 		if (fromsnap != NULL) {
2702bd6a198fSTom Erickson 			nvlist_t *renamed = NULL;
2703bd6a198fSTom Erickson 			nvpair_t *pair = NULL;
2704bd6a198fSTom Erickson 
2705*675fc291SMatthew Ahrens 			(void) strlcpy(tofs, destname, sizeof (tofs));
270619b94df9SMatthew Ahrens 			if (flags->isprefix) {
2707bd6a198fSTom Erickson 				struct drr_begin *drrb = &drr->drr_u.drr_begin;
2708bd6a198fSTom Erickson 				int i;
2709bd6a198fSTom Erickson 
271019b94df9SMatthew Ahrens 				if (flags->istail) {
2711bd6a198fSTom Erickson 					cp = strrchr(drrb->drr_toname, '/');
2712bd6a198fSTom Erickson 					if (cp == NULL) {
2713bd6a198fSTom Erickson 						(void) strlcat(tofs, "/",
2714*675fc291SMatthew Ahrens 						    sizeof (tofs));
2715bd6a198fSTom Erickson 						i = 0;
2716bd6a198fSTom Erickson 					} else {
2717bd6a198fSTom Erickson 						i = (cp - drrb->drr_toname);
2718bd6a198fSTom Erickson 					}
2719bd6a198fSTom Erickson 				} else {
2720bd6a198fSTom Erickson 					i = strcspn(drrb->drr_toname, "/@");
2721bd6a198fSTom Erickson 				}
27223cb34c60Sahrens 				/* zfs_receive_one() will create_parents() */
2723bd6a198fSTom Erickson 				(void) strlcat(tofs, &drrb->drr_toname[i],
2724*675fc291SMatthew Ahrens 				    sizeof (tofs));
27253cb34c60Sahrens 				*strchr(tofs, '@') = '\0';
27263cb34c60Sahrens 			}
2727bd6a198fSTom Erickson 
272819b94df9SMatthew Ahrens 			if (recursive && !flags->dryrun && !flags->nomount) {
2729bd6a198fSTom Erickson 				VERIFY(0 == nvlist_alloc(&renamed,
2730bd6a198fSTom Erickson 				    NV_UNIQUE_NAME, 0));
2731bd6a198fSTom Erickson 			}
2732bd6a198fSTom Erickson 
2733bd6a198fSTom Erickson 			softerr = recv_incremental_replication(hdl, tofs, flags,
2734bd6a198fSTom Erickson 			    stream_nv, stream_avl, renamed);
2735bd6a198fSTom Erickson 
2736bd6a198fSTom Erickson 			/* Unmount renamed filesystems before receiving. */
2737bd6a198fSTom Erickson 			while ((pair = nvlist_next_nvpair(renamed,
2738bd6a198fSTom Erickson 			    pair)) != NULL) {
2739bd6a198fSTom Erickson 				zfs_handle_t *zhp;
2740bd6a198fSTom Erickson 				prop_changelist_t *clp = NULL;
2741bd6a198fSTom Erickson 
2742bd6a198fSTom Erickson 				zhp = zfs_open(hdl, nvpair_name(pair),
2743bd6a198fSTom Erickson 				    ZFS_TYPE_FILESYSTEM);
2744bd6a198fSTom Erickson 				if (zhp != NULL) {
2745bd6a198fSTom Erickson 					clp = changelist_gather(zhp,
2746bd6a198fSTom Erickson 					    ZFS_PROP_MOUNTPOINT, 0, 0);
2747bd6a198fSTom Erickson 					zfs_close(zhp);
2748bd6a198fSTom Erickson 					if (clp != NULL) {
2749bd6a198fSTom Erickson 						softerr |=
2750bd6a198fSTom Erickson 						    changelist_prefix(clp);
2751bd6a198fSTom Erickson 						changelist_free(clp);
2752bd6a198fSTom Erickson 					}
27533cb34c60Sahrens 				}
27543cb34c60Sahrens 			}
27553cb34c60Sahrens 
2756bd6a198fSTom Erickson 			nvlist_free(renamed);
2757bd6a198fSTom Erickson 		}
2758bd6a198fSTom Erickson 	}
2759bd6a198fSTom Erickson 
2760bd6a198fSTom Erickson 	/*
2761bd6a198fSTom Erickson 	 * Get the fs specified by the first path in the stream (the top level
2762bd6a198fSTom Erickson 	 * specified by 'zfs send') and pass it to each invocation of
2763bd6a198fSTom Erickson 	 * zfs_receive_one().
2764bd6a198fSTom Erickson 	 */
2765bd6a198fSTom Erickson 	(void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname,
2766*675fc291SMatthew Ahrens 	    sizeof (sendfs));
2767bd6a198fSTom Erickson 	if ((cp = strchr(sendfs, '@')) != NULL)
2768bd6a198fSTom Erickson 		*cp = '\0';
27693cb34c60Sahrens 
27703cb34c60Sahrens 	/* Finally, receive each contained stream */
27713cb34c60Sahrens 	do {
27723cb34c60Sahrens 		/*
27733cb34c60Sahrens 		 * we should figure out if it has a recoverable
27743cb34c60Sahrens 		 * error, in which case do a recv_skip() and drive on.
27753cb34c60Sahrens 		 * Note, if we fail due to already having this guid,
27763cb34c60Sahrens 		 * zfs_receive_one() will take care of it (ie,
27773cb34c60Sahrens 		 * recv_skip() and return 0).
27783cb34c60Sahrens 		 */
27795f9bb2f3SPaul Dagnelie 		error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
2780c99e4bdcSChris Kirby 		    sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
2781c99e4bdcSChris Kirby 		    action_handlep);
27823cb34c60Sahrens 		if (error == ENODATA) {
27833cb34c60Sahrens 			error = 0;
27843cb34c60Sahrens 			break;
27853cb34c60Sahrens 		}
27863cb34c60Sahrens 		anyerr |= error;
27873cb34c60Sahrens 	} while (error == 0);
27883cb34c60Sahrens 
27893cb34c60Sahrens 	if (drr->drr_payloadlen != 0 && fromsnap != NULL) {
27903cb34c60Sahrens 		/*
27913cb34c60Sahrens 		 * Now that we have the fs's they sent us, try the
27923cb34c60Sahrens 		 * renames again.
27933cb34c60Sahrens 		 */
2794137fa067Sahrens 		softerr = recv_incremental_replication(hdl, tofs, flags,
2795bd6a198fSTom Erickson 		    stream_nv, stream_avl, NULL);
27963cb34c60Sahrens 	}
27973cb34c60Sahrens 
27983cb34c60Sahrens out:
27993cb34c60Sahrens 	fsavl_destroy(stream_avl);
28003cb34c60Sahrens 	if (stream_nv)
28013cb34c60Sahrens 		nvlist_free(stream_nv);
2802137fa067Sahrens 	if (softerr)
2803137fa067Sahrens 		error = -2;
28043cb34c60Sahrens 	if (anyerr)
28053cb34c60Sahrens 		error = -1;
28063cb34c60Sahrens 	return (error);
28073cb34c60Sahrens }
28083cb34c60Sahrens 
280992241e0bSTom Erickson static void
trunc_prop_errs(int truncated)281092241e0bSTom Erickson trunc_prop_errs(int truncated)
281192241e0bSTom Erickson {
281292241e0bSTom Erickson 	ASSERT(truncated != 0);
281392241e0bSTom Erickson 
281492241e0bSTom Erickson 	if (truncated == 1)
281592241e0bSTom Erickson 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
281692241e0bSTom Erickson 		    "1 more property could not be set\n"));
281792241e0bSTom Erickson 	else
281892241e0bSTom Erickson 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
281992241e0bSTom Erickson 		    "%d more properties could not be set\n"), truncated);
282092241e0bSTom Erickson }
282192241e0bSTom Erickson 
28223cb34c60Sahrens static int
recv_skip(libzfs_handle_t * hdl,int fd,boolean_t byteswap)28233cb34c60Sahrens recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
28243cb34c60Sahrens {
28253cb34c60Sahrens 	dmu_replay_record_t *drr;
2826d1a98260SMatthew Ahrens 	void *buf = zfs_alloc(hdl, SPA_MAXBLOCKSIZE);
28279e69d7d0SLori Alt 	char errbuf[1024];
28289e69d7d0SLori Alt 
28299e69d7d0SLori Alt 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
28309e69d7d0SLori Alt 	    "cannot receive:"));
28313cb34c60Sahrens 
28323cb34c60Sahrens 	/* XXX would be great to use lseek if possible... */
28333cb34c60Sahrens 	drr = buf;
28343cb34c60Sahrens 
28353cb34c60Sahrens 	while (recv_read(hdl, fd, drr, sizeof (dmu_replay_record_t),
28363cb34c60Sahrens 	    byteswap, NULL) == 0) {
28373cb34c60Sahrens 		if (byteswap)
28383cb34c60Sahrens 			drr->drr_type = BSWAP_32(drr->drr_type);
28393cb34c60Sahrens 
28403cb34c60Sahrens 		switch (drr->drr_type) {
28413cb34c60Sahrens 		case DRR_BEGIN:
28429e69d7d0SLori Alt 			if (drr->drr_payloadlen != 0) {
28438429b235SMatthew Ahrens 				(void) recv_read(hdl, fd, buf,
28448429b235SMatthew Ahrens 				    drr->drr_payloadlen, B_FALSE, NULL);
28459e69d7d0SLori Alt 			}
28463cb34c60Sahrens 			break;
28473cb34c60Sahrens 
28483cb34c60Sahrens 		case DRR_END:
28493cb34c60Sahrens 			free(buf);
28503cb34c60Sahrens 			return (0);
28513cb34c60Sahrens 
28523cb34c60Sahrens 		case DRR_OBJECT:
28533cb34c60Sahrens 			if (byteswap) {
28543cb34c60Sahrens 				drr->drr_u.drr_object.drr_bonuslen =
28553cb34c60Sahrens 				    BSWAP_32(drr->drr_u.drr_object.
28563cb34c60Sahrens 				    drr_bonuslen);
28573cb34c60Sahrens 			}
28583cb34c60Sahrens 			(void) recv_read(hdl, fd, buf,
28593cb34c60Sahrens 			    P2ROUNDUP(drr->drr_u.drr_object.drr_bonuslen, 8),
28603cb34c60Sahrens 			    B_FALSE, NULL);
28613cb34c60Sahrens 			break;
28623cb34c60Sahrens 
28633cb34c60Sahrens 		case DRR_WRITE:
28643cb34c60Sahrens 			if (byteswap) {
28653cb34c60Sahrens 				drr->drr_u.drr_write.drr_length =
28660069fd67STim Haley 				    BSWAP_64(drr->drr_u.drr_write.drr_length);
28673cb34c60Sahrens 			}
28683cb34c60Sahrens 			(void) recv_read(hdl, fd, buf,
28693cb34c60Sahrens 			    drr->drr_u.drr_write.drr_length, B_FALSE, NULL);
28703cb34c60Sahrens 			break;
28710a586ceaSMark Shellenbaum 		case DRR_SPILL:
28720a586ceaSMark Shellenbaum 			if (byteswap) {
28730a586ceaSMark Shellenbaum 				drr->drr_u.drr_write.drr_length =
28740a586ceaSMark Shellenbaum 				    BSWAP_64(drr->drr_u.drr_spill.drr_length);
28750a586ceaSMark Shellenbaum 			}
28760a586ceaSMark Shellenbaum 			(void) recv_read(hdl, fd, buf,
28770a586ceaSMark Shellenbaum 			    drr->drr_u.drr_spill.drr_length, B_FALSE, NULL);
28780a586ceaSMark Shellenbaum 			break;
2879e54e0be9SMatthew Ahrens 		case DRR_WRITE_EMBEDDED:
2880e54e0be9SMatthew Ahrens 			if (byteswap) {
2881e54e0be9SMatthew Ahrens 				drr->drr_u.drr_write_embedded.drr_psize =
2882e54e0be9SMatthew Ahrens 				    BSWAP_32(drr->drr_u.drr_write_embedded.
2883e54e0be9SMatthew Ahrens 				    drr_psize);
2884e54e0be9SMatthew Ahrens 			}
2885e54e0be9SMatthew Ahrens 			(void) recv_read(hdl, fd, buf,
2886e54e0be9SMatthew Ahrens 			    P2ROUNDUP(drr->drr_u.drr_write_embedded.drr_psize,
2887e54e0be9SMatthew Ahrens 			    8), B_FALSE, NULL);
2888e54e0be9SMatthew Ahrens 			break;
28899e69d7d0SLori Alt 		case DRR_WRITE_BYREF:
28903cb34c60Sahrens 		case DRR_FREEOBJECTS:
28913cb34c60Sahrens 		case DRR_FREE:
28923cb34c60Sahrens 			break;
28933cb34c60Sahrens 
28943cb34c60Sahrens 		default:
28959e69d7d0SLori Alt 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
28969e69d7d0SLori Alt 			    "invalid record type"));
28979e69d7d0SLori Alt 			return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
28983cb34c60Sahrens 		}
28993cb34c60Sahrens 	}
29003cb34c60Sahrens 
29013cb34c60Sahrens 	free(buf);
29023cb34c60Sahrens 	return (-1);
29033cb34c60Sahrens }
29043cb34c60Sahrens 
29058429b235SMatthew Ahrens static void
recv_ecksum_set_aux(libzfs_handle_t * hdl,const char * target_snap,boolean_t resumable)29068429b235SMatthew Ahrens recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap,
29078429b235SMatthew Ahrens     boolean_t resumable)
29088429b235SMatthew Ahrens {
2909*675fc291SMatthew Ahrens 	char target_fs[ZFS_MAX_DATASET_NAME_LEN];
29108429b235SMatthew Ahrens 
29118429b235SMatthew Ahrens 	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
29128429b235SMatthew Ahrens 	    "checksum mismatch or incomplete stream"));
29138429b235SMatthew Ahrens 
29148429b235SMatthew Ahrens 	if (!resumable)
29158429b235SMatthew Ahrens 		return;
29168429b235SMatthew Ahrens 	(void) strlcpy(target_fs, target_snap, sizeof (target_fs));
29178429b235SMatthew Ahrens 	*strchr(target_fs, '@') = '\0';
29188429b235SMatthew Ahrens 	zfs_handle_t *zhp = zfs_open(hdl, target_fs,
29198429b235SMatthew Ahrens 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
29208429b235SMatthew Ahrens 	if (zhp == NULL)
29218429b235SMatthew Ahrens 		return;
29228429b235SMatthew Ahrens 
29238429b235SMatthew Ahrens 	char token_buf[ZFS_MAXPROPLEN];
29248429b235SMatthew Ahrens 	int error = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
29258429b235SMatthew Ahrens 	    token_buf, sizeof (token_buf),
29268429b235SMatthew Ahrens 	    NULL, NULL, 0, B_TRUE);
29278429b235SMatthew Ahrens 	if (error == 0) {
29288429b235SMatthew Ahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
29298429b235SMatthew Ahrens 		    "checksum mismatch or incomplete stream.\n"
29308429b235SMatthew Ahrens 		    "Partially received snapshot is saved.\n"
29318429b235SMatthew Ahrens 		    "A resuming stream can be generated on the sending "
29328429b235SMatthew Ahrens 		    "system by running:\n"
29338429b235SMatthew Ahrens 		    "    zfs send -t %s"),
29348429b235SMatthew Ahrens 		    token_buf);
29358429b235SMatthew Ahrens 	}
29368429b235SMatthew Ahrens 	zfs_close(zhp);
29378429b235SMatthew Ahrens }
29388429b235SMatthew Ahrens 
29393cb34c60Sahrens /*
29403cb34c60Sahrens  * Restores a backup of tosnap from the file descriptor specified by infd.
29413cb34c60Sahrens  */
29423cb34c60Sahrens static int
zfs_receive_one(libzfs_handle_t * hdl,int infd,const char * tosnap,const char * originsnap,recvflags_t * flags,dmu_replay_record_t * drr,dmu_replay_record_t * drr_noswap,const char * sendfs,nvlist_t * stream_nv,avl_tree_t * stream_avl,char ** top_zfs,int cleanup_fd,uint64_t * action_handlep)29433cb34c60Sahrens zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
29445f9bb2f3SPaul Dagnelie     const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
29455f9bb2f3SPaul Dagnelie     dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
29465f9bb2f3SPaul Dagnelie     avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
2947c99e4bdcSChris Kirby     uint64_t *action_handlep)
29483cb34c60Sahrens {
29493cb34c60Sahrens 	zfs_cmd_t zc = { 0 };
29503cb34c60Sahrens 	time_t begin_time;
2951bd6a198fSTom Erickson 	int ioctl_err, ioctl_errno, err;
29523cb34c60Sahrens 	char *cp;
29533cb34c60Sahrens 	struct drr_begin *drrb = &drr->drr_u.drr_begin;
29543cb34c60Sahrens 	char errbuf[1024];
295592241e0bSTom Erickson 	char prop_errbuf[1024];
2956bd6a198fSTom Erickson 	const char *chopprefix;
29573cb34c60Sahrens 	boolean_t newfs = B_FALSE;
29583cb34c60Sahrens 	boolean_t stream_wantsnewfs;
29593cb34c60Sahrens 	uint64_t parent_snapguid = 0;
29603cb34c60Sahrens 	prop_changelist_t *clp = NULL;
2961bb0ade09Sahrens 	nvlist_t *snapprops_nvlist = NULL;
296292241e0bSTom Erickson 	zprop_errflags_t prop_errflags;
2963bd6a198fSTom Erickson 	boolean_t recursive;
29643cb34c60Sahrens 
29653cb34c60Sahrens 	begin_time = time(NULL);
29663cb34c60Sahrens 
29673cb34c60Sahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
29683cb34c60Sahrens 	    "cannot receive"));
29693cb34c60Sahrens 
2970bd6a198fSTom Erickson 	recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
2971bd6a198fSTom Erickson 	    ENOENT);
2972bd6a198fSTom Erickson 
29733cb34c60Sahrens 	if (stream_avl != NULL) {
2974bb0ade09Sahrens 		char *snapname;
2975bb0ade09Sahrens 		nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
2976bb0ade09Sahrens 		    &snapname);
29773cb34c60Sahrens 		nvlist_t *props;
297800954d7bSahl 		int ret;
29793cb34c60Sahrens 
29803cb34c60Sahrens 		(void) nvlist_lookup_uint64(fs, "parentfromsnap",
29813cb34c60Sahrens 		    &parent_snapguid);
29823cb34c60Sahrens 		err = nvlist_lookup_nvlist(fs, "props", &props);
29833cb34c60Sahrens 		if (err)
29843cb34c60Sahrens 			VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
298500954d7bSahl 
298619b94df9SMatthew Ahrens 		if (flags->canmountoff) {
29873cb34c60Sahrens 			VERIFY(0 == nvlist_add_uint64(props,
29883cb34c60Sahrens 			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
29893cb34c60Sahrens 		}
299000954d7bSahl 		ret = zcmd_write_src_nvlist(hdl, &zc, props);
29913cb34c60Sahrens 		if (err)
29923cb34c60Sahrens 			nvlist_free(props);
299300954d7bSahl 
2994bb0ade09Sahrens 		if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
2995bb0ade09Sahrens 			VERIFY(0 == nvlist_lookup_nvlist(props,
2996bb0ade09Sahrens 			    snapname, &snapprops_nvlist));
2997bb0ade09Sahrens 		}
2998bb0ade09Sahrens 
299900954d7bSahl 		if (ret != 0)
300000954d7bSahl 			return (-1);
30013cb34c60Sahrens 	}
30023cb34c60Sahrens 
3003bd6a198fSTom Erickson 	cp = NULL;
3004bd6a198fSTom Erickson 
30053cb34c60Sahrens 	/*
30063cb34c60Sahrens 	 * Determine how much of the snapshot name stored in the stream
30073cb34c60Sahrens 	 * we are going to tack on to the name they specified on the
30083cb34c60Sahrens 	 * command line, and how much we are going to chop off.
30093cb34c60Sahrens 	 *
30103cb34c60Sahrens 	 * If they specified a snapshot, chop the entire name stored in
30113cb34c60Sahrens 	 * the stream.
30123cb34c60Sahrens 	 */
301319b94df9SMatthew Ahrens 	if (flags->istail) {
30143cb34c60Sahrens 		/*
3015bd6a198fSTom Erickson 		 * A filesystem was specified with -e. We want to tack on only
3016bd6a198fSTom Erickson 		 * the tail of the sent snapshot path.
30173cb34c60Sahrens 		 */
30183cb34c60Sahrens 		if (strchr(tosnap, '@')) {
30193cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
3020bd6a198fSTom Erickson 			    "argument - snapshot not allowed with -e"));
30213cb34c60Sahrens 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
30223cb34c60Sahrens 		}
3023bd6a198fSTom Erickson 
3024bd6a198fSTom Erickson 		chopprefix = strrchr(sendfs, '/');
3025bd6a198fSTom Erickson 
3026bd6a198fSTom Erickson 		if (chopprefix == NULL) {
3027bd6a198fSTom Erickson 			/*
3028bd6a198fSTom Erickson 			 * The tail is the poolname, so we need to
3029bd6a198fSTom Erickson 			 * prepend a path separator.
3030bd6a198fSTom Erickson 			 */
3031bd6a198fSTom Erickson 			int len = strlen(drrb->drr_toname);
3032bd6a198fSTom Erickson 			cp = malloc(len + 2);
3033bd6a198fSTom Erickson 			cp[0] = '/';
3034bd6a198fSTom Erickson 			(void) strcpy(&cp[1], drrb->drr_toname);
3035bd6a198fSTom Erickson 			chopprefix = cp;
3036bd6a198fSTom Erickson 		} else {
3037bd6a198fSTom Erickson 			chopprefix = drrb->drr_toname + (chopprefix - sendfs);
3038bd6a198fSTom Erickson 		}
303919b94df9SMatthew Ahrens 	} else if (flags->isprefix) {
3040bd6a198fSTom Erickson 		/*
3041bd6a198fSTom Erickson 		 * A filesystem was specified with -d. We want to tack on
3042bd6a198fSTom Erickson 		 * everything but the first element of the sent snapshot path
3043bd6a198fSTom Erickson 		 * (all but the pool name).
3044bd6a198fSTom Erickson 		 */
3045bd6a198fSTom Erickson 		if (strchr(tosnap, '@')) {
3046bd6a198fSTom Erickson 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
3047bd6a198fSTom Erickson 			    "argument - snapshot not allowed with -d"));
3048bd6a198fSTom Erickson 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3049bd6a198fSTom Erickson 		}
3050bd6a198fSTom Erickson 
3051bd6a198fSTom Erickson 		chopprefix = strchr(drrb->drr_toname, '/');
3052bd6a198fSTom Erickson 		if (chopprefix == NULL)
3053bd6a198fSTom Erickson 			chopprefix = strchr(drrb->drr_toname, '@');
30543cb34c60Sahrens 	} else if (strchr(tosnap, '@') == NULL) {
30553cb34c60Sahrens 		/*
3056bd6a198fSTom Erickson 		 * If a filesystem was specified without -d or -e, we want to
3057bd6a198fSTom Erickson 		 * tack on everything after the fs specified by 'zfs send'.
30583cb34c60Sahrens 		 */
3059bd6a198fSTom Erickson 		chopprefix = drrb->drr_toname + strlen(sendfs);
3060bd6a198fSTom Erickson 	} else {
3061bd6a198fSTom Erickson 		/* A snapshot was specified as an exact path (no -d or -e). */
3062bd6a198fSTom Erickson 		if (recursive) {
3063bd6a198fSTom Erickson 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3064bd6a198fSTom Erickson 			    "cannot specify snapshot name for multi-snapshot "
3065bd6a198fSTom Erickson 			    "stream"));
3066bd6a198fSTom Erickson 			return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
30673cb34c60Sahrens 		}
3068bd6a198fSTom Erickson 		chopprefix = drrb->drr_toname + strlen(drrb->drr_toname);
3069bd6a198fSTom Erickson 	}
3070bd6a198fSTom Erickson 
3071bd6a198fSTom Erickson 	ASSERT(strstr(drrb->drr_toname, sendfs) == drrb->drr_toname);
3072bd6a198fSTom Erickson 	ASSERT(chopprefix > drrb->drr_toname);
3073bd6a198fSTom Erickson 	ASSERT(chopprefix <= drrb->drr_toname + strlen(drrb->drr_toname));
3074bd6a198fSTom Erickson 	ASSERT(chopprefix[0] == '/' || chopprefix[0] == '@' ||
3075bd6a198fSTom Erickson 	    chopprefix[0] == '\0');
30763cb34c60Sahrens 
30773cb34c60Sahrens 	/*
30783cb34c60Sahrens 	 * Determine name of destination snapshot, store in zc_value.
30793cb34c60Sahrens 	 */
30803cb34c60Sahrens 	(void) strcpy(zc.zc_value, tosnap);
3081bd6a198fSTom Erickson 	(void) strncat(zc.zc_value, chopprefix, sizeof (zc.zc_value));
3082bd6a198fSTom Erickson 	free(cp);
308300954d7bSahl 	if (!zfs_name_valid(zc.zc_value, ZFS_TYPE_SNAPSHOT)) {
308400954d7bSahl 		zcmd_free_nvlists(&zc);
30853cb34c60Sahrens 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
308600954d7bSahl 	}
30873cb34c60Sahrens 
30883cb34c60Sahrens 	/*
30893cb34c60Sahrens 	 * Determine the name of the origin snapshot, store in zc_string.
30903cb34c60Sahrens 	 */
30913cb34c60Sahrens 	if (drrb->drr_flags & DRR_FLAG_CLONE) {
309219b94df9SMatthew Ahrens 		if (guid_to_name(hdl, zc.zc_value,
30938429b235SMatthew Ahrens 		    drrb->drr_fromguid, B_FALSE, zc.zc_string) != 0) {
309400954d7bSahl 			zcmd_free_nvlists(&zc);
30953cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
30963cb34c60Sahrens 			    "local origin for clone %s does not exist"),
30973cb34c60Sahrens 			    zc.zc_value);
30983cb34c60Sahrens 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
30993cb34c60Sahrens 		}
310019b94df9SMatthew Ahrens 		if (flags->verbose)
31013cb34c60Sahrens 			(void) printf("found clone origin %s\n", zc.zc_string);
31025f9bb2f3SPaul Dagnelie 	} else if (originsnap) {
3103*675fc291SMatthew Ahrens 		(void) strncpy(zc.zc_string, originsnap, sizeof (zc.zc_string));
31045f9bb2f3SPaul Dagnelie 		if (flags->verbose)
31055f9bb2f3SPaul Dagnelie 			(void) printf("using provided clone origin %s\n",
31065f9bb2f3SPaul Dagnelie 			    zc.zc_string);
31073cb34c60Sahrens 	}
31083cb34c60Sahrens 
31098429b235SMatthew Ahrens 	boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
31108429b235SMatthew Ahrens 	    DMU_BACKUP_FEATURE_RESUMING;
31113cb34c60Sahrens 	stream_wantsnewfs = (drrb->drr_fromguid == NULL ||
31128429b235SMatthew Ahrens 	    (drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming;
31133cb34c60Sahrens 
31143cb34c60Sahrens 	if (stream_wantsnewfs) {
31153cb34c60Sahrens 		/*
31163cb34c60Sahrens 		 * if the parent fs does not exist, look for it based on
31173cb34c60Sahrens 		 * the parent snap GUID
31183cb34c60Sahrens 		 */
31193cb34c60Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
31203cb34c60Sahrens 		    "cannot receive new filesystem stream"));
31213cb34c60Sahrens 
31223cb34c60Sahrens 		(void) strcpy(zc.zc_name, zc.zc_value);
31233cb34c60Sahrens 		cp = strrchr(zc.zc_name, '/');
31243cb34c60Sahrens 		if (cp)
31253cb34c60Sahrens 			*cp = '\0';
31263cb34c60Sahrens 		if (cp &&
31273cb34c60Sahrens 		    !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
3128*675fc291SMatthew Ahrens 			char suffix[ZFS_MAX_DATASET_NAME_LEN];
31293cb34c60Sahrens 			(void) strcpy(suffix, strrchr(zc.zc_value, '/'));
313019b94df9SMatthew Ahrens 			if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
31318429b235SMatthew Ahrens 			    B_FALSE, zc.zc_value) == 0) {
31323cb34c60Sahrens 				*strchr(zc.zc_value, '@') = '\0';
31333cb34c60Sahrens 				(void) strcat(zc.zc_value, suffix);
31343cb34c60Sahrens 			}
31353cb34c60Sahrens 		}
31363cb34c60Sahrens 	} else {
31373cb34c60Sahrens 		/*
31383cb34c60Sahrens 		 * if the fs does not exist, look for it based on the
31393cb34c60Sahrens 		 * fromsnap GUID
31403cb34c60Sahrens 		 */
31413cb34c60Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
31423cb34c60Sahrens 		    "cannot receive incremental stream"));
31433cb34c60Sahrens 
31443cb34c60Sahrens 		(void) strcpy(zc.zc_name, zc.zc_value);
31453cb34c60Sahrens 		*strchr(zc.zc_name, '@') = '\0';
31463cb34c60Sahrens 
314783d7f9feSTom Erickson 		/*
314883d7f9feSTom Erickson 		 * If the exact receive path was specified and this is the
314983d7f9feSTom Erickson 		 * topmost path in the stream, then if the fs does not exist we
315083d7f9feSTom Erickson 		 * should look no further.
315183d7f9feSTom Erickson 		 */
315219b94df9SMatthew Ahrens 		if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
315383d7f9feSTom Erickson 		    strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
315483d7f9feSTom Erickson 		    !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
3155*675fc291SMatthew Ahrens 			char snap[ZFS_MAX_DATASET_NAME_LEN];
31563cb34c60Sahrens 			(void) strcpy(snap, strchr(zc.zc_value, '@'));
315719b94df9SMatthew Ahrens 			if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
31588429b235SMatthew Ahrens 			    B_FALSE, zc.zc_value) == 0) {
31593cb34c60Sahrens 				*strchr(zc.zc_value, '@') = '\0';
31603cb34c60Sahrens 				(void) strcat(zc.zc_value, snap);
31613cb34c60Sahrens 			}
31623cb34c60Sahrens 		}
31633cb34c60Sahrens 	}
31643cb34c60Sahrens 
31653cb34c60Sahrens 	(void) strcpy(zc.zc_name, zc.zc_value);
31663cb34c60Sahrens 	*strchr(zc.zc_name, '@') = '\0';
31673cb34c60Sahrens 
31683cb34c60Sahrens 	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
31693cb34c60Sahrens 		zfs_handle_t *zhp;
3170bd6a198fSTom Erickson 
31713cb34c60Sahrens 		/*
31728429b235SMatthew Ahrens 		 * Destination fs exists.  It must be one of these cases:
31738429b235SMatthew Ahrens 		 *  - an incremental send stream
31748429b235SMatthew Ahrens 		 *  - the stream specifies a new fs (full stream or clone)
31758429b235SMatthew Ahrens 		 *    and they want us to blow away the existing fs (and
31768429b235SMatthew Ahrens 		 *    have therefore specified -F and removed any snapshots)
31778429b235SMatthew Ahrens 		 *  - we are resuming a failed receive.
31783cb34c60Sahrens 		 */
31793cb34c60Sahrens 		if (stream_wantsnewfs) {
318019b94df9SMatthew Ahrens 			if (!flags->force) {
318100954d7bSahl 				zcmd_free_nvlists(&zc);
31823cb34c60Sahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
31833cb34c60Sahrens 				    "destination '%s' exists\n"
31843cb34c60Sahrens 				    "must specify -F to overwrite it"),
31853cb34c60Sahrens 				    zc.zc_name);
31863cb34c60Sahrens 				return (zfs_error(hdl, EZFS_EXISTS, errbuf));
31873cb34c60Sahrens 			}
31883cb34c60Sahrens 			if (ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
31893cb34c60Sahrens 			    &zc) == 0) {
319000954d7bSahl 				zcmd_free_nvlists(&zc);
31913cb34c60Sahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
31923cb34c60Sahrens 				    "destination has snapshots (eg. %s)\n"
31933cb34c60Sahrens 				    "must destroy them to overwrite it"),
31943cb34c60Sahrens 				    zc.zc_name);
31953cb34c60Sahrens 				return (zfs_error(hdl, EZFS_EXISTS, errbuf));
31963cb34c60Sahrens 			}
31973cb34c60Sahrens 		}
31983cb34c60Sahrens 
319900954d7bSahl 		if ((zhp = zfs_open(hdl, zc.zc_name,
320000954d7bSahl 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
320100954d7bSahl 			zcmd_free_nvlists(&zc);
32023cb34c60Sahrens 			return (-1);
320300954d7bSahl 		}
320400954d7bSahl 
32053cb34c60Sahrens 		if (stream_wantsnewfs &&
32063cb34c60Sahrens 		    zhp->zfs_dmustats.dds_origin[0]) {
320700954d7bSahl 			zcmd_free_nvlists(&zc);
32083cb34c60Sahrens 			zfs_close(zhp);
32093cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
32103cb34c60Sahrens 			    "destination '%s' is a clone\n"
32113cb34c60Sahrens 			    "must destroy it to overwrite it"),
32123cb34c60Sahrens 			    zc.zc_name);
32133cb34c60Sahrens 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
32143cb34c60Sahrens 		}
32153cb34c60Sahrens 
321619b94df9SMatthew Ahrens 		if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
32173cb34c60Sahrens 		    stream_wantsnewfs) {
32183cb34c60Sahrens 			/* We can't do online recv in this case */
32190069fd67STim Haley 			clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
322000954d7bSahl 			if (clp == NULL) {
322188bb18d2SLori Alt 				zfs_close(zhp);
322200954d7bSahl 				zcmd_free_nvlists(&zc);
32233cb34c60Sahrens 				return (-1);
322400954d7bSahl 			}
32253cb34c60Sahrens 			if (changelist_prefix(clp) != 0) {
32263cb34c60Sahrens 				changelist_free(clp);
322788bb18d2SLori Alt 				zfs_close(zhp);
322800954d7bSahl 				zcmd_free_nvlists(&zc);
32293cb34c60Sahrens 				return (-1);
32303cb34c60Sahrens 			}
32313cb34c60Sahrens 		}
32328429b235SMatthew Ahrens 
32338429b235SMatthew Ahrens 		/*
32348429b235SMatthew Ahrens 		 * If we are resuming a newfs, set newfs here so that we will
32358429b235SMatthew Ahrens 		 * mount it if the recv succeeds this time.  We can tell
32368429b235SMatthew Ahrens 		 * that it was a newfs on the first recv because the fs
32378429b235SMatthew Ahrens 		 * itself will be inconsistent (if the fs existed when we
32388429b235SMatthew Ahrens 		 * did the first recv, we would have received it into
32398429b235SMatthew Ahrens 		 * .../%recv).
32408429b235SMatthew Ahrens 		 */
32418429b235SMatthew Ahrens 		if (resuming && zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT))
32428429b235SMatthew Ahrens 			newfs = B_TRUE;
32438429b235SMatthew Ahrens 
32443cb34c60Sahrens 		zfs_close(zhp);
32453cb34c60Sahrens 	} else {
32463cb34c60Sahrens 		/*
324700954d7bSahl 		 * Destination filesystem does not exist.  Therefore we better
324800954d7bSahl 		 * be creating a new filesystem (either from a full backup, or
324900954d7bSahl 		 * a clone).  It would therefore be invalid if the user
325000954d7bSahl 		 * specified only the pool name (i.e. if the destination name
325100954d7bSahl 		 * contained no slash character).
32523cb34c60Sahrens 		 */
325300954d7bSahl 		if (!stream_wantsnewfs ||
325400954d7bSahl 		    (cp = strrchr(zc.zc_name, '/')) == NULL) {
325500954d7bSahl 			zcmd_free_nvlists(&zc);
32563cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
32573cb34c60Sahrens 			    "destination '%s' does not exist"), zc.zc_name);
32583cb34c60Sahrens 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
32593cb34c60Sahrens 		}
32603cb34c60Sahrens 
326100954d7bSahl 		/*
326200954d7bSahl 		 * Trim off the final dataset component so we perform the
326300954d7bSahl 		 * recvbackup ioctl to the filesystems's parent.
326400954d7bSahl 		 */
326500954d7bSahl 		*cp = '\0';
32663cb34c60Sahrens 
326719b94df9SMatthew Ahrens 		if (flags->isprefix && !flags->istail && !flags->dryrun &&
326800954d7bSahl 		    create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
326900954d7bSahl 			zcmd_free_nvlists(&zc);
327000954d7bSahl 			return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
32713cb34c60Sahrens 		}
32723cb34c60Sahrens 
32733cb34c60Sahrens 		newfs = B_TRUE;
32743cb34c60Sahrens 	}
32753cb34c60Sahrens 
32768429b235SMatthew Ahrens 	zc.zc_begin_record = *drr_noswap;
32773cb34c60Sahrens 	zc.zc_cookie = infd;
327819b94df9SMatthew Ahrens 	zc.zc_guid = flags->force;
32798429b235SMatthew Ahrens 	zc.zc_resumable = flags->resumable;
328019b94df9SMatthew Ahrens 	if (flags->verbose) {
32813cb34c60Sahrens 		(void) printf("%s %s stream of %s into %s\n",
328219b94df9SMatthew Ahrens 		    flags->dryrun ? "would receive" : "receiving",
32833cb34c60Sahrens 		    drrb->drr_fromguid ? "incremental" : "full",
32843cb34c60Sahrens 		    drrb->drr_toname, zc.zc_value);
32853cb34c60Sahrens 		(void) fflush(stdout);
32863cb34c60Sahrens 	}
32873cb34c60Sahrens 
328819b94df9SMatthew Ahrens 	if (flags->dryrun) {
328900954d7bSahl 		zcmd_free_nvlists(&zc);
329019b94df9SMatthew Ahrens 		return (recv_skip(hdl, infd, flags->byteswap));
329100954d7bSahl 	}
32923cb34c60Sahrens 
329392241e0bSTom Erickson 	zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
329492241e0bSTom Erickson 	zc.zc_nvlist_dst_size = sizeof (prop_errbuf);
3295c99e4bdcSChris Kirby 	zc.zc_cleanup_fd = cleanup_fd;
3296c99e4bdcSChris Kirby 	zc.zc_action_handle = *action_handlep;
329792241e0bSTom Erickson 
32983cb34c60Sahrens 	err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
32993cb34c60Sahrens 	ioctl_errno = errno;
330092241e0bSTom Erickson 	prop_errflags = (zprop_errflags_t)zc.zc_obj;
330192241e0bSTom Erickson 
330292241e0bSTom Erickson 	if (err == 0) {
330392241e0bSTom Erickson 		nvlist_t *prop_errors;
330492241e0bSTom Erickson 		VERIFY(0 == nvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
330592241e0bSTom Erickson 		    zc.zc_nvlist_dst_size, &prop_errors, 0));
330692241e0bSTom Erickson 
330792241e0bSTom Erickson 		nvpair_t *prop_err = NULL;
330892241e0bSTom Erickson 
330992241e0bSTom Erickson 		while ((prop_err = nvlist_next_nvpair(prop_errors,
331092241e0bSTom Erickson 		    prop_err)) != NULL) {
331192241e0bSTom Erickson 			char tbuf[1024];
331292241e0bSTom Erickson 			zfs_prop_t prop;
331392241e0bSTom Erickson 			int intval;
331492241e0bSTom Erickson 
331592241e0bSTom Erickson 			prop = zfs_name_to_prop(nvpair_name(prop_err));
331692241e0bSTom Erickson 			(void) nvpair_value_int32(prop_err, &intval);
331792241e0bSTom Erickson 			if (strcmp(nvpair_name(prop_err),
331892241e0bSTom Erickson 			    ZPROP_N_MORE_ERRORS) == 0) {
331992241e0bSTom Erickson 				trunc_prop_errs(intval);
332092241e0bSTom Erickson 				break;
332192241e0bSTom Erickson 			} else {
332292241e0bSTom Erickson 				(void) snprintf(tbuf, sizeof (tbuf),
332392241e0bSTom Erickson 				    dgettext(TEXT_DOMAIN,
332492241e0bSTom Erickson 				    "cannot receive %s property on %s"),
332592241e0bSTom Erickson 				    nvpair_name(prop_err), zc.zc_name);
332692241e0bSTom Erickson 				zfs_setprop_error(hdl, prop, intval, tbuf);
332792241e0bSTom Erickson 			}
332892241e0bSTom Erickson 		}
332992241e0bSTom Erickson 		nvlist_free(prop_errors);
333092241e0bSTom Erickson 	}
333192241e0bSTom Erickson 
333292241e0bSTom Erickson 	zc.zc_nvlist_dst = 0;
333392241e0bSTom Erickson 	zc.zc_nvlist_dst_size = 0;
3334bb0ade09Sahrens 	zcmd_free_nvlists(&zc);
3335bb0ade09Sahrens 
3336bb0ade09Sahrens 	if (err == 0 && snapprops_nvlist) {
3337bb0ade09Sahrens 		zfs_cmd_t zc2 = { 0 };
3338bb0ade09Sahrens 
3339b6c10d80Sahrens 		(void) strcpy(zc2.zc_name, zc.zc_value);
334092241e0bSTom Erickson 		zc2.zc_cookie = B_TRUE; /* received */
3341bb0ade09Sahrens 		if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) {
3342bb0ade09Sahrens 			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2);
3343bb0ade09Sahrens 			zcmd_free_nvlists(&zc2);
3344bb0ade09Sahrens 		}
3345bb0ade09Sahrens 	}
3346bb0ade09Sahrens 
3347bd6a198fSTom Erickson 	if (err && (ioctl_errno == ENOENT || ioctl_errno == EEXIST)) {
33483cb34c60Sahrens 		/*
33493cb34c60Sahrens 		 * It may be that this snapshot already exists,
33503cb34c60Sahrens 		 * in which case we want to consume & ignore it
33513cb34c60Sahrens 		 * rather than failing.
33523cb34c60Sahrens 		 */
33533cb34c60Sahrens 		avl_tree_t *local_avl;
33543cb34c60Sahrens 		nvlist_t *local_nv, *fs;
3355bd6a198fSTom Erickson 		cp = strchr(zc.zc_value, '@');
33563cb34c60Sahrens 
33573cb34c60Sahrens 		/*
33583cb34c60Sahrens 		 * XXX Do this faster by just iterating over snaps in
33593cb34c60Sahrens 		 * this fs.  Also if zc_value does not exist, we will
33603cb34c60Sahrens 		 * get a strange "does not exist" error message.
33613cb34c60Sahrens 		 */
33623cb34c60Sahrens 		*cp = '\0';
336392241e0bSTom Erickson 		if (gather_nvlist(hdl, zc.zc_value, NULL, NULL, B_FALSE,
33643cb34c60Sahrens 		    &local_nv, &local_avl) == 0) {
33653cb34c60Sahrens 			*cp = '@';
33663cb34c60Sahrens 			fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
33673cb34c60Sahrens 			fsavl_destroy(local_avl);
33683cb34c60Sahrens 			nvlist_free(local_nv);
33693cb34c60Sahrens 
33703cb34c60Sahrens 			if (fs != NULL) {
337119b94df9SMatthew Ahrens 				if (flags->verbose) {
33723cb34c60Sahrens 					(void) printf("snap %s already exists; "
33733cb34c60Sahrens 					    "ignoring\n", zc.zc_value);
33743cb34c60Sahrens 				}
337592241e0bSTom Erickson 				err = ioctl_err = recv_skip(hdl, infd,
337619b94df9SMatthew Ahrens 				    flags->byteswap);
33773cb34c60Sahrens 			}
33783cb34c60Sahrens 		}
33793cb34c60Sahrens 		*cp = '@';
33803cb34c60Sahrens 	}
33813cb34c60Sahrens 
33823cb34c60Sahrens 	if (ioctl_err != 0) {
33833cb34c60Sahrens 		switch (ioctl_errno) {
33843cb34c60Sahrens 		case ENODEV:
33853cb34c60Sahrens 			cp = strchr(zc.zc_value, '@');
33863cb34c60Sahrens 			*cp = '\0';
33873cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
33883cb34c60Sahrens 			    "most recent snapshot of %s does not\n"
33893cb34c60Sahrens 			    "match incremental source"), zc.zc_value);
33903cb34c60Sahrens 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
33913cb34c60Sahrens 			*cp = '@';
33923cb34c60Sahrens 			break;
33933cb34c60Sahrens 		case ETXTBSY:
33943cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
33953cb34c60Sahrens 			    "destination %s has been modified\n"
33963cb34c60Sahrens 			    "since most recent snapshot"), zc.zc_name);
33973cb34c60Sahrens 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
33983cb34c60Sahrens 			break;
33993cb34c60Sahrens 		case EEXIST:
34003cb34c60Sahrens 			cp = strchr(zc.zc_value, '@');
34013cb34c60Sahrens 			if (newfs) {
34023cb34c60Sahrens 				/* it's the containing fs that exists */
34033cb34c60Sahrens 				*cp = '\0';
34043cb34c60Sahrens 			}
34053cb34c60Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
34063cb34c60Sahrens 			    "destination already exists"));
34073cb34c60Sahrens 			(void) zfs_error_fmt(hdl, EZFS_EXISTS,
34083cb34c60Sahrens 			    dgettext(TEXT_DOMAIN, "cannot restore to %s"),
34093cb34c60Sahrens 			    zc.zc_value);
34103cb34c60Sahrens 			*cp = '@';
34113cb34c60Sahrens 			break;
34123cb34c60Sahrens 		case EINVAL:
34133cb34c60Sahrens 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
34143cb34c60Sahrens 			break;
34153cb34c60Sahrens 		case ECKSUM:
34168429b235SMatthew Ahrens 			recv_ecksum_set_aux(hdl, zc.zc_value, flags->resumable);
34173cb34c60Sahrens 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
34183cb34c60Sahrens 			break;
3419dc7cd546SMark Shellenbaum 		case ENOTSUP:
3420dc7cd546SMark Shellenbaum 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3421dc7cd546SMark Shellenbaum 			    "pool must be upgraded to receive this stream."));
3422dc7cd546SMark Shellenbaum 			(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
3423dc7cd546SMark Shellenbaum 			break;
342437f8ae65SJohn Harres 		case EDQUOT:
342537f8ae65SJohn Harres 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
342637f8ae65SJohn Harres 			    "destination %s space quota exceeded"), zc.zc_name);
342719b94df9SMatthew Ahrens 			(void) zfs_error(hdl, EZFS_NOSPC, errbuf);
342837f8ae65SJohn Harres 			break;
34293cb34c60Sahrens 		default:
34303cb34c60Sahrens 			(void) zfs_standard_error(hdl, ioctl_errno, errbuf);
34313cb34c60Sahrens 		}
34323cb34c60Sahrens 	}
34333cb34c60Sahrens 
34343cb34c60Sahrens 	/*
3435681d9761SEric Taylor 	 * Mount the target filesystem (if created).  Also mount any
3436681d9761SEric Taylor 	 * children of the target filesystem if we did a replication
3437681d9761SEric Taylor 	 * receive (indicated by stream_avl being non-NULL).
34383cb34c60Sahrens 	 */
34393cb34c60Sahrens 	cp = strchr(zc.zc_value, '@');
34403cb34c60Sahrens 	if (cp && (ioctl_err == 0 || !newfs)) {
34413cb34c60Sahrens 		zfs_handle_t *h;
34423cb34c60Sahrens 
34433cb34c60Sahrens 		*cp = '\0';
34443cb34c60Sahrens 		h = zfs_open(hdl, zc.zc_value,
34453cb34c60Sahrens 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
344604bb726eSahl 		if (h != NULL) {
34473cb34c60Sahrens 			if (h->zfs_type == ZFS_TYPE_VOLUME) {
34480069fd67STim Haley 				*cp = '@';
344988bb18d2SLori Alt 			} else if (newfs || stream_avl) {
34500069fd67STim Haley 				/*
34510069fd67STim Haley 				 * Track the first/top of hierarchy fs,
34520069fd67STim Haley 				 * for mounting and sharing later.
34530069fd67STim Haley 				 */
34540069fd67STim Haley 				if (top_zfs && *top_zfs == NULL)
34550069fd67STim Haley 					*top_zfs = zfs_strdup(hdl, zc.zc_value);
34563cb34c60Sahrens 			}
34573cb34c60Sahrens 			zfs_close(h);
34583cb34c60Sahrens 		}
34590069fd67STim Haley 		*cp = '@';
34603cb34c60Sahrens 	}
34613cb34c60Sahrens 
34623cb34c60Sahrens 	if (clp) {
34633cb34c60Sahrens 		err |= changelist_postfix(clp);
34643cb34c60Sahrens 		changelist_free(clp);
34653cb34c60Sahrens 	}
34663cb34c60Sahrens 
346792241e0bSTom Erickson 	if (prop_errflags & ZPROP_ERR_NOCLEAR) {
346892241e0bSTom Erickson 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
346992241e0bSTom Erickson 		    "failed to clear unreceived properties on %s"),
347092241e0bSTom Erickson 		    zc.zc_name);
347192241e0bSTom Erickson 		(void) fprintf(stderr, "\n");
347292241e0bSTom Erickson 	}
347392241e0bSTom Erickson 	if (prop_errflags & ZPROP_ERR_NORESTORE) {
347492241e0bSTom Erickson 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
347592241e0bSTom Erickson 		    "failed to restore original properties on %s"),
347692241e0bSTom Erickson 		    zc.zc_name);
347792241e0bSTom Erickson 		(void) fprintf(stderr, "\n");
347892241e0bSTom Erickson 	}
347992241e0bSTom Erickson 
34803cb34c60Sahrens 	if (err || ioctl_err)
34813cb34c60Sahrens 		return (-1);
34823cb34c60Sahrens 
3483c99e4bdcSChris Kirby 	*action_handlep = zc.zc_action_handle;
3484c99e4bdcSChris Kirby 
348519b94df9SMatthew Ahrens 	if (flags->verbose) {
34863cb34c60Sahrens 		char buf1[64];
34873cb34c60Sahrens 		char buf2[64];
34883cb34c60Sahrens 		uint64_t bytes = zc.zc_cookie;
34893cb34c60Sahrens 		time_t delta = time(NULL) - begin_time;
34903cb34c60Sahrens 		if (delta == 0)
34913cb34c60Sahrens 			delta = 1;
34923cb34c60Sahrens 		zfs_nicenum(bytes, buf1, sizeof (buf1));
34933cb34c60Sahrens 		zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
34943cb34c60Sahrens 
34953cb34c60Sahrens 		(void) printf("received %sB stream in %lu seconds (%sB/sec)\n",
34963cb34c60Sahrens 		    buf1, delta, buf2);
34973cb34c60Sahrens 	}
34983cb34c60Sahrens 
34993cb34c60Sahrens 	return (0);
35003cb34c60Sahrens }
35013cb34c60Sahrens 
35020069fd67STim Haley static int
zfs_receive_impl(libzfs_handle_t * hdl,const char * tosnap,const char * originsnap,recvflags_t * flags,int infd,const char * sendfs,nvlist_t * stream_nv,avl_tree_t * stream_avl,char ** top_zfs,int cleanup_fd,uint64_t * action_handlep)35035f9bb2f3SPaul Dagnelie zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
35045f9bb2f3SPaul Dagnelie     const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
35055f9bb2f3SPaul Dagnelie     nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
35065f9bb2f3SPaul Dagnelie     uint64_t *action_handlep)
35073cb34c60Sahrens {
35083cb34c60Sahrens 	int err;
35093cb34c60Sahrens 	dmu_replay_record_t drr, drr_noswap;
35103cb34c60Sahrens 	struct drr_begin *drrb = &drr.drr_u.drr_begin;
35113cb34c60Sahrens 	char errbuf[1024];
35123cb34c60Sahrens 	zio_cksum_t zcksum = { 0 };
35139e69d7d0SLori Alt 	uint64_t featureflags;
35149e69d7d0SLori Alt 	int hdrtype;
35153cb34c60Sahrens 
35163cb34c60Sahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
35173cb34c60Sahrens 	    "cannot receive"));
35183cb34c60Sahrens 
351919b94df9SMatthew Ahrens 	if (flags->isprefix &&
35203cb34c60Sahrens 	    !zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
35213cb34c60Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
35223cb34c60Sahrens 		    "(%s) does not exist"), tosnap);
35233cb34c60Sahrens 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
35243cb34c60Sahrens 	}
35255f9bb2f3SPaul Dagnelie 	if (originsnap &&
35265f9bb2f3SPaul Dagnelie 	    !zfs_dataset_exists(hdl, originsnap, ZFS_TYPE_DATASET)) {
35275f9bb2f3SPaul Dagnelie 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified origin fs "
35285f9bb2f3SPaul Dagnelie 		    "(%s) does not exist"), originsnap);
35295f9bb2f3SPaul Dagnelie 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
35305f9bb2f3SPaul Dagnelie 	}
35313cb34c60Sahrens 
35323cb34c60Sahrens 	/* read in the BEGIN record */
35333cb34c60Sahrens 	if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE,
35343cb34c60Sahrens 	    &zcksum)))
35353cb34c60Sahrens 		return (err);
35363cb34c60Sahrens 
35373cb34c60Sahrens 	if (drr.drr_type == DRR_END || drr.drr_type == BSWAP_32(DRR_END)) {
35383cb34c60Sahrens 		/* It's the double end record at the end of a package */
35393cb34c60Sahrens 		return (ENODATA);
35403cb34c60Sahrens 	}
35413cb34c60Sahrens 
35423cb34c60Sahrens 	/* the kernel needs the non-byteswapped begin record */
35433cb34c60Sahrens 	drr_noswap = drr;
35443cb34c60Sahrens 
354519b94df9SMatthew Ahrens 	flags->byteswap = B_FALSE;
35463cb34c60Sahrens 	if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
35473cb34c60Sahrens 		/*
35483cb34c60Sahrens 		 * We computed the checksum in the wrong byteorder in
35493cb34c60Sahrens 		 * recv_read() above; do it again correctly.
35503cb34c60Sahrens 		 */
35513cb34c60Sahrens 		bzero(&zcksum, sizeof (zio_cksum_t));
35523cb34c60Sahrens 		fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum);
355319b94df9SMatthew Ahrens 		flags->byteswap = B_TRUE;
35543cb34c60Sahrens 
35553cb34c60Sahrens 		drr.drr_type = BSWAP_32(drr.drr_type);
35563cb34c60Sahrens 		drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
35573cb34c60Sahrens 		drrb->drr_magic = BSWAP_64(drrb->drr_magic);
35589e69d7d0SLori Alt 		drrb->drr_versioninfo = BSWAP_64(drrb->drr_versioninfo);
35593cb34c60Sahrens 		drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time);
35603cb34c60Sahrens 		drrb->drr_type = BSWAP_32(drrb->drr_type);
35613cb34c60Sahrens 		drrb->drr_flags = BSWAP_32(drrb->drr_flags);
35623cb34c60Sahrens 		drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
35633cb34c60Sahrens 		drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid);
35643cb34c60Sahrens 	}
35653cb34c60Sahrens 
35663cb34c60Sahrens 	if (drrb->drr_magic != DMU_BACKUP_MAGIC || drr.drr_type != DRR_BEGIN) {
35673cb34c60Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
35683cb34c60Sahrens 		    "stream (bad magic number)"));
35693cb34c60Sahrens 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
35703cb34c60Sahrens 	}
35713cb34c60Sahrens 
35729e69d7d0SLori Alt 	featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
35739e69d7d0SLori Alt 	hdrtype = DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo);
35749e69d7d0SLori Alt 
35759e69d7d0SLori Alt 	if (!DMU_STREAM_SUPPORTED(featureflags) ||
35769e69d7d0SLori Alt 	    (hdrtype != DMU_SUBSTREAM && hdrtype != DMU_COMPOUNDSTREAM)) {
35779e69d7d0SLori Alt 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
35789e69d7d0SLori Alt 		    "stream has unsupported feature, feature flags = %lx"),
35799e69d7d0SLori Alt 		    featureflags);
35809e69d7d0SLori Alt 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
35819e69d7d0SLori Alt 	}
35829e69d7d0SLori Alt 
35833cb34c60Sahrens 	if (strchr(drrb->drr_toname, '@') == NULL) {
35843cb34c60Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
35853cb34c60Sahrens 		    "stream (bad snapshot name)"));
35863cb34c60Sahrens 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
35873cb34c60Sahrens 	}
35883cb34c60Sahrens 
35899e69d7d0SLori Alt 	if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_SUBSTREAM) {
3590*675fc291SMatthew Ahrens 		char nonpackage_sendfs[ZFS_MAX_DATASET_NAME_LEN];
3591bd6a198fSTom Erickson 		if (sendfs == NULL) {
3592bd6a198fSTom Erickson 			/*
3593bd6a198fSTom Erickson 			 * We were not called from zfs_receive_package(). Get
3594bd6a198fSTom Erickson 			 * the fs specified by 'zfs send'.
3595bd6a198fSTom Erickson 			 */
3596bd6a198fSTom Erickson 			char *cp;
3597bd6a198fSTom Erickson 			(void) strlcpy(nonpackage_sendfs,
3598*675fc291SMatthew Ahrens 			    drr.drr_u.drr_begin.drr_toname,
3599*675fc291SMatthew Ahrens 			    sizeof (nonpackage_sendfs));
3600bd6a198fSTom Erickson 			if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
3601bd6a198fSTom Erickson 				*cp = '\0';
3602bd6a198fSTom Erickson 			sendfs = nonpackage_sendfs;
3603bd6a198fSTom Erickson 		}
36045f9bb2f3SPaul Dagnelie 		return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
36055f9bb2f3SPaul Dagnelie 		    &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
36065f9bb2f3SPaul Dagnelie 		    cleanup_fd, action_handlep));
360783d7f9feSTom Erickson 	} else {
36089e69d7d0SLori Alt 		assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
36099e69d7d0SLori Alt 		    DMU_COMPOUNDSTREAM);
36105f9bb2f3SPaul Dagnelie 		return (zfs_receive_package(hdl, infd, tosnap, flags, &drr,
36115f9bb2f3SPaul Dagnelie 		    &zcksum, top_zfs, cleanup_fd, action_handlep));
36123cb34c60Sahrens 	}
36133cb34c60Sahrens }
36140069fd67STim Haley 
36150069fd67STim Haley /*
36160069fd67STim Haley  * Restores a backup of tosnap from the file descriptor specified by infd.
36170069fd67STim Haley  * Return 0 on total success, -2 if some things couldn't be
36180069fd67STim Haley  * destroyed/renamed/promoted, -1 if some things couldn't be received.
36198429b235SMatthew Ahrens  * (-1 will override -2, if -1 and the resumable flag was specified the
36208429b235SMatthew Ahrens  * transfer can be resumed if the sending side supports it).
36210069fd67STim Haley  */
36220069fd67STim Haley int
zfs_receive(libzfs_handle_t * hdl,const char * tosnap,nvlist_t * props,recvflags_t * flags,int infd,avl_tree_t * stream_avl)36235f9bb2f3SPaul Dagnelie zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
36245f9bb2f3SPaul Dagnelie     recvflags_t *flags, int infd, avl_tree_t *stream_avl)
36250069fd67STim Haley {
36260069fd67STim Haley 	char *top_zfs = NULL;
36270069fd67STim Haley 	int err;
3628c99e4bdcSChris Kirby 	int cleanup_fd;
3629c99e4bdcSChris Kirby 	uint64_t action_handle = 0;
36305f9bb2f3SPaul Dagnelie 	char *originsnap = NULL;
36315f9bb2f3SPaul Dagnelie 	if (props) {
36325f9bb2f3SPaul Dagnelie 		err = nvlist_lookup_string(props, "origin", &originsnap);
36335f9bb2f3SPaul Dagnelie 		if (err && err != ENOENT)
36345f9bb2f3SPaul Dagnelie 			return (err);
36355f9bb2f3SPaul Dagnelie 	}
3636c99e4bdcSChris Kirby 
3637c99e4bdcSChris Kirby 	cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
3638c99e4bdcSChris Kirby 	VERIFY(cleanup_fd >= 0);
36390069fd67STim Haley 
36405f9bb2f3SPaul Dagnelie 	err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
3641c99e4bdcSChris Kirby 	    stream_avl, &top_zfs, cleanup_fd, &action_handle);
3642c99e4bdcSChris Kirby 
3643c99e4bdcSChris Kirby 	VERIFY(0 == close(cleanup_fd));
36440069fd67STim Haley 
364519b94df9SMatthew Ahrens 	if (err == 0 && !flags->nomount && top_zfs) {
36460069fd67STim Haley 		zfs_handle_t *zhp;
36470069fd67STim Haley 		prop_changelist_t *clp;
36480069fd67STim Haley 
36490069fd67STim Haley 		zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM);
36500069fd67STim Haley 		if (zhp != NULL) {
36510069fd67STim Haley 			clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
36520069fd67STim Haley 			    CL_GATHER_MOUNT_ALWAYS, 0);
36530069fd67STim Haley 			zfs_close(zhp);
36540069fd67STim Haley 			if (clp != NULL) {
36550069fd67STim Haley 				/* mount and share received datasets */
36560069fd67STim Haley 				err = changelist_postfix(clp);
36570069fd67STim Haley 				changelist_free(clp);
36580069fd67STim Haley 			}
36590069fd67STim Haley 		}
36600069fd67STim Haley 		if (zhp == NULL || clp == NULL || err)
36610069fd67STim Haley 			err = -1;
36620069fd67STim Haley 	}
36630069fd67STim Haley 	if (top_zfs)
36640069fd67STim Haley 		free(top_zfs);
36650069fd67STim Haley 
36660069fd67STim Haley 	return (err);
36670069fd67STim Haley }
3668