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. 249c3fd121SMatthew 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. 275878fad7SDan McDonald * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. 28c3d26abcSMatthew Ahrens * Copyright (c) 2014 Integros [integros.com] 293cb34c60Sahrens */ 303cb34c60Sahrens 313cb34c60Sahrens #include <assert.h> 323cb34c60Sahrens #include <ctype.h> 333cb34c60Sahrens #include <errno.h> 343cb34c60Sahrens #include <libintl.h> 353cb34c60Sahrens #include <stdio.h> 363cb34c60Sahrens #include <stdlib.h> 373cb34c60Sahrens #include <strings.h> 383cb34c60Sahrens #include <unistd.h> 393cb34c60Sahrens #include <stddef.h> 403cb34c60Sahrens #include <fcntl.h> 413cb34c60Sahrens #include <sys/mount.h> 429e69d7d0SLori Alt #include <pthread.h> 439e69d7d0SLori Alt #include <umem.h> 444e3c9f44SBill Pijewski #include <time.h> 453cb34c60Sahrens 463cb34c60Sahrens #include <libzfs.h> 475d7b4d43SMatthew Ahrens #include <libzfs_core.h> 483cb34c60Sahrens 493cb34c60Sahrens #include "zfs_namecheck.h" 503cb34c60Sahrens #include "zfs_prop.h" 51495db6fbSLori Alt #include "zfs_fletcher.h" 523cb34c60Sahrens #include "libzfs_impl.h" 539c3fd121SMatthew Ahrens #include <zlib.h> 549e69d7d0SLori Alt #include <sha2.h> 558e714474SLori Alt #include <sys/zio_checksum.h> 568e714474SLori Alt #include <sys/ddt.h> 573cb34c60Sahrens 5892241e0bSTom Erickson /* in libzfs_dataset.c */ 5992241e0bSTom Erickson extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *); 6092241e0bSTom Erickson 61a2cdcdd2SPaul Dagnelie static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *, 62a2cdcdd2SPaul Dagnelie recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int, 635878fad7SDan McDonald uint64_t *, const char *); 649c3fd121SMatthew Ahrens static int guid_to_name(libzfs_handle_t *, const char *, 659c3fd121SMatthew Ahrens uint64_t, boolean_t, char *); 660069fd67STim Haley 679e69d7d0SLori Alt static const zio_cksum_t zero_cksum = { 0 }; 689e69d7d0SLori Alt 699e69d7d0SLori Alt typedef struct dedup_arg { 709e69d7d0SLori Alt int inputfd; 719e69d7d0SLori Alt int outputfd; 729e69d7d0SLori Alt libzfs_handle_t *dedup_hdl; 739e69d7d0SLori Alt } dedup_arg_t; 749e69d7d0SLori Alt 754e3c9f44SBill Pijewski typedef struct progress_arg { 764e3c9f44SBill Pijewski zfs_handle_t *pa_zhp; 774e3c9f44SBill Pijewski int pa_fd; 784e3c9f44SBill Pijewski boolean_t pa_parsable; 794e3c9f44SBill Pijewski } progress_arg_t; 804e3c9f44SBill Pijewski 819e69d7d0SLori Alt typedef struct dataref { 829e69d7d0SLori Alt uint64_t ref_guid; 839e69d7d0SLori Alt uint64_t ref_object; 849e69d7d0SLori Alt uint64_t ref_offset; 859e69d7d0SLori Alt } dataref_t; 869e69d7d0SLori Alt 879e69d7d0SLori Alt typedef struct dedup_entry { 889e69d7d0SLori Alt struct dedup_entry *dde_next; 899e69d7d0SLori Alt zio_cksum_t dde_chksum; 908e714474SLori Alt uint64_t dde_prop; 919e69d7d0SLori Alt dataref_t dde_ref; 929e69d7d0SLori Alt } dedup_entry_t; 939e69d7d0SLori Alt 949e69d7d0SLori Alt #define MAX_DDT_PHYSMEM_PERCENT 20 959e69d7d0SLori Alt #define SMALLEST_POSSIBLE_MAX_DDT_MB 128 969e69d7d0SLori Alt 979e69d7d0SLori Alt typedef struct dedup_table { 989e69d7d0SLori Alt dedup_entry_t **dedup_hash_array; 999e69d7d0SLori Alt umem_cache_t *ddecache; 1009e69d7d0SLori Alt uint64_t max_ddt_size; /* max dedup table size in bytes */ 1019e69d7d0SLori Alt uint64_t cur_ddt_size; /* current dedup table size in bytes */ 1029e69d7d0SLori Alt uint64_t ddt_count; 1039e69d7d0SLori Alt int numhashbits; 1049e69d7d0SLori Alt boolean_t ddt_full; 1059e69d7d0SLori Alt } dedup_table_t; 1069e69d7d0SLori Alt 1079e69d7d0SLori Alt static int 1089e69d7d0SLori Alt high_order_bit(uint64_t n) 1099e69d7d0SLori Alt { 1109e69d7d0SLori Alt int count; 1119e69d7d0SLori Alt 1129e69d7d0SLori Alt for (count = 0; n != 0; count++) 1139e69d7d0SLori Alt n >>= 1; 1149e69d7d0SLori Alt return (count); 1159e69d7d0SLori Alt } 1169e69d7d0SLori Alt 1179e69d7d0SLori Alt static size_t 1189e69d7d0SLori Alt ssread(void *buf, size_t len, FILE *stream) 1199e69d7d0SLori Alt { 1209e69d7d0SLori Alt size_t outlen; 1219e69d7d0SLori Alt 1229e69d7d0SLori Alt if ((outlen = fread(buf, len, 1, stream)) == 0) 1239e69d7d0SLori Alt return (0); 1249e69d7d0SLori Alt 1259e69d7d0SLori Alt return (outlen); 1269e69d7d0SLori Alt } 1279e69d7d0SLori Alt 1289e69d7d0SLori Alt static void 1299e69d7d0SLori Alt ddt_hash_append(libzfs_handle_t *hdl, dedup_table_t *ddt, dedup_entry_t **ddepp, 1308e714474SLori Alt zio_cksum_t *cs, uint64_t prop, dataref_t *dr) 1319e69d7d0SLori Alt { 1329e69d7d0SLori Alt dedup_entry_t *dde; 1339e69d7d0SLori Alt 1349e69d7d0SLori Alt if (ddt->cur_ddt_size >= ddt->max_ddt_size) { 1359e69d7d0SLori Alt if (ddt->ddt_full == B_FALSE) { 1369e69d7d0SLori Alt zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 1379e69d7d0SLori Alt "Dedup table full. Deduplication will continue " 1389e69d7d0SLori Alt "with existing table entries")); 1399e69d7d0SLori Alt ddt->ddt_full = B_TRUE; 1409e69d7d0SLori Alt } 1419e69d7d0SLori Alt return; 1429e69d7d0SLori Alt } 1439e69d7d0SLori Alt 1449e69d7d0SLori Alt if ((dde = umem_cache_alloc(ddt->ddecache, UMEM_DEFAULT)) 1459e69d7d0SLori Alt != NULL) { 1469e69d7d0SLori Alt assert(*ddepp == NULL); 1479e69d7d0SLori Alt dde->dde_next = NULL; 1489e69d7d0SLori Alt dde->dde_chksum = *cs; 1498e714474SLori Alt dde->dde_prop = prop; 1509e69d7d0SLori Alt dde->dde_ref = *dr; 1519e69d7d0SLori Alt *ddepp = dde; 1529e69d7d0SLori Alt ddt->cur_ddt_size += sizeof (dedup_entry_t); 1539e69d7d0SLori Alt ddt->ddt_count++; 1549e69d7d0SLori Alt } 1559e69d7d0SLori Alt } 1569e69d7d0SLori Alt 1579e69d7d0SLori Alt /* 1589e69d7d0SLori Alt * Using the specified dedup table, do a lookup for an entry with 1599e69d7d0SLori Alt * the checksum cs. If found, return the block's reference info 1609e69d7d0SLori Alt * in *dr. Otherwise, insert a new entry in the dedup table, using 1619e69d7d0SLori Alt * the reference information specified by *dr. 1629e69d7d0SLori Alt * 1639e69d7d0SLori Alt * return value: true - entry was found 1649e69d7d0SLori Alt * false - entry was not found 1659e69d7d0SLori Alt */ 1669e69d7d0SLori Alt static boolean_t 1679e69d7d0SLori Alt ddt_update(libzfs_handle_t *hdl, dedup_table_t *ddt, zio_cksum_t *cs, 1688e714474SLori Alt uint64_t prop, dataref_t *dr) 1699e69d7d0SLori Alt { 1709e69d7d0SLori Alt uint32_t hashcode; 1719e69d7d0SLori Alt dedup_entry_t **ddepp; 1729e69d7d0SLori Alt 1739e69d7d0SLori Alt hashcode = BF64_GET(cs->zc_word[0], 0, ddt->numhashbits); 1749e69d7d0SLori Alt 1759e69d7d0SLori Alt for (ddepp = &(ddt->dedup_hash_array[hashcode]); *ddepp != NULL; 1769e69d7d0SLori Alt ddepp = &((*ddepp)->dde_next)) { 1778e714474SLori Alt if (ZIO_CHECKSUM_EQUAL(((*ddepp)->dde_chksum), *cs) && 1788e714474SLori Alt (*ddepp)->dde_prop == prop) { 1799e69d7d0SLori Alt *dr = (*ddepp)->dde_ref; 1809e69d7d0SLori Alt return (B_TRUE); 1819e69d7d0SLori Alt } 1829e69d7d0SLori Alt } 1838e714474SLori Alt ddt_hash_append(hdl, ddt, ddepp, cs, prop, dr); 1849e69d7d0SLori Alt return (B_FALSE); 1859e69d7d0SLori Alt } 1869e69d7d0SLori Alt 1879e69d7d0SLori Alt static int 18898110f08SMatthew Ahrens dump_record(dmu_replay_record_t *drr, void *payload, int payload_len, 18998110f08SMatthew Ahrens zio_cksum_t *zc, int outfd) 1909e69d7d0SLori Alt { 19198110f08SMatthew Ahrens ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), 19298110f08SMatthew Ahrens ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t)); 19398110f08SMatthew Ahrens fletcher_4_incremental_native(drr, 19498110f08SMatthew Ahrens offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc); 19598110f08SMatthew Ahrens if (drr->drr_type != DRR_BEGIN) { 19698110f08SMatthew Ahrens ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u. 19798110f08SMatthew Ahrens drr_checksum.drr_checksum)); 19898110f08SMatthew Ahrens drr->drr_u.drr_checksum.drr_checksum = *zc; 19998110f08SMatthew Ahrens } 20098110f08SMatthew Ahrens fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum, 20198110f08SMatthew Ahrens sizeof (zio_cksum_t), zc); 20298110f08SMatthew Ahrens if (write(outfd, drr, sizeof (*drr)) == -1) 20398110f08SMatthew Ahrens return (errno); 20498110f08SMatthew Ahrens if (payload_len != 0) { 20598110f08SMatthew Ahrens fletcher_4_incremental_native(payload, payload_len, zc); 20698110f08SMatthew Ahrens if (write(outfd, payload, payload_len) == -1) 20798110f08SMatthew Ahrens return (errno); 20898110f08SMatthew Ahrens } 20998110f08SMatthew Ahrens return (0); 2109e69d7d0SLori Alt } 2119e69d7d0SLori Alt 212a0fdab9eSMarcel Telka int 213a0fdab9eSMarcel Telka zfs_send_compoundstream_begin(zfs_handle_t *zhp, const char *tosnap, 214a0fdab9eSMarcel Telka int featureflags, void *payload, int payload_len, int outfd) 215a0fdab9eSMarcel Telka { 216a0fdab9eSMarcel Telka dmu_replay_record_t drr = { 0 }; 217a0fdab9eSMarcel Telka zio_cksum_t zc = { 0 }; 218a0fdab9eSMarcel Telka int err; 219a0fdab9eSMarcel Telka 220a0fdab9eSMarcel Telka if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 221a0fdab9eSMarcel Telka uint64_t version; 222a0fdab9eSMarcel Telka version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 223a0fdab9eSMarcel Telka if (version >= ZPL_VERSION_SA) { 224a0fdab9eSMarcel Telka featureflags |= DMU_BACKUP_FEATURE_SA_SPILL; 225a0fdab9eSMarcel Telka } 226a0fdab9eSMarcel Telka } 227a0fdab9eSMarcel Telka 228a0fdab9eSMarcel Telka drr.drr_type = DRR_BEGIN; 229a0fdab9eSMarcel Telka drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC; 230a0fdab9eSMarcel Telka DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.drr_versioninfo, 231a0fdab9eSMarcel Telka DMU_COMPOUNDSTREAM); 232a0fdab9eSMarcel Telka DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.drr_versioninfo, 233a0fdab9eSMarcel Telka featureflags); 234a0fdab9eSMarcel Telka (void) snprintf(drr.drr_u.drr_begin.drr_toname, 235a0fdab9eSMarcel Telka sizeof (drr.drr_u.drr_begin.drr_toname), "%s@%s", zfs_get_name(zhp), 236a0fdab9eSMarcel Telka tosnap); 237a0fdab9eSMarcel Telka drr.drr_payloadlen = payload_len; 238a0fdab9eSMarcel Telka 239a0fdab9eSMarcel Telka err = dump_record(&drr, payload, payload_len, &zc, outfd); 240a0fdab9eSMarcel Telka if (err != 0) 241a0fdab9eSMarcel Telka return (err); 242a0fdab9eSMarcel Telka 243a0fdab9eSMarcel Telka bzero(&drr, sizeof (drr)); 244a0fdab9eSMarcel Telka drr.drr_type = DRR_END; 245a0fdab9eSMarcel Telka drr.drr_u.drr_end.drr_checksum = zc; 246a0fdab9eSMarcel Telka err = write(outfd, &drr, sizeof (drr)); 247a0fdab9eSMarcel Telka if (err == -1) { 248a0fdab9eSMarcel Telka err = errno; 249a0fdab9eSMarcel Telka return (err); 250a0fdab9eSMarcel Telka } 251a0fdab9eSMarcel Telka 252a0fdab9eSMarcel Telka return (0); 253a0fdab9eSMarcel Telka } 254a0fdab9eSMarcel Telka 255a0fdab9eSMarcel Telka int 256a0fdab9eSMarcel Telka zfs_send_compoundstream_end(int outfd) 257a0fdab9eSMarcel Telka { 258a0fdab9eSMarcel Telka dmu_replay_record_t drr = { 0 }; 259a0fdab9eSMarcel Telka 260a0fdab9eSMarcel Telka drr.drr_type = DRR_END; 261a0fdab9eSMarcel Telka if (write(outfd, &drr, sizeof (drr)) == -1) 262a0fdab9eSMarcel Telka return (errno); 263a0fdab9eSMarcel Telka 264a0fdab9eSMarcel Telka return (0); 265a0fdab9eSMarcel Telka } 266a0fdab9eSMarcel Telka 2679e69d7d0SLori Alt /* 2689e69d7d0SLori Alt * This function is started in a separate thread when the dedup option 2699e69d7d0SLori Alt * has been requested. The main send thread determines the list of 2709e69d7d0SLori Alt * snapshots to be included in the send stream and makes the ioctl calls 2719e69d7d0SLori Alt * for each one. But instead of having the ioctl send the output to the 2729e69d7d0SLori Alt * the output fd specified by the caller of zfs_send()), the 2739e69d7d0SLori Alt * ioctl is told to direct the output to a pipe, which is read by the 2749e69d7d0SLori Alt * alternate thread running THIS function. This function does the 2759e69d7d0SLori Alt * dedup'ing by: 2769e69d7d0SLori Alt * 1. building a dedup table (the DDT) 2779e69d7d0SLori Alt * 2. doing checksums on each data block and inserting a record in the DDT 2789e69d7d0SLori Alt * 3. looking for matching checksums, and 2799e69d7d0SLori Alt * 4. sending a DRR_WRITE_BYREF record instead of a write record whenever 2809e69d7d0SLori Alt * a duplicate block is found. 2819e69d7d0SLori Alt * The output of this function then goes to the output fd requested 2829e69d7d0SLori Alt * by the caller of zfs_send(). 2839e69d7d0SLori Alt */ 2849e69d7d0SLori Alt static void * 2859e69d7d0SLori Alt cksummer(void *arg) 2869e69d7d0SLori Alt { 2879e69d7d0SLori Alt dedup_arg_t *dda = arg; 288b5152584SMatthew Ahrens char *buf = zfs_alloc(dda->dedup_hdl, SPA_MAXBLOCKSIZE); 2899e69d7d0SLori Alt dmu_replay_record_t thedrr; 2909e69d7d0SLori Alt dmu_replay_record_t *drr = &thedrr; 2919e69d7d0SLori Alt FILE *ofp; 2929e69d7d0SLori Alt int outfd; 2939e69d7d0SLori Alt dedup_table_t ddt; 2949e69d7d0SLori Alt zio_cksum_t stream_cksum; 2959e69d7d0SLori Alt uint64_t physmem = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); 2969e69d7d0SLori Alt uint64_t numbuckets; 2979e69d7d0SLori Alt 2989e69d7d0SLori Alt ddt.max_ddt_size = 2999e69d7d0SLori Alt MAX((physmem * MAX_DDT_PHYSMEM_PERCENT) / 100, 3009e69d7d0SLori Alt SMALLEST_POSSIBLE_MAX_DDT_MB << 20); 3019e69d7d0SLori Alt 3029e69d7d0SLori Alt numbuckets = ddt.max_ddt_size / (sizeof (dedup_entry_t)); 3039e69d7d0SLori Alt 3049e69d7d0SLori Alt /* 3059e69d7d0SLori Alt * numbuckets must be a power of 2. Increase number to 3069e69d7d0SLori Alt * a power of 2 if necessary. 3079e69d7d0SLori Alt */ 3089e69d7d0SLori Alt if (!ISP2(numbuckets)) 3099e69d7d0SLori Alt numbuckets = 1 << high_order_bit(numbuckets); 3109e69d7d0SLori Alt 3119e69d7d0SLori Alt ddt.dedup_hash_array = calloc(numbuckets, sizeof (dedup_entry_t *)); 3129e69d7d0SLori Alt ddt.ddecache = umem_cache_create("dde", sizeof (dedup_entry_t), 0, 3139e69d7d0SLori Alt NULL, NULL, NULL, NULL, NULL, 0); 3149e69d7d0SLori Alt ddt.cur_ddt_size = numbuckets * sizeof (dedup_entry_t *); 3159e69d7d0SLori Alt ddt.numhashbits = high_order_bit(numbuckets) - 1; 3169e69d7d0SLori Alt ddt.ddt_full = B_FALSE; 3179e69d7d0SLori Alt 3189e69d7d0SLori Alt outfd = dda->outputfd; 3199e69d7d0SLori Alt ofp = fdopen(dda->inputfd, "r"); 32098110f08SMatthew Ahrens while (ssread(drr, sizeof (*drr), ofp) != 0) { 3219e69d7d0SLori Alt 3229e69d7d0SLori Alt switch (drr->drr_type) { 3239e69d7d0SLori Alt case DRR_BEGIN: 3249e69d7d0SLori Alt { 32598110f08SMatthew Ahrens struct drr_begin *drrb = &drr->drr_u.drr_begin; 3269e69d7d0SLori Alt int fflags; 32798110f08SMatthew Ahrens int sz = 0; 3289e69d7d0SLori Alt ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0); 3299e69d7d0SLori Alt 33098110f08SMatthew Ahrens ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC); 33198110f08SMatthew Ahrens 3329e69d7d0SLori Alt /* set the DEDUP feature flag for this stream */ 3339e69d7d0SLori Alt fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); 3348e714474SLori Alt fflags |= (DMU_BACKUP_FEATURE_DEDUP | 3358e714474SLori Alt DMU_BACKUP_FEATURE_DEDUPPROPS); 3369e69d7d0SLori Alt DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags); 3379e69d7d0SLori Alt 3389c3fd121SMatthew Ahrens if (drr->drr_payloadlen != 0) { 33998110f08SMatthew Ahrens sz = drr->drr_payloadlen; 3409e69d7d0SLori Alt 341b5152584SMatthew Ahrens if (sz > SPA_MAXBLOCKSIZE) { 342b5152584SMatthew Ahrens buf = zfs_realloc(dda->dedup_hdl, buf, 343b5152584SMatthew Ahrens SPA_MAXBLOCKSIZE, sz); 3449e69d7d0SLori Alt } 3459e69d7d0SLori Alt (void) ssread(buf, sz, ofp); 3469e69d7d0SLori Alt if (ferror(stdin)) 3479e69d7d0SLori Alt perror("fread"); 3489e69d7d0SLori Alt } 34998110f08SMatthew Ahrens if (dump_record(drr, buf, sz, &stream_cksum, 35098110f08SMatthew Ahrens outfd) != 0) 35198110f08SMatthew Ahrens goto out; 3529e69d7d0SLori Alt break; 3539e69d7d0SLori Alt } 3549e69d7d0SLori Alt 3559e69d7d0SLori Alt case DRR_END: 3569e69d7d0SLori Alt { 35798110f08SMatthew Ahrens struct drr_end *drre = &drr->drr_u.drr_end; 3589e69d7d0SLori Alt /* use the recalculated checksum */ 35998110f08SMatthew Ahrens drre->drr_checksum = stream_cksum; 36098110f08SMatthew Ahrens if (dump_record(drr, NULL, 0, &stream_cksum, 36198110f08SMatthew Ahrens outfd) != 0) 3629e69d7d0SLori Alt goto out; 3639e69d7d0SLori Alt break; 3649e69d7d0SLori Alt } 3659e69d7d0SLori Alt 3669e69d7d0SLori Alt case DRR_OBJECT: 3679e69d7d0SLori Alt { 36898110f08SMatthew Ahrens struct drr_object *drro = &drr->drr_u.drr_object; 3699e69d7d0SLori Alt if (drro->drr_bonuslen > 0) { 3709e69d7d0SLori Alt (void) ssread(buf, 3719e69d7d0SLori Alt P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), 3729e69d7d0SLori Alt ofp); 3739e69d7d0SLori Alt } 37498110f08SMatthew Ahrens if (dump_record(drr, buf, 37598110f08SMatthew Ahrens P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), 37698110f08SMatthew Ahrens &stream_cksum, outfd) != 0) 37798110f08SMatthew Ahrens goto out; 3789e69d7d0SLori Alt break; 3799e69d7d0SLori Alt } 3809e69d7d0SLori Alt 3810a586ceaSMark Shellenbaum case DRR_SPILL: 3820a586ceaSMark Shellenbaum { 38398110f08SMatthew Ahrens struct drr_spill *drrs = &drr->drr_u.drr_spill; 3840a586ceaSMark Shellenbaum (void) ssread(buf, drrs->drr_length, ofp); 38598110f08SMatthew Ahrens if (dump_record(drr, buf, drrs->drr_length, 38698110f08SMatthew Ahrens &stream_cksum, outfd) != 0) 3870a586ceaSMark Shellenbaum goto out; 3880a586ceaSMark Shellenbaum break; 3890a586ceaSMark Shellenbaum } 3900a586ceaSMark Shellenbaum 3919e69d7d0SLori Alt case DRR_FREEOBJECTS: 3929e69d7d0SLori Alt { 39398110f08SMatthew Ahrens if (dump_record(drr, NULL, 0, &stream_cksum, 39498110f08SMatthew Ahrens outfd) != 0) 3959e69d7d0SLori Alt goto out; 3969e69d7d0SLori Alt break; 3979e69d7d0SLori Alt } 3989e69d7d0SLori Alt 3999e69d7d0SLori Alt case DRR_WRITE: 4009e69d7d0SLori Alt { 40198110f08SMatthew Ahrens struct drr_write *drrw = &drr->drr_u.drr_write; 4029e69d7d0SLori Alt dataref_t dataref; 4039e69d7d0SLori Alt 4049e69d7d0SLori Alt (void) ssread(buf, drrw->drr_length, ofp); 4058e714474SLori Alt 4069e69d7d0SLori Alt /* 4078e714474SLori Alt * Use the existing checksum if it's dedup-capable, 4088e714474SLori Alt * else calculate a SHA256 checksum for it. 4099e69d7d0SLori Alt */ 4108e714474SLori Alt 4118e714474SLori Alt if (ZIO_CHECKSUM_EQUAL(drrw->drr_key.ddk_cksum, 4128e714474SLori Alt zero_cksum) || 4138e714474SLori Alt !DRR_IS_DEDUP_CAPABLE(drrw->drr_checksumflags)) { 4149e69d7d0SLori Alt SHA256_CTX ctx; 4159e69d7d0SLori Alt zio_cksum_t tmpsha256; 4169e69d7d0SLori Alt 4179e69d7d0SLori Alt SHA256Init(&ctx); 4189e69d7d0SLori Alt SHA256Update(&ctx, buf, drrw->drr_length); 4199e69d7d0SLori Alt SHA256Final(&tmpsha256, &ctx); 4208e714474SLori Alt drrw->drr_key.ddk_cksum.zc_word[0] = 4219e69d7d0SLori Alt BE_64(tmpsha256.zc_word[0]); 4228e714474SLori Alt drrw->drr_key.ddk_cksum.zc_word[1] = 4239e69d7d0SLori Alt BE_64(tmpsha256.zc_word[1]); 4248e714474SLori Alt drrw->drr_key.ddk_cksum.zc_word[2] = 4259e69d7d0SLori Alt BE_64(tmpsha256.zc_word[2]); 4268e714474SLori Alt drrw->drr_key.ddk_cksum.zc_word[3] = 4279e69d7d0SLori Alt BE_64(tmpsha256.zc_word[3]); 4288e714474SLori Alt drrw->drr_checksumtype = ZIO_CHECKSUM_SHA256; 4298e714474SLori Alt drrw->drr_checksumflags = DRR_CHECKSUM_DEDUP; 4309e69d7d0SLori Alt } 4319e69d7d0SLori Alt 4329e69d7d0SLori Alt dataref.ref_guid = drrw->drr_toguid; 4339e69d7d0SLori Alt dataref.ref_object = drrw->drr_object; 4349e69d7d0SLori Alt dataref.ref_offset = drrw->drr_offset; 4359e69d7d0SLori Alt 4369e69d7d0SLori Alt if (ddt_update(dda->dedup_hdl, &ddt, 4378e714474SLori Alt &drrw->drr_key.ddk_cksum, drrw->drr_key.ddk_prop, 4388e714474SLori Alt &dataref)) { 43998110f08SMatthew Ahrens dmu_replay_record_t wbr_drr = {0}; 44098110f08SMatthew Ahrens struct drr_write_byref *wbr_drrr = 44198110f08SMatthew Ahrens &wbr_drr.drr_u.drr_write_byref; 44298110f08SMatthew Ahrens 4439e69d7d0SLori Alt /* block already present in stream */ 44498110f08SMatthew Ahrens wbr_drr.drr_type = DRR_WRITE_BYREF; 44598110f08SMatthew Ahrens 4469e69d7d0SLori Alt wbr_drrr->drr_object = drrw->drr_object; 4479e69d7d0SLori Alt wbr_drrr->drr_offset = drrw->drr_offset; 4489e69d7d0SLori Alt wbr_drrr->drr_length = drrw->drr_length; 4499e69d7d0SLori Alt wbr_drrr->drr_toguid = drrw->drr_toguid; 4509e69d7d0SLori Alt wbr_drrr->drr_refguid = dataref.ref_guid; 4519e69d7d0SLori Alt wbr_drrr->drr_refobject = 4529e69d7d0SLori Alt dataref.ref_object; 4539e69d7d0SLori Alt wbr_drrr->drr_refoffset = 4549e69d7d0SLori Alt dataref.ref_offset; 4559e69d7d0SLori Alt 4568e714474SLori Alt wbr_drrr->drr_checksumtype = 4578e714474SLori Alt drrw->drr_checksumtype; 4588e714474SLori Alt wbr_drrr->drr_checksumflags = 4598e714474SLori Alt drrw->drr_checksumtype; 4608e714474SLori Alt wbr_drrr->drr_key.ddk_cksum = 4618e714474SLori Alt drrw->drr_key.ddk_cksum; 4628e714474SLori Alt wbr_drrr->drr_key.ddk_prop = 4638e714474SLori Alt drrw->drr_key.ddk_prop; 4649e69d7d0SLori Alt 46598110f08SMatthew Ahrens if (dump_record(&wbr_drr, NULL, 0, 46698110f08SMatthew Ahrens &stream_cksum, outfd) != 0) 4679e69d7d0SLori Alt goto out; 4689e69d7d0SLori Alt } else { 4699e69d7d0SLori Alt /* block not previously seen */ 47098110f08SMatthew Ahrens if (dump_record(drr, buf, drrw->drr_length, 47198110f08SMatthew Ahrens &stream_cksum, outfd) != 0) 4729e69d7d0SLori Alt goto out; 4739e69d7d0SLori Alt } 4749e69d7d0SLori Alt break; 4759e69d7d0SLori Alt } 4769e69d7d0SLori Alt 4775d7b4d43SMatthew Ahrens case DRR_WRITE_EMBEDDED: 4785d7b4d43SMatthew Ahrens { 47998110f08SMatthew Ahrens struct drr_write_embedded *drrwe = 48098110f08SMatthew Ahrens &drr->drr_u.drr_write_embedded; 4815d7b4d43SMatthew Ahrens (void) ssread(buf, 4825d7b4d43SMatthew Ahrens P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp); 48398110f08SMatthew Ahrens if (dump_record(drr, buf, 4845d7b4d43SMatthew Ahrens P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), 48598110f08SMatthew Ahrens &stream_cksum, outfd) != 0) 4865d7b4d43SMatthew Ahrens goto out; 4875d7b4d43SMatthew Ahrens break; 4885d7b4d43SMatthew Ahrens } 4895d7b4d43SMatthew Ahrens 4909e69d7d0SLori Alt case DRR_FREE: 4919e69d7d0SLori Alt { 49298110f08SMatthew Ahrens if (dump_record(drr, NULL, 0, &stream_cksum, 49398110f08SMatthew Ahrens outfd) != 0) 4949e69d7d0SLori Alt goto out; 4959e69d7d0SLori Alt break; 4969e69d7d0SLori Alt } 4979e69d7d0SLori Alt 4989e69d7d0SLori Alt default: 49998110f08SMatthew Ahrens (void) fprintf(stderr, "INVALID record type 0x%x\n", 5009e69d7d0SLori Alt drr->drr_type); 5019e69d7d0SLori Alt /* should never happen, so assert */ 5029e69d7d0SLori Alt assert(B_FALSE); 5039e69d7d0SLori Alt } 5049e69d7d0SLori Alt } 5059e69d7d0SLori Alt out: 5069e69d7d0SLori Alt umem_cache_destroy(ddt.ddecache); 5079e69d7d0SLori Alt free(ddt.dedup_hash_array); 5089e69d7d0SLori Alt free(buf); 5099e69d7d0SLori Alt (void) fclose(ofp); 5109e69d7d0SLori Alt 5119e69d7d0SLori Alt return (NULL); 5129e69d7d0SLori Alt } 5139e69d7d0SLori Alt 5143cb34c60Sahrens /* 5153cb34c60Sahrens * Routines for dealing with the AVL tree of fs-nvlists 5163cb34c60Sahrens */ 5173cb34c60Sahrens typedef struct fsavl_node { 5183cb34c60Sahrens avl_node_t fn_node; 5193cb34c60Sahrens nvlist_t *fn_nvfs; 5203cb34c60Sahrens char *fn_snapname; 5213cb34c60Sahrens uint64_t fn_guid; 5223cb34c60Sahrens } fsavl_node_t; 5233cb34c60Sahrens 5243cb34c60Sahrens static int 5253cb34c60Sahrens fsavl_compare(const void *arg1, const void *arg2) 5263cb34c60Sahrens { 5273cb34c60Sahrens const fsavl_node_t *fn1 = arg1; 5283cb34c60Sahrens const fsavl_node_t *fn2 = arg2; 5293cb34c60Sahrens 5303cb34c60Sahrens if (fn1->fn_guid > fn2->fn_guid) 5313cb34c60Sahrens return (+1); 5323cb34c60Sahrens else if (fn1->fn_guid < fn2->fn_guid) 5333cb34c60Sahrens return (-1); 5343cb34c60Sahrens else 5353cb34c60Sahrens return (0); 5363cb34c60Sahrens } 5373cb34c60Sahrens 5383cb34c60Sahrens /* 5393cb34c60Sahrens * Given the GUID of a snapshot, find its containing filesystem and 5403cb34c60Sahrens * (optionally) name. 5413cb34c60Sahrens */ 5423cb34c60Sahrens static nvlist_t * 5433cb34c60Sahrens fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname) 5443cb34c60Sahrens { 5453cb34c60Sahrens fsavl_node_t fn_find; 5463cb34c60Sahrens fsavl_node_t *fn; 5473cb34c60Sahrens 5483cb34c60Sahrens fn_find.fn_guid = snapguid; 5493cb34c60Sahrens 5503cb34c60Sahrens fn = avl_find(avl, &fn_find, NULL); 5513cb34c60Sahrens if (fn) { 5523cb34c60Sahrens if (snapname) 5533cb34c60Sahrens *snapname = fn->fn_snapname; 5543cb34c60Sahrens return (fn->fn_nvfs); 5553cb34c60Sahrens } 5563cb34c60Sahrens return (NULL); 5573cb34c60Sahrens } 5583cb34c60Sahrens 55900954d7bSahl static void 56000954d7bSahl fsavl_destroy(avl_tree_t *avl) 56100954d7bSahl { 56200954d7bSahl fsavl_node_t *fn; 56300954d7bSahl void *cookie; 56400954d7bSahl 56500954d7bSahl if (avl == NULL) 56600954d7bSahl return; 56700954d7bSahl 56800954d7bSahl cookie = NULL; 56900954d7bSahl while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL) 57000954d7bSahl free(fn); 57100954d7bSahl avl_destroy(avl); 57200954d7bSahl free(avl); 57300954d7bSahl } 57400954d7bSahl 57588bb18d2SLori Alt /* 57688bb18d2SLori Alt * Given an nvlist, produce an avl tree of snapshots, ordered by guid 57788bb18d2SLori Alt */ 5783cb34c60Sahrens static avl_tree_t * 5793cb34c60Sahrens fsavl_create(nvlist_t *fss) 5803cb34c60Sahrens { 5813cb34c60Sahrens avl_tree_t *fsavl; 5823cb34c60Sahrens nvpair_t *fselem = NULL; 5833cb34c60Sahrens 58400954d7bSahl if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL) 58500954d7bSahl return (NULL); 58600954d7bSahl 5873cb34c60Sahrens avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t), 5883cb34c60Sahrens offsetof(fsavl_node_t, fn_node)); 5893cb34c60Sahrens 5903cb34c60Sahrens while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) { 5913cb34c60Sahrens nvlist_t *nvfs, *snaps; 5923cb34c60Sahrens nvpair_t *snapelem = NULL; 5933cb34c60Sahrens 5943cb34c60Sahrens VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs)); 5953cb34c60Sahrens VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps)); 5963cb34c60Sahrens 5973cb34c60Sahrens while ((snapelem = 5983cb34c60Sahrens nvlist_next_nvpair(snaps, snapelem)) != NULL) { 5993cb34c60Sahrens fsavl_node_t *fn; 6003cb34c60Sahrens uint64_t guid; 6013cb34c60Sahrens 6023cb34c60Sahrens VERIFY(0 == nvpair_value_uint64(snapelem, &guid)); 60300954d7bSahl if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) { 60400954d7bSahl fsavl_destroy(fsavl); 60500954d7bSahl return (NULL); 60600954d7bSahl } 6073cb34c60Sahrens fn->fn_nvfs = nvfs; 6083cb34c60Sahrens fn->fn_snapname = nvpair_name(snapelem); 6093cb34c60Sahrens fn->fn_guid = guid; 6103cb34c60Sahrens 6113cb34c60Sahrens /* 6123cb34c60Sahrens * Note: if there are multiple snaps with the 6133cb34c60Sahrens * same GUID, we ignore all but one. 6143cb34c60Sahrens */ 6153cb34c60Sahrens if (avl_find(fsavl, fn, NULL) == NULL) 6163cb34c60Sahrens avl_add(fsavl, fn); 6173cb34c60Sahrens else 6183cb34c60Sahrens free(fn); 6193cb34c60Sahrens } 6203cb34c60Sahrens } 6213cb34c60Sahrens 6223cb34c60Sahrens return (fsavl); 6233cb34c60Sahrens } 6243cb34c60Sahrens 6253cb34c60Sahrens /* 6263cb34c60Sahrens * Routines for dealing with the giant nvlist of fs-nvlists, etc. 6273cb34c60Sahrens */ 6283cb34c60Sahrens typedef struct send_data { 6293cb34c60Sahrens uint64_t parent_fromsnap_guid; 6303cb34c60Sahrens nvlist_t *parent_snaps; 6313cb34c60Sahrens nvlist_t *fss; 632bb0ade09Sahrens nvlist_t *snapprops; 6333cb34c60Sahrens const char *fromsnap; 6343cb34c60Sahrens const char *tosnap; 63592241e0bSTom Erickson boolean_t recursive; 6363cb34c60Sahrens 6373cb34c60Sahrens /* 6383cb34c60Sahrens * The header nvlist is of the following format: 6393cb34c60Sahrens * { 6403cb34c60Sahrens * "tosnap" -> string 6413cb34c60Sahrens * "fromsnap" -> string (if incremental) 6423cb34c60Sahrens * "fss" -> { 6433cb34c60Sahrens * id -> { 6443cb34c60Sahrens * 6453cb34c60Sahrens * "name" -> string (full name; for debugging) 6463cb34c60Sahrens * "parentfromsnap" -> number (guid of fromsnap in parent) 6473cb34c60Sahrens * 6483cb34c60Sahrens * "props" -> { name -> value (only if set here) } 6493cb34c60Sahrens * "snaps" -> { name (lastname) -> number (guid) } 650bb0ade09Sahrens * "snapprops" -> { name (lastname) -> { name -> value } } 6513cb34c60Sahrens * 6523cb34c60Sahrens * "origin" -> number (guid) (if clone) 6533cb34c60Sahrens * "sent" -> boolean (not on-disk) 6543cb34c60Sahrens * } 6553cb34c60Sahrens * } 6563cb34c60Sahrens * } 6573cb34c60Sahrens * 6583cb34c60Sahrens */ 6593cb34c60Sahrens } send_data_t; 6603cb34c60Sahrens 661bb0ade09Sahrens static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv); 662bb0ade09Sahrens 6633cb34c60Sahrens static int 6643cb34c60Sahrens send_iterate_snap(zfs_handle_t *zhp, void *arg) 6653cb34c60Sahrens { 6663cb34c60Sahrens send_data_t *sd = arg; 6673cb34c60Sahrens uint64_t guid = zhp->zfs_dmustats.dds_guid; 6683cb34c60Sahrens char *snapname; 669bb0ade09Sahrens nvlist_t *nv; 6703cb34c60Sahrens 6713cb34c60Sahrens snapname = strrchr(zhp->zfs_name, '@')+1; 6723cb34c60Sahrens 6733cb34c60Sahrens VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid)); 6743cb34c60Sahrens /* 6753cb34c60Sahrens * NB: if there is no fromsnap here (it's a newly created fs in 6763cb34c60Sahrens * an incremental replication), we will substitute the tosnap. 6773cb34c60Sahrens */ 6783cb34c60Sahrens if ((sd->fromsnap && strcmp(snapname, sd->fromsnap) == 0) || 6793cb34c60Sahrens (sd->parent_fromsnap_guid == 0 && sd->tosnap && 6803cb34c60Sahrens strcmp(snapname, sd->tosnap) == 0)) { 6813cb34c60Sahrens sd->parent_fromsnap_guid = guid; 6823cb34c60Sahrens } 6833cb34c60Sahrens 684bb0ade09Sahrens VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0)); 685bb0ade09Sahrens send_iterate_prop(zhp, nv); 686bb0ade09Sahrens VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv)); 687bb0ade09Sahrens nvlist_free(nv); 688bb0ade09Sahrens 6893cb34c60Sahrens zfs_close(zhp); 6903cb34c60Sahrens return (0); 6913cb34c60Sahrens } 6923cb34c60Sahrens 6933cb34c60Sahrens static void 6943cb34c60Sahrens send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv) 6953cb34c60Sahrens { 6963cb34c60Sahrens nvpair_t *elem = NULL; 6973cb34c60Sahrens 6983cb34c60Sahrens while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) { 6993cb34c60Sahrens char *propname = nvpair_name(elem); 7003cb34c60Sahrens zfs_prop_t prop = zfs_name_to_prop(propname); 7013cb34c60Sahrens nvlist_t *propnv; 7023cb34c60Sahrens 703faaa6415SEric Schrock if (!zfs_prop_user(propname)) { 704faaa6415SEric Schrock /* 705faaa6415SEric Schrock * Realistically, this should never happen. However, 706faaa6415SEric Schrock * we want the ability to add DSL properties without 707faaa6415SEric Schrock * needing to make incompatible version changes. We 708faaa6415SEric Schrock * need to ignore unknown properties to allow older 709faaa6415SEric Schrock * software to still send datasets containing these 710faaa6415SEric Schrock * properties, with the unknown properties elided. 711faaa6415SEric Schrock */ 712faaa6415SEric Schrock if (prop == ZPROP_INVAL) 7133cb34c60Sahrens continue; 7143cb34c60Sahrens 715faaa6415SEric Schrock if (zfs_prop_readonly(prop)) 716faaa6415SEric Schrock continue; 717faaa6415SEric Schrock } 718faaa6415SEric Schrock 7193cb34c60Sahrens verify(nvpair_value_nvlist(elem, &propnv) == 0); 720166773ebSSanjeev Bagewadi if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION || 721166773ebSSanjeev Bagewadi prop == ZFS_PROP_REFQUOTA || 722166773ebSSanjeev Bagewadi prop == ZFS_PROP_REFRESERVATION) { 72392241e0bSTom Erickson char *source; 7243cb34c60Sahrens uint64_t value; 7253cb34c60Sahrens verify(nvlist_lookup_uint64(propnv, 7263cb34c60Sahrens ZPROP_VALUE, &value) == 0); 727bb0ade09Sahrens if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) 728bb0ade09Sahrens continue; 72992241e0bSTom Erickson /* 73092241e0bSTom Erickson * May have no source before SPA_VERSION_RECVD_PROPS, 73192241e0bSTom Erickson * but is still modifiable. 73292241e0bSTom Erickson */ 73392241e0bSTom Erickson if (nvlist_lookup_string(propnv, 73492241e0bSTom Erickson ZPROP_SOURCE, &source) == 0) { 73592241e0bSTom Erickson if ((strcmp(source, zhp->zfs_name) != 0) && 73692241e0bSTom Erickson (strcmp(source, 73792241e0bSTom Erickson ZPROP_SOURCE_VAL_RECVD) != 0)) 73892241e0bSTom Erickson continue; 73992241e0bSTom Erickson } 7403cb34c60Sahrens } else { 7413cb34c60Sahrens char *source; 7423cb34c60Sahrens if (nvlist_lookup_string(propnv, 7433cb34c60Sahrens ZPROP_SOURCE, &source) != 0) 7443cb34c60Sahrens continue; 74592241e0bSTom Erickson if ((strcmp(source, zhp->zfs_name) != 0) && 74692241e0bSTom Erickson (strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0)) 7473cb34c60Sahrens continue; 7483cb34c60Sahrens } 7493cb34c60Sahrens 7503cb34c60Sahrens if (zfs_prop_user(propname) || 7513cb34c60Sahrens zfs_prop_get_type(prop) == PROP_TYPE_STRING) { 7523cb34c60Sahrens char *value; 7533cb34c60Sahrens verify(nvlist_lookup_string(propnv, 7543cb34c60Sahrens ZPROP_VALUE, &value) == 0); 7553cb34c60Sahrens VERIFY(0 == nvlist_add_string(nv, propname, value)); 7563cb34c60Sahrens } else { 7573cb34c60Sahrens uint64_t value; 7583cb34c60Sahrens verify(nvlist_lookup_uint64(propnv, 7593cb34c60Sahrens ZPROP_VALUE, &value) == 0); 7603cb34c60Sahrens VERIFY(0 == nvlist_add_uint64(nv, propname, value)); 7613cb34c60Sahrens } 7623cb34c60Sahrens } 7633cb34c60Sahrens } 7643cb34c60Sahrens 76588bb18d2SLori Alt /* 76688bb18d2SLori Alt * recursively generate nvlists describing datasets. See comment 76788bb18d2SLori Alt * for the data structure send_data_t above for description of contents 76888bb18d2SLori Alt * of the nvlist. 76988bb18d2SLori Alt */ 7703cb34c60Sahrens static int 7713cb34c60Sahrens send_iterate_fs(zfs_handle_t *zhp, void *arg) 7723cb34c60Sahrens { 7733cb34c60Sahrens send_data_t *sd = arg; 7743cb34c60Sahrens nvlist_t *nvfs, *nv; 77592241e0bSTom Erickson int rv = 0; 7763cb34c60Sahrens uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid; 7773cb34c60Sahrens uint64_t guid = zhp->zfs_dmustats.dds_guid; 7783cb34c60Sahrens char guidstring[64]; 7793cb34c60Sahrens 7803cb34c60Sahrens VERIFY(0 == nvlist_alloc(&nvfs, NV_UNIQUE_NAME, 0)); 7813cb34c60Sahrens VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name)); 7823cb34c60Sahrens VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap", 7833cb34c60Sahrens sd->parent_fromsnap_guid)); 7843cb34c60Sahrens 7853cb34c60Sahrens if (zhp->zfs_dmustats.dds_origin[0]) { 7863cb34c60Sahrens zfs_handle_t *origin = zfs_open(zhp->zfs_hdl, 7873cb34c60Sahrens zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT); 7883cb34c60Sahrens if (origin == NULL) 7893cb34c60Sahrens return (-1); 7903cb34c60Sahrens VERIFY(0 == nvlist_add_uint64(nvfs, "origin", 7913cb34c60Sahrens origin->zfs_dmustats.dds_guid)); 7923cb34c60Sahrens } 7933cb34c60Sahrens 7943cb34c60Sahrens /* iterate over props */ 7953cb34c60Sahrens VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0)); 7963cb34c60Sahrens send_iterate_prop(zhp, nv); 7973cb34c60Sahrens VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv)); 7983cb34c60Sahrens nvlist_free(nv); 7993cb34c60Sahrens 8003cb34c60Sahrens /* iterate over snaps, and set sd->parent_fromsnap_guid */ 8013cb34c60Sahrens sd->parent_fromsnap_guid = 0; 8023cb34c60Sahrens VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0)); 803bb0ade09Sahrens VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0)); 8043cb34c60Sahrens (void) zfs_iter_snapshots(zhp, send_iterate_snap, sd); 8053cb34c60Sahrens VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps)); 806bb0ade09Sahrens VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops)); 8073cb34c60Sahrens nvlist_free(sd->parent_snaps); 808bb0ade09Sahrens nvlist_free(sd->snapprops); 8093cb34c60Sahrens 8103cb34c60Sahrens /* add this fs to nvlist */ 8113cb34c60Sahrens (void) snprintf(guidstring, sizeof (guidstring), 8123cb34c60Sahrens "0x%llx", (longlong_t)guid); 8133cb34c60Sahrens VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs)); 8143cb34c60Sahrens nvlist_free(nvfs); 8153cb34c60Sahrens 8163cb34c60Sahrens /* iterate over children */ 81792241e0bSTom Erickson if (sd->recursive) 8183cb34c60Sahrens rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd); 8193cb34c60Sahrens 8203cb34c60Sahrens sd->parent_fromsnap_guid = parent_fromsnap_guid_save; 8213cb34c60Sahrens 8223cb34c60Sahrens zfs_close(zhp); 8233cb34c60Sahrens return (rv); 8243cb34c60Sahrens } 8253cb34c60Sahrens 8263cb34c60Sahrens static int 8273cb34c60Sahrens gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap, 82892241e0bSTom Erickson const char *tosnap, boolean_t recursive, nvlist_t **nvlp, avl_tree_t **avlp) 8293cb34c60Sahrens { 8303cb34c60Sahrens zfs_handle_t *zhp; 8313cb34c60Sahrens send_data_t sd = { 0 }; 8323cb34c60Sahrens int error; 8333cb34c60Sahrens 8343cb34c60Sahrens zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 8353cb34c60Sahrens if (zhp == NULL) 8363cb34c60Sahrens return (EZFS_BADTYPE); 8373cb34c60Sahrens 8383cb34c60Sahrens VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0)); 8393cb34c60Sahrens sd.fromsnap = fromsnap; 8403cb34c60Sahrens sd.tosnap = tosnap; 84192241e0bSTom Erickson sd.recursive = recursive; 84200954d7bSahl 84300954d7bSahl if ((error = send_iterate_fs(zhp, &sd)) != 0) { 84400954d7bSahl nvlist_free(sd.fss); 84500954d7bSahl if (avlp != NULL) 84600954d7bSahl *avlp = NULL; 84700954d7bSahl *nvlp = NULL; 84800954d7bSahl return (error); 84900954d7bSahl } 85000954d7bSahl 85100954d7bSahl if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) { 85200954d7bSahl nvlist_free(sd.fss); 85300954d7bSahl *nvlp = NULL; 85400954d7bSahl return (EZFS_NOMEM); 85500954d7bSahl } 8563cb34c60Sahrens 8573cb34c60Sahrens *nvlp = sd.fss; 85800954d7bSahl return (0); 8593cb34c60Sahrens } 8603cb34c60Sahrens 8613cb34c60Sahrens /* 8623cb34c60Sahrens * Routines specific to "zfs send" 8633cb34c60Sahrens */ 8643cb34c60Sahrens typedef struct send_dump_data { 8653cb34c60Sahrens /* these are all just the short snapname (the part after the @) */ 8663cb34c60Sahrens const char *fromsnap; 8673cb34c60Sahrens const char *tosnap; 868*40a5c998SMatthew Ahrens char prevsnap[ZFS_MAX_DATASET_NAME_LEN]; 869a7f53a56SChris Kirby uint64_t prevsnap_obj; 8703cb34c60Sahrens boolean_t seenfrom, seento, replicate, doall, fromorigin; 871dc5f28a3SManoj Joseph boolean_t verbose, dryrun, parsable, progress, embed_data, std_out; 872dc5f28a3SManoj Joseph boolean_t large_block; 8733cb34c60Sahrens int outfd; 8743cb34c60Sahrens boolean_t err; 8753cb34c60Sahrens nvlist_t *fss; 876a7a845e4SSteven Hartland nvlist_t *snapholds; 8773cb34c60Sahrens avl_tree_t *fsavl; 8789e69d7d0SLori Alt snapfilter_cb_t *filter_cb; 8799e69d7d0SLori Alt void *filter_cb_arg; 8803f9d6ad7SLin Ling nvlist_t *debugnv; 881*40a5c998SMatthew Ahrens char holdtag[ZFS_MAX_DATASET_NAME_LEN]; 882a7f53a56SChris Kirby int cleanup_fd; 88319b94df9SMatthew Ahrens uint64_t size; 8843cb34c60Sahrens } send_dump_data_t; 8853cb34c60Sahrens 88619b94df9SMatthew Ahrens static int 88719b94df9SMatthew Ahrens estimate_ioctl(zfs_handle_t *zhp, uint64_t fromsnap_obj, 88819b94df9SMatthew Ahrens boolean_t fromorigin, uint64_t *sizep) 88919b94df9SMatthew Ahrens { 89019b94df9SMatthew Ahrens zfs_cmd_t zc = { 0 }; 89119b94df9SMatthew Ahrens libzfs_handle_t *hdl = zhp->zfs_hdl; 89219b94df9SMatthew Ahrens 89319b94df9SMatthew Ahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 89419b94df9SMatthew Ahrens assert(fromsnap_obj == 0 || !fromorigin); 89519b94df9SMatthew Ahrens 89619b94df9SMatthew Ahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 89719b94df9SMatthew Ahrens zc.zc_obj = fromorigin; 89819b94df9SMatthew Ahrens zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID); 89919b94df9SMatthew Ahrens zc.zc_fromobj = fromsnap_obj; 90019b94df9SMatthew Ahrens zc.zc_guid = 1; /* estimate flag */ 90119b94df9SMatthew Ahrens 90219b94df9SMatthew Ahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) { 90319b94df9SMatthew Ahrens char errbuf[1024]; 90419b94df9SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 90519b94df9SMatthew Ahrens "warning: cannot estimate space for '%s'"), zhp->zfs_name); 90619b94df9SMatthew Ahrens 90719b94df9SMatthew Ahrens switch (errno) { 90819b94df9SMatthew Ahrens case EXDEV: 90919b94df9SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 91019b94df9SMatthew Ahrens "not an earlier snapshot from the same fs")); 91119b94df9SMatthew Ahrens return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 91219b94df9SMatthew Ahrens 91319b94df9SMatthew Ahrens case ENOENT: 91419b94df9SMatthew Ahrens if (zfs_dataset_exists(hdl, zc.zc_name, 91519b94df9SMatthew Ahrens ZFS_TYPE_SNAPSHOT)) { 91619b94df9SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 91719b94df9SMatthew Ahrens "incremental source (@%s) does not exist"), 91819b94df9SMatthew Ahrens zc.zc_value); 91919b94df9SMatthew Ahrens } 92019b94df9SMatthew Ahrens return (zfs_error(hdl, EZFS_NOENT, errbuf)); 92119b94df9SMatthew Ahrens 92219b94df9SMatthew Ahrens case EDQUOT: 92319b94df9SMatthew Ahrens case EFBIG: 92419b94df9SMatthew Ahrens case EIO: 92519b94df9SMatthew Ahrens case ENOLINK: 92619b94df9SMatthew Ahrens case ENOSPC: 92719b94df9SMatthew Ahrens case ENOSTR: 92819b94df9SMatthew Ahrens case ENXIO: 92919b94df9SMatthew Ahrens case EPIPE: 93019b94df9SMatthew Ahrens case ERANGE: 93119b94df9SMatthew Ahrens case EFAULT: 93219b94df9SMatthew Ahrens case EROFS: 93319b94df9SMatthew Ahrens zfs_error_aux(hdl, strerror(errno)); 93419b94df9SMatthew Ahrens return (zfs_error(hdl, EZFS_BADBACKUP, errbuf)); 93519b94df9SMatthew Ahrens 93619b94df9SMatthew Ahrens default: 93719b94df9SMatthew Ahrens return (zfs_standard_error(hdl, errno, errbuf)); 93819b94df9SMatthew Ahrens } 93919b94df9SMatthew Ahrens } 94019b94df9SMatthew Ahrens 94119b94df9SMatthew Ahrens *sizep = zc.zc_objset_type; 94219b94df9SMatthew Ahrens 94319b94df9SMatthew Ahrens return (0); 94419b94df9SMatthew Ahrens } 94519b94df9SMatthew Ahrens 9463cb34c60Sahrens /* 9473cb34c60Sahrens * Dumps a backup of the given snapshot (incremental from fromsnap if it's not 9483cb34c60Sahrens * NULL) to the file descriptor specified by outfd. 9493cb34c60Sahrens */ 9503cb34c60Sahrens static int 951a7f53a56SChris Kirby dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj, 9525d7b4d43SMatthew Ahrens boolean_t fromorigin, int outfd, enum lzc_send_flags flags, 9535d7b4d43SMatthew Ahrens nvlist_t *debugnv) 9543cb34c60Sahrens { 9553cb34c60Sahrens zfs_cmd_t zc = { 0 }; 9563cb34c60Sahrens libzfs_handle_t *hdl = zhp->zfs_hdl; 9573f9d6ad7SLin Ling nvlist_t *thisdbg; 9583cb34c60Sahrens 9593cb34c60Sahrens assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 960a7f53a56SChris Kirby assert(fromsnap_obj == 0 || !fromorigin); 9613cb34c60Sahrens 9623cb34c60Sahrens (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 9633cb34c60Sahrens zc.zc_cookie = outfd; 9643cb34c60Sahrens zc.zc_obj = fromorigin; 965a7f53a56SChris Kirby zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID); 966a7f53a56SChris Kirby zc.zc_fromobj = fromsnap_obj; 9675d7b4d43SMatthew Ahrens zc.zc_flags = flags; 968c6fd73aeSChris Kirby 9693f9d6ad7SLin Ling VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0)); 9703f9d6ad7SLin Ling if (fromsnap && fromsnap[0] != '\0') { 9713f9d6ad7SLin Ling VERIFY(0 == nvlist_add_string(thisdbg, 9723f9d6ad7SLin Ling "fromsnap", fromsnap)); 9733f9d6ad7SLin Ling } 9743f9d6ad7SLin Ling 97519b94df9SMatthew Ahrens if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) { 9763cb34c60Sahrens char errbuf[1024]; 9773cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 9783cb34c60Sahrens "warning: cannot send '%s'"), zhp->zfs_name); 9793cb34c60Sahrens 9803f9d6ad7SLin Ling VERIFY(0 == nvlist_add_uint64(thisdbg, "error", errno)); 9813f9d6ad7SLin Ling if (debugnv) { 9823f9d6ad7SLin Ling VERIFY(0 == nvlist_add_nvlist(debugnv, 9833f9d6ad7SLin Ling zhp->zfs_name, thisdbg)); 9843f9d6ad7SLin Ling } 9853f9d6ad7SLin Ling nvlist_free(thisdbg); 9863f9d6ad7SLin Ling 9873cb34c60Sahrens switch (errno) { 9883cb34c60Sahrens case EXDEV: 9893cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9903cb34c60Sahrens "not an earlier snapshot from the same fs")); 9913cb34c60Sahrens return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 9923cb34c60Sahrens 9933cb34c60Sahrens case ENOENT: 9943cb34c60Sahrens if (zfs_dataset_exists(hdl, zc.zc_name, 9953cb34c60Sahrens ZFS_TYPE_SNAPSHOT)) { 9963cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 9973cb34c60Sahrens "incremental source (@%s) does not exist"), 9983cb34c60Sahrens zc.zc_value); 9993cb34c60Sahrens } 10003cb34c60Sahrens return (zfs_error(hdl, EZFS_NOENT, errbuf)); 10013cb34c60Sahrens 10023cb34c60Sahrens case EDQUOT: 10033cb34c60Sahrens case EFBIG: 10043cb34c60Sahrens case EIO: 10053cb34c60Sahrens case ENOLINK: 10063cb34c60Sahrens case ENOSPC: 10073cb34c60Sahrens case ENOSTR: 10083cb34c60Sahrens case ENXIO: 10093cb34c60Sahrens case EPIPE: 10103cb34c60Sahrens case ERANGE: 10113cb34c60Sahrens case EFAULT: 10123cb34c60Sahrens case EROFS: 10133cb34c60Sahrens zfs_error_aux(hdl, strerror(errno)); 10143cb34c60Sahrens return (zfs_error(hdl, EZFS_BADBACKUP, errbuf)); 10153cb34c60Sahrens 10163cb34c60Sahrens default: 10173cb34c60Sahrens return (zfs_standard_error(hdl, errno, errbuf)); 10183cb34c60Sahrens } 10193cb34c60Sahrens } 10203cb34c60Sahrens 10213f9d6ad7SLin Ling if (debugnv) 10223f9d6ad7SLin Ling VERIFY(0 == nvlist_add_nvlist(debugnv, zhp->zfs_name, thisdbg)); 10233f9d6ad7SLin Ling nvlist_free(thisdbg); 10243f9d6ad7SLin Ling 10253cb34c60Sahrens return (0); 10263cb34c60Sahrens } 10273cb34c60Sahrens 1028a7a845e4SSteven Hartland static void 1029a7a845e4SSteven Hartland gather_holds(zfs_handle_t *zhp, send_dump_data_t *sdd) 1030a7f53a56SChris Kirby { 1031a7f53a56SChris Kirby assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); 1032a7f53a56SChris Kirby 1033a7f53a56SChris Kirby /* 1034a7a845e4SSteven Hartland * zfs_send() only sets snapholds for sends that need them, 1035a7f53a56SChris Kirby * e.g. replication and doall. 1036a7f53a56SChris Kirby */ 1037a7a845e4SSteven Hartland if (sdd->snapholds == NULL) 1038a7a845e4SSteven Hartland return; 1039a7f53a56SChris Kirby 1040a7a845e4SSteven Hartland fnvlist_add_string(sdd->snapholds, zhp->zfs_name, sdd->holdtag); 1041a7f53a56SChris Kirby } 1042a7f53a56SChris Kirby 10434e3c9f44SBill Pijewski static void * 10444e3c9f44SBill Pijewski send_progress_thread(void *arg) 10454e3c9f44SBill Pijewski { 10464e3c9f44SBill Pijewski progress_arg_t *pa = arg; 10474e3c9f44SBill Pijewski zfs_cmd_t zc = { 0 }; 10484e3c9f44SBill Pijewski zfs_handle_t *zhp = pa->pa_zhp; 10494e3c9f44SBill Pijewski libzfs_handle_t *hdl = zhp->zfs_hdl; 10504e3c9f44SBill Pijewski unsigned long long bytes; 10514e3c9f44SBill Pijewski char buf[16]; 10524e3c9f44SBill Pijewski time_t t; 10534e3c9f44SBill Pijewski struct tm *tm; 10544e3c9f44SBill Pijewski 10554e3c9f44SBill Pijewski (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); 10564e3c9f44SBill Pijewski 10574e3c9f44SBill Pijewski if (!pa->pa_parsable) 10584e3c9f44SBill Pijewski (void) fprintf(stderr, "TIME SENT SNAPSHOT\n"); 10594e3c9f44SBill Pijewski 10604e3c9f44SBill Pijewski /* 10614e3c9f44SBill Pijewski * Print the progress from ZFS_IOC_SEND_PROGRESS every second. 10624e3c9f44SBill Pijewski */ 10634e3c9f44SBill Pijewski for (;;) { 10644e3c9f44SBill Pijewski (void) sleep(1); 10654e3c9f44SBill Pijewski 10664e3c9f44SBill Pijewski zc.zc_cookie = pa->pa_fd; 10674e3c9f44SBill Pijewski if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0) 10684e3c9f44SBill Pijewski return ((void *)-1); 10694e3c9f44SBill Pijewski 10704e3c9f44SBill Pijewski (void) time(&t); 10714e3c9f44SBill Pijewski tm = localtime(&t); 10724e3c9f44SBill Pijewski bytes = zc.zc_cookie; 10734e3c9f44SBill Pijewski 10744e3c9f44SBill Pijewski if (pa->pa_parsable) { 10754e3c9f44SBill Pijewski (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n", 10764e3c9f44SBill Pijewski tm->tm_hour, tm->tm_min, tm->tm_sec, 10774e3c9f44SBill Pijewski bytes, zhp->zfs_name); 10784e3c9f44SBill Pijewski } else { 10794e3c9f44SBill Pijewski zfs_nicenum(bytes, buf, sizeof (buf)); 10804e3c9f44SBill Pijewski (void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n", 10814e3c9f44SBill Pijewski tm->tm_hour, tm->tm_min, tm->tm_sec, 10824e3c9f44SBill Pijewski buf, zhp->zfs_name); 10834e3c9f44SBill Pijewski } 10844e3c9f44SBill Pijewski } 10854e3c9f44SBill Pijewski } 10864e3c9f44SBill Pijewski 10879c3fd121SMatthew Ahrens static void 10889c3fd121SMatthew Ahrens send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap, 10899c3fd121SMatthew Ahrens uint64_t size, boolean_t parsable) 10909c3fd121SMatthew Ahrens { 10919c3fd121SMatthew Ahrens if (parsable) { 10929c3fd121SMatthew Ahrens if (fromsnap != NULL) { 10939c3fd121SMatthew Ahrens (void) fprintf(fout, "incremental\t%s\t%s", 10949c3fd121SMatthew Ahrens fromsnap, tosnap); 10959c3fd121SMatthew Ahrens } else { 10969c3fd121SMatthew Ahrens (void) fprintf(fout, "full\t%s", 10979c3fd121SMatthew Ahrens tosnap); 10989c3fd121SMatthew Ahrens } 10999c3fd121SMatthew Ahrens } else { 11009c3fd121SMatthew Ahrens if (fromsnap != NULL) { 11019c3fd121SMatthew Ahrens if (strchr(fromsnap, '@') == NULL && 11029c3fd121SMatthew Ahrens strchr(fromsnap, '#') == NULL) { 11039c3fd121SMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN, 11049c3fd121SMatthew Ahrens "send from @%s to %s"), 11059c3fd121SMatthew Ahrens fromsnap, tosnap); 11069c3fd121SMatthew Ahrens } else { 11079c3fd121SMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN, 11089c3fd121SMatthew Ahrens "send from %s to %s"), 11099c3fd121SMatthew Ahrens fromsnap, tosnap); 11109c3fd121SMatthew Ahrens } 11119c3fd121SMatthew Ahrens } else { 11129c3fd121SMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN, 11139c3fd121SMatthew Ahrens "full send of %s"), 11149c3fd121SMatthew Ahrens tosnap); 11159c3fd121SMatthew Ahrens } 11169c3fd121SMatthew Ahrens } 11179c3fd121SMatthew Ahrens 11189c3fd121SMatthew Ahrens if (size != 0) { 11199c3fd121SMatthew Ahrens if (parsable) { 11209c3fd121SMatthew Ahrens (void) fprintf(fout, "\t%llu", 11219c3fd121SMatthew Ahrens (longlong_t)size); 11229c3fd121SMatthew Ahrens } else { 11239c3fd121SMatthew Ahrens char buf[16]; 11249c3fd121SMatthew Ahrens zfs_nicenum(size, buf, sizeof (buf)); 11259c3fd121SMatthew Ahrens (void) fprintf(fout, dgettext(TEXT_DOMAIN, 11269c3fd121SMatthew Ahrens " estimated size is %s"), buf); 11279c3fd121SMatthew Ahrens } 11289c3fd121SMatthew Ahrens } 11299c3fd121SMatthew Ahrens (void) fprintf(fout, "\n"); 11309c3fd121SMatthew Ahrens } 11319c3fd121SMatthew Ahrens 1132a7f53a56SChris Kirby static int 11333cb34c60Sahrens dump_snapshot(zfs_handle_t *zhp, void *arg) 11343cb34c60Sahrens { 11353cb34c60Sahrens send_dump_data_t *sdd = arg; 11364e3c9f44SBill Pijewski progress_arg_t pa = { 0 }; 11374e3c9f44SBill Pijewski pthread_t tid; 1138a7f53a56SChris Kirby char *thissnap; 11393cb34c60Sahrens int err; 114019b94df9SMatthew Ahrens boolean_t isfromsnap, istosnap, fromorigin; 1141468db2f4STom Erickson boolean_t exclude = B_FALSE; 1142dc5f28a3SManoj Joseph FILE *fout = sdd->std_out ? stdout : stderr; 11433cb34c60Sahrens 1144a7a845e4SSteven Hartland err = 0; 11453cb34c60Sahrens thissnap = strchr(zhp->zfs_name, '@') + 1; 1146468db2f4STom Erickson isfromsnap = (sdd->fromsnap != NULL && 1147468db2f4STom Erickson strcmp(sdd->fromsnap, thissnap) == 0); 11483cb34c60Sahrens 1149468db2f4STom Erickson if (!sdd->seenfrom && isfromsnap) { 1150a7a845e4SSteven Hartland gather_holds(zhp, sdd); 11513cb34c60Sahrens sdd->seenfrom = B_TRUE; 11529e69d7d0SLori Alt (void) strcpy(sdd->prevsnap, thissnap); 1153a7a845e4SSteven Hartland sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID); 11543cb34c60Sahrens zfs_close(zhp); 1155a7a845e4SSteven Hartland return (0); 11563cb34c60Sahrens } 11573cb34c60Sahrens 11583cb34c60Sahrens if (sdd->seento || !sdd->seenfrom) { 11593cb34c60Sahrens zfs_close(zhp); 11603cb34c60Sahrens return (0); 11613cb34c60Sahrens } 11623cb34c60Sahrens 1163468db2f4STom Erickson istosnap = (strcmp(sdd->tosnap, thissnap) == 0); 1164468db2f4STom Erickson if (istosnap) 11659e69d7d0SLori Alt sdd->seento = B_TRUE; 11669e69d7d0SLori Alt 1167468db2f4STom Erickson if (!sdd->doall && !isfromsnap && !istosnap) { 1168468db2f4STom Erickson if (sdd->replicate) { 1169468db2f4STom Erickson char *snapname; 1170468db2f4STom Erickson nvlist_t *snapprops; 1171468db2f4STom Erickson /* 1172468db2f4STom Erickson * Filter out all intermediate snapshots except origin 1173468db2f4STom Erickson * snapshots needed to replicate clones. 1174468db2f4STom Erickson */ 1175468db2f4STom Erickson nvlist_t *nvfs = fsavl_find(sdd->fsavl, 1176468db2f4STom Erickson zhp->zfs_dmustats.dds_guid, &snapname); 1177468db2f4STom Erickson 1178468db2f4STom Erickson VERIFY(0 == nvlist_lookup_nvlist(nvfs, 1179468db2f4STom Erickson "snapprops", &snapprops)); 1180468db2f4STom Erickson VERIFY(0 == nvlist_lookup_nvlist(snapprops, 1181468db2f4STom Erickson thissnap, &snapprops)); 1182468db2f4STom Erickson exclude = !nvlist_exists(snapprops, "is_clone_origin"); 1183468db2f4STom Erickson } else { 1184468db2f4STom Erickson exclude = B_TRUE; 1185468db2f4STom Erickson } 1186468db2f4STom Erickson } 1187468db2f4STom Erickson 11889e69d7d0SLori Alt /* 11899e69d7d0SLori Alt * If a filter function exists, call it to determine whether 11909e69d7d0SLori Alt * this snapshot will be sent. 11919e69d7d0SLori Alt */ 1192468db2f4STom Erickson if (exclude || (sdd->filter_cb != NULL && 1193468db2f4STom Erickson sdd->filter_cb(zhp, sdd->filter_cb_arg) == B_FALSE)) { 11949e69d7d0SLori Alt /* 11959e69d7d0SLori Alt * This snapshot is filtered out. Don't send it, and don't 1196a7f53a56SChris Kirby * set prevsnap_obj, so it will be as if this snapshot didn't 11979e69d7d0SLori Alt * exist, and the next accepted snapshot will be sent as 11989e69d7d0SLori Alt * an incremental from the last accepted one, or as the 11999e69d7d0SLori Alt * first (and full) snapshot in the case of a replication, 12009e69d7d0SLori Alt * non-incremental send. 12019e69d7d0SLori Alt */ 12029e69d7d0SLori Alt zfs_close(zhp); 12039e69d7d0SLori Alt return (0); 12049e69d7d0SLori Alt } 12059e69d7d0SLori Alt 1206a7a845e4SSteven Hartland gather_holds(zhp, sdd); 120719b94df9SMatthew Ahrens fromorigin = sdd->prevsnap[0] == '\0' && 120819b94df9SMatthew Ahrens (sdd->fromorigin || sdd->replicate); 120919b94df9SMatthew Ahrens 12103cb34c60Sahrens if (sdd->verbose) { 12119c3fd121SMatthew Ahrens uint64_t size = 0; 12129c3fd121SMatthew Ahrens (void) estimate_ioctl(zhp, sdd->prevsnap_obj, 121319b94df9SMatthew Ahrens fromorigin, &size); 121419b94df9SMatthew Ahrens 12159c3fd121SMatthew Ahrens send_print_verbose(fout, zhp->zfs_name, 12169c3fd121SMatthew Ahrens sdd->prevsnap[0] ? sdd->prevsnap : NULL, 12179c3fd121SMatthew Ahrens size, sdd->parsable); 121819b94df9SMatthew Ahrens sdd->size += size; 121919b94df9SMatthew Ahrens } 12203cb34c60Sahrens 122119b94df9SMatthew Ahrens if (!sdd->dryrun) { 12224e3c9f44SBill Pijewski /* 12234e3c9f44SBill Pijewski * If progress reporting is requested, spawn a new thread to 12244e3c9f44SBill Pijewski * poll ZFS_IOC_SEND_PROGRESS at a regular interval. 12254e3c9f44SBill Pijewski */ 12264e3c9f44SBill Pijewski if (sdd->progress) { 12274e3c9f44SBill Pijewski pa.pa_zhp = zhp; 12284e3c9f44SBill Pijewski pa.pa_fd = sdd->outfd; 12294e3c9f44SBill Pijewski pa.pa_parsable = sdd->parsable; 12304e3c9f44SBill Pijewski 12314e3c9f44SBill Pijewski if (err = pthread_create(&tid, NULL, 12324e3c9f44SBill Pijewski send_progress_thread, &pa)) { 12334e3c9f44SBill Pijewski zfs_close(zhp); 12344e3c9f44SBill Pijewski return (err); 12354e3c9f44SBill Pijewski } 12364e3c9f44SBill Pijewski } 12374e3c9f44SBill Pijewski 12385d7b4d43SMatthew Ahrens enum lzc_send_flags flags = 0; 1239b5152584SMatthew Ahrens if (sdd->large_block) 1240b5152584SMatthew Ahrens flags |= LZC_SEND_FLAG_LARGE_BLOCK; 12415d7b4d43SMatthew Ahrens if (sdd->embed_data) 12425d7b4d43SMatthew Ahrens flags |= LZC_SEND_FLAG_EMBED_DATA; 12435d7b4d43SMatthew Ahrens 1244a7f53a56SChris Kirby err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj, 12455d7b4d43SMatthew Ahrens fromorigin, sdd->outfd, flags, sdd->debugnv); 12464e3c9f44SBill Pijewski 12474e3c9f44SBill Pijewski if (sdd->progress) { 12484e3c9f44SBill Pijewski (void) pthread_cancel(tid); 12494e3c9f44SBill Pijewski (void) pthread_join(tid, NULL); 12504e3c9f44SBill Pijewski } 125119b94df9SMatthew Ahrens } 12523cb34c60Sahrens 12539e69d7d0SLori Alt (void) strcpy(sdd->prevsnap, thissnap); 1254a7f53a56SChris Kirby sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID); 12553cb34c60Sahrens zfs_close(zhp); 12563cb34c60Sahrens return (err); 12573cb34c60Sahrens } 12583cb34c60Sahrens 12593cb34c60Sahrens static int 12603cb34c60Sahrens dump_filesystem(zfs_handle_t *zhp, void *arg) 12613cb34c60Sahrens { 12623cb34c60Sahrens int rv = 0; 12633cb34c60Sahrens send_dump_data_t *sdd = arg; 12643cb34c60Sahrens boolean_t missingfrom = B_FALSE; 12653cb34c60Sahrens zfs_cmd_t zc = { 0 }; 12663cb34c60Sahrens 12673cb34c60Sahrens (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s", 12683cb34c60Sahrens zhp->zfs_name, sdd->tosnap); 12693cb34c60Sahrens if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) { 127019b94df9SMatthew Ahrens (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 127119b94df9SMatthew Ahrens "WARNING: could not send %s@%s: does not exist\n"), 12723cb34c60Sahrens zhp->zfs_name, sdd->tosnap); 12733cb34c60Sahrens sdd->err = B_TRUE; 12743cb34c60Sahrens return (0); 12753cb34c60Sahrens } 12763cb34c60Sahrens 12773cb34c60Sahrens if (sdd->replicate && sdd->fromsnap) { 12783cb34c60Sahrens /* 12793cb34c60Sahrens * If this fs does not have fromsnap, and we're doing 12803cb34c60Sahrens * recursive, we need to send a full stream from the 12813cb34c60Sahrens * beginning (or an incremental from the origin if this 12823cb34c60Sahrens * is a clone). If we're doing non-recursive, then let 12833cb34c60Sahrens * them get the error. 12843cb34c60Sahrens */ 12853cb34c60Sahrens (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s", 12863cb34c60Sahrens zhp->zfs_name, sdd->fromsnap); 12873cb34c60Sahrens if (ioctl(zhp->zfs_hdl->libzfs_fd, 12883cb34c60Sahrens ZFS_IOC_OBJSET_STATS, &zc) != 0) { 12893cb34c60Sahrens missingfrom = B_TRUE; 12903cb34c60Sahrens } 12913cb34c60Sahrens } 12923cb34c60Sahrens 12939e69d7d0SLori Alt sdd->seenfrom = sdd->seento = sdd->prevsnap[0] = 0; 1294a7f53a56SChris Kirby sdd->prevsnap_obj = 0; 12953cb34c60Sahrens if (sdd->fromsnap == NULL || missingfrom) 12963cb34c60Sahrens sdd->seenfrom = B_TRUE; 12973cb34c60Sahrens 12983cb34c60Sahrens rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg); 12993cb34c60Sahrens if (!sdd->seenfrom) { 130019b94df9SMatthew Ahrens (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 13013cb34c60Sahrens "WARNING: could not send %s@%s:\n" 130219b94df9SMatthew Ahrens "incremental source (%s@%s) does not exist\n"), 13033cb34c60Sahrens zhp->zfs_name, sdd->tosnap, 13043cb34c60Sahrens zhp->zfs_name, sdd->fromsnap); 13053cb34c60Sahrens sdd->err = B_TRUE; 13063cb34c60Sahrens } else if (!sdd->seento) { 130714843421SMatthew Ahrens if (sdd->fromsnap) { 130819b94df9SMatthew Ahrens (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 13093cb34c60Sahrens "WARNING: could not send %s@%s:\n" 13103cb34c60Sahrens "incremental source (%s@%s) " 131119b94df9SMatthew Ahrens "is not earlier than it\n"), 13123cb34c60Sahrens zhp->zfs_name, sdd->tosnap, 13133cb34c60Sahrens zhp->zfs_name, sdd->fromsnap); 131414843421SMatthew Ahrens } else { 131519b94df9SMatthew Ahrens (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 131619b94df9SMatthew Ahrens "WARNING: " 131719b94df9SMatthew Ahrens "could not send %s@%s: does not exist\n"), 131814843421SMatthew Ahrens zhp->zfs_name, sdd->tosnap); 131914843421SMatthew Ahrens } 13203cb34c60Sahrens sdd->err = B_TRUE; 13213cb34c60Sahrens } 13223cb34c60Sahrens 13233cb34c60Sahrens return (rv); 13243cb34c60Sahrens } 13253cb34c60Sahrens 13263cb34c60Sahrens static int 13273cb34c60Sahrens dump_filesystems(zfs_handle_t *rzhp, void *arg) 13283cb34c60Sahrens { 13293cb34c60Sahrens send_dump_data_t *sdd = arg; 13303cb34c60Sahrens nvpair_t *fspair; 13313cb34c60Sahrens boolean_t needagain, progress; 13323cb34c60Sahrens 13333cb34c60Sahrens if (!sdd->replicate) 13343cb34c60Sahrens return (dump_filesystem(rzhp, sdd)); 13353cb34c60Sahrens 1336468db2f4STom Erickson /* Mark the clone origin snapshots. */ 1337468db2f4STom Erickson for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair; 1338468db2f4STom Erickson fspair = nvlist_next_nvpair(sdd->fss, fspair)) { 1339468db2f4STom Erickson nvlist_t *nvfs; 1340468db2f4STom Erickson uint64_t origin_guid = 0; 1341468db2f4STom Erickson 1342468db2f4STom Erickson VERIFY(0 == nvpair_value_nvlist(fspair, &nvfs)); 1343468db2f4STom Erickson (void) nvlist_lookup_uint64(nvfs, "origin", &origin_guid); 1344468db2f4STom Erickson if (origin_guid != 0) { 1345468db2f4STom Erickson char *snapname; 1346468db2f4STom Erickson nvlist_t *origin_nv = fsavl_find(sdd->fsavl, 1347468db2f4STom Erickson origin_guid, &snapname); 1348468db2f4STom Erickson if (origin_nv != NULL) { 1349468db2f4STom Erickson nvlist_t *snapprops; 1350468db2f4STom Erickson VERIFY(0 == nvlist_lookup_nvlist(origin_nv, 1351468db2f4STom Erickson "snapprops", &snapprops)); 1352468db2f4STom Erickson VERIFY(0 == nvlist_lookup_nvlist(snapprops, 1353468db2f4STom Erickson snapname, &snapprops)); 1354468db2f4STom Erickson VERIFY(0 == nvlist_add_boolean( 1355468db2f4STom Erickson snapprops, "is_clone_origin")); 1356468db2f4STom Erickson } 1357468db2f4STom Erickson } 1358468db2f4STom Erickson } 13593cb34c60Sahrens again: 13603cb34c60Sahrens needagain = progress = B_FALSE; 13613cb34c60Sahrens for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair; 13623cb34c60Sahrens fspair = nvlist_next_nvpair(sdd->fss, fspair)) { 136319b94df9SMatthew Ahrens nvlist_t *fslist, *parent_nv; 13643cb34c60Sahrens char *fsname; 13653cb34c60Sahrens zfs_handle_t *zhp; 13663cb34c60Sahrens int err; 13673cb34c60Sahrens uint64_t origin_guid = 0; 136819b94df9SMatthew Ahrens uint64_t parent_guid = 0; 13693cb34c60Sahrens 13703cb34c60Sahrens VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0); 13713cb34c60Sahrens if (nvlist_lookup_boolean(fslist, "sent") == 0) 13723cb34c60Sahrens continue; 13733cb34c60Sahrens 13743cb34c60Sahrens VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0); 13753cb34c60Sahrens (void) nvlist_lookup_uint64(fslist, "origin", &origin_guid); 137619b94df9SMatthew Ahrens (void) nvlist_lookup_uint64(fslist, "parentfromsnap", 137719b94df9SMatthew Ahrens &parent_guid); 137819b94df9SMatthew Ahrens 137919b94df9SMatthew Ahrens if (parent_guid != 0) { 138019b94df9SMatthew Ahrens parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL); 138119b94df9SMatthew Ahrens if (!nvlist_exists(parent_nv, "sent")) { 138219b94df9SMatthew Ahrens /* parent has not been sent; skip this one */ 138319b94df9SMatthew Ahrens needagain = B_TRUE; 138419b94df9SMatthew Ahrens continue; 138519b94df9SMatthew Ahrens } 138619b94df9SMatthew Ahrens } 13873cb34c60Sahrens 1388468db2f4STom Erickson if (origin_guid != 0) { 1389468db2f4STom Erickson nvlist_t *origin_nv = fsavl_find(sdd->fsavl, 1390468db2f4STom Erickson origin_guid, NULL); 1391468db2f4STom Erickson if (origin_nv != NULL && 139219b94df9SMatthew Ahrens !nvlist_exists(origin_nv, "sent")) { 13933cb34c60Sahrens /* 13943cb34c60Sahrens * origin has not been sent yet; 13953cb34c60Sahrens * skip this clone. 13963cb34c60Sahrens */ 13973cb34c60Sahrens needagain = B_TRUE; 13983cb34c60Sahrens continue; 13993cb34c60Sahrens } 1400468db2f4STom Erickson } 14013cb34c60Sahrens 14023cb34c60Sahrens zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET); 140304bb726eSahl if (zhp == NULL) 140404bb726eSahl return (-1); 14053cb34c60Sahrens err = dump_filesystem(zhp, sdd); 14063cb34c60Sahrens VERIFY(nvlist_add_boolean(fslist, "sent") == 0); 14073cb34c60Sahrens progress = B_TRUE; 14083cb34c60Sahrens zfs_close(zhp); 14093cb34c60Sahrens if (err) 14103cb34c60Sahrens return (err); 14113cb34c60Sahrens } 14123cb34c60Sahrens if (needagain) { 14133cb34c60Sahrens assert(progress); 14143cb34c60Sahrens goto again; 14153cb34c60Sahrens } 141619b94df9SMatthew Ahrens 141719b94df9SMatthew Ahrens /* clean out the sent flags in case we reuse this fss */ 141819b94df9SMatthew Ahrens for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair; 141919b94df9SMatthew Ahrens fspair = nvlist_next_nvpair(sdd->fss, fspair)) { 142019b94df9SMatthew Ahrens nvlist_t *fslist; 142119b94df9SMatthew Ahrens 142219b94df9SMatthew Ahrens VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0); 142319b94df9SMatthew Ahrens (void) nvlist_remove_all(fslist, "sent"); 142419b94df9SMatthew Ahrens } 142519b94df9SMatthew Ahrens 14263cb34c60Sahrens return (0); 14273cb34c60Sahrens } 14283cb34c60Sahrens 14299c3fd121SMatthew Ahrens nvlist_t * 14309c3fd121SMatthew Ahrens zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token) 14319c3fd121SMatthew Ahrens { 14329c3fd121SMatthew Ahrens unsigned int version; 14339c3fd121SMatthew Ahrens int nread; 14349c3fd121SMatthew Ahrens unsigned long long checksum, packed_len; 14359c3fd121SMatthew Ahrens 14369c3fd121SMatthew Ahrens /* 14379c3fd121SMatthew Ahrens * Decode token header, which is: 14389c3fd121SMatthew Ahrens * <token version>-<checksum of payload>-<uncompressed payload length> 14399c3fd121SMatthew Ahrens * Note that the only supported token version is 1. 14409c3fd121SMatthew Ahrens */ 14419c3fd121SMatthew Ahrens nread = sscanf(token, "%u-%llx-%llx-", 14429c3fd121SMatthew Ahrens &version, &checksum, &packed_len); 14439c3fd121SMatthew Ahrens if (nread != 3) { 14449c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14459c3fd121SMatthew Ahrens "resume token is corrupt (invalid format)")); 14469c3fd121SMatthew Ahrens return (NULL); 14479c3fd121SMatthew Ahrens } 14489c3fd121SMatthew Ahrens 14499c3fd121SMatthew Ahrens if (version != ZFS_SEND_RESUME_TOKEN_VERSION) { 14509c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14519c3fd121SMatthew Ahrens "resume token is corrupt (invalid version %u)"), 14529c3fd121SMatthew Ahrens version); 14539c3fd121SMatthew Ahrens return (NULL); 14549c3fd121SMatthew Ahrens } 14559c3fd121SMatthew Ahrens 14569c3fd121SMatthew Ahrens /* convert hexadecimal representation to binary */ 14579c3fd121SMatthew Ahrens token = strrchr(token, '-') + 1; 14589c3fd121SMatthew Ahrens int len = strlen(token) / 2; 14599c3fd121SMatthew Ahrens unsigned char *compressed = zfs_alloc(hdl, len); 14609c3fd121SMatthew Ahrens for (int i = 0; i < len; i++) { 14619c3fd121SMatthew Ahrens nread = sscanf(token + i * 2, "%2hhx", compressed + i); 14629c3fd121SMatthew Ahrens if (nread != 1) { 14639c3fd121SMatthew Ahrens free(compressed); 14649c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14659c3fd121SMatthew Ahrens "resume token is corrupt " 14669c3fd121SMatthew Ahrens "(payload is not hex-encoded)")); 14679c3fd121SMatthew Ahrens return (NULL); 14689c3fd121SMatthew Ahrens } 14699c3fd121SMatthew Ahrens } 14709c3fd121SMatthew Ahrens 14719c3fd121SMatthew Ahrens /* verify checksum */ 14729c3fd121SMatthew Ahrens zio_cksum_t cksum; 14739c3fd121SMatthew Ahrens fletcher_4_native(compressed, len, NULL, &cksum); 14749c3fd121SMatthew Ahrens if (cksum.zc_word[0] != checksum) { 14759c3fd121SMatthew Ahrens free(compressed); 14769c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14779c3fd121SMatthew Ahrens "resume token is corrupt (incorrect checksum)")); 14789c3fd121SMatthew Ahrens return (NULL); 14799c3fd121SMatthew Ahrens } 14809c3fd121SMatthew Ahrens 14819c3fd121SMatthew Ahrens /* uncompress */ 14829c3fd121SMatthew Ahrens void *packed = zfs_alloc(hdl, packed_len); 14839c3fd121SMatthew Ahrens uLongf packed_len_long = packed_len; 14849c3fd121SMatthew Ahrens if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK || 14859c3fd121SMatthew Ahrens packed_len_long != packed_len) { 14869c3fd121SMatthew Ahrens free(packed); 14879c3fd121SMatthew Ahrens free(compressed); 14889c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 14899c3fd121SMatthew Ahrens "resume token is corrupt (decompression failed)")); 14909c3fd121SMatthew Ahrens return (NULL); 14919c3fd121SMatthew Ahrens } 14929c3fd121SMatthew Ahrens 14939c3fd121SMatthew Ahrens /* unpack nvlist */ 14949c3fd121SMatthew Ahrens nvlist_t *nv; 14959c3fd121SMatthew Ahrens int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP); 14969c3fd121SMatthew Ahrens free(packed); 14979c3fd121SMatthew Ahrens free(compressed); 14989c3fd121SMatthew Ahrens if (error != 0) { 14999c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15009c3fd121SMatthew Ahrens "resume token is corrupt (nvlist_unpack failed)")); 15019c3fd121SMatthew Ahrens return (NULL); 15029c3fd121SMatthew Ahrens } 15039c3fd121SMatthew Ahrens return (nv); 15049c3fd121SMatthew Ahrens } 15059c3fd121SMatthew Ahrens 15069c3fd121SMatthew Ahrens int 15079c3fd121SMatthew Ahrens zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd, 15089c3fd121SMatthew Ahrens const char *resume_token) 15099c3fd121SMatthew Ahrens { 15109c3fd121SMatthew Ahrens char errbuf[1024]; 15119c3fd121SMatthew Ahrens char *toname; 15129c3fd121SMatthew Ahrens char *fromname = NULL; 15139c3fd121SMatthew Ahrens uint64_t resumeobj, resumeoff, toguid, fromguid, bytes; 15149c3fd121SMatthew Ahrens zfs_handle_t *zhp; 15159c3fd121SMatthew Ahrens int error = 0; 1516*40a5c998SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 15179c3fd121SMatthew Ahrens enum lzc_send_flags lzc_flags = 0; 15189c3fd121SMatthew Ahrens 15199c3fd121SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 15209c3fd121SMatthew Ahrens "cannot resume send")); 15219c3fd121SMatthew Ahrens 15229c3fd121SMatthew Ahrens nvlist_t *resume_nvl = 15239c3fd121SMatthew Ahrens zfs_send_resume_token_to_nvlist(hdl, resume_token); 15249c3fd121SMatthew Ahrens if (resume_nvl == NULL) { 15259c3fd121SMatthew Ahrens /* 15269c3fd121SMatthew Ahrens * zfs_error_aux has already been set by 15279c3fd121SMatthew Ahrens * zfs_send_resume_token_to_nvlist 15289c3fd121SMatthew Ahrens */ 15299c3fd121SMatthew Ahrens return (zfs_error(hdl, EZFS_FAULT, errbuf)); 15309c3fd121SMatthew Ahrens } 15319c3fd121SMatthew Ahrens if (flags->verbose) { 15329c3fd121SMatthew Ahrens (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 15339c3fd121SMatthew Ahrens "resume token contents:\n")); 15349c3fd121SMatthew Ahrens nvlist_print(stderr, resume_nvl); 15359c3fd121SMatthew Ahrens } 15369c3fd121SMatthew Ahrens 15379c3fd121SMatthew Ahrens if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 || 15389c3fd121SMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 || 15399c3fd121SMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 || 15409c3fd121SMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 || 15419c3fd121SMatthew Ahrens nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) { 15429c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15439c3fd121SMatthew Ahrens "resume token is corrupt")); 15449c3fd121SMatthew Ahrens return (zfs_error(hdl, EZFS_FAULT, errbuf)); 15459c3fd121SMatthew Ahrens } 15469c3fd121SMatthew Ahrens fromguid = 0; 15479c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid); 15489c3fd121SMatthew Ahrens 15499c3fd121SMatthew Ahrens if (flags->embed_data || nvlist_exists(resume_nvl, "embedok")) 15509c3fd121SMatthew Ahrens lzc_flags |= LZC_SEND_FLAG_EMBED_DATA; 15519c3fd121SMatthew Ahrens 15529c3fd121SMatthew Ahrens if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) { 15539c3fd121SMatthew Ahrens if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) { 15549c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15559c3fd121SMatthew Ahrens "'%s' is no longer the same snapshot used in " 15569c3fd121SMatthew Ahrens "the initial send"), toname); 15579c3fd121SMatthew Ahrens } else { 15589c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15599c3fd121SMatthew Ahrens "'%s' used in the initial send no longer exists"), 15609c3fd121SMatthew Ahrens toname); 15619c3fd121SMatthew Ahrens } 15629c3fd121SMatthew Ahrens return (zfs_error(hdl, EZFS_BADPATH, errbuf)); 15639c3fd121SMatthew Ahrens } 15649c3fd121SMatthew Ahrens zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET); 15659c3fd121SMatthew Ahrens if (zhp == NULL) { 15669c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15679c3fd121SMatthew Ahrens "unable to access '%s'"), name); 15689c3fd121SMatthew Ahrens return (zfs_error(hdl, EZFS_BADPATH, errbuf)); 15699c3fd121SMatthew Ahrens } 15709c3fd121SMatthew Ahrens 15719c3fd121SMatthew Ahrens if (fromguid != 0) { 15729c3fd121SMatthew Ahrens if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) { 15739c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 15749c3fd121SMatthew Ahrens "incremental source %#llx no longer exists"), 15759c3fd121SMatthew Ahrens (longlong_t)fromguid); 15769c3fd121SMatthew Ahrens return (zfs_error(hdl, EZFS_BADPATH, errbuf)); 15779c3fd121SMatthew Ahrens } 15789c3fd121SMatthew Ahrens fromname = name; 15799c3fd121SMatthew Ahrens } 15809c3fd121SMatthew Ahrens 15819c3fd121SMatthew Ahrens if (flags->verbose) { 15829c3fd121SMatthew Ahrens uint64_t size = 0; 15839c3fd121SMatthew Ahrens error = lzc_send_space(zhp->zfs_name, fromname, &size); 15849c3fd121SMatthew Ahrens if (error == 0) 15859c3fd121SMatthew Ahrens size = MAX(0, (int64_t)(size - bytes)); 15869c3fd121SMatthew Ahrens send_print_verbose(stderr, zhp->zfs_name, fromname, 15879c3fd121SMatthew Ahrens size, flags->parsable); 15889c3fd121SMatthew Ahrens } 15899c3fd121SMatthew Ahrens 15909c3fd121SMatthew Ahrens if (!flags->dryrun) { 15919c3fd121SMatthew Ahrens progress_arg_t pa = { 0 }; 15929c3fd121SMatthew Ahrens pthread_t tid; 15939c3fd121SMatthew Ahrens /* 15949c3fd121SMatthew Ahrens * If progress reporting is requested, spawn a new thread to 15959c3fd121SMatthew Ahrens * poll ZFS_IOC_SEND_PROGRESS at a regular interval. 15969c3fd121SMatthew Ahrens */ 15979c3fd121SMatthew Ahrens if (flags->progress) { 15989c3fd121SMatthew Ahrens pa.pa_zhp = zhp; 15999c3fd121SMatthew Ahrens pa.pa_fd = outfd; 16009c3fd121SMatthew Ahrens pa.pa_parsable = flags->parsable; 16019c3fd121SMatthew Ahrens 16029c3fd121SMatthew Ahrens error = pthread_create(&tid, NULL, 16039c3fd121SMatthew Ahrens send_progress_thread, &pa); 16049c3fd121SMatthew Ahrens if (error != 0) { 16059c3fd121SMatthew Ahrens zfs_close(zhp); 16069c3fd121SMatthew Ahrens return (error); 16079c3fd121SMatthew Ahrens } 16089c3fd121SMatthew Ahrens } 16099c3fd121SMatthew Ahrens 16109c3fd121SMatthew Ahrens error = lzc_send_resume(zhp->zfs_name, fromname, outfd, 16119c3fd121SMatthew Ahrens lzc_flags, resumeobj, resumeoff); 16129c3fd121SMatthew Ahrens 16139c3fd121SMatthew Ahrens if (flags->progress) { 16149c3fd121SMatthew Ahrens (void) pthread_cancel(tid); 16159c3fd121SMatthew Ahrens (void) pthread_join(tid, NULL); 16169c3fd121SMatthew Ahrens } 16179c3fd121SMatthew Ahrens 16189c3fd121SMatthew Ahrens char errbuf[1024]; 16199c3fd121SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 16209c3fd121SMatthew Ahrens "warning: cannot send '%s'"), zhp->zfs_name); 16219c3fd121SMatthew Ahrens 16229c3fd121SMatthew Ahrens zfs_close(zhp); 16239c3fd121SMatthew Ahrens 16249c3fd121SMatthew Ahrens switch (error) { 16259c3fd121SMatthew Ahrens case 0: 16269c3fd121SMatthew Ahrens return (0); 16279c3fd121SMatthew Ahrens case EXDEV: 16289c3fd121SMatthew Ahrens case ENOENT: 16299c3fd121SMatthew Ahrens case EDQUOT: 16309c3fd121SMatthew Ahrens case EFBIG: 16319c3fd121SMatthew Ahrens case EIO: 16329c3fd121SMatthew Ahrens case ENOLINK: 16339c3fd121SMatthew Ahrens case ENOSPC: 16349c3fd121SMatthew Ahrens case ENOSTR: 16359c3fd121SMatthew Ahrens case ENXIO: 16369c3fd121SMatthew Ahrens case EPIPE: 16379c3fd121SMatthew Ahrens case ERANGE: 16389c3fd121SMatthew Ahrens case EFAULT: 16399c3fd121SMatthew Ahrens case EROFS: 16409c3fd121SMatthew Ahrens zfs_error_aux(hdl, strerror(errno)); 16419c3fd121SMatthew Ahrens return (zfs_error(hdl, EZFS_BADBACKUP, errbuf)); 16429c3fd121SMatthew Ahrens 16439c3fd121SMatthew Ahrens default: 16449c3fd121SMatthew Ahrens return (zfs_standard_error(hdl, errno, errbuf)); 16459c3fd121SMatthew Ahrens } 16469c3fd121SMatthew Ahrens } 16479c3fd121SMatthew Ahrens 16489c3fd121SMatthew Ahrens 16499c3fd121SMatthew Ahrens zfs_close(zhp); 16509c3fd121SMatthew Ahrens 16519c3fd121SMatthew Ahrens return (error); 16529c3fd121SMatthew Ahrens } 16539c3fd121SMatthew Ahrens 16543cb34c60Sahrens /* 165588bb18d2SLori Alt * Generate a send stream for the dataset identified by the argument zhp. 165688bb18d2SLori Alt * 165788bb18d2SLori Alt * The content of the send stream is the snapshot identified by 165888bb18d2SLori Alt * 'tosnap'. Incremental streams are requested in two ways: 165988bb18d2SLori Alt * - from the snapshot identified by "fromsnap" (if non-null) or 166088bb18d2SLori Alt * - from the origin of the dataset identified by zhp, which must 166188bb18d2SLori Alt * be a clone. In this case, "fromsnap" is null and "fromorigin" 166288bb18d2SLori Alt * is TRUE. 166388bb18d2SLori Alt * 166488bb18d2SLori Alt * The send stream is recursive (i.e. dumps a hierarchy of snapshots) and 16659e69d7d0SLori Alt * uses a special header (with a hdrtype field of DMU_COMPOUNDSTREAM) 166688bb18d2SLori Alt * if "replicate" is set. If "doall" is set, dump all the intermediate 16679e69d7d0SLori Alt * snapshots. The DMU_COMPOUNDSTREAM header is used in the "doall" 166892241e0bSTom Erickson * case too. If "props" is set, send properties. 16693cb34c60Sahrens */ 16703cb34c60Sahrens int 16713cb34c60Sahrens zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 167219b94df9SMatthew Ahrens sendflags_t *flags, int outfd, snapfilter_cb_t filter_func, 16733f9d6ad7SLin Ling void *cb_arg, nvlist_t **debugnvp) 16743cb34c60Sahrens { 16753cb34c60Sahrens char errbuf[1024]; 16763cb34c60Sahrens send_dump_data_t sdd = { 0 }; 167719b94df9SMatthew Ahrens int err = 0; 16783cb34c60Sahrens nvlist_t *fss = NULL; 16793cb34c60Sahrens avl_tree_t *fsavl = NULL; 1680ca45db41SChris Kirby static uint64_t holdseq; 168154809200SChris Kirby int spa_version; 1682a7a845e4SSteven Hartland pthread_t tid = 0; 16839e69d7d0SLori Alt int pipefd[2]; 16849e69d7d0SLori Alt dedup_arg_t dda = { 0 }; 16859e69d7d0SLori Alt int featureflags = 0; 1686dc5f28a3SManoj Joseph FILE *fout; 16870a586ceaSMark Shellenbaum 16883cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 16893cb34c60Sahrens "cannot send '%s'"), zhp->zfs_name); 16903cb34c60Sahrens 16913cb34c60Sahrens if (fromsnap && fromsnap[0] == '\0') { 16923cb34c60Sahrens zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 16933cb34c60Sahrens "zero-length incremental source")); 16943cb34c60Sahrens return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf)); 16953cb34c60Sahrens } 16963cb34c60Sahrens 1697a7f53a56SChris Kirby if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM) { 1698a7f53a56SChris Kirby uint64_t version; 1699a7f53a56SChris Kirby version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 1700a7f53a56SChris Kirby if (version >= ZPL_VERSION_SA) { 1701a7f53a56SChris Kirby featureflags |= DMU_BACKUP_FEATURE_SA_SPILL; 1702a7f53a56SChris Kirby } 1703a7f53a56SChris Kirby } 1704a7f53a56SChris Kirby 170519b94df9SMatthew Ahrens if (flags->dedup && !flags->dryrun) { 17068e714474SLori Alt featureflags |= (DMU_BACKUP_FEATURE_DEDUP | 17078e714474SLori Alt DMU_BACKUP_FEATURE_DEDUPPROPS); 17089e69d7d0SLori Alt if (err = pipe(pipefd)) { 17099e69d7d0SLori Alt zfs_error_aux(zhp->zfs_hdl, strerror(errno)); 17109e69d7d0SLori Alt return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, 17119e69d7d0SLori Alt errbuf)); 17129e69d7d0SLori Alt } 17139e69d7d0SLori Alt dda.outputfd = outfd; 17149e69d7d0SLori Alt dda.inputfd = pipefd[1]; 17159e69d7d0SLori Alt dda.dedup_hdl = zhp->zfs_hdl; 17169e69d7d0SLori Alt if (err = pthread_create(&tid, NULL, cksummer, &dda)) { 17179e69d7d0SLori Alt (void) close(pipefd[0]); 17189e69d7d0SLori Alt (void) close(pipefd[1]); 17199e69d7d0SLori Alt zfs_error_aux(zhp->zfs_hdl, strerror(errno)); 17209e69d7d0SLori Alt return (zfs_error(zhp->zfs_hdl, 17219e69d7d0SLori Alt EZFS_THREADCREATEFAILED, errbuf)); 17229e69d7d0SLori Alt } 17239e69d7d0SLori Alt } 17249e69d7d0SLori Alt 172519b94df9SMatthew Ahrens if (flags->replicate || flags->doall || flags->props) { 17263cb34c60Sahrens dmu_replay_record_t drr = { 0 }; 17273cb34c60Sahrens char *packbuf = NULL; 17283cb34c60Sahrens size_t buflen = 0; 17293cb34c60Sahrens zio_cksum_t zc = { 0 }; 17303cb34c60Sahrens 173119b94df9SMatthew Ahrens if (flags->replicate || flags->props) { 17323cb34c60Sahrens nvlist_t *hdrnv; 17333cb34c60Sahrens 17343cb34c60Sahrens VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0)); 17353cb34c60Sahrens if (fromsnap) { 17363cb34c60Sahrens VERIFY(0 == nvlist_add_string(hdrnv, 17373cb34c60Sahrens "fromsnap", fromsnap)); 17383cb34c60Sahrens } 17393cb34c60Sahrens VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap)); 174019b94df9SMatthew Ahrens if (!flags->replicate) { 174192241e0bSTom Erickson VERIFY(0 == nvlist_add_boolean(hdrnv, 174292241e0bSTom Erickson "not_recursive")); 174392241e0bSTom Erickson } 17443cb34c60Sahrens 17453cb34c60Sahrens err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name, 174619b94df9SMatthew Ahrens fromsnap, tosnap, flags->replicate, &fss, &fsavl); 1747c99e4bdcSChris Kirby if (err) 17489e69d7d0SLori Alt goto err_out; 17493cb34c60Sahrens VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss)); 17503cb34c60Sahrens err = nvlist_pack(hdrnv, &packbuf, &buflen, 17513cb34c60Sahrens NV_ENCODE_XDR, 0); 17523f9d6ad7SLin Ling if (debugnvp) 17533f9d6ad7SLin Ling *debugnvp = hdrnv; 17543f9d6ad7SLin Ling else 17553cb34c60Sahrens nvlist_free(hdrnv); 1756a7a845e4SSteven Hartland if (err) 17579e69d7d0SLori Alt goto stderr_out; 17583cb34c60Sahrens } 17593cb34c60Sahrens 176019b94df9SMatthew Ahrens if (!flags->dryrun) { 17613cb34c60Sahrens /* write first begin record */ 17623cb34c60Sahrens drr.drr_type = DRR_BEGIN; 17633cb34c60Sahrens drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC; 176419b94df9SMatthew Ahrens DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin. 176519b94df9SMatthew Ahrens drr_versioninfo, DMU_COMPOUNDSTREAM); 176619b94df9SMatthew Ahrens DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin. 176719b94df9SMatthew Ahrens drr_versioninfo, featureflags); 17683cb34c60Sahrens (void) snprintf(drr.drr_u.drr_begin.drr_toname, 17693cb34c60Sahrens sizeof (drr.drr_u.drr_begin.drr_toname), 17703cb34c60Sahrens "%s@%s", zhp->zfs_name, tosnap); 17713cb34c60Sahrens drr.drr_payloadlen = buflen; 17723cb34c60Sahrens 177398110f08SMatthew Ahrens err = dump_record(&drr, packbuf, buflen, &zc, outfd); 17743cb34c60Sahrens free(packbuf); 177598110f08SMatthew Ahrens if (err != 0) 17769e69d7d0SLori Alt goto stderr_out; 17773cb34c60Sahrens 17783cb34c60Sahrens /* write end record */ 17793cb34c60Sahrens bzero(&drr, sizeof (drr)); 17803cb34c60Sahrens drr.drr_type = DRR_END; 17813cb34c60Sahrens drr.drr_u.drr_end.drr_checksum = zc; 17823cb34c60Sahrens err = write(outfd, &drr, sizeof (drr)); 17833cb34c60Sahrens if (err == -1) { 1784c6fd73aeSChris Kirby err = errno; 17859e69d7d0SLori Alt goto stderr_out; 17863cb34c60Sahrens } 178719b94df9SMatthew Ahrens 178819b94df9SMatthew Ahrens err = 0; 17893cb34c60Sahrens } 17903cb34c60Sahrens } 17913cb34c60Sahrens 17923cb34c60Sahrens /* dump each stream */ 17933cb34c60Sahrens sdd.fromsnap = fromsnap; 17943cb34c60Sahrens sdd.tosnap = tosnap; 1795a7a845e4SSteven Hartland if (tid != 0) 17969e69d7d0SLori Alt sdd.outfd = pipefd[0]; 17979e69d7d0SLori Alt else 17983cb34c60Sahrens sdd.outfd = outfd; 179919b94df9SMatthew Ahrens sdd.replicate = flags->replicate; 180019b94df9SMatthew Ahrens sdd.doall = flags->doall; 180119b94df9SMatthew Ahrens sdd.fromorigin = flags->fromorigin; 18023cb34c60Sahrens sdd.fss = fss; 18033cb34c60Sahrens sdd.fsavl = fsavl; 180419b94df9SMatthew Ahrens sdd.verbose = flags->verbose; 180519b94df9SMatthew Ahrens sdd.parsable = flags->parsable; 18064e3c9f44SBill Pijewski sdd.progress = flags->progress; 180719b94df9SMatthew Ahrens sdd.dryrun = flags->dryrun; 1808b5152584SMatthew Ahrens sdd.large_block = flags->largeblock; 18095d7b4d43SMatthew Ahrens sdd.embed_data = flags->embed_data; 18109e69d7d0SLori Alt sdd.filter_cb = filter_func; 18119e69d7d0SLori Alt sdd.filter_cb_arg = cb_arg; 18123f9d6ad7SLin Ling if (debugnvp) 18133f9d6ad7SLin Ling sdd.debugnv = *debugnvp; 1814dc5f28a3SManoj Joseph if (sdd.verbose && sdd.dryrun) 1815dc5f28a3SManoj Joseph sdd.std_out = B_TRUE; 1816dc5f28a3SManoj Joseph fout = sdd.std_out ? stdout : stderr; 181765fec9f6SChristopher Siden 181865fec9f6SChristopher Siden /* 181965fec9f6SChristopher Siden * Some flags require that we place user holds on the datasets that are 182065fec9f6SChristopher Siden * being sent so they don't get destroyed during the send. We can skip 182165fec9f6SChristopher Siden * this step if the pool is imported read-only since the datasets cannot 182265fec9f6SChristopher Siden * be destroyed. 182365fec9f6SChristopher Siden */ 182465fec9f6SChristopher Siden if (!flags->dryrun && !zpool_get_prop_int(zfs_get_pool_handle(zhp), 182565fec9f6SChristopher Siden ZPOOL_PROP_READONLY, NULL) && 182665fec9f6SChristopher Siden zfs_spa_version(zhp, &spa_version) == 0 && 182765fec9f6SChristopher Siden spa_version >= SPA_VERSION_USERREFS && 182865fec9f6SChristopher Siden (flags->doall || flags->replicate)) { 1829a7f53a56SChris Kirby ++holdseq; 1830a7f53a56SChris Kirby (void) snprintf(sdd.holdtag, sizeof (sdd.holdtag), 1831a7f53a56SChris Kirby ".send-%d-%llu", getpid(), (u_longlong_t)holdseq); 1832a7f53a56SChris Kirby sdd.cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL); 1833a7f53a56SChris Kirby if (sdd.cleanup_fd < 0) { 1834a7f53a56SChris Kirby err = errno; 1835a7f53a56SChris Kirby goto stderr_out; 1836a7f53a56SChris Kirby } 1837a7a845e4SSteven Hartland sdd.snapholds = fnvlist_alloc(); 1838a7f53a56SChris Kirby } else { 1839a7f53a56SChris Kirby sdd.cleanup_fd = -1; 1840a7a845e4SSteven Hartland sdd.snapholds = NULL; 1841a7f53a56SChris Kirby } 1842a7a845e4SSteven Hartland if (flags->verbose || sdd.snapholds != NULL) { 184319b94df9SMatthew Ahrens /* 184419b94df9SMatthew Ahrens * Do a verbose no-op dry run to get all the verbose output 1845a7a845e4SSteven Hartland * or to gather snapshot hold's before generating any data, 1846a7a845e4SSteven Hartland * then do a non-verbose real run to generate the streams. 184719b94df9SMatthew Ahrens */ 184819b94df9SMatthew Ahrens sdd.dryrun = B_TRUE; 184919b94df9SMatthew Ahrens err = dump_filesystems(zhp, &sdd); 1850a7a845e4SSteven Hartland 1851a7a845e4SSteven Hartland if (err != 0) 1852a7a845e4SSteven Hartland goto stderr_out; 1853a7a845e4SSteven Hartland 1854a7a845e4SSteven Hartland if (flags->verbose) { 185519b94df9SMatthew Ahrens if (flags->parsable) { 1856dc5f28a3SManoj Joseph (void) fprintf(fout, "size\t%llu\n", 185719b94df9SMatthew Ahrens (longlong_t)sdd.size); 185819b94df9SMatthew Ahrens } else { 185919b94df9SMatthew Ahrens char buf[16]; 186019b94df9SMatthew Ahrens zfs_nicenum(sdd.size, buf, sizeof (buf)); 1861dc5f28a3SManoj Joseph (void) fprintf(fout, dgettext(TEXT_DOMAIN, 186219b94df9SMatthew Ahrens "total estimated size is %s\n"), buf); 186319b94df9SMatthew Ahrens } 186419b94df9SMatthew Ahrens } 1865a7a845e4SSteven Hartland 1866a7a845e4SSteven Hartland /* Ensure no snaps found is treated as an error. */ 1867a7a845e4SSteven Hartland if (!sdd.seento) { 1868a7a845e4SSteven Hartland err = ENOENT; 1869a7a845e4SSteven Hartland goto err_out; 1870a7a845e4SSteven Hartland } 1871a7a845e4SSteven Hartland 1872a7a845e4SSteven Hartland /* Skip the second run if dryrun was requested. */ 1873a7a845e4SSteven Hartland if (flags->dryrun) 1874a7a845e4SSteven Hartland goto err_out; 1875a7a845e4SSteven Hartland 1876a7a845e4SSteven Hartland if (sdd.snapholds != NULL) { 1877a7a845e4SSteven Hartland err = zfs_hold_nvl(zhp, sdd.cleanup_fd, sdd.snapholds); 1878a7a845e4SSteven Hartland if (err != 0) 1879a7a845e4SSteven Hartland goto stderr_out; 1880a7a845e4SSteven Hartland 1881a7a845e4SSteven Hartland fnvlist_free(sdd.snapholds); 1882a7a845e4SSteven Hartland sdd.snapholds = NULL; 1883a7a845e4SSteven Hartland } 1884a7a845e4SSteven Hartland 1885a7a845e4SSteven Hartland sdd.dryrun = B_FALSE; 1886a7a845e4SSteven Hartland sdd.verbose = B_FALSE; 1887a7a845e4SSteven Hartland } 1888a7a845e4SSteven Hartland 18893cb34c60Sahrens err = dump_filesystems(zhp, &sdd); 18903cb34c60Sahrens fsavl_destroy(fsavl); 18913cb34c60Sahrens nvlist_free(fss); 18923cb34c60Sahrens 1893a7a845e4SSteven Hartland /* Ensure no snaps found is treated as an error. */ 1894a7a845e4SSteven Hartland if (err == 0 && !sdd.seento) 1895a7a845e4SSteven Hartland err = ENOENT; 1896a7a845e4SSteven Hartland 1897a7a845e4SSteven Hartland if (tid != 0) { 1898a7a845e4SSteven Hartland if (err != 0) 1899a7a845e4SSteven Hartland (void) pthread_cancel(tid); 1900a7a845e4SSteven Hartland (void) close(pipefd[0]); 190136f7455dSSteven Hartland (void) pthread_join(tid, NULL); 19029e69d7d0SLori Alt } 190392241e0bSTom Erickson 1904a7f53a56SChris Kirby if (sdd.cleanup_fd != -1) { 1905a7f53a56SChris Kirby VERIFY(0 == close(sdd.cleanup_fd)); 1906a7f53a56SChris Kirby sdd.cleanup_fd = -1; 1907c99e4bdcSChris Kirby } 1908c99e4bdcSChris Kirby 190919b94df9SMatthew Ahrens if (!flags->dryrun && (flags->replicate || flags->doall || 191019b94df9SMatthew Ahrens flags->props)) { 19113cb34c60Sahrens /* 19123cb34c60Sahrens * write final end record. NB: want to do this even if 19133cb34c60Sahrens * there was some error, because it might not be totally 19143cb34c60Sahrens * failed. 19153cb34c60Sahrens */ 19163cb34c60Sahrens dmu_replay_record_t drr = { 0 }; 19173cb34c60Sahrens drr.drr_type = DRR_END; 19183cb34c60Sahrens if (write(outfd, &drr, sizeof (drr)) == -1) { 19193cb34c60Sahrens return (zfs_standard_error(zhp->zfs_hdl, 19203cb34c60Sahrens errno, errbuf)); 19213cb34c60Sahrens } 19223cb34c60Sahrens } 19233cb34c60Sahrens 19243cb34c60Sahrens return (err || sdd.err); 19259e69d7d0SLori Alt 19269e69d7d0SLori Alt stderr_out: 19279e69d7d0SLori Alt err = zfs_standard_error(zhp->zfs_hdl, err, errbuf); 19289e69d7d0SLori Alt err_out: 1929a7a845e4SSteven Hartland fsavl_destroy(fsavl); 1930a7a845e4SSteven Hartland nvlist_free(fss); 1931a7a845e4SSteven Hartland fnvlist_free(sdd.snapholds); 1932a7a845e4SSteven Hartland 1933a7f53a56SChris Kirby if (sdd.cleanup_fd != -1) 1934a7f53a56SChris Kirby VERIFY(0 == close(sdd.cleanup_fd)); 1935a7a845e4SSteven Hartland if (tid != 0) { 19369e69d7d0SLori Alt (void) pthread_cancel(tid); 19379e69d7d0SLori Alt (void) close(pipefd[0]); 193836f7455dSSteven Hartland (void) pthread_join(tid, NULL); 19399e69d7d0SLori Alt } 19409e69d7d0SLori Alt return (err); 19413cb34c60Sahrens } 19423cb34c60Sahrens 194378f17100SMatthew Ahrens int 19445d7b4d43SMatthew Ahrens zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, 19455d7b4d43SMatthew Ahrens enum lzc_send_flags flags) 194678f17100SMatthew Ahrens { 194778f17100SMatthew Ahrens int err; 194878f17100SMatthew Ahrens libzfs_handle_t *hdl = zhp->zfs_hdl; 194978f17100SMatthew Ahrens 195078f17100SMatthew Ahrens char errbuf[1024]; 195178f17100SMatthew Ahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 195278f17100SMatthew Ahrens "warning: cannot send '%s'"), zhp->zfs_name); 195378f17100SMatthew Ahrens 19545d7b4d43SMatthew Ahrens err = lzc_send(zhp->zfs_name, from, fd, flags); 195578f17100SMatthew Ahrens if (err != 0) { 195678f17100SMatthew Ahrens switch (errno) { 195778f17100SMatthew Ahrens case EXDEV: 195878f17100SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 195978f17100SMatthew Ahrens "not an earlier snapshot from the same fs")); 196078f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); 196178f17100SMatthew Ahrens 196278f17100SMatthew Ahrens case ENOENT: 196378f17100SMatthew Ahrens case ESRCH: 196478f17100SMatthew Ahrens if (lzc_exists(zhp->zfs_name)) { 196578f17100SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 196678f17100SMatthew Ahrens "incremental source (%s) does not exist"), 196778f17100SMatthew Ahrens from); 196878f17100SMatthew Ahrens } 196978f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_NOENT, errbuf)); 197078f17100SMatthew Ahrens 197178f17100SMatthew Ahrens case EBUSY: 197278f17100SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 197378f17100SMatthew Ahrens "target is busy; if a filesystem, " 197478f17100SMatthew Ahrens "it must not be mounted")); 197578f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_BUSY, errbuf)); 197678f17100SMatthew Ahrens 197778f17100SMatthew Ahrens case EDQUOT: 197878f17100SMatthew Ahrens case EFBIG: 197978f17100SMatthew Ahrens case EIO: 198078f17100SMatthew Ahrens case ENOLINK: 198178f17100SMatthew Ahrens case ENOSPC: 198278f17100SMatthew Ahrens case ENOSTR: 198378f17100SMatthew Ahrens case ENXIO: 198478f17100SMatthew Ahrens case EPIPE: 198578f17100SMatthew Ahrens case ERANGE: 198678f17100SMatthew Ahrens case EFAULT: 198778f17100SMatthew Ahrens case EROFS: 198878f17100SMatthew Ahrens zfs_error_aux(hdl, strerror(errno)); 198978f17100SMatthew Ahrens return (zfs_error(hdl, EZFS_BADBACKUP, errbuf)); 199078f17100SMatthew Ahrens 199178f17100SMatthew Ahrens default: 199278f17100SMatthew Ahrens return (zfs_standard_error(hdl, errno, errbuf)); 199378f17100SMatthew Ahrens } 199478f17100SMatthew Ahrens } 199578f17100SMatthew Ahrens return (err != 0); 199678f17100SMatthew Ahrens } 199778f17100SMatthew Ahrens 19983cb34c60Sahrens /* 19993cb34c60Sahrens * Routines specific to "zfs recv" 20003cb34c60Sahrens */ 20013cb34c60Sahrens 20023cb34c60Sahrens static int 20033cb34c60Sahrens recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen, 20043cb34c60Sahrens boolean_t byteswap, zio_cksum_t *zc) 20053cb34c60Sahrens { 20063cb34c60Sahrens char *cp = buf; 20073cb34c60Sahrens int rv; 20083cb34c60Sahrens int len = ilen; 20093cb34c60Sahrens 201098110f08SMatthew Ahrens assert(ilen <= SPA_MAXBLOCKSIZE); 201198110f08SMatthew Ahrens 20123cb34c60Sahrens do { 20133cb34c60Sahrens rv = read(fd, cp, len); 20143cb34c60Sahrens cp += rv; 20153cb34c60Sahrens len -= rv; 20163cb34c60Sahrens } while (rv > 0); 20173cb34c60Sahrens 20183cb34c60Sahrens if (rv < 0 || len != 0) { 20193cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 20203cb34c60Sahrens "failed to read from stream")); 20213cb34c60Sahrens return (zfs_error(hdl, EZFS_BADSTREAM, dgettext(TEXT_DOMAIN, 20223cb34c60Sahrens "cannot receive"))); 20233cb34c60Sahrens } 20243cb34c60Sahrens 20253cb34c60Sahrens if (zc) { 20263cb34c60Sahrens if (byteswap) 20273cb34c60Sahrens fletcher_4_incremental_byteswap(buf, ilen, zc); 20283cb34c60Sahrens else 20293cb34c60Sahrens fletcher_4_incremental_native(buf, ilen, zc); 20303cb34c60Sahrens } 20313cb34c60Sahrens return (0); 20323cb34c60Sahrens } 20333cb34c60Sahrens 20343cb34c60Sahrens static int 20353cb34c60Sahrens recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp, 20363cb34c60Sahrens boolean_t byteswap, zio_cksum_t *zc) 20373cb34c60Sahrens { 20383cb34c60Sahrens char *buf; 20393cb34c60Sahrens int err; 20403cb34c60Sahrens 20413cb34c60Sahrens buf = zfs_alloc(hdl, len); 20423cb34c60Sahrens if (buf == NULL) 20433cb34c60Sahrens return (ENOMEM); 20443cb34c60Sahrens 20453cb34c60Sahrens err = recv_read(hdl, fd, buf, len, byteswap, zc); 20463cb34c60Sahrens if (err != 0) { 20473cb34c60Sahrens free(buf); 20483cb34c60Sahrens return (err); 20493cb34c60Sahrens } 20503cb34c60Sahrens 20513cb34c60Sahrens err = nvlist_unpack(buf, len, nvp, 0); 20523cb34c60Sahrens free(buf); 20533cb34c60Sahrens if (err != 0) { 20543cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " 20553cb34c60Sahrens "stream (malformed nvlist)")); 20563cb34c60Sahrens return (EINVAL); 20573cb34c60Sahrens } 20583cb34c60Sahrens return (0); 20593cb34c60Sahrens } 20603cb34c60Sahrens 20613cb34c60Sahrens static int 20623cb34c60Sahrens recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname, 206319b94df9SMatthew Ahrens int baselen, char *newname, recvflags_t *flags) 20643cb34c60Sahrens { 20653cb34c60Sahrens static int seq; 20663cb34c60Sahrens zfs_cmd_t zc = { 0 }; 20673cb34c60Sahrens int err; 2068d8d77200Sahrens prop_changelist_t *clp; 2069d8d77200Sahrens zfs_handle_t *zhp; 20703cb34c60Sahrens 2071d8d77200Sahrens zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET); 20723cb34c60Sahrens if (zhp == NULL) 20733cb34c60Sahrens return (-1); 20740069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 207519b94df9SMatthew Ahrens flags->force ? MS_FORCE : 0); 20763cb34c60Sahrens zfs_close(zhp); 20773cb34c60Sahrens if (clp == NULL) 20783cb34c60Sahrens return (-1); 20793cb34c60Sahrens err = changelist_prefix(clp); 20803cb34c60Sahrens if (err) 20813cb34c60Sahrens return (err); 20823cb34c60Sahrens 2083842727c2SChris Kirby zc.zc_objset_type = DMU_OST_ZFS; 2084842727c2SChris Kirby (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 2085842727c2SChris Kirby 20863cb34c60Sahrens if (tryname) { 20873cb34c60Sahrens (void) strcpy(newname, tryname); 20883cb34c60Sahrens 20893cb34c60Sahrens (void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value)); 20903cb34c60Sahrens 209119b94df9SMatthew Ahrens if (flags->verbose) { 20923cb34c60Sahrens (void) printf("attempting rename %s to %s\n", 20933cb34c60Sahrens zc.zc_name, zc.zc_value); 20943cb34c60Sahrens } 20953cb34c60Sahrens err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc); 2096d8d77200Sahrens if (err == 0) 20973cb34c60Sahrens changelist_rename(clp, name, tryname); 20983cb34c60Sahrens } else { 20993cb34c60Sahrens err = ENOENT; 21003cb34c60Sahrens } 21013cb34c60Sahrens 21023cb34c60Sahrens if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) { 21033cb34c60Sahrens seq++; 21043cb34c60Sahrens 2105*40a5c998SMatthew Ahrens (void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN, 2106*40a5c998SMatthew Ahrens "%.*srecv-%u-%u", baselen, name, getpid(), seq); 21073cb34c60Sahrens (void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value)); 21083cb34c60Sahrens 210919b94df9SMatthew Ahrens if (flags->verbose) { 21103cb34c60Sahrens (void) printf("failed - trying rename %s to %s\n", 21113cb34c60Sahrens zc.zc_name, zc.zc_value); 21123cb34c60Sahrens } 21133cb34c60Sahrens err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc); 2114d8d77200Sahrens if (err == 0) 21153cb34c60Sahrens changelist_rename(clp, name, newname); 211619b94df9SMatthew Ahrens if (err && flags->verbose) { 21173cb34c60Sahrens (void) printf("failed (%u) - " 21183cb34c60Sahrens "will try again on next pass\n", errno); 21193cb34c60Sahrens } 21203cb34c60Sahrens err = EAGAIN; 212119b94df9SMatthew Ahrens } else if (flags->verbose) { 21223cb34c60Sahrens if (err == 0) 21233cb34c60Sahrens (void) printf("success\n"); 21243cb34c60Sahrens else 21253cb34c60Sahrens (void) printf("failed (%u)\n", errno); 21263cb34c60Sahrens } 21273cb34c60Sahrens 21283cb34c60Sahrens (void) changelist_postfix(clp); 21293cb34c60Sahrens changelist_free(clp); 21303cb34c60Sahrens 21313cb34c60Sahrens return (err); 21323cb34c60Sahrens } 21333cb34c60Sahrens 21343cb34c60Sahrens static int 21353cb34c60Sahrens recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, 213619b94df9SMatthew Ahrens char *newname, recvflags_t *flags) 21373cb34c60Sahrens { 21383cb34c60Sahrens zfs_cmd_t zc = { 0 }; 2139d8d77200Sahrens int err = 0; 2140d8d77200Sahrens prop_changelist_t *clp; 2141d8d77200Sahrens zfs_handle_t *zhp; 2142842727c2SChris Kirby boolean_t defer = B_FALSE; 2143842727c2SChris Kirby int spa_version; 2144d8d77200Sahrens 2145d8d77200Sahrens zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET); 2146d8d77200Sahrens if (zhp == NULL) 2147d8d77200Sahrens return (-1); 21480069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 214919b94df9SMatthew Ahrens flags->force ? MS_FORCE : 0); 2150842727c2SChris Kirby if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 2151842727c2SChris Kirby zfs_spa_version(zhp, &spa_version) == 0 && 2152842727c2SChris Kirby spa_version >= SPA_VERSION_USERREFS) 2153842727c2SChris Kirby defer = B_TRUE; 2154d8d77200Sahrens zfs_close(zhp); 2155d8d77200Sahrens if (clp == NULL) 2156d8d77200Sahrens return (-1); 2157d8d77200Sahrens err = changelist_prefix(clp); 2158d8d77200Sahrens if (err) 2159d8d77200Sahrens return (err); 21603cb34c60Sahrens 21613cb34c60Sahrens zc.zc_objset_type = DMU_OST_ZFS; 2162842727c2SChris Kirby zc.zc_defer_destroy = defer; 21633cb34c60Sahrens (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); 21643cb34c60Sahrens 216519b94df9SMatthew Ahrens if (flags->verbose) 21663cb34c60Sahrens (void) printf("attempting destroy %s\n", zc.zc_name); 21673cb34c60Sahrens err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc); 2168d8d77200Sahrens if (err == 0) { 216919b94df9SMatthew Ahrens if (flags->verbose) 21703cb34c60Sahrens (void) printf("success\n"); 2171d8d77200Sahrens changelist_remove(clp, zc.zc_name); 21723cb34c60Sahrens } 21733cb34c60Sahrens 2174d8d77200Sahrens (void) changelist_postfix(clp); 2175d8d77200Sahrens changelist_free(clp); 2176d8d77200Sahrens 2177842727c2SChris Kirby /* 217892241e0bSTom Erickson * Deferred destroy might destroy the snapshot or only mark it to be 217992241e0bSTom Erickson * destroyed later, and it returns success in either case. 2180842727c2SChris Kirby */ 218192241e0bSTom Erickson if (err != 0 || (defer && zfs_dataset_exists(hdl, name, 218292241e0bSTom Erickson ZFS_TYPE_SNAPSHOT))) { 2183d8d77200Sahrens err = recv_rename(hdl, name, NULL, baselen, newname, flags); 218492241e0bSTom Erickson } 2185d8d77200Sahrens 21863cb34c60Sahrens return (err); 21873cb34c60Sahrens } 21883cb34c60Sahrens 21893cb34c60Sahrens typedef struct guid_to_name_data { 21903cb34c60Sahrens uint64_t guid; 21919c3fd121SMatthew Ahrens boolean_t bookmark_ok; 21923cb34c60Sahrens char *name; 219319b94df9SMatthew Ahrens char *skip; 21943cb34c60Sahrens } guid_to_name_data_t; 21953cb34c60Sahrens 21963cb34c60Sahrens static int 21973cb34c60Sahrens guid_to_name_cb(zfs_handle_t *zhp, void *arg) 21983cb34c60Sahrens { 21993cb34c60Sahrens guid_to_name_data_t *gtnd = arg; 22009c3fd121SMatthew Ahrens const char *slash; 22013cb34c60Sahrens int err; 22023cb34c60Sahrens 220319b94df9SMatthew Ahrens if (gtnd->skip != NULL && 22049c3fd121SMatthew Ahrens (slash = strrchr(zhp->zfs_name, '/')) != NULL && 22059c3fd121SMatthew Ahrens strcmp(slash + 1, gtnd->skip) == 0) { 22069c3fd121SMatthew Ahrens zfs_close(zhp); 220719b94df9SMatthew Ahrens return (0); 220819b94df9SMatthew Ahrens } 220919b94df9SMatthew Ahrens 22109c3fd121SMatthew Ahrens if (zfs_prop_get_int(zhp, ZFS_PROP_GUID) == gtnd->guid) { 22113cb34c60Sahrens (void) strcpy(gtnd->name, zhp->zfs_name); 2212a79992aaSTom Erickson zfs_close(zhp); 22133cb34c60Sahrens return (EEXIST); 22143cb34c60Sahrens } 221519b94df9SMatthew Ahrens 22163cb34c60Sahrens err = zfs_iter_children(zhp, guid_to_name_cb, gtnd); 22179c3fd121SMatthew Ahrens if (err != EEXIST && gtnd->bookmark_ok) 22189c3fd121SMatthew Ahrens err = zfs_iter_bookmarks(zhp, guid_to_name_cb, gtnd); 22193cb34c60Sahrens zfs_close(zhp); 22203cb34c60Sahrens return (err); 22213cb34c60Sahrens } 22223cb34c60Sahrens 222319b94df9SMatthew Ahrens /* 222419b94df9SMatthew Ahrens * Attempt to find the local dataset associated with this guid. In the case of 222519b94df9SMatthew Ahrens * multiple matches, we attempt to find the "best" match by searching 222619b94df9SMatthew Ahrens * progressively larger portions of the hierarchy. This allows one to send a 222719b94df9SMatthew Ahrens * tree of datasets individually and guarantee that we will find the source 222819b94df9SMatthew Ahrens * guid within that hierarchy, even if there are multiple matches elsewhere. 222919b94df9SMatthew Ahrens */ 22303cb34c60Sahrens static int 22313cb34c60Sahrens guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid, 22329c3fd121SMatthew Ahrens boolean_t bookmark_ok, char *name) 22333cb34c60Sahrens { 2234*40a5c998SMatthew Ahrens char pname[ZFS_MAX_DATASET_NAME_LEN]; 22353cb34c60Sahrens guid_to_name_data_t gtnd; 22363cb34c60Sahrens 22373cb34c60Sahrens gtnd.guid = guid; 22389c3fd121SMatthew Ahrens gtnd.bookmark_ok = bookmark_ok; 22393cb34c60Sahrens gtnd.name = name; 224019b94df9SMatthew Ahrens gtnd.skip = NULL; 22413cb34c60Sahrens 224219b94df9SMatthew Ahrens /* 22439c3fd121SMatthew Ahrens * Search progressively larger portions of the hierarchy, starting 22449c3fd121SMatthew Ahrens * with the filesystem specified by 'parent'. This will 224519b94df9SMatthew Ahrens * select the "most local" version of the origin snapshot in the case 224619b94df9SMatthew Ahrens * that there are multiple matching snapshots in the system. 224719b94df9SMatthew Ahrens */ 22489c3fd121SMatthew Ahrens (void) strlcpy(pname, parent, sizeof (pname)); 22499c3fd121SMatthew Ahrens char *cp = strrchr(pname, '@'); 22509c3fd121SMatthew Ahrens if (cp == NULL) 22519c3fd121SMatthew Ahrens cp = strchr(pname, '\0'); 22529c3fd121SMatthew Ahrens for (; cp != NULL; cp = strrchr(pname, '/')) { 225319b94df9SMatthew Ahrens /* Chop off the last component and open the parent */ 225419b94df9SMatthew Ahrens *cp = '\0'; 22559c3fd121SMatthew Ahrens zfs_handle_t *zhp = make_dataset_handle(hdl, pname); 225619b94df9SMatthew Ahrens 225719b94df9SMatthew Ahrens if (zhp == NULL) 225819b94df9SMatthew Ahrens continue; 22599c3fd121SMatthew Ahrens int err = guid_to_name_cb(zfs_handle_dup(zhp), >nd); 22609c3fd121SMatthew Ahrens if (err != EEXIST) 22613cb34c60Sahrens err = zfs_iter_children(zhp, guid_to_name_cb, >nd); 22629c3fd121SMatthew Ahrens if (err != EEXIST && bookmark_ok) 22639c3fd121SMatthew Ahrens err = zfs_iter_bookmarks(zhp, guid_to_name_cb, >nd); 22643cb34c60Sahrens zfs_close(zhp); 22653cb34c60Sahrens if (err == EEXIST) 22663cb34c60Sahrens return (0); 226719b94df9SMatthew Ahrens 226819b94df9SMatthew Ahrens /* 22699c3fd121SMatthew Ahrens * Remember the last portion of the dataset so we skip it next 22709c3fd121SMatthew Ahrens * time through (as we've already searched that portion of the 22719c3fd121SMatthew Ahrens * hierarchy). 227219b94df9SMatthew Ahrens */ 22739c3fd121SMatthew Ahrens gtnd.skip = strrchr(pname, '/') + 1; 22743cb34c60Sahrens } 22753cb34c60Sahrens 227619b94df9SMatthew Ahrens return (ENOENT); 22773cb34c60Sahrens } 22783cb34c60Sahrens 22793cb34c60Sahrens /* 228019b94df9SMatthew Ahrens * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if 228119b94df9SMatthew Ahrens * guid1 is after guid2. 22823cb34c60Sahrens */ 228304bb726eSahl static int 22843cb34c60Sahrens created_before(libzfs_handle_t *hdl, avl_tree_t *avl, 22853cb34c60Sahrens uint64_t guid1, uint64_t guid2) 22863cb34c60Sahrens { 22873cb34c60Sahrens nvlist_t *nvfs; 22883cb34c60Sahrens char *fsname, *snapname; 2289*40a5c998SMatthew Ahrens char buf[ZFS_MAX_DATASET_NAME_LEN]; 229004bb726eSahl int rv; 229119b94df9SMatthew Ahrens zfs_handle_t *guid1hdl, *guid2hdl; 229219b94df9SMatthew Ahrens uint64_t create1, create2; 22933cb34c60Sahrens 22943cb34c60Sahrens if (guid2 == 0) 229504bb726eSahl return (0); 22963cb34c60Sahrens if (guid1 == 0) 229704bb726eSahl return (1); 22983cb34c60Sahrens 22993cb34c60Sahrens nvfs = fsavl_find(avl, guid1, &snapname); 23003cb34c60Sahrens VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname)); 23013cb34c60Sahrens (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname); 230219b94df9SMatthew Ahrens guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT); 230319b94df9SMatthew Ahrens if (guid1hdl == NULL) 230404bb726eSahl return (-1); 23053cb34c60Sahrens 23063cb34c60Sahrens nvfs = fsavl_find(avl, guid2, &snapname); 23073cb34c60Sahrens VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname)); 23083cb34c60Sahrens (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname); 230919b94df9SMatthew Ahrens guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT); 231019b94df9SMatthew Ahrens if (guid2hdl == NULL) { 231119b94df9SMatthew Ahrens zfs_close(guid1hdl); 231204bb726eSahl return (-1); 231304bb726eSahl } 23143cb34c60Sahrens 231519b94df9SMatthew Ahrens create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG); 231619b94df9SMatthew Ahrens create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG); 23173cb34c60Sahrens 231819b94df9SMatthew Ahrens if (create1 < create2) 231919b94df9SMatthew Ahrens rv = -1; 232019b94df9SMatthew Ahrens else if (create1 > create2) 232119b94df9SMatthew Ahrens rv = +1; 232219b94df9SMatthew Ahrens else 232319b94df9SMatthew Ahrens rv = 0; 232419b94df9SMatthew Ahrens 232519b94df9SMatthew Ahrens zfs_close(guid1hdl); 232619b94df9SMatthew Ahrens zfs_close(guid2hdl); 23273cb34c60Sahrens 23283cb34c60Sahrens return (rv); 23293cb34c60Sahrens } 23303cb34c60Sahrens 23313cb34c60Sahrens static int 23323cb34c60Sahrens recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs, 233319b94df9SMatthew Ahrens recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl, 2334bd6a198fSTom Erickson nvlist_t *renamed) 23353cb34c60Sahrens { 23363cb34c60Sahrens nvlist_t *local_nv; 23373cb34c60Sahrens avl_tree_t *local_avl; 23383cb34c60Sahrens nvpair_t *fselem, *nextfselem; 2339bd6a198fSTom Erickson char *fromsnap; 2340*40a5c998SMatthew Ahrens char newname[ZFS_MAX_DATASET_NAME_LEN]; 23413cb34c60Sahrens int error; 234292241e0bSTom Erickson boolean_t needagain, progress, recursive; 2343fb2d63c6SPrabahar Jeyaram char *s1, *s2; 23443cb34c60Sahrens 23453cb34c60Sahrens VERIFY(0 == nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap)); 23463cb34c60Sahrens 234792241e0bSTom Erickson recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") == 234892241e0bSTom Erickson ENOENT); 234992241e0bSTom Erickson 235019b94df9SMatthew Ahrens if (flags->dryrun) 23513cb34c60Sahrens return (0); 23523cb34c60Sahrens 23533cb34c60Sahrens again: 23543cb34c60Sahrens needagain = progress = B_FALSE; 23553cb34c60Sahrens 23563cb34c60Sahrens if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL, 235792241e0bSTom Erickson recursive, &local_nv, &local_avl)) != 0) 23583cb34c60Sahrens return (error); 23593cb34c60Sahrens 23603cb34c60Sahrens /* 23613cb34c60Sahrens * Process deletes and renames 23623cb34c60Sahrens */ 23633cb34c60Sahrens for (fselem = nvlist_next_nvpair(local_nv, NULL); 23643cb34c60Sahrens fselem; fselem = nextfselem) { 23653cb34c60Sahrens nvlist_t *nvfs, *snaps; 23663cb34c60Sahrens nvlist_t *stream_nvfs = NULL; 23673cb34c60Sahrens nvpair_t *snapelem, *nextsnapelem; 23683cb34c60Sahrens uint64_t fromguid = 0; 23693cb34c60Sahrens uint64_t originguid = 0; 23703cb34c60Sahrens uint64_t stream_originguid = 0; 23713cb34c60Sahrens uint64_t parent_fromsnap_guid, stream_parent_fromsnap_guid; 23723cb34c60Sahrens char *fsname, *stream_fsname; 23733cb34c60Sahrens 23743cb34c60Sahrens nextfselem = nvlist_next_nvpair(local_nv, fselem); 23753cb34c60Sahrens 23763cb34c60Sahrens VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs)); 23773cb34c60Sahrens VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps)); 23783cb34c60Sahrens VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname)); 23793cb34c60Sahrens VERIFY(0 == nvlist_lookup_uint64(nvfs, "parentfromsnap", 23803cb34c60Sahrens &parent_fromsnap_guid)); 23813cb34c60Sahrens (void) nvlist_lookup_uint64(nvfs, "origin", &originguid); 23823cb34c60Sahrens 23833cb34c60Sahrens /* 23843cb34c60Sahrens * First find the stream's fs, so we can check for 23853cb34c60Sahrens * a different origin (due to "zfs promote") 23863cb34c60Sahrens */ 23873cb34c60Sahrens for (snapelem = nvlist_next_nvpair(snaps, NULL); 23883cb34c60Sahrens snapelem; snapelem = nvlist_next_nvpair(snaps, snapelem)) { 23893cb34c60Sahrens uint64_t thisguid; 23903cb34c60Sahrens 23913cb34c60Sahrens VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid)); 23923cb34c60Sahrens stream_nvfs = fsavl_find(stream_avl, thisguid, NULL); 23933cb34c60Sahrens 23943cb34c60Sahrens if (stream_nvfs != NULL) 23953cb34c60Sahrens break; 23963cb34c60Sahrens } 23973cb34c60Sahrens 23983cb34c60Sahrens /* check for promote */ 23993cb34c60Sahrens (void) nvlist_lookup_uint64(stream_nvfs, "origin", 24003cb34c60Sahrens &stream_originguid); 24013cb34c60Sahrens if (stream_nvfs && originguid != stream_originguid) { 240204bb726eSahl switch (created_before(hdl, local_avl, 240304bb726eSahl stream_originguid, originguid)) { 240404bb726eSahl case 1: { 24053cb34c60Sahrens /* promote it! */ 24063cb34c60Sahrens zfs_cmd_t zc = { 0 }; 24073cb34c60Sahrens nvlist_t *origin_nvfs; 24083cb34c60Sahrens char *origin_fsname; 24093cb34c60Sahrens 241019b94df9SMatthew Ahrens if (flags->verbose) 24113cb34c60Sahrens (void) printf("promoting %s\n", fsname); 24123cb34c60Sahrens 24133cb34c60Sahrens origin_nvfs = fsavl_find(local_avl, originguid, 24143cb34c60Sahrens NULL); 24153cb34c60Sahrens VERIFY(0 == nvlist_lookup_string(origin_nvfs, 24163cb34c60Sahrens "name", &origin_fsname)); 24173cb34c60Sahrens (void) strlcpy(zc.zc_value, origin_fsname, 24183cb34c60Sahrens sizeof (zc.zc_value)); 24193cb34c60Sahrens (void) strlcpy(zc.zc_name, fsname, 24203cb34c60Sahrens sizeof (zc.zc_name)); 24213cb34c60Sahrens error = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc); 24223cb34c60Sahrens if (error == 0) 24233cb34c60Sahrens progress = B_TRUE; 242404bb726eSahl break; 242504bb726eSahl } 242604bb726eSahl default: 242704bb726eSahl break; 242804bb726eSahl case -1: 242904bb726eSahl fsavl_destroy(local_avl); 243004bb726eSahl nvlist_free(local_nv); 243104bb726eSahl return (-1); 24323cb34c60Sahrens } 24333cb34c60Sahrens /* 24343cb34c60Sahrens * We had/have the wrong origin, therefore our 24353cb34c60Sahrens * list of snapshots is wrong. Need to handle 24363cb34c60Sahrens * them on the next pass. 24373cb34c60Sahrens */ 24383cb34c60Sahrens needagain = B_TRUE; 24393cb34c60Sahrens continue; 24403cb34c60Sahrens } 24413cb34c60Sahrens 24423cb34c60Sahrens for (snapelem = nvlist_next_nvpair(snaps, NULL); 24433cb34c60Sahrens snapelem; snapelem = nextsnapelem) { 24443cb34c60Sahrens uint64_t thisguid; 24453cb34c60Sahrens char *stream_snapname; 2446bb0ade09Sahrens nvlist_t *found, *props; 24473cb34c60Sahrens 24483cb34c60Sahrens nextsnapelem = nvlist_next_nvpair(snaps, snapelem); 24493cb34c60Sahrens 24503cb34c60Sahrens VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid)); 24513cb34c60Sahrens found = fsavl_find(stream_avl, thisguid, 24523cb34c60Sahrens &stream_snapname); 24533cb34c60Sahrens 24543cb34c60Sahrens /* check for delete */ 24553cb34c60Sahrens if (found == NULL) { 2456*40a5c998SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 24573cb34c60Sahrens 245819b94df9SMatthew Ahrens if (!flags->force) 24593cb34c60Sahrens continue; 24603cb34c60Sahrens 24613cb34c60Sahrens (void) snprintf(name, sizeof (name), "%s@%s", 24623cb34c60Sahrens fsname, nvpair_name(snapelem)); 24633cb34c60Sahrens 24643cb34c60Sahrens error = recv_destroy(hdl, name, 24653cb34c60Sahrens strlen(fsname)+1, newname, flags); 24663cb34c60Sahrens if (error) 24673cb34c60Sahrens needagain = B_TRUE; 24683cb34c60Sahrens else 24693cb34c60Sahrens progress = B_TRUE; 24703cb34c60Sahrens continue; 24713cb34c60Sahrens } 24723cb34c60Sahrens 24733cb34c60Sahrens stream_nvfs = found; 24743cb34c60Sahrens 2475bb0ade09Sahrens if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops", 2476bb0ade09Sahrens &props) && 0 == nvlist_lookup_nvlist(props, 2477bb0ade09Sahrens stream_snapname, &props)) { 2478bb0ade09Sahrens zfs_cmd_t zc = { 0 }; 2479bb0ade09Sahrens 248092241e0bSTom Erickson zc.zc_cookie = B_TRUE; /* received */ 2481b6c10d80Sahrens (void) snprintf(zc.zc_name, sizeof (zc.zc_name), 2482bb0ade09Sahrens "%s@%s", fsname, nvpair_name(snapelem)); 2483bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc, 2484bb0ade09Sahrens props) == 0) { 2485bb0ade09Sahrens (void) zfs_ioctl(hdl, 2486bb0ade09Sahrens ZFS_IOC_SET_PROP, &zc); 2487bb0ade09Sahrens zcmd_free_nvlists(&zc); 2488bb0ade09Sahrens } 2489bb0ade09Sahrens } 2490bb0ade09Sahrens 24913cb34c60Sahrens /* check for different snapname */ 24923cb34c60Sahrens if (strcmp(nvpair_name(snapelem), 24933cb34c60Sahrens stream_snapname) != 0) { 2494*40a5c998SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN]; 2495*40a5c998SMatthew Ahrens char tryname[ZFS_MAX_DATASET_NAME_LEN]; 24963cb34c60Sahrens 24973cb34c60Sahrens (void) snprintf(name, sizeof (name), "%s@%s", 24983cb34c60Sahrens fsname, nvpair_name(snapelem)); 24993cb34c60Sahrens (void) snprintf(tryname, sizeof (name), "%s@%s", 25003cb34c60Sahrens fsname, stream_snapname); 25013cb34c60Sahrens 25023cb34c60Sahrens error = recv_rename(hdl, name, tryname, 25033cb34c60Sahrens strlen(fsname)+1, newname, flags); 25043cb34c60Sahrens if (error) 25053cb34c60Sahrens needagain = B_TRUE; 25063cb34c60Sahrens else 25073cb34c60Sahrens progress = B_TRUE; 25083cb34c60Sahrens } 25093cb34c60Sahrens 25103cb34c60Sahrens if (strcmp(stream_snapname, fromsnap) == 0) 25113cb34c60Sahrens fromguid = thisguid; 25123cb34c60Sahrens } 25133cb34c60Sahrens 25143cb34c60Sahrens /* check for delete */ 25153cb34c60Sahrens if (stream_nvfs == NULL) { 251619b94df9SMatthew Ahrens if (!flags->force) 25173cb34c60Sahrens continue; 25183cb34c60Sahrens 25193cb34c60Sahrens error = recv_destroy(hdl, fsname, strlen(tofs)+1, 25203cb34c60Sahrens newname, flags); 25213cb34c60Sahrens if (error) 25223cb34c60Sahrens needagain = B_TRUE; 25233cb34c60Sahrens else 25243cb34c60Sahrens progress = B_TRUE; 25253cb34c60Sahrens continue; 25263cb34c60Sahrens } 25273cb34c60Sahrens 2528bd6a198fSTom Erickson if (fromguid == 0) { 252919b94df9SMatthew Ahrens if (flags->verbose) { 2530bd6a198fSTom Erickson (void) printf("local fs %s does not have " 2531bd6a198fSTom Erickson "fromsnap (%s in stream); must have " 2532bd6a198fSTom Erickson "been deleted locally; ignoring\n", 2533bd6a198fSTom Erickson fsname, fromsnap); 2534bd6a198fSTom Erickson } 25353cb34c60Sahrens continue; 25363cb34c60Sahrens } 25373cb34c60Sahrens 25383cb34c60Sahrens VERIFY(0 == nvlist_lookup_string(stream_nvfs, 25393cb34c60Sahrens "name", &stream_fsname)); 25403cb34c60Sahrens VERIFY(0 == nvlist_lookup_uint64(stream_nvfs, 25413cb34c60Sahrens "parentfromsnap", &stream_parent_fromsnap_guid)); 25423cb34c60Sahrens 254394f5c1a2SPrabahar Jeyaram s1 = strrchr(fsname, '/'); 254494f5c1a2SPrabahar Jeyaram s2 = strrchr(stream_fsname, '/'); 254594f5c1a2SPrabahar Jeyaram 2546bd6a198fSTom Erickson /* 2547bd6a198fSTom Erickson * Check for rename. If the exact receive path is specified, it 2548bd6a198fSTom Erickson * does not count as a rename, but we still need to check the 2549bd6a198fSTom Erickson * datasets beneath it. 2550bd6a198fSTom Erickson */ 25513cb34c60Sahrens if ((stream_parent_fromsnap_guid != 0 && 2552bd6a198fSTom Erickson parent_fromsnap_guid != 0 && 25533cb34c60Sahrens stream_parent_fromsnap_guid != parent_fromsnap_guid) || 255419b94df9SMatthew Ahrens ((flags->isprefix || strcmp(tofs, fsname) != 0) && 2555bd6a198fSTom Erickson (s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) { 25563cb34c60Sahrens nvlist_t *parent; 2557*40a5c998SMatthew Ahrens char tryname[ZFS_MAX_DATASET_NAME_LEN]; 25583cb34c60Sahrens 25593cb34c60Sahrens parent = fsavl_find(local_avl, 25603cb34c60Sahrens stream_parent_fromsnap_guid, NULL); 25613cb34c60Sahrens /* 25623cb34c60Sahrens * NB: parent might not be found if we used the 25633cb34c60Sahrens * tosnap for stream_parent_fromsnap_guid, 25643cb34c60Sahrens * because the parent is a newly-created fs; 25653cb34c60Sahrens * we'll be able to rename it after we recv the 25663cb34c60Sahrens * new fs. 25673cb34c60Sahrens */ 25683cb34c60Sahrens if (parent != NULL) { 25693cb34c60Sahrens char *pname; 25703cb34c60Sahrens 25713cb34c60Sahrens VERIFY(0 == nvlist_lookup_string(parent, "name", 25723cb34c60Sahrens &pname)); 25733cb34c60Sahrens (void) snprintf(tryname, sizeof (tryname), 25743cb34c60Sahrens "%s%s", pname, strrchr(stream_fsname, '/')); 25753cb34c60Sahrens } else { 25763cb34c60Sahrens tryname[0] = '\0'; 257719b94df9SMatthew Ahrens if (flags->verbose) { 25783cb34c60Sahrens (void) printf("local fs %s new parent " 25793cb34c60Sahrens "not found\n", fsname); 25803cb34c60Sahrens } 25813cb34c60Sahrens } 25823cb34c60Sahrens 2583bd6a198fSTom Erickson newname[0] = '\0'; 2584bd6a198fSTom Erickson 25853cb34c60Sahrens error = recv_rename(hdl, fsname, tryname, 25863cb34c60Sahrens strlen(tofs)+1, newname, flags); 2587bd6a198fSTom Erickson 2588bd6a198fSTom Erickson if (renamed != NULL && newname[0] != '\0') { 2589bd6a198fSTom Erickson VERIFY(0 == nvlist_add_boolean(renamed, 2590bd6a198fSTom Erickson newname)); 2591bd6a198fSTom Erickson } 2592bd6a198fSTom Erickson 25933cb34c60Sahrens if (error) 25943cb34c60Sahrens needagain = B_TRUE; 25953cb34c60Sahrens else 25963cb34c60Sahrens progress = B_TRUE; 25973cb34c60Sahrens } 25983cb34c60Sahrens } 25993cb34c60Sahrens 26003cb34c60Sahrens fsavl_destroy(local_avl); 26013cb34c60Sahrens nvlist_free(local_nv); 26023cb34c60Sahrens 26033cb34c60Sahrens if (needagain && progress) { 26043cb34c60Sahrens /* do another pass to fix up temporary names */ 260519b94df9SMatthew Ahrens if (flags->verbose) 26063cb34c60Sahrens (void) printf("another pass:\n"); 26073cb34c60Sahrens goto again; 26083cb34c60Sahrens } 26093cb34c60Sahrens 26103cb34c60Sahrens return (needagain); 26113cb34c60Sahrens } 26123cb34c60Sahrens 26133cb34c60Sahrens static int 26143cb34c60Sahrens zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, 261519b94df9SMatthew Ahrens recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc, 2616c99e4bdcSChris Kirby char **top_zfs, int cleanup_fd, uint64_t *action_handlep) 26173cb34c60Sahrens { 26183cb34c60Sahrens nvlist_t *stream_nv = NULL; 26193cb34c60Sahrens avl_tree_t *stream_avl = NULL; 26203cb34c60Sahrens char *fromsnap = NULL; 26215878fad7SDan McDonald char *sendsnap = NULL; 2622bd6a198fSTom Erickson char *cp; 2623*40a5c998SMatthew Ahrens char tofs[ZFS_MAX_DATASET_NAME_LEN]; 2624*40a5c998SMatthew Ahrens char sendfs[ZFS_MAX_DATASET_NAME_LEN]; 26253cb34c60Sahrens char errbuf[1024]; 26263cb34c60Sahrens dmu_replay_record_t drre; 26273cb34c60Sahrens int error; 26283cb34c60Sahrens boolean_t anyerr = B_FALSE; 2629137fa067Sahrens boolean_t softerr = B_FALSE; 2630bd6a198fSTom Erickson boolean_t recursive; 26313cb34c60Sahrens 26323cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 26333cb34c60Sahrens "cannot receive")); 26343cb34c60Sahrens 26353cb34c60Sahrens assert(drr->drr_type == DRR_BEGIN); 26363cb34c60Sahrens assert(drr->drr_u.drr_begin.drr_magic == DMU_BACKUP_MAGIC); 26379e69d7d0SLori Alt assert(DMU_GET_STREAM_HDRTYPE(drr->drr_u.drr_begin.drr_versioninfo) == 26389e69d7d0SLori Alt DMU_COMPOUNDSTREAM); 26393cb34c60Sahrens 26403cb34c60Sahrens /* 26413cb34c60Sahrens * Read in the nvlist from the stream. 26423cb34c60Sahrens */ 26433cb34c60Sahrens if (drr->drr_payloadlen != 0) { 26443cb34c60Sahrens error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen, 264519b94df9SMatthew Ahrens &stream_nv, flags->byteswap, zc); 26463cb34c60Sahrens if (error) { 26473cb34c60Sahrens error = zfs_error(hdl, EZFS_BADSTREAM, errbuf); 26483cb34c60Sahrens goto out; 26493cb34c60Sahrens } 26503cb34c60Sahrens } 26513cb34c60Sahrens 2652bd6a198fSTom Erickson recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") == 2653bd6a198fSTom Erickson ENOENT); 2654bd6a198fSTom Erickson 2655bd6a198fSTom Erickson if (recursive && strchr(destname, '@')) { 2656bd6a198fSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 2657bd6a198fSTom Erickson "cannot specify snapshot name for multi-snapshot stream")); 2658bd6a198fSTom Erickson error = zfs_error(hdl, EZFS_BADSTREAM, errbuf); 2659bd6a198fSTom Erickson goto out; 2660bd6a198fSTom Erickson } 2661bd6a198fSTom Erickson 26623cb34c60Sahrens /* 26633cb34c60Sahrens * Read in the end record and verify checksum. 26643cb34c60Sahrens */ 26653cb34c60Sahrens if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre), 266619b94df9SMatthew Ahrens flags->byteswap, NULL))) 26673cb34c60Sahrens goto out; 266819b94df9SMatthew Ahrens if (flags->byteswap) { 26693cb34c60Sahrens drre.drr_type = BSWAP_32(drre.drr_type); 26703cb34c60Sahrens drre.drr_u.drr_end.drr_checksum.zc_word[0] = 26713cb34c60Sahrens BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]); 26723cb34c60Sahrens drre.drr_u.drr_end.drr_checksum.zc_word[1] = 26733cb34c60Sahrens BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[1]); 26743cb34c60Sahrens drre.drr_u.drr_end.drr_checksum.zc_word[2] = 26753cb34c60Sahrens BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[2]); 26763cb34c60Sahrens drre.drr_u.drr_end.drr_checksum.zc_word[3] = 26773cb34c60Sahrens BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[3]); 26783cb34c60Sahrens } 26793cb34c60Sahrens if (drre.drr_type != DRR_END) { 26803cb34c60Sahrens error = zfs_error(hdl, EZFS_BADSTREAM, errbuf); 26813cb34c60Sahrens goto out; 26823cb34c60Sahrens } 26833cb34c60Sahrens if (!ZIO_CHECKSUM_EQUAL(drre.drr_u.drr_end.drr_checksum, *zc)) { 26843cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 26853cb34c60Sahrens "incorrect header checksum")); 26863cb34c60Sahrens error = zfs_error(hdl, EZFS_BADSTREAM, errbuf); 26873cb34c60Sahrens goto out; 26883cb34c60Sahrens } 26893cb34c60Sahrens 26903cb34c60Sahrens (void) nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap); 26913cb34c60Sahrens 26923cb34c60Sahrens if (drr->drr_payloadlen != 0) { 26933cb34c60Sahrens nvlist_t *stream_fss; 26943cb34c60Sahrens 26953cb34c60Sahrens VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss", 26963cb34c60Sahrens &stream_fss)); 269700954d7bSahl if ((stream_avl = fsavl_create(stream_fss)) == NULL) { 269800954d7bSahl zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 269900954d7bSahl "couldn't allocate avl tree")); 270000954d7bSahl error = zfs_error(hdl, EZFS_NOMEM, errbuf); 270100954d7bSahl goto out; 270200954d7bSahl } 27033cb34c60Sahrens 27043cb34c60Sahrens if (fromsnap != NULL) { 2705bd6a198fSTom Erickson nvlist_t *renamed = NULL; 2706bd6a198fSTom Erickson nvpair_t *pair = NULL; 2707bd6a198fSTom Erickson 2708*40a5c998SMatthew Ahrens (void) strlcpy(tofs, destname, sizeof (tofs)); 270919b94df9SMatthew Ahrens if (flags->isprefix) { 2710bd6a198fSTom Erickson struct drr_begin *drrb = &drr->drr_u.drr_begin; 2711bd6a198fSTom Erickson int i; 2712bd6a198fSTom Erickson 271319b94df9SMatthew Ahrens if (flags->istail) { 2714bd6a198fSTom Erickson cp = strrchr(drrb->drr_toname, '/'); 2715bd6a198fSTom Erickson if (cp == NULL) { 2716bd6a198fSTom Erickson (void) strlcat(tofs, "/", 2717*40a5c998SMatthew Ahrens sizeof (tofs)); 2718bd6a198fSTom Erickson i = 0; 2719bd6a198fSTom Erickson } else { 2720bd6a198fSTom Erickson i = (cp - drrb->drr_toname); 2721bd6a198fSTom Erickson } 2722bd6a198fSTom Erickson } else { 2723bd6a198fSTom Erickson i = strcspn(drrb->drr_toname, "/@"); 2724bd6a198fSTom Erickson } 27253cb34c60Sahrens /* zfs_receive_one() will create_parents() */ 2726bd6a198fSTom Erickson (void) strlcat(tofs, &drrb->drr_toname[i], 2727*40a5c998SMatthew Ahrens sizeof (tofs)); 27283cb34c60Sahrens *strchr(tofs, '@') = '\0'; 27293cb34c60Sahrens } 2730bd6a198fSTom Erickson 273119b94df9SMatthew Ahrens if (recursive && !flags->dryrun && !flags->nomount) { 2732bd6a198fSTom Erickson VERIFY(0 == nvlist_alloc(&renamed, 2733bd6a198fSTom Erickson NV_UNIQUE_NAME, 0)); 2734bd6a198fSTom Erickson } 2735bd6a198fSTom Erickson 2736bd6a198fSTom Erickson softerr = recv_incremental_replication(hdl, tofs, flags, 2737bd6a198fSTom Erickson stream_nv, stream_avl, renamed); 2738bd6a198fSTom Erickson 2739bd6a198fSTom Erickson /* Unmount renamed filesystems before receiving. */ 2740bd6a198fSTom Erickson while ((pair = nvlist_next_nvpair(renamed, 2741bd6a198fSTom Erickson pair)) != NULL) { 2742bd6a198fSTom Erickson zfs_handle_t *zhp; 2743bd6a198fSTom Erickson prop_changelist_t *clp = NULL; 2744bd6a198fSTom Erickson 2745bd6a198fSTom Erickson zhp = zfs_open(hdl, nvpair_name(pair), 2746bd6a198fSTom Erickson ZFS_TYPE_FILESYSTEM); 2747bd6a198fSTom Erickson if (zhp != NULL) { 2748bd6a198fSTom Erickson clp = changelist_gather(zhp, 2749bd6a198fSTom Erickson ZFS_PROP_MOUNTPOINT, 0, 0); 2750bd6a198fSTom Erickson zfs_close(zhp); 2751bd6a198fSTom Erickson if (clp != NULL) { 2752bd6a198fSTom Erickson softerr |= 2753bd6a198fSTom Erickson changelist_prefix(clp); 2754bd6a198fSTom Erickson changelist_free(clp); 2755bd6a198fSTom Erickson } 27563cb34c60Sahrens } 27573cb34c60Sahrens } 27583cb34c60Sahrens 2759bd6a198fSTom Erickson nvlist_free(renamed); 2760bd6a198fSTom Erickson } 2761bd6a198fSTom Erickson } 2762bd6a198fSTom Erickson 2763bd6a198fSTom Erickson /* 2764bd6a198fSTom Erickson * Get the fs specified by the first path in the stream (the top level 2765bd6a198fSTom Erickson * specified by 'zfs send') and pass it to each invocation of 2766bd6a198fSTom Erickson * zfs_receive_one(). 2767bd6a198fSTom Erickson */ 2768bd6a198fSTom Erickson (void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname, 2769*40a5c998SMatthew Ahrens sizeof (sendfs)); 27705878fad7SDan McDonald if ((cp = strchr(sendfs, '@')) != NULL) { 2771bd6a198fSTom Erickson *cp = '\0'; 27725878fad7SDan McDonald /* 27735878fad7SDan McDonald * Find the "sendsnap", the final snapshot in a replication 27745878fad7SDan McDonald * stream. zfs_receive_one() handles certain errors 27755878fad7SDan McDonald * differently, depending on if the contained stream is the 27765878fad7SDan McDonald * last one or not. 27775878fad7SDan McDonald */ 27785878fad7SDan McDonald sendsnap = (cp + 1); 27795878fad7SDan McDonald } 27803cb34c60Sahrens 27813cb34c60Sahrens /* Finally, receive each contained stream */ 27823cb34c60Sahrens do { 27833cb34c60Sahrens /* 27843cb34c60Sahrens * we should figure out if it has a recoverable 27853cb34c60Sahrens * error, in which case do a recv_skip() and drive on. 27863cb34c60Sahrens * Note, if we fail due to already having this guid, 27873cb34c60Sahrens * zfs_receive_one() will take care of it (ie, 27883cb34c60Sahrens * recv_skip() and return 0). 27893cb34c60Sahrens */ 2790a2cdcdd2SPaul Dagnelie error = zfs_receive_impl(hdl, destname, NULL, flags, fd, 2791c99e4bdcSChris Kirby sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd, 27925878fad7SDan McDonald action_handlep, sendsnap); 27933cb34c60Sahrens if (error == ENODATA) { 27943cb34c60Sahrens error = 0; 27953cb34c60Sahrens break; 27963cb34c60Sahrens } 27973cb34c60Sahrens anyerr |= error; 27983cb34c60Sahrens } while (error == 0); 27993cb34c60Sahrens 28003cb34c60Sahrens if (drr->drr_payloadlen != 0 && fromsnap != NULL) { 28013cb34c60Sahrens /* 28023cb34c60Sahrens * Now that we have the fs's they sent us, try the 28033cb34c60Sahrens * renames again. 28043cb34c60Sahrens */ 2805137fa067Sahrens softerr = recv_incremental_replication(hdl, tofs, flags, 2806bd6a198fSTom Erickson stream_nv, stream_avl, NULL); 28073cb34c60Sahrens } 28083cb34c60Sahrens 28093cb34c60Sahrens out: 28103cb34c60Sahrens fsavl_destroy(stream_avl); 28113cb34c60Sahrens nvlist_free(stream_nv); 2812137fa067Sahrens if (softerr) 2813137fa067Sahrens error = -2; 28143cb34c60Sahrens if (anyerr) 28153cb34c60Sahrens error = -1; 28163cb34c60Sahrens return (error); 28173cb34c60Sahrens } 28183cb34c60Sahrens 281992241e0bSTom Erickson static void 282092241e0bSTom Erickson trunc_prop_errs(int truncated) 282192241e0bSTom Erickson { 282292241e0bSTom Erickson ASSERT(truncated != 0); 282392241e0bSTom Erickson 282492241e0bSTom Erickson if (truncated == 1) 282592241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 282692241e0bSTom Erickson "1 more property could not be set\n")); 282792241e0bSTom Erickson else 282892241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 282992241e0bSTom Erickson "%d more properties could not be set\n"), truncated); 283092241e0bSTom Erickson } 283192241e0bSTom Erickson 28323cb34c60Sahrens static int 28333cb34c60Sahrens recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap) 28343cb34c60Sahrens { 28353cb34c60Sahrens dmu_replay_record_t *drr; 2836b5152584SMatthew Ahrens void *buf = zfs_alloc(hdl, SPA_MAXBLOCKSIZE); 28379e69d7d0SLori Alt char errbuf[1024]; 28389e69d7d0SLori Alt 28399e69d7d0SLori Alt (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 28409e69d7d0SLori Alt "cannot receive:")); 28413cb34c60Sahrens 28423cb34c60Sahrens /* XXX would be great to use lseek if possible... */ 28433cb34c60Sahrens drr = buf; 28443cb34c60Sahrens 28453cb34c60Sahrens while (recv_read(hdl, fd, drr, sizeof (dmu_replay_record_t), 28463cb34c60Sahrens byteswap, NULL) == 0) { 28473cb34c60Sahrens if (byteswap) 28483cb34c60Sahrens drr->drr_type = BSWAP_32(drr->drr_type); 28493cb34c60Sahrens 28503cb34c60Sahrens switch (drr->drr_type) { 28513cb34c60Sahrens case DRR_BEGIN: 28529e69d7d0SLori Alt if (drr->drr_payloadlen != 0) { 28539c3fd121SMatthew Ahrens (void) recv_read(hdl, fd, buf, 28549c3fd121SMatthew Ahrens drr->drr_payloadlen, B_FALSE, NULL); 28559e69d7d0SLori Alt } 28563cb34c60Sahrens break; 28573cb34c60Sahrens 28583cb34c60Sahrens case DRR_END: 28593cb34c60Sahrens free(buf); 28603cb34c60Sahrens return (0); 28613cb34c60Sahrens 28623cb34c60Sahrens case DRR_OBJECT: 28633cb34c60Sahrens if (byteswap) { 28643cb34c60Sahrens drr->drr_u.drr_object.drr_bonuslen = 28653cb34c60Sahrens BSWAP_32(drr->drr_u.drr_object. 28663cb34c60Sahrens drr_bonuslen); 28673cb34c60Sahrens } 28683cb34c60Sahrens (void) recv_read(hdl, fd, buf, 28693cb34c60Sahrens P2ROUNDUP(drr->drr_u.drr_object.drr_bonuslen, 8), 28703cb34c60Sahrens B_FALSE, NULL); 28713cb34c60Sahrens break; 28723cb34c60Sahrens 28733cb34c60Sahrens case DRR_WRITE: 28743cb34c60Sahrens if (byteswap) { 28753cb34c60Sahrens drr->drr_u.drr_write.drr_length = 28760069fd67STim Haley BSWAP_64(drr->drr_u.drr_write.drr_length); 28773cb34c60Sahrens } 28783cb34c60Sahrens (void) recv_read(hdl, fd, buf, 28793cb34c60Sahrens drr->drr_u.drr_write.drr_length, B_FALSE, NULL); 28803cb34c60Sahrens break; 28810a586ceaSMark Shellenbaum case DRR_SPILL: 28820a586ceaSMark Shellenbaum if (byteswap) { 28830a586ceaSMark Shellenbaum drr->drr_u.drr_write.drr_length = 28840a586ceaSMark Shellenbaum BSWAP_64(drr->drr_u.drr_spill.drr_length); 28850a586ceaSMark Shellenbaum } 28860a586ceaSMark Shellenbaum (void) recv_read(hdl, fd, buf, 28870a586ceaSMark Shellenbaum drr->drr_u.drr_spill.drr_length, B_FALSE, NULL); 28880a586ceaSMark Shellenbaum break; 28895d7b4d43SMatthew Ahrens case DRR_WRITE_EMBEDDED: 28905d7b4d43SMatthew Ahrens if (byteswap) { 28915d7b4d43SMatthew Ahrens drr->drr_u.drr_write_embedded.drr_psize = 28925d7b4d43SMatthew Ahrens BSWAP_32(drr->drr_u.drr_write_embedded. 28935d7b4d43SMatthew Ahrens drr_psize); 28945d7b4d43SMatthew Ahrens } 28955d7b4d43SMatthew Ahrens (void) recv_read(hdl, fd, buf, 28965d7b4d43SMatthew Ahrens P2ROUNDUP(drr->drr_u.drr_write_embedded.drr_psize, 28975d7b4d43SMatthew Ahrens 8), B_FALSE, NULL); 28985d7b4d43SMatthew Ahrens break; 28999e69d7d0SLori Alt case DRR_WRITE_BYREF: 29003cb34c60Sahrens case DRR_FREEOBJECTS: 29013cb34c60Sahrens case DRR_FREE: 29023cb34c60Sahrens break; 29033cb34c60Sahrens 29043cb34c60Sahrens default: 29059e69d7d0SLori Alt zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29069e69d7d0SLori Alt "invalid record type")); 29079e69d7d0SLori Alt return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 29083cb34c60Sahrens } 29093cb34c60Sahrens } 29103cb34c60Sahrens 29113cb34c60Sahrens free(buf); 29123cb34c60Sahrens return (-1); 29133cb34c60Sahrens } 29143cb34c60Sahrens 29159c3fd121SMatthew Ahrens static void 29169c3fd121SMatthew Ahrens recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap, 29179c3fd121SMatthew Ahrens boolean_t resumable) 29189c3fd121SMatthew Ahrens { 2919*40a5c998SMatthew Ahrens char target_fs[ZFS_MAX_DATASET_NAME_LEN]; 29209c3fd121SMatthew Ahrens 29219c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29229c3fd121SMatthew Ahrens "checksum mismatch or incomplete stream")); 29239c3fd121SMatthew Ahrens 29249c3fd121SMatthew Ahrens if (!resumable) 29259c3fd121SMatthew Ahrens return; 29269c3fd121SMatthew Ahrens (void) strlcpy(target_fs, target_snap, sizeof (target_fs)); 29279c3fd121SMatthew Ahrens *strchr(target_fs, '@') = '\0'; 29289c3fd121SMatthew Ahrens zfs_handle_t *zhp = zfs_open(hdl, target_fs, 29299c3fd121SMatthew Ahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 29309c3fd121SMatthew Ahrens if (zhp == NULL) 29319c3fd121SMatthew Ahrens return; 29329c3fd121SMatthew Ahrens 29339c3fd121SMatthew Ahrens char token_buf[ZFS_MAXPROPLEN]; 29349c3fd121SMatthew Ahrens int error = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, 29359c3fd121SMatthew Ahrens token_buf, sizeof (token_buf), 29369c3fd121SMatthew Ahrens NULL, NULL, 0, B_TRUE); 29379c3fd121SMatthew Ahrens if (error == 0) { 29389c3fd121SMatthew Ahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 29399c3fd121SMatthew Ahrens "checksum mismatch or incomplete stream.\n" 29409c3fd121SMatthew Ahrens "Partially received snapshot is saved.\n" 29419c3fd121SMatthew Ahrens "A resuming stream can be generated on the sending " 29429c3fd121SMatthew Ahrens "system by running:\n" 29439c3fd121SMatthew Ahrens " zfs send -t %s"), 29449c3fd121SMatthew Ahrens token_buf); 29459c3fd121SMatthew Ahrens } 29469c3fd121SMatthew Ahrens zfs_close(zhp); 29479c3fd121SMatthew Ahrens } 29489c3fd121SMatthew Ahrens 29493cb34c60Sahrens /* 29503cb34c60Sahrens * Restores a backup of tosnap from the file descriptor specified by infd. 29513cb34c60Sahrens */ 29523cb34c60Sahrens static int 29533cb34c60Sahrens zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, 2954a2cdcdd2SPaul Dagnelie const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr, 2955a2cdcdd2SPaul Dagnelie dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv, 2956a2cdcdd2SPaul Dagnelie avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd, 29575878fad7SDan McDonald uint64_t *action_handlep, const char *finalsnap) 29583cb34c60Sahrens { 29593cb34c60Sahrens zfs_cmd_t zc = { 0 }; 29603cb34c60Sahrens time_t begin_time; 2961bd6a198fSTom Erickson int ioctl_err, ioctl_errno, err; 29623cb34c60Sahrens char *cp; 29633cb34c60Sahrens struct drr_begin *drrb = &drr->drr_u.drr_begin; 29643cb34c60Sahrens char errbuf[1024]; 296592241e0bSTom Erickson char prop_errbuf[1024]; 2966bd6a198fSTom Erickson const char *chopprefix; 29673cb34c60Sahrens boolean_t newfs = B_FALSE; 29683cb34c60Sahrens boolean_t stream_wantsnewfs; 29693cb34c60Sahrens uint64_t parent_snapguid = 0; 29703cb34c60Sahrens prop_changelist_t *clp = NULL; 2971bb0ade09Sahrens nvlist_t *snapprops_nvlist = NULL; 297292241e0bSTom Erickson zprop_errflags_t prop_errflags; 2973bd6a198fSTom Erickson boolean_t recursive; 29745878fad7SDan McDonald char *snapname = NULL; 29753cb34c60Sahrens 29763cb34c60Sahrens begin_time = time(NULL); 29773cb34c60Sahrens 29783cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 29793cb34c60Sahrens "cannot receive")); 29803cb34c60Sahrens 2981bd6a198fSTom Erickson recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") == 2982bd6a198fSTom Erickson ENOENT); 2983bd6a198fSTom Erickson 29843cb34c60Sahrens if (stream_avl != NULL) { 2985bb0ade09Sahrens nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid, 2986bb0ade09Sahrens &snapname); 29873cb34c60Sahrens nvlist_t *props; 298800954d7bSahl int ret; 29893cb34c60Sahrens 29903cb34c60Sahrens (void) nvlist_lookup_uint64(fs, "parentfromsnap", 29913cb34c60Sahrens &parent_snapguid); 29923cb34c60Sahrens err = nvlist_lookup_nvlist(fs, "props", &props); 29933cb34c60Sahrens if (err) 29943cb34c60Sahrens VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0)); 299500954d7bSahl 299619b94df9SMatthew Ahrens if (flags->canmountoff) { 29973cb34c60Sahrens VERIFY(0 == nvlist_add_uint64(props, 29983cb34c60Sahrens zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0)); 29993cb34c60Sahrens } 300000954d7bSahl ret = zcmd_write_src_nvlist(hdl, &zc, props); 30013cb34c60Sahrens if (err) 30023cb34c60Sahrens nvlist_free(props); 300300954d7bSahl 3004bb0ade09Sahrens if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) { 3005bb0ade09Sahrens VERIFY(0 == nvlist_lookup_nvlist(props, 3006bb0ade09Sahrens snapname, &snapprops_nvlist)); 3007bb0ade09Sahrens } 3008bb0ade09Sahrens 300900954d7bSahl if (ret != 0) 301000954d7bSahl return (-1); 30113cb34c60Sahrens } 30123cb34c60Sahrens 3013bd6a198fSTom Erickson cp = NULL; 3014bd6a198fSTom Erickson 30153cb34c60Sahrens /* 30163cb34c60Sahrens * Determine how much of the snapshot name stored in the stream 30173cb34c60Sahrens * we are going to tack on to the name they specified on the 30183cb34c60Sahrens * command line, and how much we are going to chop off. 30193cb34c60Sahrens * 30203cb34c60Sahrens * If they specified a snapshot, chop the entire name stored in 30213cb34c60Sahrens * the stream. 30223cb34c60Sahrens */ 302319b94df9SMatthew Ahrens if (flags->istail) { 30243cb34c60Sahrens /* 3025bd6a198fSTom Erickson * A filesystem was specified with -e. We want to tack on only 3026bd6a198fSTom Erickson * the tail of the sent snapshot path. 30273cb34c60Sahrens */ 30283cb34c60Sahrens if (strchr(tosnap, '@')) { 30293cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " 3030bd6a198fSTom Erickson "argument - snapshot not allowed with -e")); 30313cb34c60Sahrens return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 30323cb34c60Sahrens } 3033bd6a198fSTom Erickson 3034bd6a198fSTom Erickson chopprefix = strrchr(sendfs, '/'); 3035bd6a198fSTom Erickson 3036bd6a198fSTom Erickson if (chopprefix == NULL) { 3037bd6a198fSTom Erickson /* 3038bd6a198fSTom Erickson * The tail is the poolname, so we need to 3039bd6a198fSTom Erickson * prepend a path separator. 3040bd6a198fSTom Erickson */ 3041bd6a198fSTom Erickson int len = strlen(drrb->drr_toname); 3042bd6a198fSTom Erickson cp = malloc(len + 2); 3043bd6a198fSTom Erickson cp[0] = '/'; 3044bd6a198fSTom Erickson (void) strcpy(&cp[1], drrb->drr_toname); 3045bd6a198fSTom Erickson chopprefix = cp; 3046bd6a198fSTom Erickson } else { 3047bd6a198fSTom Erickson chopprefix = drrb->drr_toname + (chopprefix - sendfs); 3048bd6a198fSTom Erickson } 304919b94df9SMatthew Ahrens } else if (flags->isprefix) { 3050bd6a198fSTom Erickson /* 3051bd6a198fSTom Erickson * A filesystem was specified with -d. We want to tack on 3052bd6a198fSTom Erickson * everything but the first element of the sent snapshot path 3053bd6a198fSTom Erickson * (all but the pool name). 3054bd6a198fSTom Erickson */ 3055bd6a198fSTom Erickson if (strchr(tosnap, '@')) { 3056bd6a198fSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " 3057bd6a198fSTom Erickson "argument - snapshot not allowed with -d")); 3058bd6a198fSTom Erickson return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 3059bd6a198fSTom Erickson } 3060bd6a198fSTom Erickson 3061bd6a198fSTom Erickson chopprefix = strchr(drrb->drr_toname, '/'); 3062bd6a198fSTom Erickson if (chopprefix == NULL) 3063bd6a198fSTom Erickson chopprefix = strchr(drrb->drr_toname, '@'); 30643cb34c60Sahrens } else if (strchr(tosnap, '@') == NULL) { 30653cb34c60Sahrens /* 3066bd6a198fSTom Erickson * If a filesystem was specified without -d or -e, we want to 3067bd6a198fSTom Erickson * tack on everything after the fs specified by 'zfs send'. 30683cb34c60Sahrens */ 3069bd6a198fSTom Erickson chopprefix = drrb->drr_toname + strlen(sendfs); 3070bd6a198fSTom Erickson } else { 3071bd6a198fSTom Erickson /* A snapshot was specified as an exact path (no -d or -e). */ 3072bd6a198fSTom Erickson if (recursive) { 3073bd6a198fSTom Erickson zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3074bd6a198fSTom Erickson "cannot specify snapshot name for multi-snapshot " 3075bd6a198fSTom Erickson "stream")); 3076bd6a198fSTom Erickson return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 30773cb34c60Sahrens } 3078bd6a198fSTom Erickson chopprefix = drrb->drr_toname + strlen(drrb->drr_toname); 3079bd6a198fSTom Erickson } 3080bd6a198fSTom Erickson 3081bd6a198fSTom Erickson ASSERT(strstr(drrb->drr_toname, sendfs) == drrb->drr_toname); 3082bd6a198fSTom Erickson ASSERT(chopprefix > drrb->drr_toname); 3083bd6a198fSTom Erickson ASSERT(chopprefix <= drrb->drr_toname + strlen(drrb->drr_toname)); 3084bd6a198fSTom Erickson ASSERT(chopprefix[0] == '/' || chopprefix[0] == '@' || 3085bd6a198fSTom Erickson chopprefix[0] == '\0'); 30863cb34c60Sahrens 30873cb34c60Sahrens /* 30883cb34c60Sahrens * Determine name of destination snapshot, store in zc_value. 30893cb34c60Sahrens */ 30903cb34c60Sahrens (void) strcpy(zc.zc_value, tosnap); 3091bd6a198fSTom Erickson (void) strncat(zc.zc_value, chopprefix, sizeof (zc.zc_value)); 3092bd6a198fSTom Erickson free(cp); 309300954d7bSahl if (!zfs_name_valid(zc.zc_value, ZFS_TYPE_SNAPSHOT)) { 309400954d7bSahl zcmd_free_nvlists(&zc); 30953cb34c60Sahrens return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); 309600954d7bSahl } 30973cb34c60Sahrens 30983cb34c60Sahrens /* 30993cb34c60Sahrens * Determine the name of the origin snapshot, store in zc_string. 31003cb34c60Sahrens */ 31013cb34c60Sahrens if (drrb->drr_flags & DRR_FLAG_CLONE) { 310219b94df9SMatthew Ahrens if (guid_to_name(hdl, zc.zc_value, 31039c3fd121SMatthew Ahrens drrb->drr_fromguid, B_FALSE, zc.zc_string) != 0) { 310400954d7bSahl zcmd_free_nvlists(&zc); 31053cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31063cb34c60Sahrens "local origin for clone %s does not exist"), 31073cb34c60Sahrens zc.zc_value); 31083cb34c60Sahrens return (zfs_error(hdl, EZFS_NOENT, errbuf)); 31093cb34c60Sahrens } 311019b94df9SMatthew Ahrens if (flags->verbose) 31113cb34c60Sahrens (void) printf("found clone origin %s\n", zc.zc_string); 3112a2cdcdd2SPaul Dagnelie } else if (originsnap) { 3113*40a5c998SMatthew Ahrens (void) strncpy(zc.zc_string, originsnap, sizeof (zc.zc_string)); 3114a2cdcdd2SPaul Dagnelie if (flags->verbose) 3115a2cdcdd2SPaul Dagnelie (void) printf("using provided clone origin %s\n", 3116a2cdcdd2SPaul Dagnelie zc.zc_string); 31173cb34c60Sahrens } 31183cb34c60Sahrens 31199c3fd121SMatthew Ahrens boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) & 31209c3fd121SMatthew Ahrens DMU_BACKUP_FEATURE_RESUMING; 31213cb34c60Sahrens stream_wantsnewfs = (drrb->drr_fromguid == NULL || 31229c3fd121SMatthew Ahrens (drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming; 31233cb34c60Sahrens 31243cb34c60Sahrens if (stream_wantsnewfs) { 31253cb34c60Sahrens /* 31263cb34c60Sahrens * if the parent fs does not exist, look for it based on 31273cb34c60Sahrens * the parent snap GUID 31283cb34c60Sahrens */ 31293cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31303cb34c60Sahrens "cannot receive new filesystem stream")); 31313cb34c60Sahrens 31323cb34c60Sahrens (void) strcpy(zc.zc_name, zc.zc_value); 31333cb34c60Sahrens cp = strrchr(zc.zc_name, '/'); 31343cb34c60Sahrens if (cp) 31353cb34c60Sahrens *cp = '\0'; 31363cb34c60Sahrens if (cp && 31373cb34c60Sahrens !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 3138*40a5c998SMatthew Ahrens char suffix[ZFS_MAX_DATASET_NAME_LEN]; 31393cb34c60Sahrens (void) strcpy(suffix, strrchr(zc.zc_value, '/')); 314019b94df9SMatthew Ahrens if (guid_to_name(hdl, zc.zc_name, parent_snapguid, 31419c3fd121SMatthew Ahrens B_FALSE, zc.zc_value) == 0) { 31423cb34c60Sahrens *strchr(zc.zc_value, '@') = '\0'; 31433cb34c60Sahrens (void) strcat(zc.zc_value, suffix); 31443cb34c60Sahrens } 31453cb34c60Sahrens } 31463cb34c60Sahrens } else { 31473cb34c60Sahrens /* 31483cb34c60Sahrens * if the fs does not exist, look for it based on the 31493cb34c60Sahrens * fromsnap GUID 31503cb34c60Sahrens */ 31513cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 31523cb34c60Sahrens "cannot receive incremental stream")); 31533cb34c60Sahrens 31543cb34c60Sahrens (void) strcpy(zc.zc_name, zc.zc_value); 31553cb34c60Sahrens *strchr(zc.zc_name, '@') = '\0'; 31563cb34c60Sahrens 315783d7f9feSTom Erickson /* 315883d7f9feSTom Erickson * If the exact receive path was specified and this is the 315983d7f9feSTom Erickson * topmost path in the stream, then if the fs does not exist we 316083d7f9feSTom Erickson * should look no further. 316183d7f9feSTom Erickson */ 316219b94df9SMatthew Ahrens if ((flags->isprefix || (*(chopprefix = drrb->drr_toname + 316383d7f9feSTom Erickson strlen(sendfs)) != '\0' && *chopprefix != '@')) && 316483d7f9feSTom Erickson !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 3165*40a5c998SMatthew Ahrens char snap[ZFS_MAX_DATASET_NAME_LEN]; 31663cb34c60Sahrens (void) strcpy(snap, strchr(zc.zc_value, '@')); 316719b94df9SMatthew Ahrens if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid, 31689c3fd121SMatthew Ahrens B_FALSE, zc.zc_value) == 0) { 31693cb34c60Sahrens *strchr(zc.zc_value, '@') = '\0'; 31703cb34c60Sahrens (void) strcat(zc.zc_value, snap); 31713cb34c60Sahrens } 31723cb34c60Sahrens } 31733cb34c60Sahrens } 31743cb34c60Sahrens 31753cb34c60Sahrens (void) strcpy(zc.zc_name, zc.zc_value); 31763cb34c60Sahrens *strchr(zc.zc_name, '@') = '\0'; 31773cb34c60Sahrens 31783cb34c60Sahrens if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { 31793cb34c60Sahrens zfs_handle_t *zhp; 3180bd6a198fSTom Erickson 31813cb34c60Sahrens /* 31829c3fd121SMatthew Ahrens * Destination fs exists. It must be one of these cases: 31839c3fd121SMatthew Ahrens * - an incremental send stream 31849c3fd121SMatthew Ahrens * - the stream specifies a new fs (full stream or clone) 31859c3fd121SMatthew Ahrens * and they want us to blow away the existing fs (and 31869c3fd121SMatthew Ahrens * have therefore specified -F and removed any snapshots) 31879c3fd121SMatthew Ahrens * - we are resuming a failed receive. 31883cb34c60Sahrens */ 31893cb34c60Sahrens if (stream_wantsnewfs) { 319019b94df9SMatthew Ahrens if (!flags->force) { 319100954d7bSahl zcmd_free_nvlists(&zc); 31923cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 31933cb34c60Sahrens "destination '%s' exists\n" 31943cb34c60Sahrens "must specify -F to overwrite it"), 31953cb34c60Sahrens zc.zc_name); 31963cb34c60Sahrens return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 31973cb34c60Sahrens } 31983cb34c60Sahrens if (ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, 31993cb34c60Sahrens &zc) == 0) { 320000954d7bSahl zcmd_free_nvlists(&zc); 32013cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 32023cb34c60Sahrens "destination has snapshots (eg. %s)\n" 32033cb34c60Sahrens "must destroy them to overwrite it"), 32043cb34c60Sahrens zc.zc_name); 32053cb34c60Sahrens return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 32063cb34c60Sahrens } 32073cb34c60Sahrens } 32083cb34c60Sahrens 320900954d7bSahl if ((zhp = zfs_open(hdl, zc.zc_name, 321000954d7bSahl ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) { 321100954d7bSahl zcmd_free_nvlists(&zc); 32123cb34c60Sahrens return (-1); 321300954d7bSahl } 321400954d7bSahl 32153cb34c60Sahrens if (stream_wantsnewfs && 32163cb34c60Sahrens zhp->zfs_dmustats.dds_origin[0]) { 321700954d7bSahl zcmd_free_nvlists(&zc); 32183cb34c60Sahrens zfs_close(zhp); 32193cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 32203cb34c60Sahrens "destination '%s' is a clone\n" 32213cb34c60Sahrens "must destroy it to overwrite it"), 32223cb34c60Sahrens zc.zc_name); 32233cb34c60Sahrens return (zfs_error(hdl, EZFS_EXISTS, errbuf)); 32243cb34c60Sahrens } 32253cb34c60Sahrens 322619b94df9SMatthew Ahrens if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM && 32273cb34c60Sahrens stream_wantsnewfs) { 32283cb34c60Sahrens /* We can't do online recv in this case */ 32290069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0); 323000954d7bSahl if (clp == NULL) { 323188bb18d2SLori Alt zfs_close(zhp); 323200954d7bSahl zcmd_free_nvlists(&zc); 32333cb34c60Sahrens return (-1); 323400954d7bSahl } 32353cb34c60Sahrens if (changelist_prefix(clp) != 0) { 32363cb34c60Sahrens changelist_free(clp); 323788bb18d2SLori Alt zfs_close(zhp); 323800954d7bSahl zcmd_free_nvlists(&zc); 32393cb34c60Sahrens return (-1); 32403cb34c60Sahrens } 32413cb34c60Sahrens } 32429c3fd121SMatthew Ahrens 32439c3fd121SMatthew Ahrens /* 32449c3fd121SMatthew Ahrens * If we are resuming a newfs, set newfs here so that we will 32459c3fd121SMatthew Ahrens * mount it if the recv succeeds this time. We can tell 32469c3fd121SMatthew Ahrens * that it was a newfs on the first recv because the fs 32479c3fd121SMatthew Ahrens * itself will be inconsistent (if the fs existed when we 32489c3fd121SMatthew Ahrens * did the first recv, we would have received it into 32499c3fd121SMatthew Ahrens * .../%recv). 32509c3fd121SMatthew Ahrens */ 32519c3fd121SMatthew Ahrens if (resuming && zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT)) 32529c3fd121SMatthew Ahrens newfs = B_TRUE; 32539c3fd121SMatthew Ahrens 32543cb34c60Sahrens zfs_close(zhp); 32553cb34c60Sahrens } else { 32563cb34c60Sahrens /* 325700954d7bSahl * Destination filesystem does not exist. Therefore we better 325800954d7bSahl * be creating a new filesystem (either from a full backup, or 325900954d7bSahl * a clone). It would therefore be invalid if the user 326000954d7bSahl * specified only the pool name (i.e. if the destination name 326100954d7bSahl * contained no slash character). 32623cb34c60Sahrens */ 326300954d7bSahl if (!stream_wantsnewfs || 326400954d7bSahl (cp = strrchr(zc.zc_name, '/')) == NULL) { 326500954d7bSahl zcmd_free_nvlists(&zc); 32663cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 32673cb34c60Sahrens "destination '%s' does not exist"), zc.zc_name); 32683cb34c60Sahrens return (zfs_error(hdl, EZFS_NOENT, errbuf)); 32693cb34c60Sahrens } 32703cb34c60Sahrens 327100954d7bSahl /* 327200954d7bSahl * Trim off the final dataset component so we perform the 327300954d7bSahl * recvbackup ioctl to the filesystems's parent. 327400954d7bSahl */ 327500954d7bSahl *cp = '\0'; 32763cb34c60Sahrens 327719b94df9SMatthew Ahrens if (flags->isprefix && !flags->istail && !flags->dryrun && 327800954d7bSahl create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) { 327900954d7bSahl zcmd_free_nvlists(&zc); 328000954d7bSahl return (zfs_error(hdl, EZFS_BADRESTORE, errbuf)); 32813cb34c60Sahrens } 32823cb34c60Sahrens 32833cb34c60Sahrens newfs = B_TRUE; 32843cb34c60Sahrens } 32853cb34c60Sahrens 32869c3fd121SMatthew Ahrens zc.zc_begin_record = *drr_noswap; 32873cb34c60Sahrens zc.zc_cookie = infd; 328819b94df9SMatthew Ahrens zc.zc_guid = flags->force; 32899c3fd121SMatthew Ahrens zc.zc_resumable = flags->resumable; 329019b94df9SMatthew Ahrens if (flags->verbose) { 32913cb34c60Sahrens (void) printf("%s %s stream of %s into %s\n", 329219b94df9SMatthew Ahrens flags->dryrun ? "would receive" : "receiving", 32933cb34c60Sahrens drrb->drr_fromguid ? "incremental" : "full", 32943cb34c60Sahrens drrb->drr_toname, zc.zc_value); 32953cb34c60Sahrens (void) fflush(stdout); 32963cb34c60Sahrens } 32973cb34c60Sahrens 329819b94df9SMatthew Ahrens if (flags->dryrun) { 329900954d7bSahl zcmd_free_nvlists(&zc); 330019b94df9SMatthew Ahrens return (recv_skip(hdl, infd, flags->byteswap)); 330100954d7bSahl } 33023cb34c60Sahrens 330392241e0bSTom Erickson zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf; 330492241e0bSTom Erickson zc.zc_nvlist_dst_size = sizeof (prop_errbuf); 3305c99e4bdcSChris Kirby zc.zc_cleanup_fd = cleanup_fd; 3306c99e4bdcSChris Kirby zc.zc_action_handle = *action_handlep; 330792241e0bSTom Erickson 33083cb34c60Sahrens err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc); 33093cb34c60Sahrens ioctl_errno = errno; 331092241e0bSTom Erickson prop_errflags = (zprop_errflags_t)zc.zc_obj; 331192241e0bSTom Erickson 331292241e0bSTom Erickson if (err == 0) { 331392241e0bSTom Erickson nvlist_t *prop_errors; 331492241e0bSTom Erickson VERIFY(0 == nvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst, 331592241e0bSTom Erickson zc.zc_nvlist_dst_size, &prop_errors, 0)); 331692241e0bSTom Erickson 331792241e0bSTom Erickson nvpair_t *prop_err = NULL; 331892241e0bSTom Erickson 331992241e0bSTom Erickson while ((prop_err = nvlist_next_nvpair(prop_errors, 332092241e0bSTom Erickson prop_err)) != NULL) { 332192241e0bSTom Erickson char tbuf[1024]; 332292241e0bSTom Erickson zfs_prop_t prop; 332392241e0bSTom Erickson int intval; 332492241e0bSTom Erickson 332592241e0bSTom Erickson prop = zfs_name_to_prop(nvpair_name(prop_err)); 332692241e0bSTom Erickson (void) nvpair_value_int32(prop_err, &intval); 332792241e0bSTom Erickson if (strcmp(nvpair_name(prop_err), 332892241e0bSTom Erickson ZPROP_N_MORE_ERRORS) == 0) { 332992241e0bSTom Erickson trunc_prop_errs(intval); 333092241e0bSTom Erickson break; 33315878fad7SDan McDonald } else if (snapname == NULL || finalsnap == NULL || 33325878fad7SDan McDonald strcmp(finalsnap, snapname) == 0 || 33335878fad7SDan McDonald strcmp(nvpair_name(prop_err), 33345878fad7SDan McDonald zfs_prop_to_name(ZFS_PROP_REFQUOTA)) != 0) { 33355878fad7SDan McDonald /* 33365878fad7SDan McDonald * Skip the special case of, for example, 33375878fad7SDan McDonald * "refquota", errors on intermediate 33385878fad7SDan McDonald * snapshots leading up to a final one. 33395878fad7SDan McDonald * That's why we have all of the checks above. 33405878fad7SDan McDonald * 33415878fad7SDan McDonald * See zfs_ioctl.c's extract_delay_props() for 33425878fad7SDan McDonald * a list of props which can fail on 33435878fad7SDan McDonald * intermediate snapshots, but shouldn't 33445878fad7SDan McDonald * affect the overall receive. 33455878fad7SDan McDonald */ 334692241e0bSTom Erickson (void) snprintf(tbuf, sizeof (tbuf), 334792241e0bSTom Erickson dgettext(TEXT_DOMAIN, 334892241e0bSTom Erickson "cannot receive %s property on %s"), 334992241e0bSTom Erickson nvpair_name(prop_err), zc.zc_name); 335092241e0bSTom Erickson zfs_setprop_error(hdl, prop, intval, tbuf); 335192241e0bSTom Erickson } 335292241e0bSTom Erickson } 335392241e0bSTom Erickson nvlist_free(prop_errors); 335492241e0bSTom Erickson } 335592241e0bSTom Erickson 335692241e0bSTom Erickson zc.zc_nvlist_dst = 0; 335792241e0bSTom Erickson zc.zc_nvlist_dst_size = 0; 3358bb0ade09Sahrens zcmd_free_nvlists(&zc); 3359bb0ade09Sahrens 3360bb0ade09Sahrens if (err == 0 && snapprops_nvlist) { 3361bb0ade09Sahrens zfs_cmd_t zc2 = { 0 }; 3362bb0ade09Sahrens 3363b6c10d80Sahrens (void) strcpy(zc2.zc_name, zc.zc_value); 336492241e0bSTom Erickson zc2.zc_cookie = B_TRUE; /* received */ 3365bb0ade09Sahrens if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) { 3366bb0ade09Sahrens (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2); 3367bb0ade09Sahrens zcmd_free_nvlists(&zc2); 3368bb0ade09Sahrens } 3369bb0ade09Sahrens } 3370bb0ade09Sahrens 3371bd6a198fSTom Erickson if (err && (ioctl_errno == ENOENT || ioctl_errno == EEXIST)) { 33723cb34c60Sahrens /* 33733cb34c60Sahrens * It may be that this snapshot already exists, 33743cb34c60Sahrens * in which case we want to consume & ignore it 33753cb34c60Sahrens * rather than failing. 33763cb34c60Sahrens */ 33773cb34c60Sahrens avl_tree_t *local_avl; 33783cb34c60Sahrens nvlist_t *local_nv, *fs; 3379bd6a198fSTom Erickson cp = strchr(zc.zc_value, '@'); 33803cb34c60Sahrens 33813cb34c60Sahrens /* 33823cb34c60Sahrens * XXX Do this faster by just iterating over snaps in 33833cb34c60Sahrens * this fs. Also if zc_value does not exist, we will 33843cb34c60Sahrens * get a strange "does not exist" error message. 33853cb34c60Sahrens */ 33863cb34c60Sahrens *cp = '\0'; 338792241e0bSTom Erickson if (gather_nvlist(hdl, zc.zc_value, NULL, NULL, B_FALSE, 33883cb34c60Sahrens &local_nv, &local_avl) == 0) { 33893cb34c60Sahrens *cp = '@'; 33903cb34c60Sahrens fs = fsavl_find(local_avl, drrb->drr_toguid, NULL); 33913cb34c60Sahrens fsavl_destroy(local_avl); 33923cb34c60Sahrens nvlist_free(local_nv); 33933cb34c60Sahrens 33943cb34c60Sahrens if (fs != NULL) { 339519b94df9SMatthew Ahrens if (flags->verbose) { 33963cb34c60Sahrens (void) printf("snap %s already exists; " 33973cb34c60Sahrens "ignoring\n", zc.zc_value); 33983cb34c60Sahrens } 339992241e0bSTom Erickson err = ioctl_err = recv_skip(hdl, infd, 340019b94df9SMatthew Ahrens flags->byteswap); 34013cb34c60Sahrens } 34023cb34c60Sahrens } 34033cb34c60Sahrens *cp = '@'; 34043cb34c60Sahrens } 34053cb34c60Sahrens 34063cb34c60Sahrens if (ioctl_err != 0) { 34073cb34c60Sahrens switch (ioctl_errno) { 34083cb34c60Sahrens case ENODEV: 34093cb34c60Sahrens cp = strchr(zc.zc_value, '@'); 34103cb34c60Sahrens *cp = '\0'; 34113cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34123cb34c60Sahrens "most recent snapshot of %s does not\n" 34133cb34c60Sahrens "match incremental source"), zc.zc_value); 34143cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf); 34153cb34c60Sahrens *cp = '@'; 34163cb34c60Sahrens break; 34173cb34c60Sahrens case ETXTBSY: 34183cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34193cb34c60Sahrens "destination %s has been modified\n" 34203cb34c60Sahrens "since most recent snapshot"), zc.zc_name); 34213cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf); 34223cb34c60Sahrens break; 34233cb34c60Sahrens case EEXIST: 34243cb34c60Sahrens cp = strchr(zc.zc_value, '@'); 34253cb34c60Sahrens if (newfs) { 34263cb34c60Sahrens /* it's the containing fs that exists */ 34273cb34c60Sahrens *cp = '\0'; 34283cb34c60Sahrens } 34293cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 34303cb34c60Sahrens "destination already exists")); 34313cb34c60Sahrens (void) zfs_error_fmt(hdl, EZFS_EXISTS, 34323cb34c60Sahrens dgettext(TEXT_DOMAIN, "cannot restore to %s"), 34333cb34c60Sahrens zc.zc_value); 34343cb34c60Sahrens *cp = '@'; 34353cb34c60Sahrens break; 34363cb34c60Sahrens case EINVAL: 34373cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); 34383cb34c60Sahrens break; 34393cb34c60Sahrens case ECKSUM: 34409c3fd121SMatthew Ahrens recv_ecksum_set_aux(hdl, zc.zc_value, flags->resumable); 34413cb34c60Sahrens (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); 34423cb34c60Sahrens break; 3443dc7cd546SMark Shellenbaum case ENOTSUP: 3444dc7cd546SMark Shellenbaum zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 3445dc7cd546SMark Shellenbaum "pool must be upgraded to receive this stream.")); 3446dc7cd546SMark Shellenbaum (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); 3447dc7cd546SMark Shellenbaum break; 344837f8ae65SJohn Harres case EDQUOT: 344937f8ae65SJohn Harres zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 345037f8ae65SJohn Harres "destination %s space quota exceeded"), zc.zc_name); 345119b94df9SMatthew Ahrens (void) zfs_error(hdl, EZFS_NOSPC, errbuf); 345237f8ae65SJohn Harres break; 34533cb34c60Sahrens default: 34543cb34c60Sahrens (void) zfs_standard_error(hdl, ioctl_errno, errbuf); 34553cb34c60Sahrens } 34563cb34c60Sahrens } 34573cb34c60Sahrens 34583cb34c60Sahrens /* 3459681d9761SEric Taylor * Mount the target filesystem (if created). Also mount any 3460681d9761SEric Taylor * children of the target filesystem if we did a replication 3461681d9761SEric Taylor * receive (indicated by stream_avl being non-NULL). 34623cb34c60Sahrens */ 34633cb34c60Sahrens cp = strchr(zc.zc_value, '@'); 34643cb34c60Sahrens if (cp && (ioctl_err == 0 || !newfs)) { 34653cb34c60Sahrens zfs_handle_t *h; 34663cb34c60Sahrens 34673cb34c60Sahrens *cp = '\0'; 34683cb34c60Sahrens h = zfs_open(hdl, zc.zc_value, 34693cb34c60Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 347004bb726eSahl if (h != NULL) { 34713cb34c60Sahrens if (h->zfs_type == ZFS_TYPE_VOLUME) { 34720069fd67STim Haley *cp = '@'; 347388bb18d2SLori Alt } else if (newfs || stream_avl) { 34740069fd67STim Haley /* 34750069fd67STim Haley * Track the first/top of hierarchy fs, 34760069fd67STim Haley * for mounting and sharing later. 34770069fd67STim Haley */ 34780069fd67STim Haley if (top_zfs && *top_zfs == NULL) 34790069fd67STim Haley *top_zfs = zfs_strdup(hdl, zc.zc_value); 34803cb34c60Sahrens } 34813cb34c60Sahrens zfs_close(h); 34823cb34c60Sahrens } 34830069fd67STim Haley *cp = '@'; 34843cb34c60Sahrens } 34853cb34c60Sahrens 34863cb34c60Sahrens if (clp) { 34873cb34c60Sahrens err |= changelist_postfix(clp); 34883cb34c60Sahrens changelist_free(clp); 34893cb34c60Sahrens } 34903cb34c60Sahrens 349192241e0bSTom Erickson if (prop_errflags & ZPROP_ERR_NOCLEAR) { 349292241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: " 349392241e0bSTom Erickson "failed to clear unreceived properties on %s"), 349492241e0bSTom Erickson zc.zc_name); 349592241e0bSTom Erickson (void) fprintf(stderr, "\n"); 349692241e0bSTom Erickson } 349792241e0bSTom Erickson if (prop_errflags & ZPROP_ERR_NORESTORE) { 349892241e0bSTom Erickson (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: " 349992241e0bSTom Erickson "failed to restore original properties on %s"), 350092241e0bSTom Erickson zc.zc_name); 350192241e0bSTom Erickson (void) fprintf(stderr, "\n"); 350292241e0bSTom Erickson } 350392241e0bSTom Erickson 35043cb34c60Sahrens if (err || ioctl_err) 35053cb34c60Sahrens return (-1); 35063cb34c60Sahrens 3507c99e4bdcSChris Kirby *action_handlep = zc.zc_action_handle; 3508c99e4bdcSChris Kirby 350919b94df9SMatthew Ahrens if (flags->verbose) { 35103cb34c60Sahrens char buf1[64]; 35113cb34c60Sahrens char buf2[64]; 35123cb34c60Sahrens uint64_t bytes = zc.zc_cookie; 35133cb34c60Sahrens time_t delta = time(NULL) - begin_time; 35143cb34c60Sahrens if (delta == 0) 35153cb34c60Sahrens delta = 1; 35163cb34c60Sahrens zfs_nicenum(bytes, buf1, sizeof (buf1)); 35173cb34c60Sahrens zfs_nicenum(bytes/delta, buf2, sizeof (buf1)); 35183cb34c60Sahrens 35193cb34c60Sahrens (void) printf("received %sB stream in %lu seconds (%sB/sec)\n", 35203cb34c60Sahrens buf1, delta, buf2); 35213cb34c60Sahrens } 35223cb34c60Sahrens 35233cb34c60Sahrens return (0); 35243cb34c60Sahrens } 35253cb34c60Sahrens 35260069fd67STim Haley static int 3527a2cdcdd2SPaul Dagnelie zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, 3528a2cdcdd2SPaul Dagnelie const char *originsnap, recvflags_t *flags, int infd, const char *sendfs, 3529a2cdcdd2SPaul Dagnelie nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd, 35305878fad7SDan McDonald uint64_t *action_handlep, const char *finalsnap) 35313cb34c60Sahrens { 35323cb34c60Sahrens int err; 35333cb34c60Sahrens dmu_replay_record_t drr, drr_noswap; 35343cb34c60Sahrens struct drr_begin *drrb = &drr.drr_u.drr_begin; 35353cb34c60Sahrens char errbuf[1024]; 35363cb34c60Sahrens zio_cksum_t zcksum = { 0 }; 35379e69d7d0SLori Alt uint64_t featureflags; 35389e69d7d0SLori Alt int hdrtype; 35393cb34c60Sahrens 35403cb34c60Sahrens (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, 35413cb34c60Sahrens "cannot receive")); 35423cb34c60Sahrens 354319b94df9SMatthew Ahrens if (flags->isprefix && 35443cb34c60Sahrens !zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) { 35453cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs " 35463cb34c60Sahrens "(%s) does not exist"), tosnap); 35473cb34c60Sahrens return (zfs_error(hdl, EZFS_NOENT, errbuf)); 35483cb34c60Sahrens } 3549a2cdcdd2SPaul Dagnelie if (originsnap && 3550a2cdcdd2SPaul Dagnelie !zfs_dataset_exists(hdl, originsnap, ZFS_TYPE_DATASET)) { 3551a2cdcdd2SPaul Dagnelie zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified origin fs " 3552a2cdcdd2SPaul Dagnelie "(%s) does not exist"), originsnap); 3553a2cdcdd2SPaul Dagnelie return (zfs_error(hdl, EZFS_NOENT, errbuf)); 3554a2cdcdd2SPaul Dagnelie } 35553cb34c60Sahrens 35563cb34c60Sahrens /* read in the BEGIN record */ 35573cb34c60Sahrens if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE, 35583cb34c60Sahrens &zcksum))) 35593cb34c60Sahrens return (err); 35603cb34c60Sahrens 35613cb34c60Sahrens if (drr.drr_type == DRR_END || drr.drr_type == BSWAP_32(DRR_END)) { 35623cb34c60Sahrens /* It's the double end record at the end of a package */ 35633cb34c60Sahrens return (ENODATA); 35643cb34c60Sahrens } 35653cb34c60Sahrens 35663cb34c60Sahrens /* the kernel needs the non-byteswapped begin record */ 35673cb34c60Sahrens drr_noswap = drr; 35683cb34c60Sahrens 356919b94df9SMatthew Ahrens flags->byteswap = B_FALSE; 35703cb34c60Sahrens if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { 35713cb34c60Sahrens /* 35723cb34c60Sahrens * We computed the checksum in the wrong byteorder in 35733cb34c60Sahrens * recv_read() above; do it again correctly. 35743cb34c60Sahrens */ 35753cb34c60Sahrens bzero(&zcksum, sizeof (zio_cksum_t)); 35763cb34c60Sahrens fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum); 357719b94df9SMatthew Ahrens flags->byteswap = B_TRUE; 35783cb34c60Sahrens 35793cb34c60Sahrens drr.drr_type = BSWAP_32(drr.drr_type); 35803cb34c60Sahrens drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen); 35813cb34c60Sahrens drrb->drr_magic = BSWAP_64(drrb->drr_magic); 35829e69d7d0SLori Alt drrb->drr_versioninfo = BSWAP_64(drrb->drr_versioninfo); 35833cb34c60Sahrens drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time); 35843cb34c60Sahrens drrb->drr_type = BSWAP_32(drrb->drr_type); 35853cb34c60Sahrens drrb->drr_flags = BSWAP_32(drrb->drr_flags); 35863cb34c60Sahrens drrb->drr_toguid = BSWAP_64(drrb->drr_toguid); 35873cb34c60Sahrens drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid); 35883cb34c60Sahrens } 35893cb34c60Sahrens 35903cb34c60Sahrens if (drrb->drr_magic != DMU_BACKUP_MAGIC || drr.drr_type != DRR_BEGIN) { 35913cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " 35923cb34c60Sahrens "stream (bad magic number)")); 35933cb34c60Sahrens return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 35943cb34c60Sahrens } 35953cb34c60Sahrens 35969e69d7d0SLori Alt featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); 35979e69d7d0SLori Alt hdrtype = DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo); 35989e69d7d0SLori Alt 35999e69d7d0SLori Alt if (!DMU_STREAM_SUPPORTED(featureflags) || 36009e69d7d0SLori Alt (hdrtype != DMU_SUBSTREAM && hdrtype != DMU_COMPOUNDSTREAM)) { 36019e69d7d0SLori Alt zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, 36029e69d7d0SLori Alt "stream has unsupported feature, feature flags = %lx"), 36039e69d7d0SLori Alt featureflags); 36049e69d7d0SLori Alt return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 36059e69d7d0SLori Alt } 36069e69d7d0SLori Alt 36073cb34c60Sahrens if (strchr(drrb->drr_toname, '@') == NULL) { 36083cb34c60Sahrens zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " 36093cb34c60Sahrens "stream (bad snapshot name)")); 36103cb34c60Sahrens return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); 36113cb34c60Sahrens } 36123cb34c60Sahrens 36139e69d7d0SLori Alt if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_SUBSTREAM) { 3614*40a5c998SMatthew Ahrens char nonpackage_sendfs[ZFS_MAX_DATASET_NAME_LEN]; 3615bd6a198fSTom Erickson if (sendfs == NULL) { 3616bd6a198fSTom Erickson /* 3617bd6a198fSTom Erickson * We were not called from zfs_receive_package(). Get 3618bd6a198fSTom Erickson * the fs specified by 'zfs send'. 3619bd6a198fSTom Erickson */ 3620bd6a198fSTom Erickson char *cp; 3621bd6a198fSTom Erickson (void) strlcpy(nonpackage_sendfs, 3622*40a5c998SMatthew Ahrens drr.drr_u.drr_begin.drr_toname, 3623*40a5c998SMatthew Ahrens sizeof (nonpackage_sendfs)); 3624bd6a198fSTom Erickson if ((cp = strchr(nonpackage_sendfs, '@')) != NULL) 3625bd6a198fSTom Erickson *cp = '\0'; 3626bd6a198fSTom Erickson sendfs = nonpackage_sendfs; 36275878fad7SDan McDonald VERIFY(finalsnap == NULL); 3628bd6a198fSTom Erickson } 3629a2cdcdd2SPaul Dagnelie return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags, 3630a2cdcdd2SPaul Dagnelie &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs, 36315878fad7SDan McDonald cleanup_fd, action_handlep, finalsnap)); 363283d7f9feSTom Erickson } else { 36339e69d7d0SLori Alt assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == 36349e69d7d0SLori Alt DMU_COMPOUNDSTREAM); 3635a2cdcdd2SPaul Dagnelie return (zfs_receive_package(hdl, infd, tosnap, flags, &drr, 3636a2cdcdd2SPaul Dagnelie &zcksum, top_zfs, cleanup_fd, action_handlep)); 36373cb34c60Sahrens } 36383cb34c60Sahrens } 36390069fd67STim Haley 36400069fd67STim Haley /* 36410069fd67STim Haley * Restores a backup of tosnap from the file descriptor specified by infd. 36420069fd67STim Haley * Return 0 on total success, -2 if some things couldn't be 36430069fd67STim Haley * destroyed/renamed/promoted, -1 if some things couldn't be received. 36449c3fd121SMatthew Ahrens * (-1 will override -2, if -1 and the resumable flag was specified the 36459c3fd121SMatthew Ahrens * transfer can be resumed if the sending side supports it). 36460069fd67STim Haley */ 36470069fd67STim Haley int 3648a2cdcdd2SPaul Dagnelie zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props, 3649a2cdcdd2SPaul Dagnelie recvflags_t *flags, int infd, avl_tree_t *stream_avl) 36500069fd67STim Haley { 36510069fd67STim Haley char *top_zfs = NULL; 36520069fd67STim Haley int err; 3653c99e4bdcSChris Kirby int cleanup_fd; 3654c99e4bdcSChris Kirby uint64_t action_handle = 0; 3655a2cdcdd2SPaul Dagnelie char *originsnap = NULL; 3656a2cdcdd2SPaul Dagnelie if (props) { 3657a2cdcdd2SPaul Dagnelie err = nvlist_lookup_string(props, "origin", &originsnap); 3658a2cdcdd2SPaul Dagnelie if (err && err != ENOENT) 3659a2cdcdd2SPaul Dagnelie return (err); 3660a2cdcdd2SPaul Dagnelie } 3661c99e4bdcSChris Kirby 3662c99e4bdcSChris Kirby cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL); 3663c99e4bdcSChris Kirby VERIFY(cleanup_fd >= 0); 36640069fd67STim Haley 3665a2cdcdd2SPaul Dagnelie err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL, 36665878fad7SDan McDonald stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL); 3667c99e4bdcSChris Kirby 3668c99e4bdcSChris Kirby VERIFY(0 == close(cleanup_fd)); 36690069fd67STim Haley 367019b94df9SMatthew Ahrens if (err == 0 && !flags->nomount && top_zfs) { 36710069fd67STim Haley zfs_handle_t *zhp; 36720069fd67STim Haley prop_changelist_t *clp; 36730069fd67STim Haley 36740069fd67STim Haley zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM); 36750069fd67STim Haley if (zhp != NULL) { 36760069fd67STim Haley clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, 36770069fd67STim Haley CL_GATHER_MOUNT_ALWAYS, 0); 36780069fd67STim Haley zfs_close(zhp); 36790069fd67STim Haley if (clp != NULL) { 36800069fd67STim Haley /* mount and share received datasets */ 36810069fd67STim Haley err = changelist_postfix(clp); 36820069fd67STim Haley changelist_free(clp); 36830069fd67STim Haley } 36840069fd67STim Haley } 36850069fd67STim Haley if (zhp == NULL || clp == NULL || err) 36860069fd67STim Haley err = -1; 36870069fd67STim Haley } 36880069fd67STim Haley if (top_zfs) 36890069fd67STim Haley free(top_zfs); 36900069fd67STim Haley 36910069fd67STim Haley return (err); 36920069fd67STim Haley } 3693