Print this page
NEX-15090 Due to mismerge "zfs send" command cannot generate compressed stream
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-15090 Due to mismerge "zfs send" command cannot generate compressed stream
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-9732 Infinite loop in zfs receive (lint fix)
NEX-9732 Infinite loop in zfs receive
NEX-10194 replication Assertion failed: 0 == nvlist_lookup_string(nvfs, "name", &fsname)
NEX-5300 ZFS receive failed: no error
NEX-10201 Replication successfully completed but zfs receive exit with status 1 and without error message
Reviewed by: Eugene Khudyakoff <eugene.khudyakoff@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Cynthia Eastham <cynthia.eastham@nexenta.com>
NEX-1520 Incorrect behavior of ZFS recv in case of incremental recursive replication to a cloned FS
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-6115 Segmentation Fault on zfs recv -x property command
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5928 KRRP: Integrate illumos/openzfs resume-token, to resume replication from a given synced offset
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-5728 Autosync Destination retention policy not being honoured
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5795 Rename 'wrc' as 'wbc' in the source and in the tech docs
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-5239 Recursive auto-sync may get broken if new child datasets are added between the job's runs
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-5272 KRRP: replicate snapshot properties
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-5270 WBC: Incorrect error message when trying to 'zfs recv' into wrcached dataset
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
4986 receiving replication stream fails if any snapshot exceeds refquota
Reviewed by: John Kennedy <john.kennedy@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Gordon Ross <gordon.ross@nexenta.com>
6298 zfs_create_008_neg and zpool_create_023_neg need to be updated for large block support
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: John Kennedy <john.kennedy@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
2605 want to resume interrupted zfs send
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Xin Li <delphij@freebsd.org>
Reviewed by: Arne Jansen <sensille@gmx.net>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-4582 update wrc test cases for allow to use write back cache per tree of datasets
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
5960 zfs recv should prefetch indirect blocks
5925 zfs receive -o origin=
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
5746 more checksumming in zfs send
Reviewed by: Christopher Siden <christopher.siden@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Bayard Bell <buffer.g.overflow@gmail.com>
Approved by: Albert Lee <trisk@omniti.com>
5764 "zfs send -nv" directs output to stderr
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Paul Dagnelie <paul.dagnelie@delphix.com>
Reviewed by: Basil Crow <basil.crow@delphix.com>
Reviewed by: Steven Hartland <killing@multiplay.co.uk>
Reviewed by: Bayard Bell <buffer.g.overflow@gmail.com>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Revert "NEX-4476 WRC: Allow to use write back cache per tree of datasets"
This reverts commit fe97b74444278a6f36fec93179133641296312da.
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-1456 Part 2, port FreeBSD patch - new zfs recv options support
Bug 10481 - cstyle/lint cleanup
Bug 10481 - Dry run option in 'zfs send' isn't the same as in NexentaStor 3.1
*** 21,30 ****
--- 21,31 ----
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
*** 46,58 ****
--- 47,61 ----
#include <time.h>
#include <libzfs.h>
#include <libzfs_core.h>
+ #include "zfs_errno.h"
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "zfs_fletcher.h"
+ #include "zfs_sendrecv.h"
#include "libzfs_impl.h"
#include <zlib.h>
#include <sha2.h>
#include <sys/zio_checksum.h>
#include <sys/ddt.h>
*** 59,78 ****
/* in libzfs_dataset.c */
extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
! recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
! uint64_t *, const char *);
static int guid_to_name(libzfs_handle_t *, const char *,
uint64_t, boolean_t, char *);
static const zio_cksum_t zero_cksum = { 0 };
typedef struct dedup_arg {
int inputfd;
int outputfd;
libzfs_handle_t *dedup_hdl;
} dedup_arg_t;
typedef struct progress_arg {
zfs_handle_t *pa_zhp;
--- 62,83 ----
/* in libzfs_dataset.c */
extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
! recvflags_t *, int, nvlist_t *, nvlist_t *, const char *, nvlist_t *,
! avl_tree_t *, char **, int, uint64_t *, const char *);
static int guid_to_name(libzfs_handle_t *, const char *,
uint64_t, boolean_t, char *);
static const zio_cksum_t zero_cksum = { 0 };
typedef struct dedup_arg {
int inputfd;
int outputfd;
+ uint64_t dedup_data_sz;
+ boolean_t sendsize;
libzfs_handle_t *dedup_hdl;
} dedup_arg_t;
typedef struct progress_arg {
zfs_handle_t *pa_zhp;
*** 185,199 ****
ddt_hash_append(hdl, ddt, ddepp, cs, prop, dr);
return (B_FALSE);
}
static int
! dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
! zio_cksum_t *zc, int outfd)
{
ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
(void) fletcher_4_incremental_native(drr,
offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
if (drr->drr_type != DRR_BEGIN) {
ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
drr_checksum.drr_checksum));
--- 190,208 ----
ddt_hash_append(hdl, ddt, ddepp, cs, prop, dr);
return (B_FALSE);
}
static int
! dump_record(dedup_arg_t *dda, dmu_replay_record_t *drr, void *payload,
! int payload_len, zio_cksum_t *zc, int outfd)
{
ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
+ if (dda != NULL) {
+ dda->dedup_data_sz +=
+ sizeof (dmu_replay_record_t) + payload_len;
+ }
(void) fletcher_4_incremental_native(drr,
offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
if (drr->drr_type != DRR_BEGIN) {
ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
drr_checksum.drr_checksum));
*** 300,310 ****
}
(void) ssread(buf, sz, ofp);
if (ferror(stdin))
perror("fread");
}
! if (dump_record(drr, buf, sz, &stream_cksum,
outfd) != 0)
goto out;
break;
}
--- 309,319 ----
}
(void) ssread(buf, sz, ofp);
if (ferror(stdin))
perror("fread");
}
! if (dump_record(dda, drr, buf, sz, &stream_cksum,
outfd) != 0)
goto out;
break;
}
*** 311,321 ****
case DRR_END:
{
struct drr_end *drre = &drr->drr_u.drr_end;
/* use the recalculated checksum */
drre->drr_checksum = stream_cksum;
! if (dump_record(drr, NULL, 0, &stream_cksum,
outfd) != 0)
goto out;
break;
}
--- 320,330 ----
case DRR_END:
{
struct drr_end *drre = &drr->drr_u.drr_end;
/* use the recalculated checksum */
drre->drr_checksum = stream_cksum;
! if (dump_record(dda, drr, NULL, 0, &stream_cksum,
outfd) != 0)
goto out;
break;
}
*** 325,335 ****
if (drro->drr_bonuslen > 0) {
(void) ssread(buf,
P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
ofp);
}
! if (dump_record(drr, buf,
P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
&stream_cksum, outfd) != 0)
goto out;
break;
}
--- 334,344 ----
if (drro->drr_bonuslen > 0) {
(void) ssread(buf,
P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
ofp);
}
! if (dump_record(dda, drr, buf,
P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
&stream_cksum, outfd) != 0)
goto out;
break;
}
*** 336,354 ****
case DRR_SPILL:
{
struct drr_spill *drrs = &drr->drr_u.drr_spill;
(void) ssread(buf, drrs->drr_length, ofp);
! if (dump_record(drr, buf, drrs->drr_length,
&stream_cksum, outfd) != 0)
goto out;
break;
}
case DRR_FREEOBJECTS:
{
! if (dump_record(drr, NULL, 0, &stream_cksum,
outfd) != 0)
goto out;
break;
}
--- 345,363 ----
case DRR_SPILL:
{
struct drr_spill *drrs = &drr->drr_u.drr_spill;
(void) ssread(buf, drrs->drr_length, ofp);
! if (dump_record(dda, drr, buf, drrs->drr_length,
&stream_cksum, outfd) != 0)
goto out;
break;
}
case DRR_FREEOBJECTS:
{
! if (dump_record(dda, drr, NULL, 0, &stream_cksum,
outfd) != 0)
goto out;
break;
}
*** 418,433 ****
wbr_drrr->drr_key.ddk_cksum =
drrw->drr_key.ddk_cksum;
wbr_drrr->drr_key.ddk_prop =
drrw->drr_key.ddk_prop;
! if (dump_record(&wbr_drr, NULL, 0,
&stream_cksum, outfd) != 0)
goto out;
} else {
/* block not previously seen */
! if (dump_record(drr, buf, payload_size,
&stream_cksum, outfd) != 0)
goto out;
}
break;
}
--- 427,442 ----
wbr_drrr->drr_key.ddk_cksum =
drrw->drr_key.ddk_cksum;
wbr_drrr->drr_key.ddk_prop =
drrw->drr_key.ddk_prop;
! if (dump_record(dda, &wbr_drr, NULL, 0,
&stream_cksum, outfd) != 0)
goto out;
} else {
/* block not previously seen */
! if (dump_record(dda, drr, buf, payload_size,
&stream_cksum, outfd) != 0)
goto out;
}
break;
}
*** 436,455 ****
{
struct drr_write_embedded *drrwe =
&drr->drr_u.drr_write_embedded;
(void) ssread(buf,
P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp);
! if (dump_record(drr, buf,
P2ROUNDUP((uint64_t)drrwe->drr_psize, 8),
&stream_cksum, outfd) != 0)
goto out;
break;
}
case DRR_FREE:
{
! if (dump_record(drr, NULL, 0, &stream_cksum,
outfd) != 0)
goto out;
break;
}
--- 445,464 ----
{
struct drr_write_embedded *drrwe =
&drr->drr_u.drr_write_embedded;
(void) ssread(buf,
P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp);
! if (dump_record(dda, drr, buf,
P2ROUNDUP((uint64_t)drrwe->drr_psize, 8),
&stream_cksum, outfd) != 0)
goto out;
break;
}
case DRR_FREE:
{
! if (dump_record(dda, drr, NULL, 0, &stream_cksum,
outfd) != 0)
goto out;
break;
}
*** 468,588 ****
return (NULL);
}
/*
- * Routines for dealing with the AVL tree of fs-nvlists
- */
- typedef struct fsavl_node {
- avl_node_t fn_node;
- nvlist_t *fn_nvfs;
- char *fn_snapname;
- uint64_t fn_guid;
- } fsavl_node_t;
-
- static int
- fsavl_compare(const void *arg1, const void *arg2)
- {
- const fsavl_node_t *fn1 = arg1;
- const fsavl_node_t *fn2 = arg2;
-
- if (fn1->fn_guid > fn2->fn_guid)
- return (+1);
- else if (fn1->fn_guid < fn2->fn_guid)
- return (-1);
- else
- return (0);
- }
-
- /*
- * Given the GUID of a snapshot, find its containing filesystem and
- * (optionally) name.
- */
- static nvlist_t *
- fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
- {
- fsavl_node_t fn_find;
- fsavl_node_t *fn;
-
- fn_find.fn_guid = snapguid;
-
- fn = avl_find(avl, &fn_find, NULL);
- if (fn) {
- if (snapname)
- *snapname = fn->fn_snapname;
- return (fn->fn_nvfs);
- }
- return (NULL);
- }
-
- static void
- fsavl_destroy(avl_tree_t *avl)
- {
- fsavl_node_t *fn;
- void *cookie;
-
- if (avl == NULL)
- return;
-
- cookie = NULL;
- while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
- free(fn);
- avl_destroy(avl);
- free(avl);
- }
-
- /*
- * Given an nvlist, produce an avl tree of snapshots, ordered by guid
- */
- static avl_tree_t *
- fsavl_create(nvlist_t *fss)
- {
- avl_tree_t *fsavl;
- nvpair_t *fselem = NULL;
-
- if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL)
- return (NULL);
-
- avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
- offsetof(fsavl_node_t, fn_node));
-
- while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
- nvlist_t *nvfs, *snaps;
- nvpair_t *snapelem = NULL;
-
- VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
- VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
-
- while ((snapelem =
- nvlist_next_nvpair(snaps, snapelem)) != NULL) {
- fsavl_node_t *fn;
- uint64_t guid;
-
- VERIFY(0 == nvpair_value_uint64(snapelem, &guid));
- if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) {
- fsavl_destroy(fsavl);
- return (NULL);
- }
- fn->fn_nvfs = nvfs;
- fn->fn_snapname = nvpair_name(snapelem);
- fn->fn_guid = guid;
-
- /*
- * Note: if there are multiple snaps with the
- * same GUID, we ignore all but one.
- */
- if (avl_find(fsavl, fn, NULL) == NULL)
- avl_add(fsavl, fn);
- else
- free(fn);
- }
- }
-
- return (fsavl);
- }
-
- /*
* Routines for dealing with the giant nvlist of fs-nvlists, etc.
*/
typedef struct send_data {
/*
* assigned inside every recursive call,
--- 477,486 ----
*** 622,632 ****
*
* "props" -> { name -> value (only if set here) }
* "snaps" -> { name (lastname) -> number (guid) }
* "snapprops" -> { name (lastname) -> { name -> value } }
*
! * "origin" -> number (guid) (if clone)
* "sent" -> boolean (not on-disk)
* }
* }
* }
*
--- 520,531 ----
*
* "props" -> { name -> value (only if set here) }
* "snaps" -> { name (lastname) -> number (guid) }
* "snapprops" -> { name (lastname) -> { name -> value } }
*
! * "origin" -> number (guid of origin snapshot) (if clone)
! * "origin_fsname" -> string (full name of origin file system)
* "sent" -> boolean (not on-disk)
* }
* }
* }
*
*** 685,694 ****
--- 584,600 ----
while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
char *propname = nvpair_name(elem);
zfs_prop_t prop = zfs_name_to_prop(propname);
nvlist_t *propnv;
+ /*
+ * This property make sense only to this dataset,
+ * so no reasons to include it into stream
+ */
+ if (prop == ZFS_PROP_WBC_MODE)
+ continue;
+
if (!zfs_prop_user(propname)) {
/*
* Realistically, this should never happen. However,
* we want the ability to add DSL properties without
* needing to make incompatible version changes. We
*** 832,849 ****
--- 738,762 ----
VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap",
sd->parent_fromsnap_guid));
if (zhp->zfs_dmustats.dds_origin[0]) {
+ char origin_fsname[ZFS_MAX_DATASET_NAME_LEN];
zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
if (origin == NULL) {
rv = -1;
goto out;
}
VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
origin->zfs_dmustats.dds_guid));
+ zfs_close(origin);
+ (void) strlcpy(origin_fsname, zhp->zfs_dmustats.dds_origin,
+ sizeof (origin_fsname));
+ *strchr(origin_fsname, '@') = '\0';
+ VERIFY(0 == nvlist_add_string(nvfs, "origin_fsname",
+ origin_fsname));
}
/* iterate over props */
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
send_iterate_prop(zhp, nv);
*** 852,862 ****
/* iterate over snaps, and set sd->parent_fromsnap_guid */
sd->parent_fromsnap_guid = 0;
VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
! (void) zfs_iter_snapshots(zhp, B_FALSE, send_iterate_snap, sd);
VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
nvlist_free(sd->parent_snaps);
nvlist_free(sd->snapprops);
--- 765,775 ----
/* iterate over snaps, and set sd->parent_fromsnap_guid */
sd->parent_fromsnap_guid = 0;
VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
! (void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd);
VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
nvlist_free(sd->parent_snaps);
nvlist_free(sd->snapprops);
*** 905,915 ****
*avlp = NULL;
*nvlp = NULL;
return (error);
}
! if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) {
nvlist_free(sd.fss);
*nvlp = NULL;
return (EZFS_NOMEM);
}
--- 818,828 ----
*avlp = NULL;
*nvlp = NULL;
return (error);
}
! if (avlp != NULL && fsavl_create(sd.fss, avlp) != 0) {
nvlist_free(sd.fss);
*nvlp = NULL;
return (EZFS_NOMEM);
}
*** 925,936 ****
const char *fromsnap;
const char *tosnap;
char prevsnap[ZFS_MAX_DATASET_NAME_LEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
! boolean_t verbose, dryrun, parsable, progress, embed_data, std_out;
boolean_t large_block, compress;
int outfd;
boolean_t err;
nvlist_t *fss;
nvlist_t *snapholds;
avl_tree_t *fsavl;
--- 838,852 ----
const char *fromsnap;
const char *tosnap;
char prevsnap[ZFS_MAX_DATASET_NAME_LEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
! boolean_t verbose, dryrun, dedup, parsable, progress, embed_data, std_out;
boolean_t large_block, compress;
+ boolean_t sendsize;
+ uint32_t hdr_send_sz;
+ uint64_t send_sz;
int outfd;
boolean_t err;
nvlist_t *fss;
nvlist_t *snapholds;
avl_tree_t *fsavl;
*** 1008,1018 ****
* NULL) to the file descriptor specified by outfd.
*/
static int
dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
boolean_t fromorigin, int outfd, enum lzc_send_flags flags,
! nvlist_t *debugnv)
{
zfs_cmd_t zc = { 0 };
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *thisdbg;
--- 924,934 ----
* NULL) to the file descriptor specified by outfd.
*/
static int
dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
boolean_t fromorigin, int outfd, enum lzc_send_flags flags,
! nvlist_t *debugnv, boolean_t sendsize, uint64_t *sendcounter)
{
zfs_cmd_t zc = { 0 };
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *thisdbg;
*** 1023,1032 ****
--- 939,950 ----
zc.zc_cookie = outfd;
zc.zc_obj = fromorigin;
zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
zc.zc_fromobj = fromsnap_obj;
zc.zc_flags = flags;
+ zc.zc_sendsize = sendsize;
+ zc.zc_sendcounter = 0;
VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0));
if (fromsnap && fromsnap[0] != '\0') {
VERIFY(0 == nvlist_add_string(thisdbg,
"fromsnap", fromsnap));
*** 1076,1085 ****
--- 994,1004 ----
default:
return (zfs_standard_error(hdl, errno, errbuf));
}
}
+ *sendcounter = (uint64_t)zc.zc_sendcounter;
if (debugnv)
VERIFY(0 == nvlist_add_nvlist(debugnv, zhp->zfs_name, thisdbg));
nvlist_free(thisdbg);
return (0);
*** 1273,1282 ****
--- 1192,1202 ----
gather_holds(zhp, sdd);
fromorigin = sdd->prevsnap[0] == '\0' &&
(sdd->fromorigin || sdd->replicate);
+ /* print out to-from and approximate size in verbose mode */
if (sdd->verbose) {
uint64_t size = 0;
(void) estimate_ioctl(zhp, sdd->prevsnap_obj,
fromorigin, flags, &size);
*** 1285,1299 ****
size, sdd->parsable);
sdd->size += size;
}
if (!sdd->dryrun) {
/*
* If progress reporting is requested, spawn a new thread to
* poll ZFS_IOC_SEND_PROGRESS at a regular interval.
*/
! if (sdd->progress) {
pa.pa_zhp = zhp;
pa.pa_fd = sdd->outfd;
pa.pa_parsable = sdd->parsable;
if ((err = pthread_create(&tid, NULL,
--- 1205,1222 ----
size, sdd->parsable);
sdd->size += size;
}
if (!sdd->dryrun) {
+ uint64_t sendcounter = 0;
+ boolean_t track_progress = (sdd->progress && !sdd->sendsize);
+ boolean_t sendsize = B_FALSE;
/*
* If progress reporting is requested, spawn a new thread to
* poll ZFS_IOC_SEND_PROGRESS at a regular interval.
*/
! if (track_progress) {
pa.pa_zhp = zhp;
pa.pa_fd = sdd->outfd;
pa.pa_parsable = sdd->parsable;
if ((err = pthread_create(&tid, NULL,
*** 1301,1314 ****
zfs_close(zhp);
return (err);
}
}
err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
! fromorigin, sdd->outfd, flags, sdd->debugnv);
! if (sdd->progress) {
(void) pthread_cancel(tid);
(void) pthread_join(tid, NULL);
}
}
--- 1224,1252 ----
zfs_close(zhp);
return (err);
}
}
+ /*
+ * We need to reset the sendsize flag being sent to
+ * kernel if sdd->dedup is set. With dedup, the file
+ * descriptor sent to kernel is one end of the pipe,
+ * and we would want the data back in the pipe for
+ * cksummer() to calculate the exact size of the dedup-ed
+ * stream. So reset the sendsize flag such that
+ * kernel writes to the pipe.
+ */
+
+ sendsize = sdd->dedup ? B_FALSE : sdd->sendsize;
+
err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
! fromorigin, sdd->outfd, flags, sdd->debugnv,
! sendsize, &sendcounter);
! sdd->send_sz += sendcounter;
!
! if (track_progress) {
(void) pthread_cancel(tid);
(void) pthread_join(tid, NULL);
}
}
*** 1489,1570 ****
}
nvlist_t *
zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
{
! unsigned int version;
! int nread;
! unsigned long long checksum, packed_len;
! /*
! * Decode token header, which is:
! * <token version>-<checksum of payload>-<uncompressed payload length>
! * Note that the only supported token version is 1.
! */
! nread = sscanf(token, "%u-%llx-%llx-",
! &version, &checksum, &packed_len);
! if (nread != 3) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (invalid format)"));
! return (NULL);
! }
!
! if (version != ZFS_SEND_RESUME_TOKEN_VERSION) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
! "resume token is corrupt (invalid version %u)"),
! version);
! return (NULL);
! }
!
! /* convert hexadecimal representation to binary */
! token = strrchr(token, '-') + 1;
! int len = strlen(token) / 2;
! unsigned char *compressed = zfs_alloc(hdl, len);
! for (int i = 0; i < len; i++) {
! nread = sscanf(token + i * 2, "%2hhx", compressed + i);
! if (nread != 1) {
! free(compressed);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt "
"(payload is not hex-encoded)"));
! return (NULL);
! }
! }
!
! /* verify checksum */
! zio_cksum_t cksum;
! fletcher_4_native(compressed, len, NULL, &cksum);
! if (cksum.zc_word[0] != checksum) {
! free(compressed);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (incorrect checksum)"));
! return (NULL);
! }
!
! /* uncompress */
! void *packed = zfs_alloc(hdl, packed_len);
! uLongf packed_len_long = packed_len;
! if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK ||
! packed_len_long != packed_len) {
! free(packed);
! free(compressed);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (decompression failed)"));
! return (NULL);
! }
!
! /* unpack nvlist */
! nvlist_t *nv;
! int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP);
! free(packed);
! free(compressed);
! if (error != 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (nvlist_unpack failed)"));
! return (NULL);
! }
! return (nv);
}
int
zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
const char *resume_token)
--- 1427,1474 ----
}
nvlist_t *
zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
{
! nvlist_t *nvl = NULL;
! int error;
! error = zfs_send_resume_token_to_nvlist_impl(token, &nvl);
! switch (error) {
! case EINVAL:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (invalid format)"));
! break;
! case ENOTSUP:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
! "resume token is corrupt (invalid version)"));
! break;
! case EBADMSG:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt "
"(payload is not hex-encoded)"));
! break;
! case ECKSUM:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (incorrect checksum)"));
! break;
! case ENOSR:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (decompression failed)"));
! break;
! case ENODATA:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"resume token is corrupt (nvlist_unpack failed)"));
! break;
! case ENOMEM:
! (void) no_memory(hdl);
! break;
! default:
! break;
! };
!
! return (nvl);
}
int
zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
const char *resume_token)
*** 1575,1585 ****
uint64_t resumeobj, resumeoff, toguid, fromguid, bytes;
zfs_handle_t *zhp;
int error = 0;
char name[ZFS_MAX_DATASET_NAME_LEN];
enum lzc_send_flags lzc_flags = 0;
- FILE *fout = (flags->verbose && flags->dryrun) ? stdout : stderr;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot resume send"));
nvlist_t *resume_nvl =
--- 1479,1488 ----
*** 1590,1602 ****
* zfs_send_resume_token_to_nvlist
*/
return (zfs_error(hdl, EZFS_FAULT, errbuf));
}
if (flags->verbose) {
! (void) fprintf(fout, dgettext(TEXT_DOMAIN,
"resume token contents:\n"));
! nvlist_print(fout, resume_nvl);
}
if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 ||
nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 ||
--- 1493,1505 ----
* zfs_send_resume_token_to_nvlist
*/
return (zfs_error(hdl, EZFS_FAULT, errbuf));
}
if (flags->verbose) {
! (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"resume token contents:\n"));
! nvlist_print(stderr, resume_nvl);
}
if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 ||
nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 ||
*** 1649,1659 ****
uint64_t size = 0;
error = lzc_send_space(zhp->zfs_name, fromname,
lzc_flags, &size);
if (error == 0)
size = MAX(0, (int64_t)(size - bytes));
! send_print_verbose(fout, zhp->zfs_name, fromname,
size, flags->parsable);
}
if (!flags->dryrun) {
progress_arg_t pa = { 0 };
--- 1552,1562 ----
uint64_t size = 0;
error = lzc_send_space(zhp->zfs_name, fromname,
lzc_flags, &size);
if (error == 0)
size = MAX(0, (int64_t)(size - bytes));
! send_print_verbose(stderr, zhp->zfs_name, fromname,
size, flags->parsable);
}
if (!flags->dryrun) {
progress_arg_t pa = { 0 };
*** 1779,1788 ****
--- 1682,1692 ----
errbuf));
}
dda.outputfd = outfd;
dda.inputfd = pipefd[1];
dda.dedup_hdl = zhp->zfs_hdl;
+ dda.sendsize = flags->sendsize;
if ((err = pthread_create(&tid, NULL, cksummer, &dda)) != 0) {
(void) close(pipefd[0]);
(void) close(pipefd[1]);
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
return (zfs_error(zhp->zfs_hdl,
*** 1837,1856 ****
(void) snprintf(drr.drr_u.drr_begin.drr_toname,
sizeof (drr.drr_u.drr_begin.drr_toname),
"%s@%s", zhp->zfs_name, tosnap);
drr.drr_payloadlen = buflen;
! err = dump_record(&drr, packbuf, buflen, &zc, outfd);
free(packbuf);
if (err != 0)
goto stderr_out;
/* write end record */
bzero(&drr, sizeof (drr));
drr.drr_type = DRR_END;
drr.drr_u.drr_end.drr_checksum = zc;
err = write(outfd, &drr, sizeof (drr));
if (err == -1) {
err = errno;
goto stderr_out;
}
--- 1741,1761 ----
(void) snprintf(drr.drr_u.drr_begin.drr_toname,
sizeof (drr.drr_u.drr_begin.drr_toname),
"%s@%s", zhp->zfs_name, tosnap);
drr.drr_payloadlen = buflen;
! err = dump_record(NULL, &drr, packbuf, buflen, &zc, outfd);
free(packbuf);
if (err != 0)
goto stderr_out;
/* write end record */
bzero(&drr, sizeof (drr));
drr.drr_type = DRR_END;
drr.drr_u.drr_end.drr_checksum = zc;
err = write(outfd, &drr, sizeof (drr));
+ sdd.hdr_send_sz += sizeof (drr);
if (err == -1) {
err = errno;
goto stderr_out;
}
*** 1869,1878 ****
--- 1774,1785 ----
sdd.doall = flags->doall;
sdd.fromorigin = flags->fromorigin;
sdd.fss = fss;
sdd.fsavl = fsavl;
sdd.verbose = flags->verbose;
+ sdd.dedup = flags->dedup;
+ sdd.sendsize = flags->sendsize;
sdd.parsable = flags->parsable;
sdd.progress = flags->progress;
sdd.dryrun = flags->dryrun;
sdd.large_block = flags->largeblock;
sdd.embed_data = flags->embed_data;
*** 1907,1917 ****
sdd.snapholds = fnvlist_alloc();
} else {
sdd.cleanup_fd = -1;
sdd.snapholds = NULL;
}
! if (flags->verbose || sdd.snapholds != NULL) {
/*
* Do a verbose no-op dry run to get all the verbose output
* or to gather snapshot hold's before generating any data,
* then do a non-verbose real run to generate the streams.
*/
--- 1814,1824 ----
sdd.snapholds = fnvlist_alloc();
} else {
sdd.cleanup_fd = -1;
sdd.snapholds = NULL;
}
! if ((flags->verbose && !flags->sendsize) || sdd.snapholds != NULL) {
/*
* Do a verbose no-op dry run to get all the verbose output
* or to gather snapshot hold's before generating any data,
* then do a non-verbose real run to generate the streams.
*/
*** 1967,1976 ****
--- 1874,1884 ----
if (tid != 0) {
if (err != 0)
(void) pthread_cancel(tid);
(void) close(pipefd[0]);
(void) pthread_join(tid, NULL);
+ sdd.send_sz = dda.dedup_data_sz;
}
if (sdd.cleanup_fd != -1) {
VERIFY(0 == close(sdd.cleanup_fd));
sdd.cleanup_fd = -1;
*** 1987,1998 ****
--- 1895,1927 ----
drr.drr_type = DRR_END;
if (write(outfd, &drr, sizeof (drr)) == -1) {
return (zfs_standard_error(zhp->zfs_hdl,
errno, errbuf));
}
+ sdd.hdr_send_sz += sizeof (drr);
}
+ if (flags->sendsize) {
+ if (flags->verbose) {
+ (void) fprintf(stderr,
+ "Send stream header size (bytes): %u\n",
+ sdd.hdr_send_sz);
+ (void) fprintf(stderr,
+ "Send stream data size (bytes): %llu\n",
+ (longlong_t)sdd.send_sz);
+ (void) fprintf(stderr,
+ "Total send stream size (bytes): %llu\n",
+ (longlong_t)(sdd.send_sz +
+ (uint64_t)sdd.hdr_send_sz));
+ } else {
+ (void) fprintf(stderr,
+ "Total send stream size (bytes): %llu\n",
+ (longlong_t)(sdd.send_sz +
+ (uint64_t)sdd.hdr_send_sz));
+ }
+ }
+
return (err || sdd.err);
stderr_out:
err = zfs_standard_error(zhp->zfs_hdl, err, errbuf);
err_out:
*** 2136,2145 ****
--- 2065,2077 ----
zfs_cmd_t zc = { 0 };
int err;
prop_changelist_t *clp;
zfs_handle_t *zhp;
+ if (!zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET))
+ return (ENOENT);
+
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
flags->force ? MS_FORCE : 0);
*** 2153,2172 ****
zc.zc_objset_type = DMU_OST_ZFS;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
if (tryname) {
(void) strcpy(newname, tryname);
-
(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
if (flags->verbose) {
! (void) printf("attempting rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
! err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
if (err == 0)
changelist_rename(clp, name, tryname);
} else {
err = ENOENT;
}
if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) {
--- 2085,2114 ----
zc.zc_objset_type = DMU_OST_ZFS;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
if (tryname) {
(void) strcpy(newname, tryname);
(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
+ err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
if (flags->verbose) {
! char errbuf[1024];
! (void) snprintf(errbuf, sizeof (errbuf),
! dgettext(TEXT_DOMAIN,
! "attempting to rename '%s' to '%s': "),
zc.zc_name, zc.zc_value);
+ if (err == 0) {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "%s: success\n"), errbuf);
+ } else {
+ (void) zfs_standard_error(hdl, errno, errbuf);
}
! }
!
if (err == 0)
changelist_rename(clp, name, tryname);
+
} else {
err = ENOENT;
}
if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) {
*** 2173,2200 ****
seq++;
(void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN,
"%.*srecv-%u-%u", baselen, name, getpid(), seq);
(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
if (flags->verbose) {
! (void) printf("failed - trying rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
! err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
if (err == 0)
changelist_rename(clp, name, newname);
! if (err && flags->verbose) {
! (void) printf("failed (%u) - "
! "will try again on next pass\n", errno);
! }
err = EAGAIN;
- } else if (flags->verbose) {
- if (err == 0)
- (void) printf("success\n");
- else
- (void) printf("failed (%u)\n", errno);
}
(void) changelist_postfix(clp);
changelist_free(clp);
--- 2115,2144 ----
seq++;
(void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN,
"%.*srecv-%u-%u", baselen, name, getpid(), seq);
(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
+ err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
if (flags->verbose) {
! char errbuf[1024];
! (void) snprintf(errbuf, sizeof (errbuf),
! dgettext(TEXT_DOMAIN,
! "attempting to temporarily rename '%s' to '%s': "),
zc.zc_name, zc.zc_value);
+ if (err == 0) {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "%s: success\n"), errbuf);
+ } else {
+ (void) zfs_standard_error(hdl, errno, errbuf);
}
! }
!
if (err == 0)
changelist_rename(clp, name, newname);
!
err = EAGAIN;
}
(void) changelist_postfix(clp);
changelist_free(clp);
*** 2210,2219 ****
--- 2154,2166 ----
prop_changelist_t *clp;
zfs_handle_t *zhp;
boolean_t defer = B_FALSE;
int spa_version;
+ if (!zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET))
+ return (ENOENT);
+
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
flags->force ? MS_FORCE : 0);
*** 2229,2248 ****
return (err);
zc.zc_objset_type = DMU_OST_ZFS;
zc.zc_defer_destroy = defer;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
-
- if (flags->verbose)
- (void) printf("attempting destroy %s\n", zc.zc_name);
err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
if (err == 0) {
! if (flags->verbose)
! (void) printf("success\n");
! changelist_remove(clp, zc.zc_name);
}
(void) changelist_postfix(clp);
changelist_free(clp);
/*
* Deferred destroy might destroy the snapshot or only mark it to be
--- 2176,2202 ----
return (err);
zc.zc_objset_type = DMU_OST_ZFS;
zc.zc_defer_destroy = defer;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
+
+ if (flags->verbose) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "attempting to destroy '%s'"), zc.zc_name);
if (err == 0) {
! (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
! "%s: success\n"), errbuf);
! } else {
! (void) zfs_standard_error(hdl, errno, errbuf);
}
+ }
+ if (err == 0)
+ changelist_remove(clp, zc.zc_name);
+
(void) changelist_postfix(clp);
changelist_free(clp);
/*
* Deferred destroy might destroy the snapshot or only mark it to be
*** 2345,2409 ****
return (ENOENT);
}
/*
! * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
! * guid1 is after guid2.
*/
static int
! created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
uint64_t guid1, uint64_t guid2)
{
nvlist_t *nvfs;
char *fsname, *snapname;
- char buf[ZFS_MAX_DATASET_NAME_LEN];
- int rv;
- zfs_handle_t *guid1hdl, *guid2hdl;
uint64_t create1, create2;
if (guid2 == 0)
return (0);
if (guid1 == 0)
return (1);
nvfs = fsavl_find(avl, guid1, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
! (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
! guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
! if (guid1hdl == NULL)
! return (-1);
nvfs = fsavl_find(avl, guid2, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
! (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
! guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
! if (guid2hdl == NULL) {
! zfs_close(guid1hdl);
return (-1);
- }
- create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
- create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
-
if (create1 < create2)
! rv = -1;
! else if (create1 > create2)
! rv = +1;
! else
! rv = 0;
! zfs_close(guid1hdl);
! zfs_close(guid2hdl);
!
! return (rv);
}
static int
recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
! nvlist_t *renamed)
{
nvlist_t *local_nv;
avl_tree_t *local_avl;
nvpair_t *fselem, *nextfselem;
char *fromsnap;
--- 2299,2354 ----
return (ENOENT);
}
/*
! * Returns a value:
! * +1 - promote is reqired
! * 0 - promote is not required
! * -1 - an error is occured
*/
static int
! check_promote(libzfs_handle_t *hdl, avl_tree_t *avl,
uint64_t guid1, uint64_t guid2)
{
nvlist_t *nvfs;
char *fsname, *snapname;
uint64_t create1, create2;
+ /* the local dataset is not cloned */
if (guid2 == 0)
return (0);
+
+ /* the stream dataset is not cloned */
if (guid1 == 0)
return (1);
nvfs = fsavl_find(avl, guid1, &snapname);
+ if (nvfs == NULL)
+ return (0);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
! create1 = get_snap_txg(hdl, fsname, snapname);
nvfs = fsavl_find(avl, guid2, &snapname);
+ if (nvfs == NULL)
+ return (0);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
! create2 = get_snap_txg(hdl, fsname, snapname);
!
! if (create1 == 0 || create2 == 0)
return (-1);
if (create1 < create2)
! return (1);
! return (0);
}
static int
recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
! nvlist_t *renamed, nvlist_t *limitds)
{
nvlist_t *local_nv;
avl_tree_t *local_avl;
nvpair_t *fselem, *nextfselem;
char *fromsnap;
*** 2438,2447 ****
--- 2383,2394 ----
uint64_t fromguid = 0;
uint64_t originguid = 0;
uint64_t stream_originguid = 0;
uint64_t parent_fromsnap_guid, stream_parent_fromsnap_guid;
char *fsname, *stream_fsname;
+ boolean_t stream_fs_exists = B_FALSE;
+ boolean_t stream_originfs_exists = B_FALSE;
nextfselem = nvlist_next_nvpair(local_nv, fselem);
VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
*** 2448,2544 ****
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
VERIFY(0 == nvlist_lookup_uint64(nvfs, "parentfromsnap",
&parent_fromsnap_guid));
(void) nvlist_lookup_uint64(nvfs, "origin", &originguid);
/*
* First find the stream's fs, so we can check for
* a different origin (due to "zfs promote")
*/
for (snapelem = nvlist_next_nvpair(snaps, NULL);
! snapelem; snapelem = nvlist_next_nvpair(snaps, snapelem)) {
! uint64_t thisguid;
! VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
! stream_nvfs = fsavl_find(stream_avl, thisguid, NULL);
! if (stream_nvfs != NULL)
break;
}
/* check for promote */
(void) nvlist_lookup_uint64(stream_nvfs, "origin",
&stream_originguid);
! if (stream_nvfs && originguid != stream_originguid) {
! switch (created_before(hdl, local_avl,
stream_originguid, originguid)) {
case 1: {
/* promote it! */
zfs_cmd_t zc = { 0 };
- nvlist_t *origin_nvfs;
char *origin_fsname;
! if (flags->verbose)
! (void) printf("promoting %s\n", fsname);
!
! origin_nvfs = fsavl_find(local_avl, originguid,
! NULL);
! VERIFY(0 == nvlist_lookup_string(origin_nvfs,
! "name", &origin_fsname));
(void) strlcpy(zc.zc_value, origin_fsname,
sizeof (zc.zc_value));
(void) strlcpy(zc.zc_name, fsname,
sizeof (zc.zc_name));
error = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
! if (error == 0)
! progress = B_TRUE;
! break;
}
- default:
- break;
- case -1:
- fsavl_destroy(local_avl);
- nvlist_free(local_nv);
- return (-1);
}
/*
* We had/have the wrong origin, therefore our
* list of snapshots is wrong. Need to handle
* them on the next pass.
*/
needagain = B_TRUE;
! continue;
}
for (snapelem = nvlist_next_nvpair(snaps, NULL);
! snapelem; snapelem = nextsnapelem) {
! uint64_t thisguid;
char *stream_snapname;
nvlist_t *found, *props;
nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
! VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
! found = fsavl_find(stream_avl, thisguid,
&stream_snapname);
/* check for delete */
if (found == NULL) {
char name[ZFS_MAX_DATASET_NAME_LEN];
if (!flags->force)
continue;
(void) snprintf(name, sizeof (name), "%s@%s",
fsname, nvpair_name(snapelem));
error = recv_destroy(hdl, name,
strlen(fsname)+1, newname, flags);
! if (error)
! needagain = B_TRUE;
! else
progress = B_TRUE;
continue;
}
stream_nvfs = found;
--- 2395,2574 ----
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
VERIFY(0 == nvlist_lookup_uint64(nvfs, "parentfromsnap",
&parent_fromsnap_guid));
(void) nvlist_lookup_uint64(nvfs, "origin", &originguid);
+ if (!nvlist_empty(limitds) && !nvlist_exists(limitds, fsname)) {
+ if (flags->verbose) {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "skip receiving for excluded '%s'\n"),
+ fsname);
+ }
+ continue;
+ }
+
/*
* First find the stream's fs, so we can check for
* a different origin (due to "zfs promote")
+ * and for preserving snapshots on the receiving side
*/
for (snapelem = nvlist_next_nvpair(snaps, NULL);
! snapelem != NULL; snapelem = nextsnapelem) {
! uint64_t snapguid;
! nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
! VERIFY(0 == nvpair_value_uint64(snapelem, &snapguid));
! stream_nvfs = fsavl_find(stream_avl, snapguid, NULL);
! if (stream_nvfs != NULL) {
! stream_fs_exists = B_TRUE;
break;
}
+ }
+ /* Check the stream's fs for origin snapshot */
+ if (stream_fs_exists && originguid != 0) {
+ nvlist_t *stream_snaps;
+
+ VERIFY(0 == nvlist_lookup_nvlist(stream_nvfs, "snaps",
+ &stream_snaps));
+
+ for (snapelem = nvlist_next_nvpair(stream_snaps, NULL);
+ snapelem != NULL; snapelem = nextsnapelem) {
+ uint64_t stream_snapguid;
+
+ nextsnapelem = nvlist_next_nvpair(stream_snaps,
+ snapelem);
+ VERIFY(0 == nvpair_value_uint64(snapelem,
+ &stream_snapguid));
+
+ if (stream_snapguid == originguid) {
+ stream_originfs_exists = B_TRUE;
+ break;
+ }
+ }
+ }
+
/* check for promote */
(void) nvlist_lookup_uint64(stream_nvfs, "origin",
&stream_originguid);
! if (originguid != stream_originguid && stream_originfs_exists) {
! switch (check_promote(hdl, local_avl,
stream_originguid, originguid)) {
+ case 0:
+ break;
case 1: {
/* promote it! */
zfs_cmd_t zc = { 0 };
char *origin_fsname;
! VERIFY(0 == nvlist_lookup_string(nvfs,
! "origin_fsname", &origin_fsname));
(void) strlcpy(zc.zc_value, origin_fsname,
sizeof (zc.zc_value));
(void) strlcpy(zc.zc_name, fsname,
sizeof (zc.zc_name));
error = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
!
! if (flags->verbose) {
! char errbuf[1024];
! (void) snprintf(errbuf, sizeof (errbuf),
! dgettext(TEXT_DOMAIN,
! "attempting to promote '%s': "),
! zc.zc_name);
! if (error == 0) {
! (void) fprintf(stderr,
! dgettext(TEXT_DOMAIN,
! "%s: success\n"), errbuf);
! } else {
! (void) zfs_standard_error(hdl,
! errno, errbuf);
}
}
+
+ if (error == 0)
+ progress = B_TRUE;
+
/*
* We had/have the wrong origin, therefore our
* list of snapshots is wrong. Need to handle
* them on the next pass.
*/
+
needagain = B_TRUE;
! goto out;
}
+ default:
+ progress = B_FALSE;
+ needagain = B_FALSE;
+ goto out;
+ }
+ }
for (snapelem = nvlist_next_nvpair(snaps, NULL);
! snapelem != NULL; snapelem = nextsnapelem) {
! uint64_t snapguid;
char *stream_snapname;
nvlist_t *found, *props;
nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
! VERIFY(0 == nvpair_value_uint64(snapelem, &snapguid));
! found = fsavl_find(stream_avl, snapguid,
&stream_snapname);
/* check for delete */
if (found == NULL) {
char name[ZFS_MAX_DATASET_NAME_LEN];
+ /*
+ * Conventional force-receive (-F) behavior
+ * combines two different steps:
+ * 1. rollback the destination dataset to the
+ * most recent received snapshot
+ * 2. destroy all those destination snapshots
+ * that are not present at the source
+ * The keepsnap flag allows to effectively
+ * separate 1 from 2 and perform forced receive
+ * while still maintaining the destination
+ * snapshots as per the corresponding snapshot
+ * retention policy (at the destination).
+ */
+
+ /*
+ * When -F (force-receive) is not specified we
+ * always keep snapshots at the destination
+ * (i.e., this has always been zfs conventional
+ * behavior). See also 'keepsnap' comment below
+ */
if (!flags->force)
continue;
+ /*
+ * keepsnap flag modifies the conventional
+ * force-receive behavior not to destroy
+ * destination snapshots that are not present
+ * at the replication source
+ */
+ if (flags->keepsnap && stream_fs_exists)
+ continue;
+
+ /*
+ * Destroy destination snapshots that do
+ * not exist at the replication source
+ */
(void) snprintf(name, sizeof (name), "%s@%s",
fsname, nvpair_name(snapelem));
error = recv_destroy(hdl, name,
strlen(fsname)+1, newname, flags);
!
! if (error == 0)
progress = B_TRUE;
+ else
+ needagain = B_TRUE;
+
continue;
}
stream_nvfs = found;
*** 2569,2612 ****
(void) snprintf(tryname, sizeof (name), "%s@%s",
fsname, stream_snapname);
error = recv_rename(hdl, name, tryname,
strlen(fsname)+1, newname, flags);
! if (error)
! needagain = B_TRUE;
! else
progress = B_TRUE;
}
if (strcmp(stream_snapname, fromsnap) == 0)
! fromguid = thisguid;
}
/* check for delete */
if (stream_nvfs == NULL) {
if (!flags->force)
continue;
error = recv_destroy(hdl, fsname, strlen(tofs)+1,
newname, flags);
! if (error)
! needagain = B_TRUE;
! else
progress = B_TRUE;
! continue;
}
- if (fromguid == 0) {
- if (flags->verbose) {
- (void) printf("local fs %s does not have "
- "fromsnap (%s in stream); must have "
- "been deleted locally; ignoring\n",
- fsname, fromsnap);
- }
continue;
}
VERIFY(0 == nvlist_lookup_string(stream_nvfs,
"name", &stream_fsname));
VERIFY(0 == nvlist_lookup_uint64(stream_nvfs,
"parentfromsnap", &stream_parent_fromsnap_guid));
--- 2599,2647 ----
(void) snprintf(tryname, sizeof (name), "%s@%s",
fsname, stream_snapname);
error = recv_rename(hdl, name, tryname,
strlen(fsname)+1, newname, flags);
!
! if (error == 0)
progress = B_TRUE;
+ else
+ needagain = B_TRUE;
}
if (strcmp(stream_snapname, fromsnap) == 0)
! fromguid = snapguid;
}
/* check for delete */
if (stream_nvfs == NULL) {
if (!flags->force)
continue;
error = recv_destroy(hdl, fsname, strlen(tofs)+1,
newname, flags);
!
! switch (error) {
! case 0:
progress = B_TRUE;
! break;
! case EAGAIN:
! progress = B_TRUE;
! needagain = B_TRUE;
! goto out;
! default:
! needagain = B_TRUE;
! break;
}
continue;
}
+ /* skip destroyed or re-created datasets */
+ if (fromguid == 0)
+ continue;
+
VERIFY(0 == nvlist_lookup_string(stream_nvfs,
"name", &stream_fsname));
VERIFY(0 == nvlist_lookup_uint64(stream_nvfs,
"parentfromsnap", &stream_parent_fromsnap_guid));
*** 2643,2654 ****
(void) snprintf(tryname, sizeof (tryname),
"%s%s", pname, strrchr(stream_fsname, '/'));
} else {
tryname[0] = '\0';
if (flags->verbose) {
! (void) printf("local fs %s new parent "
! "not found\n", fsname);
}
}
newname[0] = '\0';
--- 2678,2691 ----
(void) snprintf(tryname, sizeof (tryname),
"%s%s", pname, strrchr(stream_fsname, '/'));
} else {
tryname[0] = '\0';
if (flags->verbose) {
! (void) fprintf(stderr,
! dgettext(TEXT_DOMAIN,
! "parent dataset not found for "
! "local dataset '%s'\n"), fsname);
}
}
newname[0] = '\0';
*** 2658,2691 ****
if (renamed != NULL && newname[0] != '\0') {
VERIFY(0 == nvlist_add_boolean(renamed,
newname));
}
! if (error)
! needagain = B_TRUE;
! else
progress = B_TRUE;
}
}
fsavl_destroy(local_avl);
nvlist_free(local_nv);
if (needagain && progress) {
/* do another pass to fix up temporary names */
if (flags->verbose)
! (void) printf("another pass:\n");
goto again;
}
return (needagain);
}
static int
zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
! recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
! char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
nvlist_t *stream_nv = NULL;
avl_tree_t *stream_avl = NULL;
char *fromsnap = NULL;
char *sendsnap = NULL;
--- 2695,2731 ----
if (renamed != NULL && newname[0] != '\0') {
VERIFY(0 == nvlist_add_boolean(renamed,
newname));
}
! if (error == 0)
progress = B_TRUE;
+ else
+ needagain = B_TRUE;
}
}
+ out:
fsavl_destroy(local_avl);
nvlist_free(local_nv);
if (needagain && progress) {
/* do another pass to fix up temporary names */
if (flags->verbose)
! (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
! "another pass for promote, destroy and rename:\n"));
goto again;
}
return (needagain);
}
static int
zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
! recvflags_t *flags, nvlist_t *exprops, nvlist_t *limitds,
! dmu_replay_record_t *drr, zio_cksum_t *zc, char **top_zfs, int cleanup_fd,
! uint64_t *action_handlep)
{
nvlist_t *stream_nv = NULL;
avl_tree_t *stream_avl = NULL;
char *fromsnap = NULL;
char *sendsnap = NULL;
*** 2762,2775 ****
if (drr->drr_payloadlen != 0) {
nvlist_t *stream_fss;
VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss",
&stream_fss));
! if ((stream_avl = fsavl_create(stream_fss)) == NULL) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"couldn't allocate avl tree"));
error = zfs_error(hdl, EZFS_NOMEM, errbuf);
goto out;
}
if (fromsnap != NULL && recursive) {
nvlist_t *renamed = NULL;
--- 2802,2820 ----
if (drr->drr_payloadlen != 0) {
nvlist_t *stream_fss;
VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss",
&stream_fss));
! error = fsavl_create(stream_fss, &stream_avl);
! if (error != 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"couldn't allocate avl tree"));
+ if (error == ENOMEM)
error = zfs_error(hdl, EZFS_NOMEM, errbuf);
+ else
+ error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
+
goto out;
}
if (fromsnap != NULL && recursive) {
nvlist_t *renamed = NULL;
*** 2802,2812 ****
VERIFY(0 == nvlist_alloc(&renamed,
NV_UNIQUE_NAME, 0));
}
softerr = recv_incremental_replication(hdl, tofs, flags,
! stream_nv, stream_avl, renamed);
/* Unmount renamed filesystems before receiving. */
while ((pair = nvlist_next_nvpair(renamed,
pair)) != NULL) {
zfs_handle_t *zhp;
--- 2847,2857 ----
VERIFY(0 == nvlist_alloc(&renamed,
NV_UNIQUE_NAME, 0));
}
softerr = recv_incremental_replication(hdl, tofs, flags,
! stream_nv, stream_avl, renamed, limitds);
/* Unmount renamed filesystems before receiving. */
while ((pair = nvlist_next_nvpair(renamed,
pair)) != NULL) {
zfs_handle_t *zhp;
*** 2856,2867 ****
* Note, if we fail due to already having this guid,
* zfs_receive_one() will take care of it (ie,
* recv_skip() and return 0).
*/
error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
! sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
! action_handlep, sendsnap);
if (error == ENODATA) {
error = 0;
break;
}
anyerr |= error;
--- 2901,2912 ----
* Note, if we fail due to already having this guid,
* zfs_receive_one() will take care of it (ie,
* recv_skip() and return 0).
*/
error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
! exprops, limitds, sendfs, stream_nv, stream_avl, top_zfs,
! cleanup_fd, action_handlep, sendsnap);
if (error == ENODATA) {
error = 0;
break;
}
anyerr |= error;
*** 2871,2881 ****
/*
* Now that we have the fs's they sent us, try the
* renames again.
*/
softerr = recv_incremental_replication(hdl, tofs, flags,
! stream_nv, stream_avl, NULL);
}
out:
fsavl_destroy(stream_avl);
nvlist_free(stream_nv);
--- 2916,2926 ----
/*
* Now that we have the fs's they sent us, try the
* renames again.
*/
softerr = recv_incremental_replication(hdl, tofs, flags,
! stream_nv, stream_avl, NULL, limitds);
}
out:
fsavl_destroy(stream_avl);
nvlist_free(stream_nv);
*** 3021,3054 ****
}
zfs_close(zhp);
}
/*
* Restores a backup of tosnap from the file descriptor specified by infd.
*/
static int
zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
! const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
! dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
! avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
! uint64_t *action_handlep, const char *finalsnap)
{
zfs_cmd_t zc = { 0 };
time_t begin_time;
int ioctl_err, ioctl_errno, err;
char *cp;
struct drr_begin *drrb = &drr->drr_u.drr_begin;
char errbuf[1024];
char prop_errbuf[1024];
const char *chopprefix;
boolean_t newfs = B_FALSE;
boolean_t stream_wantsnewfs;
uint64_t parent_snapguid = 0;
prop_changelist_t *clp = NULL;
! nvlist_t *snapprops_nvlist = NULL;
zprop_errflags_t prop_errflags;
! boolean_t recursive;
char *snapname = NULL;
begin_time = time(NULL);
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
--- 3066,3196 ----
}
zfs_close(zhp);
}
/*
+ * Calculate a list of properties for the current dataset taking into account
+ * stream properties (props) and the properties specified on the command line
+ * using -x and/or -o options (exprops)
+ *
+ * This calculation:
+ * - Removes excluded properties (booleans)
+ * - Changes the values of overridden properties (strings)
+ *
+ */
+ static int
+ props_override(char *dsname, nvlist_t *props, nvlist_t *exprops,
+ nvlist_t **merged_propsp, recvflags_t *flags, libzfs_handle_t *hdl,
+ zfs_type_t type, uint64_t zoned, zfs_handle_t *zhp,
+ zpool_handle_t *zpool_hdl, const char *errbuf)
+ {
+ nvlist_t *goprops, *gxprops, *merged_props, *vprops;
+ nvpair_t *pair;
+ int ret = 0;
+
+ if (nvlist_empty(props) || nvlist_empty(exprops))
+ return (0); /* No properties */
+
+ if (nvlist_dup(props, &merged_props, 0) != 0)
+ return (-1);
+
+ VERIFY(nvlist_alloc(&goprops, NV_UNIQUE_NAME, 0) == 0);
+ VERIFY(nvlist_alloc(&gxprops, NV_UNIQUE_NAME, 0) == 0);
+
+ /* build lists to process in order */
+ for (pair = nvlist_next_nvpair(exprops, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(exprops, pair)) {
+ const char *propname = nvpair_name(pair);
+ switch (nvpair_type(pair)) {
+ case DATA_TYPE_BOOLEAN:
+ VERIFY0(nvlist_add_nvpair(gxprops, pair));
+ break;
+ case DATA_TYPE_STRING:
+ VERIFY0(nvlist_add_nvpair(goprops, pair));
+ break;
+ default:
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "property '%s' must be a string or boolean"),
+ propname);
+ /* should never happen, so assert */
+ assert(B_FALSE);
+ }
+ }
+
+ /* convert override properties e.g. strings to native */
+ if ((vprops = zfs_valid_proplist(hdl, type, goprops, zoned, zhp,
+ zpool_hdl, errbuf)) == NULL)
+ goto error;
+
+ nvlist_free(goprops);
+ goprops = vprops;
+
+ /* override / set properties */
+ for (nvpair_t *pair = nvlist_next_nvpair(goprops, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(goprops, pair)) {
+ const char *pname = nvpair_name(pair);
+ if (!nvlist_exists(gxprops, pname)) {
+ if (flags->verbose) {
+ (void) printf("%s %s property from %s\n",
+ nvlist_exists(merged_props, pname) ?
+ "overriding" : "setting", pname, dsname);
+ }
+ VERIFY0(nvlist_add_nvpair(merged_props, pair));
+ }
+ }
+
+ /* exclude properties */
+ for (nvpair_t *pair = nvlist_next_nvpair(gxprops, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(gxprops, pair)) {
+ const char *pname = nvpair_name(pair);
+ if (nvlist_exists(merged_props, pname)) {
+ if (flags->verbose) {
+ (void) printf("excluding %s property "
+ "from %s\n", pname, dsname);
+ }
+ VERIFY0(nvlist_remove_all(merged_props, pname));
+ }
+ }
+
+ *merged_propsp = merged_props;
+
+ error:
+ if (0 != ret)
+ nvlist_free(merged_props);
+ nvlist_free(goprops);
+ nvlist_free(gxprops);
+
+ return (ret);
+ }
+
+ /*
* Restores a backup of tosnap from the file descriptor specified by infd.
*/
static int
zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
! const char *originsnap, recvflags_t *flags, nvlist_t *exprops,
! nvlist_t *limitds, dmu_replay_record_t *drr,
! dmu_replay_record_t *drr_noswap,
! const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl,
! char **top_zfs, int cleanup_fd, uint64_t *action_handlep, const char *finalsnap)
{
zfs_cmd_t zc = { 0 };
time_t begin_time;
int ioctl_err, ioctl_errno, err;
char *cp;
struct drr_begin *drrb = &drr->drr_u.drr_begin;
+ char dsname[ZFS_MAX_DATASET_NAME_LEN];
char errbuf[1024];
char prop_errbuf[1024];
const char *chopprefix;
boolean_t newfs = B_FALSE;
boolean_t stream_wantsnewfs;
uint64_t parent_snapguid = 0;
prop_changelist_t *clp = NULL;
! nvlist_t *snapprops_nvlist = NULL, *props = NULL, *merged_props = NULL;
zprop_errflags_t prop_errflags;
! boolean_t recursive, skip;
char *snapname = NULL;
begin_time = time(NULL);
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
*** 3056,3069 ****
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
ENOENT);
if (stream_avl != NULL) {
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
&snapname);
- nvlist_t *props;
- int ret;
(void) nvlist_lookup_uint64(fs, "parentfromsnap",
&parent_snapguid);
err = nvlist_lookup_nvlist(fs, "props", &props);
if (err)
--- 3198,3210 ----
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
ENOENT);
if (stream_avl != NULL) {
+ nvlist_t *snapprops;
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
&snapname);
(void) nvlist_lookup_uint64(fs, "parentfromsnap",
&parent_snapguid);
err = nvlist_lookup_nvlist(fs, "props", &props);
if (err)
*** 3071,3091 ****
if (flags->canmountoff) {
VERIFY(0 == nvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
}
! ret = zcmd_write_src_nvlist(hdl, &zc, props);
! if (err)
nvlist_free(props);
! if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
! VERIFY(0 == nvlist_lookup_nvlist(props,
snapname, &snapprops_nvlist));
}
-
- if (ret != 0)
- return (-1);
}
cp = NULL;
/*
--- 3212,3231 ----
if (flags->canmountoff) {
VERIFY(0 == nvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
}
!
! if (err) {
nvlist_free(props);
+ props = NULL;
+ }
! if (0 == nvlist_lookup_nvlist(fs, "snapprops", &snapprops)) {
! VERIFY(0 == nvlist_lookup_nvlist(snapprops,
snapname, &snapprops_nvlist));
}
}
cp = NULL;
/*
*** 3190,3199 ****
--- 3330,3342 ----
}
if (flags->verbose)
(void) printf("found clone origin %s\n", zc.zc_string);
}
+ (void) strcpy(dsname, drrb->drr_toname);
+ *strchr(dsname, '@') = '\0';
+
boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
DMU_BACKUP_FEATURE_RESUMING;
stream_wantsnewfs = (drrb->drr_fromguid == NULL ||
(drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming;
*** 3314,3323 ****
--- 3457,3476 ----
zcmd_free_nvlists(&zc);
return (-1);
}
}
+ /* convert override properties e.g. strings to native */
+ if (!nvlist_empty(exprops) && props_override(dsname, props,
+ exprops, &merged_props, flags, hdl, zhp->zfs_type,
+ zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,
+ errbuf) != 0) {
+ zfs_close(zhp);
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+
/*
* If we are resuming a newfs, set newfs here so that we will
* mount it if the recv succeeds this time. We can tell
* that it was a newfs on the first recv because the fs
* itself will be inconsistent (if the fs existed when we
*** 3355,3388 ****
zcmd_free_nvlists(&zc);
return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
}
newfs = B_TRUE;
}
zc.zc_begin_record = *drr_noswap;
zc.zc_cookie = infd;
zc.zc_guid = flags->force;
zc.zc_resumable = flags->resumable;
if (flags->verbose) {
(void) printf("%s %s stream of %s into %s\n",
! flags->dryrun ? "would receive" : "receiving",
drrb->drr_fromguid ? "incremental" : "full",
drrb->drr_toname, zc.zc_value);
(void) fflush(stdout);
}
! if (flags->dryrun) {
zcmd_free_nvlists(&zc);
return (recv_skip(hdl, infd, flags->byteswap));
}
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
zc.zc_nvlist_dst_size = sizeof (prop_errbuf);
zc.zc_cleanup_fd = cleanup_fd;
zc.zc_action_handle = *action_handlep;
err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
ioctl_errno = errno;
prop_errflags = (zprop_errflags_t)zc.zc_obj;
if (err == 0) {
--- 3508,3588 ----
zcmd_free_nvlists(&zc);
return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
}
newfs = B_TRUE;
+
+ if (!nvlist_empty(exprops)) {
+ /* Create an override set of properties if needed */
+ uint64_t zoned = 0;
+ char zp_name[MAXNAMELEN];
+ zpool_handle_t *zp_handle;
+ if (flags->isprefix && !flags->istail &&
+ !flags->dryrun) {
+ /* Check if we're zoned or not */
+ if (check_parents(hdl, zc.zc_value, &zoned,
+ B_FALSE, NULL) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
}
+ }
+ (void) strlcpy(zp_name, zc.zc_name, sizeof (zp_name));
+ cp = strchr(zp_name, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ zp_handle = zpool_open(hdl, zp_name);
+ if (zp_handle != NULL &&
+ props_override(dsname, props,exprops,
+ &merged_props, flags, hdl, ZFS_TYPE_DATASET,
+ zoned, NULL, zp_handle, errbuf) != 0) {
+ zcmd_free_nvlists(&zc);
+ zpool_close(zp_handle);
+ return (-1);
+ }
+ if (zp_handle != NULL)
+ zpool_close(zp_handle);
+ }
+ }
+
zc.zc_begin_record = *drr_noswap;
zc.zc_cookie = infd;
zc.zc_guid = flags->force;
zc.zc_resumable = flags->resumable;
+ skip = !nvlist_empty(limitds) && !nvlist_exists(limitds, dsname);
if (flags->verbose) {
(void) printf("%s %s stream of %s into %s\n",
! skip ? (flags->dryrun ? "would skip" : "skipping") :
! (flags->dryrun ? "would receive" : "receiving"),
drrb->drr_fromguid ? "incremental" : "full",
drrb->drr_toname, zc.zc_value);
(void) fflush(stdout);
}
! if (flags->dryrun || skip) {
zcmd_free_nvlists(&zc);
return (recv_skip(hdl, infd, flags->byteswap));
}
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
zc.zc_nvlist_dst_size = sizeof (prop_errbuf);
zc.zc_cleanup_fd = cleanup_fd;
zc.zc_action_handle = *action_handlep;
+ /*
+ * if we ended up overriding props, use the merged ones,
+ * otherwise use the ones that we got from the send stream
+ */
+ if (merged_props) {
+ if (zcmd_write_src_nvlist(hdl, &zc, merged_props) != 0) {
+ nvlist_free(merged_props);
+ return (-1);
+ }
+ nvlist_free(merged_props);
+ } else if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
+ return (-1);
+
err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
ioctl_errno = errno;
prop_errflags = (zprop_errflags_t)zc.zc_obj;
if (err == 0) {
*** 3524,3533 ****
--- 3724,3738 ----
case EDQUOT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination %s space quota exceeded"), zc.zc_name);
(void) zfs_error(hdl, EZFS_NOSPC, errbuf);
break;
+ case EKZFS_WBCNOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "write back cached datasets do not support recv"));
+ (void) zfs_error(hdl, EZFS_WBCNOTSUP, errbuf);
+ break;
default:
(void) zfs_standard_error(hdl, ioctl_errno, errbuf);
}
}
*** 3600,3610 ****
return (0);
}
static int
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
! const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
uint64_t *action_handlep, const char *finalsnap)
{
int err;
dmu_replay_record_t drr, drr_noswap;
--- 3805,3816 ----
return (0);
}
static int
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
! const char *originsnap, recvflags_t *flags,
! int infd, nvlist_t *exprops, nvlist_t *limitds, const char *sendfs,
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
uint64_t *action_handlep, const char *finalsnap)
{
int err;
dmu_replay_record_t drr, drr_noswap;
*** 3703,3719 ****
*cp = '\0';
sendfs = nonpackage_sendfs;
VERIFY(finalsnap == NULL);
}
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
! &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
! cleanup_fd, action_handlep, finalsnap));
} else {
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
DMU_COMPOUNDSTREAM);
! return (zfs_receive_package(hdl, infd, tosnap, flags, &drr,
! &zcksum, top_zfs, cleanup_fd, action_handlep));
}
}
/*
* Restores a backup of tosnap from the file descriptor specified by infd.
--- 3909,3926 ----
*cp = '\0';
sendfs = nonpackage_sendfs;
VERIFY(finalsnap == NULL);
}
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
! exprops, limitds, &drr, &drr_noswap, sendfs, stream_nv,
! stream_avl, top_zfs, cleanup_fd, action_handlep, finalsnap));
} else {
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
DMU_COMPOUNDSTREAM);
! return (zfs_receive_package(hdl, infd, tosnap, flags, exprops,
! limitds, &drr, &zcksum, top_zfs, cleanup_fd,
! action_handlep));
}
}
/*
* Restores a backup of tosnap from the file descriptor specified by infd.
*** 3721,3749 ****
* destroyed/renamed/promoted, -1 if some things couldn't be received.
* (-1 will override -2, if -1 and the resumable flag was specified the
* transfer can be resumed if the sending side supports it).
*/
int
! zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
! recvflags_t *flags, int infd, avl_tree_t *stream_avl)
{
char *top_zfs = NULL;
int err;
int cleanup_fd;
uint64_t action_handle = 0;
char *originsnap = NULL;
! if (props) {
! err = nvlist_lookup_string(props, "origin", &originsnap);
if (err && err != ENOENT)
return (err);
}
cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
VERIFY(cleanup_fd >= 0);
! err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
! stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL);
VERIFY(0 == close(cleanup_fd));
if (err == 0 && !flags->nomount && top_zfs) {
zfs_handle_t *zhp;
--- 3928,3957 ----
* destroyed/renamed/promoted, -1 if some things couldn't be received.
* (-1 will override -2, if -1 and the resumable flag was specified the
* transfer can be resumed if the sending side supports it).
*/
int
! zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
! int infd, nvlist_t *exprops, nvlist_t *limitds, avl_tree_t *stream_avl)
{
char *top_zfs = NULL;
int err;
int cleanup_fd;
uint64_t action_handle = 0;
char *originsnap = NULL;
! if (exprops) {
! err = nvlist_lookup_string(exprops, "origin", &originsnap);
if (err && err != ENOENT)
return (err);
}
cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
VERIFY(cleanup_fd >= 0);
! err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, exprops,
! limitds, NULL, NULL, stream_avl, &top_zfs, cleanup_fd,
! &action_handle, NULL);
VERIFY(0 == close(cleanup_fd));
if (err == 0 && !flags->nomount && top_zfs) {
zfs_handle_t *zhp;