Skip to content
Permalink
Browse files

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>
  • Loading branch information
ahrens committed Oct 10, 2015
1 parent 89fdfac commit 9c3fd1216fa7fb02cfbc78a2518a686d54b48ab8
Showing with 1,855 additions and 269 deletions.
  1. +2 −2 usr/src/cmd/truss/expound.c
  2. +103 −17 usr/src/cmd/zfs/zfs_main.c
  3. +2 −3 usr/src/cmd/zstreamdump/zstreamdump.c
  4. +4 −0 usr/src/common/zfs/zfs_prop.c
  5. +4 −1 usr/src/lib/libzfs/Makefile.com
  6. +10 −0 usr/src/lib/libzfs/common/libzfs.h
  7. +12 −15 usr/src/lib/libzfs/common/libzfs_dataset.c
  8. +11 −0 usr/src/lib/libzfs/common/libzfs_mount.c
  9. +373 −70 usr/src/lib/libzfs/common/libzfs_sendrecv.c
  10. +2 −0 usr/src/lib/libzfs/common/mapfile-vers
  11. +50 −19 usr/src/lib/libzfs_core/common/libzfs_core.c
  12. +5 −1 usr/src/lib/libzfs_core/common/libzfs_core.h
  13. +3 −1 usr/src/lib/libzfs_core/common/mapfile-vers
  14. +65 −4 usr/src/man/man1m/zfs.1m
  15. +5 −0 usr/src/pkg/manifests/system-test-zfstest.mf
  16. +2 −0 usr/src/test/zfs-tests/include/commands.cfg
  17. +3 −1 usr/src/test/zfs-tests/runfiles/delphix.run
  18. +3 −1 usr/src/test/zfs-tests/runfiles/omnios.run
  19. +3 −1 usr/src/test/zfs-tests/runfiles/openindiana.run
  20. +5 −0 usr/src/test/zfs-tests/tests/functional/rsend/Makefile
  21. +186 −11 usr/src/test/zfs-tests/tests/functional/rsend/rsend.kshlib
  22. +51 −0 usr/src/test/zfs-tests/tests/functional/rsend/rsend_019_pos.ksh
  23. +49 −0 usr/src/test/zfs-tests/tests/functional/rsend/rsend_020_pos.ksh
  24. +52 −0 usr/src/test/zfs-tests/tests/functional/rsend/rsend_021_pos.ksh
  25. +60 −0 usr/src/test/zfs-tests/tests/functional/rsend/rsend_022_pos.ksh
  26. +52 −0 usr/src/test/zfs-tests/tests/functional/rsend/rsend_024_pos.ksh
  27. +20 −0 usr/src/uts/common/fs/zfs/dmu_objset.c
  28. +463 −71 usr/src/uts/common/fs/zfs/dmu_send.c
  29. +41 −31 usr/src/uts/common/fs/zfs/dmu_traverse.c
  30. +138 −0 usr/src/uts/common/fs/zfs/dsl_dataset.c
  31. +10 −2 usr/src/uts/common/fs/zfs/dsl_destroy.c
  32. +3 −1 usr/src/uts/common/fs/zfs/sys/dmu_impl.h
  33. +11 −5 usr/src/uts/common/fs/zfs/sys/dmu_send.h
  34. +2 −0 usr/src/uts/common/fs/zfs/sys/dmu_traverse.h
  35. +23 −0 usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
  36. +14 −8 usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
  37. +12 −4 usr/src/uts/common/fs/zfs/zfs_ioctl.c
  38. +1 −0 usr/src/uts/common/sys/fs/zfs.h
@@ -22,7 +22,7 @@
/*
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
*/

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -4976,7 +4976,7 @@ show_zfs_ioc(private_t *pri, long addr)

