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.
24c1a50c7eSMatthew 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>
455d7b4d43SMatthew 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"
51c1a50c7eSMatthew 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
59a2cdcdd2SPaul Dagnelie static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
60a2cdcdd2SPaul Dagnelie recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
61a2cdcdd2SPaul Dagnelie uint64_t *);
62c1a50c7eSMatthew Ahrens static int guid_to_name(libzfs_handle_t *, const char *,
63c1a50c7eSMatthew 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)18698110f08SMatthew Ahrens dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
18798110f08SMatthew Ahrens zio_cksum_t *zc, int outfd)
1889e69d7d0SLori Alt {
18998110f08SMatthew Ahrens ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
19098110f08SMatthew Ahrens ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
19198110f08SMatthew Ahrens fletcher_4_incremental_native(drr,
19298110f08SMatthew Ahrens offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
19398110f08SMatthew Ahrens if (drr->drr_type != DRR_BEGIN) {
19498110f08SMatthew Ahrens ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
19598110f08SMatthew Ahrens drr_checksum.drr_checksum));
19698110f08SMatthew Ahrens drr->drr_u.drr_checksum.drr_checksum = *zc;
19798110f08SMatthew Ahrens }
19898110f08SMatthew Ahrens fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum,
19998110f08SMatthew Ahrens sizeof (zio_cksum_t), zc);
20098110f08SMatthew Ahrens if (write(outfd, drr, sizeof (*drr)) == -1)
20198110f08SMatthew Ahrens return (errno);
20298110f08SMatthew Ahrens if (payload_len != 0) {
20398110f08SMatthew Ahrens fletcher_4_incremental_native(payload, payload_len, zc);
20498110f08SMatthew Ahrens if (write(outfd, payload, payload_len) == -1)
20598110f08SMatthew Ahrens return (errno);
20698110f08SMatthew Ahrens }
20798110f08SMatthew Ahrens return (0);
2089e69d7d0SLori Alt }
2099e69d7d0SLori Alt
21098f9d73dSMarcel Telka int
zfs_send_compoundstream_begin(zfs_handle_t * zhp,const char * tosnap,int featureflags,void * payload,int payload_len,int outfd)21198f9d73dSMarcel Telka zfs_send_compoundstream_begin(zfs_handle_t *zhp, const char *tosnap,
21298f9d73dSMarcel Telka int featureflags, void *payload, int payload_len, int outfd)
21398f9d73dSMarcel Telka {
21498f9d73dSMarcel Telka dmu_replay_record_t drr = { 0 };
21598f9d73dSMarcel Telka zio_cksum_t zc = { 0 };
21698f9d73dSMarcel Telka int err;
21798f9d73dSMarcel Telka
21898f9d73dSMarcel Telka if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
21998f9d73dSMarcel Telka uint64_t version;
22098f9d73dSMarcel Telka version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
22198f9d73dSMarcel Telka if (version >= ZPL_VERSION_SA) {
22298f9d73dSMarcel Telka featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
22398f9d73dSMarcel Telka }
22498f9d73dSMarcel Telka }
22598f9d73dSMarcel Telka
22698f9d73dSMarcel Telka drr.drr_type = DRR_BEGIN;
22798f9d73dSMarcel Telka drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
22898f9d73dSMarcel Telka DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.drr_versioninfo,
22998f9d73dSMarcel Telka DMU_COMPOUNDSTREAM);
23098f9d73dSMarcel Telka DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.drr_versioninfo,
23198f9d73dSMarcel Telka featureflags);
23298f9d73dSMarcel Telka (void) snprintf(drr.drr_u.drr_begin.drr_toname,
23398f9d73dSMarcel Telka sizeof (drr.drr_u.drr_begin.drr_toname), "%s@%s", zfs_get_name(zhp),
23498f9d73dSMarcel Telka tosnap);
23598f9d73dSMarcel Telka drr.drr_payloadlen = payload_len;
23698f9d73dSMarcel Telka
23798f9d73dSMarcel Telka err = dump_record(&drr, payload, payload_len, &zc, outfd);
23898f9d73dSMarcel Telka if (err != 0)
23998f9d73dSMarcel Telka return (err);
24098f9d73dSMarcel Telka
24198f9d73dSMarcel Telka bzero(&drr, sizeof (drr));
24298f9d73dSMarcel Telka drr.drr_type = DRR_END;
24398f9d73dSMarcel Telka drr.drr_u.drr_end.drr_checksum = zc;
24498f9d73dSMarcel Telka err = write(outfd, &drr, sizeof (drr));
24598f9d73dSMarcel Telka if (err == -1) {
24698f9d73dSMarcel Telka err = errno;
24798f9d73dSMarcel Telka return (err);
24898f9d73dSMarcel Telka }
24998f9d73dSMarcel Telka
25098f9d73dSMarcel Telka return (0);
25198f9d73dSMarcel Telka }
25298f9d73dSMarcel Telka
25398f9d73dSMarcel Telka int
zfs_send_compoundstream_end(int outfd)25498f9d73dSMarcel Telka zfs_send_compoundstream_end(int outfd)
25598f9d73dSMarcel Telka {
25698f9d73dSMarcel Telka dmu_replay_record_t drr = { 0 };
25798f9d73dSMarcel Telka
25898f9d73dSMarcel Telka drr.drr_type = DRR_END;
25998f9d73dSMarcel Telka if (write(outfd, &drr, sizeof (drr)) == -1)
26098f9d73dSMarcel Telka return (errno);
26198f9d73dSMarcel Telka
26298f9d73dSMarcel Telka return (0);
26398f9d73dSMarcel Telka }
26498f9d73dSMarcel 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;
286b5152584SMatthew 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");
31898110f08SMatthew Ahrens while (ssread(drr, sizeof (*drr), ofp) != 0) {
3199e69d7d0SLori Alt
3209e69d7d0SLori Alt switch (drr->drr_type) {
3219e69d7d0SLori Alt case DRR_BEGIN:
3229e69d7d0SLori Alt {
32398110f08SMatthew Ahrens struct drr_begin *drrb = &drr->drr_u.drr_begin;
3249e69d7d0SLori Alt int fflags;
32598110f08SMatthew Ahrens int sz = 0;
3269e69d7d0SLori Alt ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
3279e69d7d0SLori Alt
32898110f08SMatthew Ahrens ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC);
32998110f08SMatthew 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
336c1a50c7eSMatthew Ahrens if (drr->drr_payloadlen != 0) {
33798110f08SMatthew Ahrens sz = drr->drr_payloadlen;
3389e69d7d0SLori Alt
339b5152584SMatthew Ahrens if (sz > SPA_MAXBLOCKSIZE) {
340b5152584SMatthew Ahrens buf = zfs_realloc(dda->dedup_hdl, buf,
341b5152584SMatthew Ahrens SPA_MAXBLOCKSIZE, sz);
3429e69d7d0SLori Alt }
3439e69d7d0SLori Alt (void) ssread(buf, sz, ofp);
3449e69d7d0SLori Alt if (ferror(stdin))
3459e69d7d0SLori Alt perror("fread");
3469e69d7d0SLori Alt }
34798110f08SMatthew Ahrens if (dump_record(drr, buf, sz, &stream_cksum,
34898110f08SMatthew Ahrens outfd) != 0)
34998110f08SMatthew Ahrens goto out;
3509e69d7d0SLori Alt break;
3519e69d7d0SLori Alt }
3529e69d7d0SLori Alt
3539e69d7d0SLori Alt case DRR_END:
3549e69d7d0SLori Alt {
35598110f08SMatthew Ahrens struct drr_end *drre = &drr->drr_u.drr_end;
3569e69d7d0SLori Alt /* use the recalculated checksum */
35798110f08SMatthew Ahrens drre->drr_checksum = stream_cksum;
35898110f08SMatthew Ahrens if (dump_record(drr, NULL, 0, &stream_cksum,
35998110f08SMatthew Ahrens outfd) != 0)
3609e69d7d0SLori Alt goto out;
3619e69d7d0SLori Alt break;
3629e69d7d0SLori Alt }
3639e69d7d0SLori Alt
3649e69d7d0SLori Alt case DRR_OBJECT:
3659e69d7d0SLori Alt {
36698110f08SMatthew 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 }
37298110f08SMatthew Ahrens if (dump_record(drr, buf,
37398110f08SMatthew Ahrens P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
37498110f08SMatthew Ahrens &stream_cksum, outfd) != 0)
37598110f08SMatthew Ahrens goto out;
3769e69d7d0SLori Alt break;
3779e69d7d0SLori Alt }
3789e69d7d0SLori Alt
3790a586ceaSMark Shellenbaum case DRR_SPILL:
3800a586ceaSMark Shellenbaum {
38198110f08SMatthew Ahrens struct drr_spill *drrs = &drr->drr_u.drr_spill;
3820a586ceaSMark Shellenbaum (void) ssread(buf, drrs->drr_length, ofp);
38398110f08SMatthew Ahrens if (dump_record(drr, buf, drrs->drr_length,
38498110f08SMatthew Ahrens &stream_cksum, outfd) != 0)
3850a586ceaSMark Shellenbaum goto out;
3860a586ceaSMark Shellenbaum break;
3870a586ceaSMark Shellenbaum }
3880a586ceaSMark Shellenbaum
3899e69d7d0SLori Alt case DRR_FREEOBJECTS:
3909e69d7d0SLori Alt {
39198110f08SMatthew Ahrens if (dump_record(drr, NULL, 0, &stream_cksum,
39298110f08SMatthew Ahrens outfd) != 0)
3939e69d7d0SLori Alt goto out;
3949e69d7d0SLori Alt break;
3959e69d7d0SLori Alt }
3969e69d7d0SLori Alt
3979e69d7d0SLori Alt case DRR_WRITE:
3989e69d7d0SLori Alt {
39998110f08SMatthew 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)) {
43798110f08SMatthew Ahrens dmu_replay_record_t wbr_drr = {0};
43898110f08SMatthew Ahrens struct drr_write_byref *wbr_drrr =
43998110f08SMatthew Ahrens &wbr_drr.drr_u.drr_write_byref;
44098110f08SMatthew Ahrens
4419e69d7d0SLori Alt /* block already present in stream */
44298110f08SMatthew Ahrens wbr_drr.drr_type = DRR_WRITE_BYREF;
44398110f08SMatthew 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
46398110f08SMatthew Ahrens if (dump_record(&wbr_drr, NULL, 0,
46498110f08SMatthew Ahrens &stream_cksum, outfd) != 0)
4659e69d7d0SLori Alt goto out;
4669e69d7d0SLori Alt } else {
4679e69d7d0SLori Alt /* block not previously seen */
46898110f08SMatthew Ahrens if (dump_record(drr, buf, drrw->drr_length,
46998110f08SMatthew Ahrens &stream_cksum, outfd) != 0)
4709e69d7d0SLori Alt goto out;
4719e69d7d0SLori Alt }
4729e69d7d0SLori Alt break;
4739e69d7d0SLori Alt }
4749e69d7d0SLori Alt
4755d7b4d43SMatthew Ahrens case DRR_WRITE_EMBEDDED:
4765d7b4d43SMatthew Ahrens {
47798110f08SMatthew Ahrens struct drr_write_embedded *drrwe =
47898110f08SMatthew Ahrens &drr->drr_u.drr_write_embedded;
4795d7b4d43SMatthew Ahrens (void) ssread(buf,
4805d7b4d43SMatthew Ahrens P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp);
48198110f08SMatthew Ahrens if (dump_record(drr, buf,
4825d7b4d43SMatthew Ahrens P2ROUNDUP((uint64_t)drrwe->drr_psize, 8),
48398110f08SMatthew Ahrens &stream_cksum, outfd) != 0)
4845d7b4d43SMatthew Ahrens goto out;
4855d7b4d43SMatthew Ahrens break;
4865d7b4d43SMatthew Ahrens }
4875d7b4d43SMatthew Ahrens
4889e69d7d0SLori Alt case DRR_FREE:
4899e69d7d0SLori Alt {
49098110f08SMatthew Ahrens if (dump_record(drr, NULL, 0, &stream_cksum,
49198110f08SMatthew Ahrens outfd) != 0)
4929e69d7d0SLori Alt goto out;
4939e69d7d0SLori Alt break;
4949e69d7d0SLori Alt }
4959e69d7d0SLori Alt
4969e69d7d0SLori Alt default:
49798110f08SMatthew 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*a1988827SMatthew Ahrens char prevsnap[ZFS_MAX_DATASET_NAME_LEN];
867a7f53a56SChris Kirby uint64_t prevsnap_obj;
8683cb34c60Sahrens boolean_t seenfrom, seento, replicate, doall, fromorigin;
869dc5f28a3SManoj Joseph boolean_t verbose, dryrun, parsable, progress, embed_data, std_out;
870dc5f28a3SManoj 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*a1988827SMatthew 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,
9505d7b4d43SMatthew Ahrens boolean_t fromorigin, int outfd, enum lzc_send_flags flags,
9515d7b4d43SMatthew 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;
9655d7b4d43SMatthew 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
1085c1a50c7eSMatthew Ahrens static void
send_print_verbose(FILE * fout,const char * tosnap,const char * fromsnap,uint64_t size,boolean_t parsable)1086c1a50c7eSMatthew Ahrens send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap,
1087c1a50c7eSMatthew Ahrens uint64_t size, boolean_t parsable)
1088c1a50c7eSMatthew Ahrens {
1089c1a50c7eSMatthew Ahrens if (parsable) {
1090c1a50c7eSMatthew Ahrens if (fromsnap != NULL) {
1091c1a50c7eSMatthew Ahrens (void) fprintf(fout, "incremental\t%s\t%s",
1092c1a50c7eSMatthew Ahrens fromsnap, tosnap);
1093c1a50c7eSMatthew Ahrens } else {
1094c1a50c7eSMatthew Ahrens (void) fprintf(fout, "full\t%s",
1095c1a50c7eSMatthew Ahrens tosnap);
1096c1a50c7eSMatthew Ahrens }
1097c1a50c7eSMatthew Ahrens } else {
1098c1a50c7eSMatthew Ahrens if (fromsnap != NULL) {
1099c1a50c7eSMatthew Ahrens if (strchr(fromsnap, '@') == NULL &&
1100c1a50c7eSMatthew Ahrens strchr(fromsnap, '#') == NULL) {
1101c1a50c7eSMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN,
1102c1a50c7eSMatthew Ahrens "send from @%s to %s"),
1103c1a50c7eSMatthew Ahrens fromsnap, tosnap);
1104c1a50c7eSMatthew Ahrens } else {
1105c1a50c7eSMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN,
1106c1a50c7eSMatthew Ahrens "send from %s to %s"),
1107c1a50c7eSMatthew Ahrens fromsnap, tosnap);
1108c1a50c7eSMatthew Ahrens }
1109c1a50c7eSMatthew Ahrens } else {
1110c1a50c7eSMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN,
1111c1a50c7eSMatthew Ahrens "full send of %s"),
1112c1a50c7eSMatthew Ahrens tosnap);
1113c1a50c7eSMatthew Ahrens }
1114c1a50c7eSMatthew Ahrens }
1115c1a50c7eSMatthew Ahrens
1116c1a50c7eSMatthew Ahrens if (size != 0) {
1117c1a50c7eSMatthew Ahrens if (parsable) {
1118c1a50c7eSMatthew Ahrens (void) fprintf(fout, "\t%llu",
1119c1a50c7eSMatthew Ahrens (longlong_t)size);
1120c1a50c7eSMatthew Ahrens } else {
1121c1a50c7eSMatthew Ahrens char buf[16];
1122c1a50c7eSMatthew Ahrens zfs_nicenum(size, buf, sizeof (buf));
1123c1a50c7eSMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN,
1124c1a50c7eSMatthew Ahrens " estimated size is %s"), buf);
1125c1a50c7eSMatthew Ahrens }
1126c1a50c7eSMatthew Ahrens }
1127c1a50c7eSMatthew Ahrens (void) fprintf(fout, "\n");
1128c1a50c7eSMatthew Ahrens }
1129c1a50c7eSMatthew 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;
1140dc5f28a3SManoj 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) {
1209c1a50c7eSMatthew Ahrens uint64_t size = 0;
1210c1a50c7eSMatthew Ahrens (void) estimate_ioctl(zhp, sdd->prevsnap_obj,
121119b94df9SMatthew Ahrens fromorigin, &size);
121219b94df9SMatthew Ahrens
1213c1a50c7eSMatthew Ahrens send_print_verbose(fout, zhp->zfs_name,
1214c1a50c7eSMatthew Ahrens sdd->prevsnap[0] ? sdd->prevsnap : NULL,
1215c1a50c7eSMatthew 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
12365d7b4d43SMatthew Ahrens enum lzc_send_flags flags = 0;
1237b5152584SMatthew Ahrens if (sdd->large_block)
1238b5152584SMatthew Ahrens flags |= LZC_SEND_FLAG_LARGE_BLOCK;
12395d7b4d43SMatthew Ahrens if (sdd->embed_data)
12405d7b4d43SMatthew Ahrens flags |= LZC_SEND_FLAG_EMBED_DATA;
12415d7b4d43SMatthew Ahrens
1242a7f53a56SChris Kirby err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
12435d7b4d43SMatthew 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
1427c1a50c7eSMatthew Ahrens nvlist_t *
zfs_send_resume_token_to_nvlist(libzfs_handle_t * hdl,const char * token)1428c1a50c7eSMatthew Ahrens zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
1429c1a50c7eSMatthew Ahrens {
1430c1a50c7eSMatthew Ahrens unsigned int version;
1431c1a50c7eSMatthew Ahrens int nread;
1432c1a50c7eSMatthew Ahrens unsigned long long checksum, packed_len;
1433c1a50c7eSMatthew Ahrens
1434c1a50c7eSMatthew Ahrens /*
1435c1a50c7eSMatthew Ahrens * Decode token header, which is:
1436c1a50c7eSMatthew Ahrens * <token version>-<checksum of payload>-<uncompressed payload length>
1437c1a50c7eSMatthew Ahrens * Note that the only supported token version is 1.
1438c1a50c7eSMatthew Ahrens */
1439c1a50c7eSMatthew Ahrens nread = sscanf(token, "%u-%llx-%llx-",
1440c1a50c7eSMatthew Ahrens &version, &checksum, &packed_len);
1441c1a50c7eSMatthew Ahrens if (nread != 3) {
1442c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1443c1a50c7eSMatthew Ahrens "resume token is corrupt (invalid format)"));
1444c1a50c7eSMatthew Ahrens return (NULL);
1445c1a50c7eSMatthew Ahrens }
1446c1a50c7eSMatthew Ahrens
1447c1a50c7eSMatthew Ahrens if (version != ZFS_SEND_RESUME_TOKEN_VERSION) {
1448c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1449c1a50c7eSMatthew Ahrens "resume token is corrupt (invalid version %u)"),
1450c1a50c7eSMatthew Ahrens version);
1451c1a50c7eSMatthew Ahrens return (NULL);
1452c1a50c7eSMatthew Ahrens }
1453c1a50c7eSMatthew Ahrens
1454c1a50c7eSMatthew Ahrens /* convert hexadecimal representation to binary */
1455c1a50c7eSMatthew Ahrens token = strrchr(token, '-') + 1;
1456c1a50c7eSMatthew Ahrens int len = strlen(token) / 2;
1457c1a50c7eSMatthew Ahrens unsigned char *compressed = zfs_alloc(hdl, len);
1458c1a50c7eSMatthew Ahrens for (int i = 0; i < len; i++) {
1459c1a50c7eSMatthew Ahrens nread = sscanf(token + i * 2, "%2hhx", compressed + i);
1460c1a50c7eSMatthew Ahrens if (nread != 1) {
1461c1a50c7eSMatthew Ahrens free(compressed);
1462c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1463c1a50c7eSMatthew Ahrens "resume token is corrupt "
1464c1a50c7eSMatthew Ahrens "(payload is not hex-encoded)"));
1465c1a50c7eSMatthew Ahrens return (NULL);
1466c1a50c7eSMatthew Ahrens }
1467c1a50c7eSMatthew Ahrens }
1468c1a50c7eSMatthew Ahrens
1469c1a50c7eSMatthew Ahrens /* verify checksum */
1470c1a50c7eSMatthew Ahrens zio_cksum_t cksum;
1471c1a50c7eSMatthew Ahrens fletcher_4_native(compressed, len, &cksum);
1472c1a50c7eSMatthew Ahrens if (cksum.zc_word[0] != checksum) {
1473c1a50c7eSMatthew Ahrens free(compressed);
1474c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1475c1a50c7eSMatthew Ahrens "resume token is corrupt (incorrect checksum)"));
1476c1a50c7eSMatthew Ahrens return (NULL);
1477c1a50c7eSMatthew Ahrens }
1478c1a50c7eSMatthew Ahrens
1479c1a50c7eSMatthew Ahrens /* uncompress */
1480c1a50c7eSMatthew Ahrens void *packed = zfs_alloc(hdl, packed_len);
1481c1a50c7eSMatthew Ahrens uLongf packed_len_long = packed_len;
1482c1a50c7eSMatthew Ahrens if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK ||
1483c1a50c7eSMatthew Ahrens packed_len_long != packed_len) {
1484c1a50c7eSMatthew Ahrens free(packed);
1485c1a50c7eSMatthew Ahrens free(compressed);
1486c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1487c1a50c7eSMatthew Ahrens "resume token is corrupt (decompression failed)"));
1488c1a50c7eSMatthew Ahrens return (NULL);
1489c1a50c7eSMatthew Ahrens }
1490c1a50c7eSMatthew Ahrens
1491c1a50c7eSMatthew Ahrens /* unpack nvlist */
1492c1a50c7eSMatthew Ahrens nvlist_t *nv;
1493c1a50c7eSMatthew Ahrens int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP);
1494c1a50c7eSMatthew Ahrens free(packed);
1495c1a50c7eSMatthew Ahrens free(compressed);
1496c1a50c7eSMatthew Ahrens if (error != 0) {
1497c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1498c1a50c7eSMatthew Ahrens "resume token is corrupt (nvlist_unpack failed)"));
1499c1a50c7eSMatthew Ahrens return (NULL);
1500c1a50c7eSMatthew Ahrens }
1501c1a50c7eSMatthew Ahrens return (nv);
1502c1a50c7eSMatthew Ahrens }
1503c1a50c7eSMatthew Ahrens
1504c1a50c7eSMatthew Ahrens int
zfs_send_resume(libzfs_handle_t * hdl,sendflags_t * flags,int outfd,const char * resume_token)1505c1a50c7eSMatthew Ahrens zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
1506c1a50c7eSMatthew Ahrens const char *resume_token)
1507c1a50c7eSMatthew Ahrens {
1508c1a50c7eSMatthew Ahrens char errbuf[1024];
1509c1a50c7eSMatthew Ahrens char *toname;
1510c1a50c7eSMatthew Ahrens char *fromname = NULL;
1511c1a50c7eSMatthew Ahrens uint64_t resumeobj, resumeoff, toguid, fromguid, bytes;
1512c1a50c7eSMatthew Ahrens zfs_handle_t *zhp;
1513c1a50c7eSMatthew Ahrens int error = 0;
1514*a1988827SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN];
1515c1a50c7eSMatthew Ahrens enum lzc_send_flags lzc_flags = 0;
1516c1a50c7eSMatthew Ahrens
1517c1a50c7eSMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
1518c1a50c7eSMatthew Ahrens "cannot resume send"));
1519c1a50c7eSMatthew Ahrens
1520c1a50c7eSMatthew Ahrens nvlist_t *resume_nvl =
1521c1a50c7eSMatthew Ahrens zfs_send_resume_token_to_nvlist(hdl, resume_token);
1522c1a50c7eSMatthew Ahrens if (resume_nvl == NULL) {
1523c1a50c7eSMatthew Ahrens /*
1524c1a50c7eSMatthew Ahrens * zfs_error_aux has already been set by
1525c1a50c7eSMatthew Ahrens * zfs_send_resume_token_to_nvlist
1526c1a50c7eSMatthew Ahrens */
1527c1a50c7eSMatthew Ahrens return (zfs_error(hdl, EZFS_FAULT, errbuf));
1528c1a50c7eSMatthew Ahrens }
1529c1a50c7eSMatthew Ahrens if (flags->verbose) {
1530c1a50c7eSMatthew Ahrens (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1531c1a50c7eSMatthew Ahrens "resume token contents:\n"));
1532c1a50c7eSMatthew Ahrens nvlist_print(stderr, resume_nvl);
1533c1a50c7eSMatthew Ahrens }
1534c1a50c7eSMatthew Ahrens
1535c1a50c7eSMatthew Ahrens if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
1536c1a50c7eSMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 ||
1537c1a50c7eSMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 ||
1538c1a50c7eSMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 ||
1539c1a50c7eSMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) {
1540c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1541c1a50c7eSMatthew Ahrens "resume token is corrupt"));
1542c1a50c7eSMatthew Ahrens return (zfs_error(hdl, EZFS_FAULT, errbuf));
1543c1a50c7eSMatthew Ahrens }
1544c1a50c7eSMatthew Ahrens fromguid = 0;
1545c1a50c7eSMatthew Ahrens (void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid);
1546c1a50c7eSMatthew Ahrens
1547c1a50c7eSMatthew Ahrens if (flags->embed_data || nvlist_exists(resume_nvl, "embedok"))
1548c1a50c7eSMatthew Ahrens lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
1549c1a50c7eSMatthew Ahrens
1550c1a50c7eSMatthew Ahrens if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) {
1551c1a50c7eSMatthew Ahrens if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) {
1552c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1553c1a50c7eSMatthew Ahrens "'%s' is no longer the same snapshot used in "
1554c1a50c7eSMatthew Ahrens "the initial send"), toname);
1555c1a50c7eSMatthew Ahrens } else {
1556c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1557c1a50c7eSMatthew Ahrens "'%s' used in the initial send no longer exists"),
1558c1a50c7eSMatthew Ahrens toname);
1559c1a50c7eSMatthew Ahrens }
1560c1a50c7eSMatthew Ahrens return (zfs_error(hdl, EZFS_BADPATH, errbuf));
1561c1a50c7eSMatthew Ahrens }
1562c1a50c7eSMatthew Ahrens zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
1563c1a50c7eSMatthew Ahrens if (zhp == NULL) {
1564c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1565c1a50c7eSMatthew Ahrens "unable to access '%s'"), name);
1566c1a50c7eSMatthew Ahrens return (zfs_error(hdl, EZFS_BADPATH, errbuf));
1567c1a50c7eSMatthew Ahrens }
1568c1a50c7eSMatthew Ahrens
1569c1a50c7eSMatthew Ahrens if (fromguid != 0) {
1570c1a50c7eSMatthew Ahrens if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) {
1571c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1572c1a50c7eSMatthew Ahrens "incremental source %#llx no longer exists"),
1573c1a50c7eSMatthew Ahrens (longlong_t)fromguid);
1574c1a50c7eSMatthew Ahrens return (zfs_error(hdl, EZFS_BADPATH, errbuf));
1575c1a50c7eSMatthew Ahrens }
1576c1a50c7eSMatthew Ahrens fromname = name;
1577c1a50c7eSMatthew Ahrens }
1578c1a50c7eSMatthew Ahrens
1579c1a50c7eSMatthew Ahrens if (flags->verbose) {
1580c1a50c7eSMatthew Ahrens uint64_t size = 0;
1581c1a50c7eSMatthew Ahrens error = lzc_send_space(zhp->zfs_name, fromname, &size);
1582c1a50c7eSMatthew Ahrens if (error == 0)
1583c1a50c7eSMatthew Ahrens size = MAX(0, (int64_t)(size - bytes));
1584c1a50c7eSMatthew Ahrens send_print_verbose(stderr, zhp->zfs_name, fromname,
1585c1a50c7eSMatthew Ahrens size, flags->parsable);
1586c1a50c7eSMatthew Ahrens }
1587c1a50c7eSMatthew Ahrens
1588c1a50c7eSMatthew Ahrens if (!flags->dryrun) {
1589c1a50c7eSMatthew Ahrens progress_arg_t pa = { 0 };
1590c1a50c7eSMatthew Ahrens pthread_t tid;
1591c1a50c7eSMatthew Ahrens /*
1592c1a50c7eSMatthew Ahrens * If progress reporting is requested, spawn a new thread to
1593c1a50c7eSMatthew Ahrens * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
1594c1a50c7eSMatthew Ahrens */
1595c1a50c7eSMatthew Ahrens if (flags->progress) {
1596c1a50c7eSMatthew Ahrens pa.pa_zhp = zhp;
1597c1a50c7eSMatthew Ahrens pa.pa_fd = outfd;
1598c1a50c7eSMatthew Ahrens pa.pa_parsable = flags->parsable;
1599c1a50c7eSMatthew Ahrens
1600c1a50c7eSMatthew Ahrens error = pthread_create(&tid, NULL,
1601c1a50c7eSMatthew Ahrens send_progress_thread, &pa);
1602c1a50c7eSMatthew Ahrens if (error != 0) {
1603c1a50c7eSMatthew Ahrens zfs_close(zhp);
1604c1a50c7eSMatthew Ahrens return (error);
1605c1a50c7eSMatthew Ahrens }
1606c1a50c7eSMatthew Ahrens }
1607c1a50c7eSMatthew Ahrens
1608c1a50c7eSMatthew Ahrens error = lzc_send_resume(zhp->zfs_name, fromname, outfd,
1609c1a50c7eSMatthew Ahrens lzc_flags, resumeobj, resumeoff);
1610c1a50c7eSMatthew Ahrens
1611c1a50c7eSMatthew Ahrens if (flags->progress) {
1612c1a50c7eSMatthew Ahrens (void) pthread_cancel(tid);
1613c1a50c7eSMatthew Ahrens (void) pthread_join(tid, NULL);
1614c1a50c7eSMatthew Ahrens }
1615c1a50c7eSMatthew Ahrens
1616c1a50c7eSMatthew Ahrens char errbuf[1024];
1617c1a50c7eSMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
1618c1a50c7eSMatthew Ahrens "warning: cannot send '%s'"), zhp->zfs_name);
1619c1a50c7eSMatthew Ahrens
1620c1a50c7eSMatthew Ahrens zfs_close(zhp);
1621c1a50c7eSMatthew Ahrens
1622c1a50c7eSMatthew Ahrens switch (error) {
1623c1a50c7eSMatthew Ahrens case 0:
1624c1a50c7eSMatthew Ahrens return (0);
1625c1a50c7eSMatthew Ahrens case EXDEV:
1626c1a50c7eSMatthew Ahrens case ENOENT:
1627c1a50c7eSMatthew Ahrens case EDQUOT:
1628c1a50c7eSMatthew Ahrens case EFBIG:
1629c1a50c7eSMatthew Ahrens case EIO:
1630c1a50c7eSMatthew Ahrens case ENOLINK:
1631c1a50c7eSMatthew Ahrens case ENOSPC:
1632c1a50c7eSMatthew Ahrens case ENOSTR:
1633c1a50c7eSMatthew Ahrens case ENXIO:
1634c1a50c7eSMatthew Ahrens case EPIPE:
1635c1a50c7eSMatthew Ahrens case ERANGE:
1636c1a50c7eSMatthew Ahrens case EFAULT:
1637c1a50c7eSMatthew Ahrens case EROFS:
1638c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, strerror(errno));
1639c1a50c7eSMatthew Ahrens return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
1640c1a50c7eSMatthew Ahrens
1641c1a50c7eSMatthew Ahrens default:
1642c1a50c7eSMatthew Ahrens return (zfs_standard_error(hdl, errno, errbuf));
1643c1a50c7eSMatthew Ahrens }
1644c1a50c7eSMatthew Ahrens }
1645c1a50c7eSMatthew Ahrens
1646c1a50c7eSMatthew Ahrens
1647c1a50c7eSMatthew Ahrens zfs_close(zhp);
1648c1a50c7eSMatthew Ahrens
1649c1a50c7eSMatthew Ahrens return (error);
1650c1a50c7eSMatthew Ahrens }
1651c1a50c7eSMatthew 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;
1684dc5f28a3SManoj 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
177198110f08SMatthew Ahrens err = dump_record(&drr, packbuf, buflen, &zc, outfd);
17723cb34c60Sahrens free(packbuf);
177398110f08SMatthew 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;
1806b5152584SMatthew Ahrens sdd.large_block = flags->largeblock;
18075d7b4d43SMatthew 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;
1812dc5f28a3SManoj Joseph if (sdd.verbose && sdd.dryrun)
1813dc5f28a3SManoj Joseph sdd.std_out = B_TRUE;
1814dc5f28a3SManoj 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) {
1854dc5f28a3SManoj 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));
1859dc5f28a3SManoj 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
194178f17100SMatthew Ahrens int
zfs_send_one(zfs_handle_t * zhp,const char * from,int fd,enum lzc_send_flags flags)19425d7b4d43SMatthew Ahrens zfs_send_one(zfs_handle_t *zhp, const char *from, int fd,
19435d7b4d43SMatthew Ahrens enum lzc_send_flags flags)
194478f17100SMatthew Ahrens {
194578f17100SMatthew Ahrens int err;
194678f17100SMatthew Ahrens libzfs_handle_t *hdl = zhp->zfs_hdl;
194778f17100SMatthew Ahrens
194878f17100SMatthew Ahrens char errbuf[1024];
194978f17100SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
195078f17100SMatthew Ahrens "warning: cannot send '%s'"), zhp->zfs_name);
195178f17100SMatthew Ahrens
19525d7b4d43SMatthew Ahrens err = lzc_send(zhp->zfs_name, from, fd, flags);
195378f17100SMatthew Ahrens if (err != 0) {
195478f17100SMatthew Ahrens switch (errno) {
195578f17100SMatthew Ahrens case EXDEV:
195678f17100SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
195778f17100SMatthew Ahrens "not an earlier snapshot from the same fs"));
195878f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
195978f17100SMatthew Ahrens
196078f17100SMatthew Ahrens case ENOENT:
196178f17100SMatthew Ahrens case ESRCH:
196278f17100SMatthew Ahrens if (lzc_exists(zhp->zfs_name)) {
196378f17100SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
196478f17100SMatthew Ahrens "incremental source (%s) does not exist"),
196578f17100SMatthew Ahrens from);
196678f17100SMatthew Ahrens }
196778f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_NOENT, errbuf));
196878f17100SMatthew Ahrens
196978f17100SMatthew Ahrens case EBUSY:
197078f17100SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
197178f17100SMatthew Ahrens "target is busy; if a filesystem, "
197278f17100SMatthew Ahrens "it must not be mounted"));
197378f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_BUSY, errbuf));
197478f17100SMatthew Ahrens
197578f17100SMatthew Ahrens case EDQUOT:
197678f17100SMatthew Ahrens case EFBIG:
197778f17100SMatthew Ahrens case EIO:
197878f17100SMatthew Ahrens case ENOLINK:
197978f17100SMatthew Ahrens case ENOSPC:
198078f17100SMatthew Ahrens case ENOSTR:
198178f17100SMatthew Ahrens case ENXIO:
198278f17100SMatthew Ahrens case EPIPE:
198378f17100SMatthew Ahrens case ERANGE:
198478f17100SMatthew Ahrens case EFAULT:
198578f17100SMatthew Ahrens case EROFS:
198678f17100SMatthew Ahrens zfs_error_aux(hdl, strerror(errno));
198778f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
198878f17100SMatthew Ahrens
198978f17100SMatthew Ahrens default:
199078f17100SMatthew Ahrens return (zfs_standard_error(hdl, errno, errbuf));
199178f17100SMatthew Ahrens }
199278f17100SMatthew Ahrens }
199378f17100SMatthew Ahrens return (err != 0);
199478f17100SMatthew Ahrens }
199578f17100SMatthew 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
200898110f08SMatthew Ahrens assert(ilen <= SPA_MAXBLOCKSIZE);
200998110f08SMatthew 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*a1988827SMatthew Ahrens (void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN,
2104*a1988827SMatthew 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;
2189c1a50c7eSMatthew 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;
2198c1a50c7eSMatthew Ahrens const char *slash;
21993cb34c60Sahrens int err;
22003cb34c60Sahrens
220119b94df9SMatthew Ahrens if (gtnd->skip != NULL &&
2202c1a50c7eSMatthew Ahrens (slash = strrchr(zhp->zfs_name, '/')) != NULL &&
2203c1a50c7eSMatthew Ahrens strcmp(slash + 1, gtnd->skip) == 0) {
2204c1a50c7eSMatthew Ahrens zfs_close(zhp);
220519b94df9SMatthew Ahrens return (0);
220619b94df9SMatthew Ahrens }
220719b94df9SMatthew Ahrens
2208c1a50c7eSMatthew 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);
2215c1a50c7eSMatthew Ahrens if (err != EEXIST && gtnd->bookmark_ok)
2216c1a50c7eSMatthew 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,
2230c1a50c7eSMatthew Ahrens boolean_t bookmark_ok, char *name)
22313cb34c60Sahrens {
2232*a1988827SMatthew Ahrens char pname[ZFS_MAX_DATASET_NAME_LEN];
22333cb34c60Sahrens guid_to_name_data_t gtnd;
22343cb34c60Sahrens
22353cb34c60Sahrens gtnd.guid = guid;
2236c1a50c7eSMatthew Ahrens gtnd.bookmark_ok = bookmark_ok;
22373cb34c60Sahrens gtnd.name = name;
223819b94df9SMatthew Ahrens gtnd.skip = NULL;
22393cb34c60Sahrens
224019b94df9SMatthew Ahrens /*
2241c1a50c7eSMatthew Ahrens * Search progressively larger portions of the hierarchy, starting
2242c1a50c7eSMatthew 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 */
2246c1a50c7eSMatthew Ahrens (void) strlcpy(pname, parent, sizeof (pname));
2247c1a50c7eSMatthew Ahrens char *cp = strrchr(pname, '@');
2248c1a50c7eSMatthew Ahrens if (cp == NULL)
2249c1a50c7eSMatthew Ahrens cp = strchr(pname, '\0');
2250c1a50c7eSMatthew Ahrens for (; cp != NULL; cp = strrchr(pname, '/')) {
225119b94df9SMatthew Ahrens /* Chop off the last component and open the parent */
225219b94df9SMatthew Ahrens *cp = '\0';
2253c1a50c7eSMatthew Ahrens zfs_handle_t *zhp = make_dataset_handle(hdl, pname);
225419b94df9SMatthew Ahrens
225519b94df9SMatthew Ahrens if (zhp == NULL)
225619b94df9SMatthew Ahrens continue;
2257c1a50c7eSMatthew Ahrens int err = guid_to_name_cb(zfs_handle_dup(zhp), >nd);
2258c1a50c7eSMatthew Ahrens if (err != EEXIST)
22593cb34c60Sahrens err = zfs_iter_children(zhp, guid_to_name_cb, >nd);
2260c1a50c7eSMatthew Ahrens if (err != EEXIST && bookmark_ok)
2261c1a50c7eSMatthew Ahrens err = zfs_iter_bookmarks(zhp, guid_to_name_cb, >nd);
22623cb34c60Sahrens zfs_close(zhp);
22633cb34c60Sahrens if (err == EEXIST)
22643cb34c60Sahrens return (0);
226519b94df9SMatthew Ahrens
226619b94df9SMatthew Ahrens /*
2267c1a50c7eSMatthew Ahrens * Remember the last portion of the dataset so we skip it next
2268c1a50c7eSMatthew Ahrens * time through (as we've already searched that portion of the
2269c1a50c7eSMatthew Ahrens * hierarchy).
227019b94df9SMatthew Ahrens */
2271c1a50c7eSMatthew 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*a1988827SMatthew 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*a1988827SMatthew 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*a1988827SMatthew 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*a1988827SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN];
2493*a1988827SMatthew 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*a1988827SMatthew 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*a1988827SMatthew Ahrens char tofs[ZFS_MAX_DATASET_NAME_LEN];
2621*a1988827SMatthew 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*a1988827SMatthew 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*a1988827SMatthew 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*a1988827SMatthew 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*a1988827SMatthew 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 */
2779a2cdcdd2SPaul 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 nvlist_free(stream_nv);
2801137fa067Sahrens if (softerr)
2802137fa067Sahrens error = -2;
28033cb34c60Sahrens if (anyerr)
28043cb34c60Sahrens error = -1;
28053cb34c60Sahrens return (error);
28063cb34c60Sahrens }
28073cb34c60Sahrens
280892241e0bSTom Erickson static void
trunc_prop_errs(int truncated)280992241e0bSTom Erickson trunc_prop_errs(int truncated)
281092241e0bSTom Erickson {
281192241e0bSTom Erickson ASSERT(truncated != 0);
281292241e0bSTom Erickson
281392241e0bSTom Erickson if (truncated == 1)
281492241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
281592241e0bSTom Erickson "1 more property could not be set\n"));
281692241e0bSTom Erickson else
281792241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
281892241e0bSTom Erickson "%d more properties could not be set\n"), truncated);
281992241e0bSTom Erickson }
282092241e0bSTom Erickson
28213cb34c60Sahrens static int
recv_skip(libzfs_handle_t * hdl,int fd,boolean_t byteswap)28223cb34c60Sahrens recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
28233cb34c60Sahrens {
28243cb34c60Sahrens dmu_replay_record_t *drr;
2825b5152584SMatthew Ahrens void *buf = zfs_alloc(hdl, SPA_MAXBLOCKSIZE);
28269e69d7d0SLori Alt char errbuf[1024];
28279e69d7d0SLori Alt
28289e69d7d0SLori Alt (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
28299e69d7d0SLori Alt "cannot receive:"));
28303cb34c60Sahrens
28313cb34c60Sahrens /* XXX would be great to use lseek if possible... */
28323cb34c60Sahrens drr = buf;
28333cb34c60Sahrens
28343cb34c60Sahrens while (recv_read(hdl, fd, drr, sizeof (dmu_replay_record_t),
28353cb34c60Sahrens byteswap, NULL) == 0) {
28363cb34c60Sahrens if (byteswap)
28373cb34c60Sahrens drr->drr_type = BSWAP_32(drr->drr_type);
28383cb34c60Sahrens
28393cb34c60Sahrens switch (drr->drr_type) {
28403cb34c60Sahrens case DRR_BEGIN:
28419e69d7d0SLori Alt if (drr->drr_payloadlen != 0) {
2842c1a50c7eSMatthew Ahrens (void) recv_read(hdl, fd, buf,
2843c1a50c7eSMatthew Ahrens drr->drr_payloadlen, B_FALSE, NULL);
28449e69d7d0SLori Alt }
28453cb34c60Sahrens break;
28463cb34c60Sahrens
28473cb34c60Sahrens case DRR_END:
28483cb34c60Sahrens free(buf);
28493cb34c60Sahrens return (0);
28503cb34c60Sahrens
28513cb34c60Sahrens case DRR_OBJECT:
28523cb34c60Sahrens if (byteswap) {
28533cb34c60Sahrens drr->drr_u.drr_object.drr_bonuslen =
28543cb34c60Sahrens BSWAP_32(drr->drr_u.drr_object.
28553cb34c60Sahrens drr_bonuslen);
28563cb34c60Sahrens }
28573cb34c60Sahrens (void) recv_read(hdl, fd, buf,
28583cb34c60Sahrens P2ROUNDUP(drr->drr_u.drr_object.drr_bonuslen, 8),
28593cb34c60Sahrens B_FALSE, NULL);
28603cb34c60Sahrens break;
28613cb34c60Sahrens
28623cb34c60Sahrens case DRR_WRITE:
28633cb34c60Sahrens if (byteswap) {
28643cb34c60Sahrens drr->drr_u.drr_write.drr_length =
28650069fd67STim Haley BSWAP_64(drr->drr_u.drr_write.drr_length);
28663cb34c60Sahrens }
28673cb34c60Sahrens (void) recv_read(hdl, fd, buf,
28683cb34c60Sahrens drr->drr_u.drr_write.drr_length, B_FALSE, NULL);
28693cb34c60Sahrens break;
28700a586ceaSMark Shellenbaum case DRR_SPILL:
28710a586ceaSMark Shellenbaum if (byteswap) {
28720a586ceaSMark Shellenbaum drr->drr_u.drr_write.drr_length =
28730a586ceaSMark Shellenbaum BSWAP_64(drr->drr_u.drr_spill.drr_length);
28740a586ceaSMark Shellenbaum }
28750a586ceaSMark Shellenbaum (void) recv_read(hdl, fd, buf,
28760a586ceaSMark Shellenbaum drr->drr_u.drr_spill.drr_length, B_FALSE, NULL);
28770a586ceaSMark Shellenbaum break;
28785d7b4d43SMatthew Ahrens case DRR_WRITE_EMBEDDED:
28795d7b4d43SMatthew Ahrens if (byteswap) {
28805d7b4d43SMatthew Ahrens drr->drr_u.drr_write_embedded.drr_psize =
28815d7b4d43SMatthew Ahrens BSWAP_32(drr->drr_u.drr_write_embedded.
28825d7b4d43SMatthew Ahrens drr_psize);
28835d7b4d43SMatthew Ahrens }
28845d7b4d43SMatthew Ahrens (void) recv_read(hdl, fd, buf,
28855d7b4d43SMatthew Ahrens P2ROUNDUP(drr->drr_u.drr_write_embedded.drr_psize,
28865d7b4d43SMatthew Ahrens 8), B_FALSE, NULL);
28875d7b4d43SMatthew Ahrens break;
28889e69d7d0SLori Alt case DRR_WRITE_BYREF:
28893cb34c60Sahrens case DRR_FREEOBJECTS:
28903cb34c60Sahrens case DRR_FREE:
28913cb34c60Sahrens break;
28923cb34c60Sahrens
28933cb34c60Sahrens default:
28949e69d7d0SLori Alt zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
28959e69d7d0SLori Alt "invalid record type"));
28969e69d7d0SLori Alt return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
28973cb34c60Sahrens }
28983cb34c60Sahrens }
28993cb34c60Sahrens
29003cb34c60Sahrens free(buf);
29013cb34c60Sahrens return (-1);
29023cb34c60Sahrens }
29033cb34c60Sahrens
2904c1a50c7eSMatthew Ahrens static void
recv_ecksum_set_aux(libzfs_handle_t * hdl,const char * target_snap,boolean_t resumable)2905c1a50c7eSMatthew Ahrens recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap,
2906c1a50c7eSMatthew Ahrens boolean_t resumable)
2907c1a50c7eSMatthew Ahrens {
2908*a1988827SMatthew Ahrens char target_fs[ZFS_MAX_DATASET_NAME_LEN];
2909c1a50c7eSMatthew Ahrens
2910c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2911c1a50c7eSMatthew Ahrens "checksum mismatch or incomplete stream"));
2912c1a50c7eSMatthew Ahrens
2913c1a50c7eSMatthew Ahrens if (!resumable)
2914c1a50c7eSMatthew Ahrens return;
2915c1a50c7eSMatthew Ahrens (void) strlcpy(target_fs, target_snap, sizeof (target_fs));
2916c1a50c7eSMatthew Ahrens *strchr(target_fs, '@') = '\0';
2917c1a50c7eSMatthew Ahrens zfs_handle_t *zhp = zfs_open(hdl, target_fs,
2918c1a50c7eSMatthew Ahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
2919c1a50c7eSMatthew Ahrens if (zhp == NULL)
2920c1a50c7eSMatthew Ahrens return;
2921c1a50c7eSMatthew Ahrens
2922c1a50c7eSMatthew Ahrens char token_buf[ZFS_MAXPROPLEN];
2923c1a50c7eSMatthew Ahrens int error = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
2924c1a50c7eSMatthew Ahrens token_buf, sizeof (token_buf),
2925c1a50c7eSMatthew Ahrens NULL, NULL, 0, B_TRUE);
2926c1a50c7eSMatthew Ahrens if (error == 0) {
2927c1a50c7eSMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2928c1a50c7eSMatthew Ahrens "checksum mismatch or incomplete stream.\n"
2929c1a50c7eSMatthew Ahrens "Partially received snapshot is saved.\n"
2930c1a50c7eSMatthew Ahrens "A resuming stream can be generated on the sending "
2931c1a50c7eSMatthew Ahrens "system by running:\n"
2932c1a50c7eSMatthew Ahrens " zfs send -t %s"),
2933c1a50c7eSMatthew Ahrens token_buf);
2934c1a50c7eSMatthew Ahrens }
2935c1a50c7eSMatthew Ahrens zfs_close(zhp);
2936c1a50c7eSMatthew Ahrens }
2937c1a50c7eSMatthew Ahrens
29383cb34c60Sahrens /*
29393cb34c60Sahrens * Restores a backup of tosnap from the file descriptor specified by infd.
29403cb34c60Sahrens */
29413cb34c60Sahrens 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)29423cb34c60Sahrens zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
2943a2cdcdd2SPaul Dagnelie const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
2944a2cdcdd2SPaul Dagnelie dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
2945a2cdcdd2SPaul Dagnelie avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
2946c99e4bdcSChris Kirby uint64_t *action_handlep)
29473cb34c60Sahrens {
29483cb34c60Sahrens zfs_cmd_t zc = { 0 };
29493cb34c60Sahrens time_t begin_time;
2950bd6a198fSTom Erickson int ioctl_err, ioctl_errno, err;
29513cb34c60Sahrens char *cp;
29523cb34c60Sahrens struct drr_begin *drrb = &drr->drr_u.drr_begin;
29533cb34c60Sahrens char errbuf[1024];
295492241e0bSTom Erickson char prop_errbuf[1024];
2955bd6a198fSTom Erickson const char *chopprefix;
29563cb34c60Sahrens boolean_t newfs = B_FALSE;
29573cb34c60Sahrens boolean_t stream_wantsnewfs;
29583cb34c60Sahrens uint64_t parent_snapguid = 0;
29593cb34c60Sahrens prop_changelist_t *clp = NULL;
2960bb0ade09Sahrens nvlist_t *snapprops_nvlist = NULL;
296192241e0bSTom Erickson zprop_errflags_t prop_errflags;
2962bd6a198fSTom Erickson boolean_t recursive;
29633cb34c60Sahrens
29643cb34c60Sahrens begin_time = time(NULL);
29653cb34c60Sahrens
29663cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
29673cb34c60Sahrens "cannot receive"));
29683cb34c60Sahrens
2969bd6a198fSTom Erickson recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
2970bd6a198fSTom Erickson ENOENT);
2971bd6a198fSTom Erickson
29723cb34c60Sahrens if (stream_avl != NULL) {
2973bb0ade09Sahrens char *snapname;
2974bb0ade09Sahrens nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
2975bb0ade09Sahrens &snapname);
29763cb34c60Sahrens nvlist_t *props;
297700954d7bSahl int ret;
29783cb34c60Sahrens
29793cb34c60Sahrens (void) nvlist_lookup_uint64(fs, "parentfromsnap",
29803cb34c60Sahrens &parent_snapguid);
29813cb34c60Sahrens err = nvlist_lookup_nvlist(fs, "props", &props);
29823cb34c60Sahrens if (err)
29833cb34c60Sahrens VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
298400954d7bSahl
298519b94df9SMatthew Ahrens if (flags->canmountoff) {
29863cb34c60Sahrens VERIFY(0 == nvlist_add_uint64(props,
29873cb34c60Sahrens zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
29883cb34c60Sahrens }
298900954d7bSahl ret = zcmd_write_src_nvlist(hdl, &zc, props);
29903cb34c60Sahrens if (err)
29913cb34c60Sahrens nvlist_free(props);
299200954d7bSahl
2993bb0ade09Sahrens if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
2994bb0ade09Sahrens VERIFY(0 == nvlist_lookup_nvlist(props,
2995bb0ade09Sahrens snapname, &snapprops_nvlist));
2996bb0ade09Sahrens }
2997bb0ade09Sahrens
299800954d7bSahl if (ret != 0)
299900954d7bSahl return (-1);
30003cb34c60Sahrens }
30013cb34c60Sahrens
3002bd6a198fSTom Erickson cp = NULL;
3003bd6a198fSTom Erickson
30043cb34c60Sahrens /*
30053cb34c60Sahrens * Determine how much of the snapshot name stored in the stream
30063cb34c60Sahrens * we are going to tack on to the name they specified on the
30073cb34c60Sahrens * command line, and how much we are going to chop off.
30083cb34c60Sahrens *
30093cb34c60Sahrens * If they specified a snapshot, chop the entire name stored in
30103cb34c60Sahrens * the stream.
30113cb34c60Sahrens */
301219b94df9SMatthew Ahrens if (flags->istail) {
30133cb34c60Sahrens /*
3014bd6a198fSTom Erickson * A filesystem was specified with -e. We want to tack on only
3015bd6a198fSTom Erickson * the tail of the sent snapshot path.
30163cb34c60Sahrens */
30173cb34c60Sahrens if (strchr(tosnap, '@')) {
30183cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
3019bd6a198fSTom Erickson "argument - snapshot not allowed with -e"));
30203cb34c60Sahrens return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
30213cb34c60Sahrens }
3022bd6a198fSTom Erickson
3023bd6a198fSTom Erickson chopprefix = strrchr(sendfs, '/');
3024bd6a198fSTom Erickson
3025bd6a198fSTom Erickson if (chopprefix == NULL) {
3026bd6a198fSTom Erickson /*
3027bd6a198fSTom Erickson * The tail is the poolname, so we need to
3028bd6a198fSTom Erickson * prepend a path separator.
3029bd6a198fSTom Erickson */
3030bd6a198fSTom Erickson int len = strlen(drrb->drr_toname);
3031bd6a198fSTom Erickson cp = malloc(len + 2);
3032bd6a198fSTom Erickson cp[0] = '/';
3033bd6a198fSTom Erickson (void) strcpy(&cp[1], drrb->drr_toname);
3034bd6a198fSTom Erickson chopprefix = cp;
3035bd6a198fSTom Erickson } else {
3036bd6a198fSTom Erickson chopprefix = drrb->drr_toname + (chopprefix - sendfs);
3037bd6a198fSTom Erickson }
303819b94df9SMatthew Ahrens } else if (flags->isprefix) {
3039bd6a198fSTom Erickson /*
3040bd6a198fSTom Erickson * A filesystem was specified with -d. We want to tack on
3041bd6a198fSTom Erickson * everything but the first element of the sent snapshot path
3042bd6a198fSTom Erickson * (all but the pool name).
3043bd6a198fSTom Erickson */
3044bd6a198fSTom Erickson if (strchr(tosnap, '@')) {
3045bd6a198fSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
3046bd6a198fSTom Erickson "argument - snapshot not allowed with -d"));
3047bd6a198fSTom Erickson return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3048bd6a198fSTom Erickson }
3049bd6a198fSTom Erickson
3050bd6a198fSTom Erickson chopprefix = strchr(drrb->drr_toname, '/');
3051bd6a198fSTom Erickson if (chopprefix == NULL)
3052bd6a198fSTom Erickson chopprefix = strchr(drrb->drr_toname, '@');
30533cb34c60Sahrens } else if (strchr(tosnap, '@') == NULL) {
30543cb34c60Sahrens /*
3055bd6a198fSTom Erickson * If a filesystem was specified without -d or -e, we want to
3056bd6a198fSTom Erickson * tack on everything after the fs specified by 'zfs send'.
30573cb34c60Sahrens */
3058bd6a198fSTom Erickson chopprefix = drrb->drr_toname + strlen(sendfs);
3059bd6a198fSTom Erickson } else {
3060bd6a198fSTom Erickson /* A snapshot was specified as an exact path (no -d or -e). */
3061bd6a198fSTom Erickson if (recursive) {
3062bd6a198fSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3063bd6a198fSTom Erickson "cannot specify snapshot name for multi-snapshot "
3064bd6a198fSTom Erickson "stream"));
3065bd6a198fSTom Erickson return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
30663cb34c60Sahrens }
3067bd6a198fSTom Erickson chopprefix = drrb->drr_toname + strlen(drrb->drr_toname);
3068bd6a198fSTom Erickson }
3069bd6a198fSTom Erickson
3070bd6a198fSTom Erickson ASSERT(strstr(drrb->drr_toname, sendfs) == drrb->drr_toname);
3071bd6a198fSTom Erickson ASSERT(chopprefix > drrb->drr_toname);
3072bd6a198fSTom Erickson ASSERT(chopprefix <= drrb->drr_toname + strlen(drrb->drr_toname));
3073bd6a198fSTom Erickson ASSERT(chopprefix[0] == '/' || chopprefix[0] == '@' ||
3074bd6a198fSTom Erickson chopprefix[0] == '\0');
30753cb34c60Sahrens
30763cb34c60Sahrens /*
30773cb34c60Sahrens * Determine name of destination snapshot, store in zc_value.
30783cb34c60Sahrens */
30793cb34c60Sahrens (void) strcpy(zc.zc_value, tosnap);
3080bd6a198fSTom Erickson (void) strncat(zc.zc_value, chopprefix, sizeof (zc.zc_value));
3081bd6a198fSTom Erickson free(cp);
308200954d7bSahl if (!zfs_name_valid(zc.zc_value, ZFS_TYPE_SNAPSHOT)) {
308300954d7bSahl zcmd_free_nvlists(&zc);
30843cb34c60Sahrens return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
308500954d7bSahl }
30863cb34c60Sahrens
30873cb34c60Sahrens /*
30883cb34c60Sahrens * Determine the name of the origin snapshot, store in zc_string.
30893cb34c60Sahrens */
30903cb34c60Sahrens if (drrb->drr_flags & DRR_FLAG_CLONE) {
309119b94df9SMatthew Ahrens if (guid_to_name(hdl, zc.zc_value,
3092c1a50c7eSMatthew Ahrens drrb->drr_fromguid, B_FALSE, zc.zc_string) != 0) {
309300954d7bSahl zcmd_free_nvlists(&zc);
30943cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
30953cb34c60Sahrens "local origin for clone %s does not exist"),
30963cb34c60Sahrens zc.zc_value);
30973cb34c60Sahrens return (zfs_error(hdl, EZFS_NOENT, errbuf));
30983cb34c60Sahrens }
309919b94df9SMatthew Ahrens if (flags->verbose)
31003cb34c60Sahrens (void) printf("found clone origin %s\n", zc.zc_string);
3101a2cdcdd2SPaul Dagnelie } else if (originsnap) {
3102*a1988827SMatthew Ahrens (void) strncpy(zc.zc_string, originsnap, sizeof (zc.zc_string));
3103a2cdcdd2SPaul Dagnelie if (flags->verbose)
3104a2cdcdd2SPaul Dagnelie (void) printf("using provided clone origin %s\n",
3105a2cdcdd2SPaul Dagnelie zc.zc_string);
31063cb34c60Sahrens }
31073cb34c60Sahrens
3108c1a50c7eSMatthew Ahrens boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
3109c1a50c7eSMatthew Ahrens DMU_BACKUP_FEATURE_RESUMING;
31103cb34c60Sahrens stream_wantsnewfs = (drrb->drr_fromguid == NULL ||
3111c1a50c7eSMatthew Ahrens (drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming;
31123cb34c60Sahrens
31133cb34c60Sahrens if (stream_wantsnewfs) {
31143cb34c60Sahrens /*
31153cb34c60Sahrens * if the parent fs does not exist, look for it based on
31163cb34c60Sahrens * the parent snap GUID
31173cb34c60Sahrens */
31183cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
31193cb34c60Sahrens "cannot receive new filesystem stream"));
31203cb34c60Sahrens
31213cb34c60Sahrens (void) strcpy(zc.zc_name, zc.zc_value);
31223cb34c60Sahrens cp = strrchr(zc.zc_name, '/');
31233cb34c60Sahrens if (cp)
31243cb34c60Sahrens *cp = '\0';
31253cb34c60Sahrens if (cp &&
31263cb34c60Sahrens !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
3127*a1988827SMatthew Ahrens char suffix[ZFS_MAX_DATASET_NAME_LEN];
31283cb34c60Sahrens (void) strcpy(suffix, strrchr(zc.zc_value, '/'));
312919b94df9SMatthew Ahrens if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
3130c1a50c7eSMatthew Ahrens B_FALSE, zc.zc_value) == 0) {
31313cb34c60Sahrens *strchr(zc.zc_value, '@') = '\0';
31323cb34c60Sahrens (void) strcat(zc.zc_value, suffix);
31333cb34c60Sahrens }
31343cb34c60Sahrens }
31353cb34c60Sahrens } else {
31363cb34c60Sahrens /*
31373cb34c60Sahrens * if the fs does not exist, look for it based on the
31383cb34c60Sahrens * fromsnap GUID
31393cb34c60Sahrens */
31403cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
31413cb34c60Sahrens "cannot receive incremental stream"));
31423cb34c60Sahrens
31433cb34c60Sahrens (void) strcpy(zc.zc_name, zc.zc_value);
31443cb34c60Sahrens *strchr(zc.zc_name, '@') = '\0';
31453cb34c60Sahrens
314683d7f9feSTom Erickson /*
314783d7f9feSTom Erickson * If the exact receive path was specified and this is the
314883d7f9feSTom Erickson * topmost path in the stream, then if the fs does not exist we
314983d7f9feSTom Erickson * should look no further.
315083d7f9feSTom Erickson */
315119b94df9SMatthew Ahrens if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
315283d7f9feSTom Erickson strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
315383d7f9feSTom Erickson !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
3154*a1988827SMatthew Ahrens char snap[ZFS_MAX_DATASET_NAME_LEN];
31553cb34c60Sahrens (void) strcpy(snap, strchr(zc.zc_value, '@'));
315619b94df9SMatthew Ahrens if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
3157c1a50c7eSMatthew Ahrens B_FALSE, zc.zc_value) == 0) {
31583cb34c60Sahrens *strchr(zc.zc_value, '@') = '\0';
31593cb34c60Sahrens (void) strcat(zc.zc_value, snap);
31603cb34c60Sahrens }
31613cb34c60Sahrens }
31623cb34c60Sahrens }
31633cb34c60Sahrens
31643cb34c60Sahrens (void) strcpy(zc.zc_name, zc.zc_value);
31653cb34c60Sahrens *strchr(zc.zc_name, '@') = '\0';
31663cb34c60Sahrens
31673cb34c60Sahrens if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
31683cb34c60Sahrens zfs_handle_t *zhp;
3169bd6a198fSTom Erickson
31703cb34c60Sahrens /*
3171c1a50c7eSMatthew Ahrens * Destination fs exists. It must be one of these cases:
3172c1a50c7eSMatthew Ahrens * - an incremental send stream
3173c1a50c7eSMatthew Ahrens * - the stream specifies a new fs (full stream or clone)
3174c1a50c7eSMatthew Ahrens * and they want us to blow away the existing fs (and
3175c1a50c7eSMatthew Ahrens * have therefore specified -F and removed any snapshots)
3176c1a50c7eSMatthew Ahrens * - we are resuming a failed receive.
31773cb34c60Sahrens */
31783cb34c60Sahrens if (stream_wantsnewfs) {
317919b94df9SMatthew Ahrens if (!flags->force) {
318000954d7bSahl zcmd_free_nvlists(&zc);
31813cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
31823cb34c60Sahrens "destination '%s' exists\n"
31833cb34c60Sahrens "must specify -F to overwrite it"),
31843cb34c60Sahrens zc.zc_name);
31853cb34c60Sahrens return (zfs_error(hdl, EZFS_EXISTS, errbuf));
31863cb34c60Sahrens }
31873cb34c60Sahrens if (ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
31883cb34c60Sahrens &zc) == 0) {
318900954d7bSahl zcmd_free_nvlists(&zc);
31903cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
31913cb34c60Sahrens "destination has snapshots (eg. %s)\n"
31923cb34c60Sahrens "must destroy them to overwrite it"),
31933cb34c60Sahrens zc.zc_name);
31943cb34c60Sahrens return (zfs_error(hdl, EZFS_EXISTS, errbuf));
31953cb34c60Sahrens }
31963cb34c60Sahrens }
31973cb34c60Sahrens
319800954d7bSahl if ((zhp = zfs_open(hdl, zc.zc_name,
319900954d7bSahl ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
320000954d7bSahl zcmd_free_nvlists(&zc);
32013cb34c60Sahrens return (-1);
320200954d7bSahl }
320300954d7bSahl
32043cb34c60Sahrens if (stream_wantsnewfs &&
32053cb34c60Sahrens zhp->zfs_dmustats.dds_origin[0]) {
320600954d7bSahl zcmd_free_nvlists(&zc);
32073cb34c60Sahrens zfs_close(zhp);
32083cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
32093cb34c60Sahrens "destination '%s' is a clone\n"
32103cb34c60Sahrens "must destroy it to overwrite it"),
32113cb34c60Sahrens zc.zc_name);
32123cb34c60Sahrens return (zfs_error(hdl, EZFS_EXISTS, errbuf));
32133cb34c60Sahrens }
32143cb34c60Sahrens
321519b94df9SMatthew Ahrens if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
32163cb34c60Sahrens stream_wantsnewfs) {
32173cb34c60Sahrens /* We can't do online recv in this case */
32180069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
321900954d7bSahl if (clp == NULL) {
322088bb18d2SLori Alt zfs_close(zhp);
322100954d7bSahl zcmd_free_nvlists(&zc);
32223cb34c60Sahrens return (-1);
322300954d7bSahl }
32243cb34c60Sahrens if (changelist_prefix(clp) != 0) {
32253cb34c60Sahrens changelist_free(clp);
322688bb18d2SLori Alt zfs_close(zhp);
322700954d7bSahl zcmd_free_nvlists(&zc);
32283cb34c60Sahrens return (-1);
32293cb34c60Sahrens }
32303cb34c60Sahrens }
3231c1a50c7eSMatthew Ahrens
3232c1a50c7eSMatthew Ahrens /*
3233c1a50c7eSMatthew Ahrens * If we are resuming a newfs, set newfs here so that we will
3234c1a50c7eSMatthew Ahrens * mount it if the recv succeeds this time. We can tell
3235c1a50c7eSMatthew Ahrens * that it was a newfs on the first recv because the fs
3236c1a50c7eSMatthew Ahrens * itself will be inconsistent (if the fs existed when we
3237c1a50c7eSMatthew Ahrens * did the first recv, we would have received it into
3238c1a50c7eSMatthew Ahrens * .../%recv).
3239c1a50c7eSMatthew Ahrens */
3240c1a50c7eSMatthew Ahrens if (resuming && zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT))
3241c1a50c7eSMatthew Ahrens newfs = B_TRUE;
3242c1a50c7eSMatthew Ahrens
32433cb34c60Sahrens zfs_close(zhp);
32443cb34c60Sahrens } else {
32453cb34c60Sahrens /*
324600954d7bSahl * Destination filesystem does not exist. Therefore we better
324700954d7bSahl * be creating a new filesystem (either from a full backup, or
324800954d7bSahl * a clone). It would therefore be invalid if the user
324900954d7bSahl * specified only the pool name (i.e. if the destination name
325000954d7bSahl * contained no slash character).
32513cb34c60Sahrens */
325200954d7bSahl if (!stream_wantsnewfs ||
325300954d7bSahl (cp = strrchr(zc.zc_name, '/')) == NULL) {
325400954d7bSahl zcmd_free_nvlists(&zc);
32553cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
32563cb34c60Sahrens "destination '%s' does not exist"), zc.zc_name);
32573cb34c60Sahrens return (zfs_error(hdl, EZFS_NOENT, errbuf));
32583cb34c60Sahrens }
32593cb34c60Sahrens
326000954d7bSahl /*
326100954d7bSahl * Trim off the final dataset component so we perform the
326200954d7bSahl * recvbackup ioctl to the filesystems's parent.
326300954d7bSahl */
326400954d7bSahl *cp = '\0';
32653cb34c60Sahrens
326619b94df9SMatthew Ahrens if (flags->isprefix && !flags->istail && !flags->dryrun &&
326700954d7bSahl create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
326800954d7bSahl zcmd_free_nvlists(&zc);
326900954d7bSahl return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
32703cb34c60Sahrens }
32713cb34c60Sahrens
32723cb34c60Sahrens newfs = B_TRUE;
32733cb34c60Sahrens }
32743cb34c60Sahrens
3275c1a50c7eSMatthew Ahrens zc.zc_begin_record = *drr_noswap;
32763cb34c60Sahrens zc.zc_cookie = infd;
327719b94df9SMatthew Ahrens zc.zc_guid = flags->force;
3278c1a50c7eSMatthew Ahrens zc.zc_resumable = flags->resumable;
327919b94df9SMatthew Ahrens if (flags->verbose) {
32803cb34c60Sahrens (void) printf("%s %s stream of %s into %s\n",
328119b94df9SMatthew Ahrens flags->dryrun ? "would receive" : "receiving",
32823cb34c60Sahrens drrb->drr_fromguid ? "incremental" : "full",
32833cb34c60Sahrens drrb->drr_toname, zc.zc_value);
32843cb34c60Sahrens (void) fflush(stdout);
32853cb34c60Sahrens }
32863cb34c60Sahrens
328719b94df9SMatthew Ahrens if (flags->dryrun) {
328800954d7bSahl zcmd_free_nvlists(&zc);
328919b94df9SMatthew Ahrens return (recv_skip(hdl, infd, flags->byteswap));
329000954d7bSahl }
32913cb34c60Sahrens
329292241e0bSTom Erickson zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
329392241e0bSTom Erickson zc.zc_nvlist_dst_size = sizeof (prop_errbuf);
3294c99e4bdcSChris Kirby zc.zc_cleanup_fd = cleanup_fd;
3295c99e4bdcSChris Kirby zc.zc_action_handle = *action_handlep;
329692241e0bSTom Erickson
32973cb34c60Sahrens err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
32983cb34c60Sahrens ioctl_errno = errno;
329992241e0bSTom Erickson prop_errflags = (zprop_errflags_t)zc.zc_obj;
330092241e0bSTom Erickson
330192241e0bSTom Erickson if (err == 0) {
330292241e0bSTom Erickson nvlist_t *prop_errors;
330392241e0bSTom Erickson VERIFY(0 == nvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
330492241e0bSTom Erickson zc.zc_nvlist_dst_size, &prop_errors, 0));
330592241e0bSTom Erickson
330692241e0bSTom Erickson nvpair_t *prop_err = NULL;
330792241e0bSTom Erickson
330892241e0bSTom Erickson while ((prop_err = nvlist_next_nvpair(prop_errors,
330992241e0bSTom Erickson prop_err)) != NULL) {
331092241e0bSTom Erickson char tbuf[1024];
331192241e0bSTom Erickson zfs_prop_t prop;
331292241e0bSTom Erickson int intval;
331392241e0bSTom Erickson
331492241e0bSTom Erickson prop = zfs_name_to_prop(nvpair_name(prop_err));
331592241e0bSTom Erickson (void) nvpair_value_int32(prop_err, &intval);
331692241e0bSTom Erickson if (strcmp(nvpair_name(prop_err),
331792241e0bSTom Erickson ZPROP_N_MORE_ERRORS) == 0) {
331892241e0bSTom Erickson trunc_prop_errs(intval);
331992241e0bSTom Erickson break;
332092241e0bSTom Erickson } else {
332192241e0bSTom Erickson (void) snprintf(tbuf, sizeof (tbuf),
332292241e0bSTom Erickson dgettext(TEXT_DOMAIN,
332392241e0bSTom Erickson "cannot receive %s property on %s"),
332492241e0bSTom Erickson nvpair_name(prop_err), zc.zc_name);
332592241e0bSTom Erickson zfs_setprop_error(hdl, prop, intval, tbuf);
332692241e0bSTom Erickson }
332792241e0bSTom Erickson }
332892241e0bSTom Erickson nvlist_free(prop_errors);
332992241e0bSTom Erickson }
333092241e0bSTom Erickson
333192241e0bSTom Erickson zc.zc_nvlist_dst = 0;
333292241e0bSTom Erickson zc.zc_nvlist_dst_size = 0;
3333bb0ade09Sahrens zcmd_free_nvlists(&zc);
3334bb0ade09Sahrens
3335bb0ade09Sahrens if (err == 0 && snapprops_nvlist) {
3336bb0ade09Sahrens zfs_cmd_t zc2 = { 0 };
3337bb0ade09Sahrens
3338b6c10d80Sahrens (void) strcpy(zc2.zc_name, zc.zc_value);
333992241e0bSTom Erickson zc2.zc_cookie = B_TRUE; /* received */
3340bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) {
3341bb0ade09Sahrens (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2);
3342bb0ade09Sahrens zcmd_free_nvlists(&zc2);
3343bb0ade09Sahrens }
3344bb0ade09Sahrens }
3345bb0ade09Sahrens
3346bd6a198fSTom Erickson if (err && (ioctl_errno == ENOENT || ioctl_errno == EEXIST)) {
33473cb34c60Sahrens /*
33483cb34c60Sahrens * It may be that this snapshot already exists,
33493cb34c60Sahrens * in which case we want to consume & ignore it
33503cb34c60Sahrens * rather than failing.
33513cb34c60Sahrens */
33523cb34c60Sahrens avl_tree_t *local_avl;
33533cb34c60Sahrens nvlist_t *local_nv, *fs;
3354bd6a198fSTom Erickson cp = strchr(zc.zc_value, '@');
33553cb34c60Sahrens
33563cb34c60Sahrens /*
33573cb34c60Sahrens * XXX Do this faster by just iterating over snaps in
33583cb34c60Sahrens * this fs. Also if zc_value does not exist, we will
33593cb34c60Sahrens * get a strange "does not exist" error message.
33603cb34c60Sahrens */
33613cb34c60Sahrens *cp = '\0';
336292241e0bSTom Erickson if (gather_nvlist(hdl, zc.zc_value, NULL, NULL, B_FALSE,
33633cb34c60Sahrens &local_nv, &local_avl) == 0) {
33643cb34c60Sahrens *cp = '@';
33653cb34c60Sahrens fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
33663cb34c60Sahrens fsavl_destroy(local_avl);
33673cb34c60Sahrens nvlist_free(local_nv);
33683cb34c60Sahrens
33693cb34c60Sahrens if (fs != NULL) {
337019b94df9SMatthew Ahrens if (flags->verbose) {
33713cb34c60Sahrens (void) printf("snap %s already exists; "
33723cb34c60Sahrens "ignoring\n", zc.zc_value);
33733cb34c60Sahrens }
337492241e0bSTom Erickson err = ioctl_err = recv_skip(hdl, infd,
337519b94df9SMatthew Ahrens flags->byteswap);
33763cb34c60Sahrens }
33773cb34c60Sahrens }
33783cb34c60Sahrens *cp = '@';
33793cb34c60Sahrens }
33803cb34c60Sahrens
33813cb34c60Sahrens if (ioctl_err != 0) {
33823cb34c60Sahrens switch (ioctl_errno) {
33833cb34c60Sahrens case ENODEV:
33843cb34c60Sahrens cp = strchr(zc.zc_value, '@');
33853cb34c60Sahrens *cp = '\0';
33863cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
33873cb34c60Sahrens "most recent snapshot of %s does not\n"
33883cb34c60Sahrens "match incremental source"), zc.zc_value);
33893cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
33903cb34c60Sahrens *cp = '@';
33913cb34c60Sahrens break;
33923cb34c60Sahrens case ETXTBSY:
33933cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
33943cb34c60Sahrens "destination %s has been modified\n"
33953cb34c60Sahrens "since most recent snapshot"), zc.zc_name);
33963cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
33973cb34c60Sahrens break;
33983cb34c60Sahrens case EEXIST:
33993cb34c60Sahrens cp = strchr(zc.zc_value, '@');
34003cb34c60Sahrens if (newfs) {
34013cb34c60Sahrens /* it's the containing fs that exists */
34023cb34c60Sahrens *cp = '\0';
34033cb34c60Sahrens }
34043cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
34053cb34c60Sahrens "destination already exists"));
34063cb34c60Sahrens (void) zfs_error_fmt(hdl, EZFS_EXISTS,
34073cb34c60Sahrens dgettext(TEXT_DOMAIN, "cannot restore to %s"),
34083cb34c60Sahrens zc.zc_value);
34093cb34c60Sahrens *cp = '@';
34103cb34c60Sahrens break;
34113cb34c60Sahrens case EINVAL:
34123cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
34133cb34c60Sahrens break;
34143cb34c60Sahrens case ECKSUM:
3415c1a50c7eSMatthew Ahrens recv_ecksum_set_aux(hdl, zc.zc_value, flags->resumable);
34163cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
34173cb34c60Sahrens break;
3418dc7cd546SMark Shellenbaum case ENOTSUP:
3419dc7cd546SMark Shellenbaum zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3420dc7cd546SMark Shellenbaum "pool must be upgraded to receive this stream."));
3421dc7cd546SMark Shellenbaum (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
3422dc7cd546SMark Shellenbaum break;
342337f8ae65SJohn Harres case EDQUOT:
342437f8ae65SJohn Harres zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
342537f8ae65SJohn Harres "destination %s space quota exceeded"), zc.zc_name);
342619b94df9SMatthew Ahrens (void) zfs_error(hdl, EZFS_NOSPC, errbuf);
342737f8ae65SJohn Harres break;
34283cb34c60Sahrens default:
34293cb34c60Sahrens (void) zfs_standard_error(hdl, ioctl_errno, errbuf);
34303cb34c60Sahrens }
34313cb34c60Sahrens }
34323cb34c60Sahrens
34333cb34c60Sahrens /*
3434681d9761SEric Taylor * Mount the target filesystem (if created). Also mount any
3435681d9761SEric Taylor * children of the target filesystem if we did a replication
3436681d9761SEric Taylor * receive (indicated by stream_avl being non-NULL).
34373cb34c60Sahrens */
34383cb34c60Sahrens cp = strchr(zc.zc_value, '@');
34393cb34c60Sahrens if (cp && (ioctl_err == 0 || !newfs)) {
34403cb34c60Sahrens zfs_handle_t *h;
34413cb34c60Sahrens
34423cb34c60Sahrens *cp = '\0';
34433cb34c60Sahrens h = zfs_open(hdl, zc.zc_value,
34443cb34c60Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
344504bb726eSahl if (h != NULL) {
34463cb34c60Sahrens if (h->zfs_type == ZFS_TYPE_VOLUME) {
34470069fd67STim Haley *cp = '@';
344888bb18d2SLori Alt } else if (newfs || stream_avl) {
34490069fd67STim Haley /*
34500069fd67STim Haley * Track the first/top of hierarchy fs,
34510069fd67STim Haley * for mounting and sharing later.
34520069fd67STim Haley */
34530069fd67STim Haley if (top_zfs && *top_zfs == NULL)
34540069fd67STim Haley *top_zfs = zfs_strdup(hdl, zc.zc_value);
34553cb34c60Sahrens }
34563cb34c60Sahrens zfs_close(h);
34573cb34c60Sahrens }
34580069fd67STim Haley *cp = '@';
34593cb34c60Sahrens }
34603cb34c60Sahrens
34613cb34c60Sahrens if (clp) {
34623cb34c60Sahrens err |= changelist_postfix(clp);
34633cb34c60Sahrens changelist_free(clp);
34643cb34c60Sahrens }
34653cb34c60Sahrens
346692241e0bSTom Erickson if (prop_errflags & ZPROP_ERR_NOCLEAR) {
346792241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
346892241e0bSTom Erickson "failed to clear unreceived properties on %s"),
346992241e0bSTom Erickson zc.zc_name);
347092241e0bSTom Erickson (void) fprintf(stderr, "\n");
347192241e0bSTom Erickson }
347292241e0bSTom Erickson if (prop_errflags & ZPROP_ERR_NORESTORE) {
347392241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
347492241e0bSTom Erickson "failed to restore original properties on %s"),
347592241e0bSTom Erickson zc.zc_name);
347692241e0bSTom Erickson (void) fprintf(stderr, "\n");
347792241e0bSTom Erickson }
347892241e0bSTom Erickson
34793cb34c60Sahrens if (err || ioctl_err)
34803cb34c60Sahrens return (-1);
34813cb34c60Sahrens
3482c99e4bdcSChris Kirby *action_handlep = zc.zc_action_handle;
3483c99e4bdcSChris Kirby
348419b94df9SMatthew Ahrens if (flags->verbose) {
34853cb34c60Sahrens char buf1[64];
34863cb34c60Sahrens char buf2[64];
34873cb34c60Sahrens uint64_t bytes = zc.zc_cookie;
34883cb34c60Sahrens time_t delta = time(NULL) - begin_time;
34893cb34c60Sahrens if (delta == 0)
34903cb34c60Sahrens delta = 1;
34913cb34c60Sahrens zfs_nicenum(bytes, buf1, sizeof (buf1));
34923cb34c60Sahrens zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
34933cb34c60Sahrens
34943cb34c60Sahrens (void) printf("received %sB stream in %lu seconds (%sB/sec)\n",
34953cb34c60Sahrens buf1, delta, buf2);
34963cb34c60Sahrens }
34973cb34c60Sahrens
34983cb34c60Sahrens return (0);
34993cb34c60Sahrens }
35003cb34c60Sahrens
35010069fd67STim 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)3502a2cdcdd2SPaul Dagnelie zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
3503a2cdcdd2SPaul Dagnelie const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
3504a2cdcdd2SPaul Dagnelie nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
3505a2cdcdd2SPaul Dagnelie uint64_t *action_handlep)
35063cb34c60Sahrens {
35073cb34c60Sahrens int err;
35083cb34c60Sahrens dmu_replay_record_t drr, drr_noswap;
35093cb34c60Sahrens struct drr_begin *drrb = &drr.drr_u.drr_begin;
35103cb34c60Sahrens char errbuf[1024];
35113cb34c60Sahrens zio_cksum_t zcksum = { 0 };
35129e69d7d0SLori Alt uint64_t featureflags;
35139e69d7d0SLori Alt int hdrtype;
35143cb34c60Sahrens
35153cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
35163cb34c60Sahrens "cannot receive"));
35173cb34c60Sahrens
351819b94df9SMatthew Ahrens if (flags->isprefix &&
35193cb34c60Sahrens !zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
35203cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
35213cb34c60Sahrens "(%s) does not exist"), tosnap);
35223cb34c60Sahrens return (zfs_error(hdl, EZFS_NOENT, errbuf));
35233cb34c60Sahrens }
3524a2cdcdd2SPaul Dagnelie if (originsnap &&
3525a2cdcdd2SPaul Dagnelie !zfs_dataset_exists(hdl, originsnap, ZFS_TYPE_DATASET)) {
3526a2cdcdd2SPaul Dagnelie zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified origin fs "
3527a2cdcdd2SPaul Dagnelie "(%s) does not exist"), originsnap);
3528a2cdcdd2SPaul Dagnelie return (zfs_error(hdl, EZFS_NOENT, errbuf));
3529a2cdcdd2SPaul Dagnelie }
35303cb34c60Sahrens
35313cb34c60Sahrens /* read in the BEGIN record */
35323cb34c60Sahrens if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE,
35333cb34c60Sahrens &zcksum)))
35343cb34c60Sahrens return (err);
35353cb34c60Sahrens
35363cb34c60Sahrens if (drr.drr_type == DRR_END || drr.drr_type == BSWAP_32(DRR_END)) {
35373cb34c60Sahrens /* It's the double end record at the end of a package */
35383cb34c60Sahrens return (ENODATA);
35393cb34c60Sahrens }
35403cb34c60Sahrens
35413cb34c60Sahrens /* the kernel needs the non-byteswapped begin record */
35423cb34c60Sahrens drr_noswap = drr;
35433cb34c60Sahrens
354419b94df9SMatthew Ahrens flags->byteswap = B_FALSE;
35453cb34c60Sahrens if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
35463cb34c60Sahrens /*
35473cb34c60Sahrens * We computed the checksum in the wrong byteorder in
35483cb34c60Sahrens * recv_read() above; do it again correctly.
35493cb34c60Sahrens */
35503cb34c60Sahrens bzero(&zcksum, sizeof (zio_cksum_t));
35513cb34c60Sahrens fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum);
355219b94df9SMatthew Ahrens flags->byteswap = B_TRUE;
35533cb34c60Sahrens
35543cb34c60Sahrens drr.drr_type = BSWAP_32(drr.drr_type);
35553cb34c60Sahrens drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
35563cb34c60Sahrens drrb->drr_magic = BSWAP_64(drrb->drr_magic);
35579e69d7d0SLori Alt drrb->drr_versioninfo = BSWAP_64(drrb->drr_versioninfo);
35583cb34c60Sahrens drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time);
35593cb34c60Sahrens drrb->drr_type = BSWAP_32(drrb->drr_type);
35603cb34c60Sahrens drrb->drr_flags = BSWAP_32(drrb->drr_flags);
35613cb34c60Sahrens drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
35623cb34c60Sahrens drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid);
35633cb34c60Sahrens }
35643cb34c60Sahrens
35653cb34c60Sahrens if (drrb->drr_magic != DMU_BACKUP_MAGIC || drr.drr_type != DRR_BEGIN) {
35663cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
35673cb34c60Sahrens "stream (bad magic number)"));
35683cb34c60Sahrens return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
35693cb34c60Sahrens }
35703cb34c60Sahrens
35719e69d7d0SLori Alt featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
35729e69d7d0SLori Alt hdrtype = DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo);
35739e69d7d0SLori Alt
35749e69d7d0SLori Alt if (!DMU_STREAM_SUPPORTED(featureflags) ||
35759e69d7d0SLori Alt (hdrtype != DMU_SUBSTREAM && hdrtype != DMU_COMPOUNDSTREAM)) {
35769e69d7d0SLori Alt zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
35779e69d7d0SLori Alt "stream has unsupported feature, feature flags = %lx"),
35789e69d7d0SLori Alt featureflags);
35799e69d7d0SLori Alt return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
35809e69d7d0SLori Alt }
35819e69d7d0SLori Alt
35823cb34c60Sahrens if (strchr(drrb->drr_toname, '@') == NULL) {
35833cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
35843cb34c60Sahrens "stream (bad snapshot name)"));
35853cb34c60Sahrens return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
35863cb34c60Sahrens }
35873cb34c60Sahrens
35889e69d7d0SLori Alt if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_SUBSTREAM) {
3589*a1988827SMatthew Ahrens char nonpackage_sendfs[ZFS_MAX_DATASET_NAME_LEN];
3590bd6a198fSTom Erickson if (sendfs == NULL) {
3591bd6a198fSTom Erickson /*
3592bd6a198fSTom Erickson * We were not called from zfs_receive_package(). Get
3593bd6a198fSTom Erickson * the fs specified by 'zfs send'.
3594bd6a198fSTom Erickson */
3595bd6a198fSTom Erickson char *cp;
3596bd6a198fSTom Erickson (void) strlcpy(nonpackage_sendfs,
3597*a1988827SMatthew Ahrens drr.drr_u.drr_begin.drr_toname,
3598*a1988827SMatthew Ahrens sizeof (nonpackage_sendfs));
3599bd6a198fSTom Erickson if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
3600bd6a198fSTom Erickson *cp = '\0';
3601bd6a198fSTom Erickson sendfs = nonpackage_sendfs;
3602bd6a198fSTom Erickson }
3603a2cdcdd2SPaul Dagnelie return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
3604a2cdcdd2SPaul Dagnelie &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
3605a2cdcdd2SPaul Dagnelie cleanup_fd, action_handlep));
360683d7f9feSTom Erickson } else {
36079e69d7d0SLori Alt assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
36089e69d7d0SLori Alt DMU_COMPOUNDSTREAM);
3609a2cdcdd2SPaul Dagnelie return (zfs_receive_package(hdl, infd, tosnap, flags, &drr,
3610a2cdcdd2SPaul Dagnelie &zcksum, top_zfs, cleanup_fd, action_handlep));
36113cb34c60Sahrens }
36123cb34c60Sahrens }
36130069fd67STim Haley
36140069fd67STim Haley /*
36150069fd67STim Haley * Restores a backup of tosnap from the file descriptor specified by infd.
36160069fd67STim Haley * Return 0 on total success, -2 if some things couldn't be
36170069fd67STim Haley * destroyed/renamed/promoted, -1 if some things couldn't be received.
3618c1a50c7eSMatthew Ahrens * (-1 will override -2, if -1 and the resumable flag was specified the
3619c1a50c7eSMatthew Ahrens * transfer can be resumed if the sending side supports it).
36200069fd67STim Haley */
36210069fd67STim Haley int
zfs_receive(libzfs_handle_t * hdl,const char * tosnap,nvlist_t * props,recvflags_t * flags,int infd,avl_tree_t * stream_avl)3622a2cdcdd2SPaul Dagnelie zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
3623a2cdcdd2SPaul Dagnelie recvflags_t *flags, int infd, avl_tree_t *stream_avl)
36240069fd67STim Haley {
36250069fd67STim Haley char *top_zfs = NULL;
36260069fd67STim Haley int err;
3627c99e4bdcSChris Kirby int cleanup_fd;
3628c99e4bdcSChris Kirby uint64_t action_handle = 0;
3629a2cdcdd2SPaul Dagnelie char *originsnap = NULL;
3630a2cdcdd2SPaul Dagnelie if (props) {
3631a2cdcdd2SPaul Dagnelie err = nvlist_lookup_string(props, "origin", &originsnap);
3632a2cdcdd2SPaul Dagnelie if (err && err != ENOENT)
3633a2cdcdd2SPaul Dagnelie return (err);
3634a2cdcdd2SPaul Dagnelie }
3635c99e4bdcSChris Kirby
3636c99e4bdcSChris Kirby cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
3637c99e4bdcSChris Kirby VERIFY(cleanup_fd >= 0);
36380069fd67STim Haley
3639a2cdcdd2SPaul Dagnelie err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
3640c99e4bdcSChris Kirby stream_avl, &top_zfs, cleanup_fd, &action_handle);
3641c99e4bdcSChris Kirby
3642c99e4bdcSChris Kirby VERIFY(0 == close(cleanup_fd));
36430069fd67STim Haley
364419b94df9SMatthew Ahrens if (err == 0 && !flags->nomount && top_zfs) {
36450069fd67STim Haley zfs_handle_t *zhp;
36460069fd67STim Haley prop_changelist_t *clp;
36470069fd67STim Haley
36480069fd67STim Haley zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM);
36490069fd67STim Haley if (zhp != NULL) {
36500069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
36510069fd67STim Haley CL_GATHER_MOUNT_ALWAYS, 0);
36520069fd67STim Haley zfs_close(zhp);
36530069fd67STim Haley if (clp != NULL) {
36540069fd67STim Haley /* mount and share received datasets */
36550069fd67STim Haley err = changelist_postfix(clp);
36560069fd67STim Haley changelist_free(clp);
36570069fd67STim Haley }
36580069fd67STim Haley }
36590069fd67STim Haley if (zhp == NULL || clp == NULL || err)
36600069fd67STim Haley err = -1;
36610069fd67STim Haley }
36620069fd67STim Haley if (top_zfs)
36630069fd67STim Haley free(top_zfs);
36640069fd67STim Haley
36650069fd67STim Haley return (err);
36660069fd67STim Haley }
3667