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