if (memcmp(&zc.zc_begin_record, &zero_drrbegin,
sizeof (zc.zc_begin_record))) {
struct drr_begin *drr = &zc.zc_begin_record;
struct drr_begin *drr = &zc.zc_begin_record.drr_u.drr_begin;
(void) printf(" zc_begin_record:\n");
if (drr->drr_magic) {
(void) printf("\tdrr_magic=%llu\n",
@@ -246,10 +246,11 @@ get_usage(zfs_help_t idx)
case HELP_PROMOTE:
return (gettext("\tpromote <clone-filesystem>\n"));
case HELP_RECEIVE:
return (gettext("\treceive [-vnFu] <filesystem|volume|"
return (gettext("\treceive [-vnsFu] <filesystem|volume|"
"snapshot>\n"
"\treceive [-vnFu] [-o origin=<snapshot>] [-d | -e] "
"<filesystem>\n"));
"\treceive [-vnsFu] [-o origin=<snapshot>] [-d | -e] "
"<filesystem>\n"
"\treceive -A <filesystem|volume>\n"));
case HELP_RENAME:
return (gettext("\trename [-f] <filesystem|volume|snapshot> "
"<filesystem|volume|snapshot>\n"
@@ -261,7 +262,8 @@ get_usage(zfs_help_t idx)
return (gettext("\tsend [-DnPpRvLe] [-[iI] snapshot] "
"<snapshot>\n"
"\tsend [-Le] [-i snapshot|bookmark] "
"<filesystem|volume|snapshot>\n"));
"<filesystem|volume|snapshot>\n"
"\tsend [-nvPe] -t <receive_resume_token>\n"));
case HELP_SET:
return (gettext("\tset <property=value> ... "
"<filesystem|volume|snapshot> ...\n"));
@@ -3655,6 +3657,7 @@ zfs_do_send(int argc, char **argv)
{
char *fromname = NULL;
char *toname = NULL;
char *resume_token = NULL;
char *cp;
zfs_handle_t *zhp;
sendflags_t flags = { 0 };
@@ -3663,7 +3666,7 @@ zfs_do_send(int argc, char **argv)
boolean_t extraverbose = B_FALSE;

/* check options */
while ((c = getopt(argc, argv, ":i:I:RDpvnPLe")) != -1) {
while ((c = getopt(argc, argv, ":i:I:RDpvnPLet:")) != -1) {
switch (c) {
case 'i':
if (fromname)
@@ -3704,6 +3707,9 @@ zfs_do_send(int argc, char **argv)
case 'e':
flags.embed_data = B_TRUE;
break;
case 't':
resume_token = optarg;
break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
@@ -3719,14 +3725,28 @@ zfs_do_send(int argc, char **argv)
argc -= optind;
argv += optind;

/* check number of arguments */
if (argc < 1) {
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
usage(B_FALSE);
}
if (argc > 1) {
(void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE);
if (resume_token != NULL) {
if (fromname != NULL || flags.replicate || flags.props ||
flags.dedup) {
(void) fprintf(stderr,
gettext("invalid flags combined with -t\n"));
usage(B_FALSE);
}
if (argc != 0) {
(void) fprintf(stderr, gettext("no additional "
"arguments are permitted with -t\n"));
usage(B_FALSE);
}
} else {
if (argc < 1) {
(void) fprintf(stderr,
gettext("missing snapshot argument\n"));
usage(B_FALSE);
}
if (argc > 1) {
(void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE);
}
}

if (!flags.dryrun && isatty(STDOUT_FILENO)) {
@@ -3736,6 +3756,11 @@ zfs_do_send(int argc, char **argv)
return (1);
}

if (resume_token != NULL) {
return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,
resume_token));
}

/*
* Special case sending a filesystem, or from a bookmark.
*/
@@ -3841,23 +3866,23 @@ zfs_do_send(int argc, char **argv)
}

/*
* zfs receive [-vnFu] [-d | -e] <fs@snap>
*
* Restore a backup stream from stdin.
*/
static int
zfs_do_receive(int argc, char **argv)
{
int c, err;
recvflags_t flags = { 0 };
boolean_t abort_resumable = B_FALSE;

nvlist_t *props;
nvpair_t *nvp = NULL;

if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
nomem();

/* check options */
while ((c = getopt(argc, argv, ":o:denuvF")) != -1) {
while ((c = getopt(argc, argv, ":o:denuvFsA")) != -1) {
switch (c) {
case 'o':
if (parseprop(props, optarg) != 0)
@@ -3879,9 +3904,15 @@ zfs_do_receive(int argc, char **argv)
case 'v':
flags.verbose = B_TRUE;
break;
case 's':
flags.resumable = B_TRUE;
break;
case 'F':
flags.force = B_TRUE;
break;
case 'A':
abort_resumable = B_TRUE;
break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
@@ -3914,14 +3945,51 @@ zfs_do_receive(int argc, char **argv)
}
}

if (abort_resumable) {
if (flags.isprefix || flags.istail || flags.dryrun ||
flags.resumable || flags.nomount) {
(void) fprintf(stderr, gettext("invalid option"));
usage(B_FALSE);
}

char namebuf[ZFS_MAXNAMELEN];
(void) snprintf(namebuf, sizeof (namebuf),
"%s/%%recv", argv[0]);

if (zfs_dataset_exists(g_zfs, namebuf,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {
zfs_handle_t *zhp = zfs_open(g_zfs,
namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
if (zhp == NULL)
return (1);
err = zfs_destroy(zhp, B_FALSE);
} else {
zfs_handle_t *zhp = zfs_open(g_zfs,
argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
if (zhp == NULL)
usage(B_FALSE);
if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||
zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {
(void) fprintf(stderr,
gettext("'%s' does not have any "
"resumable receive state to abort\n"),
argv[0]);
return (1);
}
err = zfs_destroy(zhp, B_FALSE);
}

return (err != 0);
}

if (isatty(STDIN_FILENO)) {
(void) fprintf(stderr,
gettext("Error: Backup stream can not be read "
"from a terminal.\n"
"You must redirect standard input.\n"));
return (1);
}

err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);

return (err != 0);
@@ -5742,6 +5810,24 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
return (0);
}

/*
* If this filesystem is inconsistent and has a receive resume
* token, we can not mount it.
*/
if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
if (!explicit)
return (0);

(void) fprintf(stderr, gettext("cannot %s '%s': "
"Contains partially-completed state from "
"\"zfs receive -r\", which can be resumed with "
"\"zfs send -t\"\n"),
cmdname, zfs_get_name(zhp));
return (1);
}

/*
* At this point, we have verified that the mountpoint and/or
* shareopts are appropriate for auto management. If the
@@ -125,7 +125,7 @@ read_hdr(dmu_replay_record_t *drr, zio_cksum_t *cksum)
saved_cksum.zc_word[1],
saved_cksum.zc_word[2],
saved_cksum.zc_word[3]);
exit(1);
return (0);
}
return (sizeof (*drr));
}
@@ -346,8 +346,7 @@ main(int argc, char *argv[])
if (verbose)
(void) printf("\n");

if ((DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
DMU_COMPOUNDSTREAM) && drr->drr_payloadlen != 0) {
if (drr->drr_payloadlen != 0) {
nvlist_t *nv;
int sz = drr->drr_payloadlen;

@@ -342,6 +342,10 @@ zfs_prop_init(void)
zprop_register_string(ZFS_PROP_MLSLABEL, "mlslabel",
ZFS_MLSLABEL_DEFAULT, PROP_INHERIT, ZFS_TYPE_DATASET,
"<sensitivity label>", "MLSLABEL");
zprop_register_string(ZFS_PROP_RECEIVE_RESUME_TOKEN,
"receive_resume_token",
NULL, PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
"<string token>", "RESUMETOK");

/* readonly number properties */
zprop_register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY,
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright (c) 2011, 2014 by Delphix. All rights reserved.
#

LIBRARY= libzfs.a
@@ -72,6 +72,9 @@ LDLIBS += -lc -lm -ldevid -lgen -lnvpair -luutil -lavl -lefi \
-ladm -lidmap -ltsol -lmd -lumem -lzfs_core
CPPFLAGS += $(INCS) -D_LARGEFILE64_SOURCE=1 -D_REENTRANT

# There's no lint library for zlib, so only include this when building
$(DYNLIB) := LDLIBS += -lz

CERRWARN += -_gcc=-Wno-switch
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-uninitialized
@@ -604,6 +604,10 @@ typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
extern int zfs_send(zfs_handle_t *, const char *, const char *,
sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
extern int zfs_send_one(zfs_handle_t *, const char *, int, enum lzc_send_flags);
extern int zfs_send_resume(libzfs_handle_t *, sendflags_t *, int outfd,
const char *);
extern nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl,
const char *token);

extern int zfs_promote(zfs_handle_t *);
extern int zfs_hold(zfs_handle_t *, const char *, const char *,
@@ -644,6 +648,12 @@ typedef struct recvflags {
/* set "canmount=off" on all modified filesystems */
boolean_t canmountoff;

/*
* Mark the file systems as "resumable" and do not destroy them if the
* receive is interrupted
*/
boolean_t resumable;

/* byteswap flag is used internally; callers need not specify */
boolean_t byteswap;

@@ -1814,22 +1814,21 @@ getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
return (value);
}

static char *
static const char *
getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
{
nvlist_t *nv;
char *value;
const char *value;

*source = NULL;
if (nvlist_lookup_nvlist(zhp->zfs_props,
zfs_prop_to_name(prop), &nv) == 0) {
verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
value = fnvlist_lookup_string(nv, ZPROP_VALUE);
(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
} else {
verify(!zhp->zfs_props_table ||
zhp->zfs_props_table[prop] == B_TRUE);
if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
value = "";
value = zfs_prop_default_string(prop);
*source = "";
}

@@ -2231,7 +2230,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
{
char *source = NULL;
uint64_t val;
char *str;
const char *str;
const char *strval;
boolean_t received = zfs_is_recvd_props_mode(zhp);

@@ -2336,14 +2335,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
break;

case ZFS_PROP_ORIGIN:
(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
proplen);
/*
* If there is no parent at all, return failure to indicate that
* it doesn't apply to this dataset.
*/
if (propbuf[0] == '\0')
str = getprop_string(zhp, prop, &source);
if (str == NULL)
return (-1);
(void) strlcpy(propbuf, str, proplen);
break;

case ZFS_PROP_CLONES:
@@ -2520,8 +2515,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
break;

case PROP_TYPE_STRING:
(void) strlcpy(propbuf,
getprop_string(zhp, prop, &source), proplen);
str = getprop_string(zhp, prop, &source);
if (str == NULL)
return (-1);
(void) strlcpy(propbuf, str, proplen);
break;

case PROP_TYPE_INDEX:
@@ -1016,6 +1016,17 @@ mount_cb(zfs_handle_t *zhp, void *data)
return (0);
}

/*
* If this filesystem is inconsistent and has a receive resume
* token, we can not mount it.
*/
if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
zfs_close(zhp);
return (0);
}

libzfs_add_handle(cbp, zhp);
if (zfs_iter_filesystems(zhp, mount_cb, cbp) != 0) {
zfs_close(zhp);

0 comments on commit 9c3fd12

Please sign in to comment.
You can’t perform that action at this time.