Author: mav
Date: Fri Nov 13 09:30:17 2015
New Revision: 290756
URL: https://svnweb.freebsd.org/changeset/base/290756

Log:
  MFC r289362, r289445: 2605 want to resume interrupted zfs send
  
  Reviewed by: George Wilson <george.wil...@delphix.com>
  Reviewed by: Paul Dagnelie <p...@delphix.com>
  Reviewed by: Richard Elling <richard.ell...@richardelling.com>
  Reviewed by: Xin Li <delp...@freebsd.org>
  Reviewed by: Arne Jansen <sensi...@gmx.net>
  Approved by: Dan McDonald <dan...@omniti.com>
  Author: Matthew Ahrens <mahr...@delphix.com>
  
  illumos/illumos-gate@9c3fd1216fa7fb02cfbc78a2518a686d54b48ab8
  
  For more info, see:
   - slides http://www.slideshare.net/MatthewAhrens/openzfs-send-and-receive
   - video https://www.youtube.com/watch?v=iY44jPMvxog
   - manpage changes (for zfs resume -s and zfs send -t)
   - upcoming talk at the OpenZFS Developer Summit
  
  The TL;DR is:
  Use "zfs receive -s" to save the partially received state on failure.
  On failure, get the receive token with "zfs get receive_resume_token <fs>"
  Resume the send with "zfs send -t <token_value>"
  
  Relnotes:       yes

Modified:
  stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs.8
  stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
  stable/10/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c
  stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
  stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
  stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
  stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
  stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
  stable/10/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c
  stable/10/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h
  stable/10/cddl/lib/libzfs/Makefile
  stable/10/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
  stable/10/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h
  stable/10/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_send.h
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs.8
==============================================================================
--- stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs.8    Fri Nov 13 09:25:47 
2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs.8    Fri Nov 13 09:30:17 
2015        (r290756)
@@ -189,17 +189,25 @@
 .Op Fl i Ar snapshot Ns | Ns bookmark
 .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
 .Nm
+.Cm send
+.Op Fl Penv
+.Fl t Ar receive_resume_token
+.Nm
 .Cm receive Ns | Ns Cm recv
-.Op Fl vnFu
+.Op Fl vnsFu
 .Op Fl o Sy origin Ns = Ns Ar snapshot
 .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
 .Nm
 .Cm receive Ns | Ns Cm recv
-.Op Fl vnFu
+.Op Fl vnsFu
 .Op Fl d | e
 .Op Fl o Sy origin Ns = Ns Ar snapshot
 .Ar filesystem
 .Nm
+.Cm receive Ns | Ns Cm recv
+.Fl A
+.Ar filesystem Ns | Ns Ar volume
+.Nm
 .Cm allow
 .Ar filesystem Ns | Ns Ar volume
 .Nm
@@ -597,6 +605,13 @@ For cloned file systems or volumes, the 
 created. See also the
 .Sy clones
 property.
+.It Sy receive_resume_token
+For filesystems or volumes which have saved partially-completed state from
+.Sy zfs receive -s ,
+this opaque token can be provided to
+.Sy zfs send -t
+to resume and complete the
+.Sy zfs receive .
 .It Sy referenced
 The amount of data that is accessible by this dataset, which may or may not be
 shared with other datasets in the pool. When a snapshot or clone is created, it
@@ -2714,15 +2729,28 @@ feature.
 .El
 .It Xo
 .Nm
+.Cm send
+.Op Fl Penv
+.Fl t
+.Ar receive_resume_token
+.Xc
+Creates a send stream which resumes an interrupted receive.  The
+.Ar receive_resume_token
+is the value of this property on the filesystem
+or volume that was being received into.  See the documentation for
+.Sy zfs receive -s
+for more details.
+.It Xo
+.Nm
 .Cm receive Ns | Ns Cm recv
-.Op Fl vnFu
+.Op Fl vnsFu
 .Op Fl o Sy origin Ns = Ns Ar snapshot
 .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
 .Xc
 .It Xo
 .Nm
 .Cm receive Ns | Ns Cm recv
-.Op Fl vnFu
+.Op Fl vnsFu
 .Op Fl d | e
 .Op Fl o Sy origin Ns = Ns Ar snapshot
 .Ar filesystem
@@ -2819,9 +2847,42 @@ performing the receive operation. If rec
 stream (for example, one generated by
 .Qq Nm Cm send Fl R Bro Fl i | Fl I Brc ) ,
 destroy snapshots and file systems that do not exist on the sending side.
+.It Fl s
+If the receive is interrupted, save the partially received state, rather
+than deleting it.  Interruption may be due to premature termination of
+the stream
+.Po e.g. due to network failure or failure of the remote system
+if the stream is being read over a network connection
+.Pc ,
+a checksum error in the stream, termination of the
+.Nm zfs Cm receive
+process, or unclean shutdown of the system.
+.Pp
+The receive can be resumed with a stream generated by
+.Nm zfs Cm send Fl t Ar token ,
+where the
+.Ar token
+is the value of the
+.Sy receive_resume_token
+property of the filesystem or volume which is received into.
+.Pp
+To use this flag, the storage pool must have the
+.Sy extensible_dataset
+feature enabled.  See
+.Xr zpool-features 5
+for details on ZFS feature flags.
 .El
 .It Xo
 .Nm
+.Cm receive Ns | Ns Cm recv
+.Fl A
+.Ar filesystem Ns | Ns Ar volume
+.Xc
+Abort an interrupted
+.Nm zfs Cm receive Fl s ,
+deleting its saved partially received state.
+.It Xo
+.Nm
 .Cm allow
 .Ar filesystem Ns | Ns Ar volume
 .Xc

Modified: stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c       Fri Nov 13 
09:25:47 2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c       Fri Nov 13 
09:30:17 2015        (r290756)
@@ -263,10 +263,11 @@ get_usage(zfs_help_t idx)
        case HELP_PROMOTE:
                return (gettext("\tpromote <clone-filesystem>\n"));
        case HELP_RECEIVE:
-               return (gettext("\treceive|recv [-vnFu] <filesystem|volume|"
+               return (gettext("\treceive|recv [-vnsFu] <filesystem|volume|"
                    "snapshot>\n"
-                   "\treceive|recv [-vnFu] [-o origin=<snapshot>] [-d | -e] "
-                   "<filesystem>\n"));
+                   "\treceive|recv [-vnsFu] [-o origin=<snapshot>] [-d | -e] "
+                   "<filesystem>\n"
+                   "\treceive|recv -A <filesystem|volume>\n"));
        case HELP_RENAME:
                return (gettext("\trename [-f] <filesystem|volume|snapshot> "
                    "<filesystem|volume|snapshot>\n"
@@ -279,7 +280,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"));
@@ -3728,6 +3730,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 };
@@ -3736,7 +3739,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)
@@ -3777,6 +3780,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);
@@ -3792,14 +3798,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)) {
@@ -3809,6 +3829,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.
         */
@@ -3914,8 +3939,6 @@ zfs_do_send(int argc, char **argv)
 }
 
 /*
- * zfs receive [-vnFu] [-d | -e] <fs@snap>
- *
  * Restore a backup stream from stdin.
  */
 static int
@@ -3923,6 +3946,8 @@ 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;
 
@@ -3930,7 +3955,7 @@ zfs_do_receive(int argc, char **argv)
                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)
@@ -3952,9 +3977,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);
@@ -3987,6 +4018,44 @@ 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 "
@@ -3994,7 +4063,6 @@ zfs_do_receive(int argc, char **argv)
                    "You must redirect standard input.\n"));
                return (1);
        }
-
        err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);
 
        return (err != 0);
@@ -5816,6 +5884,24 @@ share_mount_one(zfs_handle_t *zhp, int o
        }
 
        /*
+        * 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
         * filesystem is already mounted or shared, return (failing

Modified: stable/10/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c    Fri Nov 
13 09:25:47 2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c    Fri Nov 
13 09:30:17 2015        (r290756)
@@ -125,7 +125,7 @@ read_hdr(dmu_replay_record_t *drr, zio_c
                    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;
 

Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h       Fri Nov 
13 09:25:47 2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h       Fri Nov 
13 09:30:17 2015        (r290756)
@@ -621,6 +621,10 @@ typedef boolean_t (snapfilter_cb_t)(zfs_
 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 *,
@@ -661,6 +665,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;
 

Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c        
Fri Nov 13 09:25:47 2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c        
Fri Nov 13 09:30:17 2015        (r290756)
@@ -74,6 +74,9 @@ zcmd_ioctl(int fd, int request, zfs_cmd_
 
        if (zfs_ioctl_version >= ZFS_IOCVER_DEADMAN) {
                switch (zfs_ioctl_version) {
+               case ZFS_IOCVER_EDBP:
+                       cflag = ZFS_CMD_COMPAT_EDBP;
+                       break;
                case ZFS_IOCVER_ZCMD:
                        cflag = ZFS_CMD_COMPAT_ZCMD;
                        break;

Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c       
Fri Nov 13 09:25:47 2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c       
Fri Nov 13 09:30:17 2015        (r290756)
@@ -1772,22 +1772,21 @@ getprop_uint64(zfs_handle_t *zhp, zfs_pr
        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 = "";
        }
 
@@ -2189,7 +2188,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop
 {
        char *source = NULL;
        uint64_t val;
-       char *str;
+       const char *str;
        const char *strval;
        boolean_t received = zfs_is_recvd_props_mode(zhp);
 
@@ -2294,14 +2293,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop
                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:
@@ -2482,8 +2477,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop
                        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:

Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c Fri Nov 
13 09:25:47 2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c Fri Nov 
13 09:30:17 2015        (r290756)
@@ -1072,6 +1072,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);

Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c      
Fri Nov 13 09:25:47 2015        (r290755)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c      
Fri Nov 13 09:30:17 2015        (r290756)
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright (c) 2012 Pawel Jakub Dawidek <pa...@dawidek.net>.
  * All rights reserved.
@@ -51,6 +51,7 @@
 #include "zfs_prop.h"
 #include "zfs_fletcher.h"
 #include "libzfs_impl.h"
+#include <zlib.h>
 #include <sha2.h>
 #include <sys/zio_checksum.h>
 #include <sys/ddt.h>
@@ -67,6 +68,8 @@ extern void zfs_setprop_error(libzfs_han
 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 *);
+static int guid_to_name(libzfs_handle_t *, const char *,
+    uint64_t, boolean_t, char *);
 
 static const zio_cksum_t zero_cksum = { 0 };
 
@@ -284,8 +287,7 @@ cksummer(void *arg)
                            DMU_BACKUP_FEATURE_DEDUPPROPS);
                        DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags);
 
-                       if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
-                           DMU_COMPOUNDSTREAM && drr->drr_payloadlen != 0) {
+                       if (drr->drr_payloadlen != 0) {
                                sz = drr->drr_payloadlen;
 
                                if (sz > SPA_MAXBLOCKSIZE) {
@@ -995,17 +997,14 @@ static void *
 send_progress_thread(void *arg)
 {
        progress_arg_t *pa = arg;
-
        zfs_cmd_t zc = { 0 };
        zfs_handle_t *zhp = pa->pa_zhp;
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        unsigned long long bytes;
        char buf[16];
-
        time_t t;
        struct tm *tm;
 
-       assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
        if (!pa->pa_parsable)
@@ -1038,6 +1037,51 @@ send_progress_thread(void *arg)
        }
 }
 
+static void
+send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap,
+    uint64_t size, boolean_t parsable)
+{
+       if (parsable) {
+               if (fromsnap != NULL) {
+                       (void) fprintf(fout, "incremental\t%s\t%s",
+                           fromsnap, tosnap);
+               } else {
+                       (void) fprintf(fout, "full\t%s",
+                           tosnap);
+               }
+       } else {
+               if (fromsnap != NULL) {
+                       if (strchr(fromsnap, '@') == NULL &&
+                           strchr(fromsnap, '#') == NULL) {
+                               (void) fprintf(fout, dgettext(TEXT_DOMAIN,
+                                   "send from @%s to %s"),
+                                   fromsnap, tosnap);
+                       } else {
+                               (void) fprintf(fout, dgettext(TEXT_DOMAIN,
+                                   "send from %s to %s"),
+                                   fromsnap, tosnap);
+                       }
+               } else {
+                       (void) fprintf(fout, dgettext(TEXT_DOMAIN,
+                           "full send of %s"),
+                           tosnap);
+               }
+       }
+
+       if (size != 0) {
+               if (parsable) {
+                       (void) fprintf(fout, "\t%llu",
+                           (longlong_t)size);
+               } else {
+                       char buf[16];
+                       zfs_nicenum(size, buf, sizeof (buf));
+                       (void) fprintf(fout, dgettext(TEXT_DOMAIN,
+                           " estimated size is %s"), buf);
+               }
+       }
+       (void) fprintf(fout, "\n");
+}
+
 static int
 dump_snapshot(zfs_handle_t *zhp, void *arg)
 {
@@ -1117,37 +1161,14 @@ dump_snapshot(zfs_handle_t *zhp, void *a
            (sdd->fromorigin || sdd->replicate);
 
        if (sdd->verbose) {
-               uint64_t size;
-               err = estimate_ioctl(zhp, sdd->prevsnap_obj,
+               uint64_t size = 0;
+               (void) estimate_ioctl(zhp, sdd->prevsnap_obj,
                    fromorigin, &size);
 
-               if (sdd->parsable) {
-                       if (sdd->prevsnap[0] != '\0') {
-                               (void) fprintf(fout, "incremental\t%s\t%s",
-                                   sdd->prevsnap, zhp->zfs_name);
-                       } else {
-                               (void) fprintf(fout, "full\t%s",
-                                   zhp->zfs_name);
-                       }
-               } else {
-                       (void) fprintf(fout, dgettext(TEXT_DOMAIN,
-                           "send from @%s to %s"),
-                           sdd->prevsnap, zhp->zfs_name);
-               }
-               if (err == 0) {
-                       if (sdd->parsable) {
-                               (void) fprintf(fout, "\t%llu\n",
-                                   (longlong_t)size);
-                       } else {
-                               char buf[16];
-                               zfs_nicenum(size, buf, sizeof (buf));
-                               (void) fprintf(fout, dgettext(TEXT_DOMAIN,
-                                   " estimated size is %s\n"), buf);
-                       }
-                       sdd->size += size;
-               } else {
-                       (void) fprintf(fout, "\n");
-               }
+               send_print_verbose(fout, zhp->zfs_name,
+                   sdd->prevsnap[0] ? sdd->prevsnap : NULL,
+                   size, sdd->parsable);
+               sdd->size += size;
        }
 
        if (!sdd->dryrun) {
@@ -1358,6 +1379,233 @@ again:
        return (0);
 }
 
+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, &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)
+{
+       char errbuf[1024];
+       char *toname;
+       char *fromname = NULL;
+       uint64_t resumeobj, resumeoff, toguid, fromguid, bytes;
+       zfs_handle_t *zhp;
+       int error = 0;
+       char name[ZFS_MAXNAMELEN];
+       enum lzc_send_flags lzc_flags = 0;
+
+       (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+           "cannot resume send"));
+
+       nvlist_t *resume_nvl =
+           zfs_send_resume_token_to_nvlist(hdl, resume_token);
+       if (resume_nvl == NULL) {
+               /*
+                * zfs_error_aux has already been set by
+                * 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 ||
+           nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 ||
+           nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) {
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "resume token is corrupt"));
+               return (zfs_error(hdl, EZFS_FAULT, errbuf));
+       }
+       fromguid = 0;
+       (void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid);
+
+       if (flags->embed_data || nvlist_exists(resume_nvl, "embedok"))
+               lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
+
+       if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) {
+               if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "'%s' is no longer the same snapshot used in "
+                           "the initial send"), toname);
+               } else {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "'%s' used in the initial send no longer exists"),
+                           toname);
+               }
+               return (zfs_error(hdl, EZFS_BADPATH, errbuf));
+       }
+       zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
+       if (zhp == NULL) {
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "unable to access '%s'"), name);
+               return (zfs_error(hdl, EZFS_BADPATH, errbuf));
+       }
+
+       if (fromguid != 0) {
+               if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "incremental source %#llx no longer exists"),
+                           (longlong_t)fromguid);
+                       return (zfs_error(hdl, EZFS_BADPATH, errbuf));
+               }
+               fromname = name;
+       }
+
+       if (flags->verbose) {
+               uint64_t size = 0;
+               error = lzc_send_space(zhp->zfs_name, fromname, &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 };
+               pthread_t tid;
+               /*
+                * If progress reporting is requested, spawn a new thread to
+                * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
+                */
+               if (flags->progress) {
+                       pa.pa_zhp = zhp;
+                       pa.pa_fd = outfd;
+                       pa.pa_parsable = flags->parsable;
+
+                       error = pthread_create(&tid, NULL,
+                           send_progress_thread, &pa);
+                       if (error != 0) {
+                               zfs_close(zhp);
+                               return (error);
+                       }
+               }
+
+               error = lzc_send_resume(zhp->zfs_name, fromname, outfd,
+                   lzc_flags, resumeobj, resumeoff);
+
+               if (flags->progress) {
+                       (void) pthread_cancel(tid);
+                       (void) pthread_join(tid, NULL);
+               }
+
+               char errbuf[1024];
+               (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+                   "warning: cannot send '%s'"), zhp->zfs_name);
+
+               zfs_close(zhp);
+
+               switch (error) {
+               case 0:
+                       return (0);
+               case EXDEV:
+               case ENOENT:
+               case EDQUOT:
+               case EFBIG:
+               case EIO:
+               case ENOLINK:
+               case ENOSPC:
+#ifdef illumos
+               case ENOSTR:
+#endif
+               case ENXIO:
+               case EPIPE:
+               case ERANGE:
+               case EFAULT:
+               case EROFS:
+                       zfs_error_aux(hdl, strerror(errno));
+                       return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
+
+               default:
+                       return (zfs_standard_error(hdl, errno, errbuf));
+               }
+       }
+
+
+       zfs_close(zhp);
+
+       return (error);
+}
+
 /*
  * Generate a send stream for the dataset identified by the argument zhp.
  *
@@ -1897,6 +2145,7 @@ recv_destroy(libzfs_handle_t *hdl, const
 
 typedef struct guid_to_name_data {
        uint64_t guid;
+       boolean_t bookmark_ok;
        char *name;
        char *skip;
 } guid_to_name_data_t;
@@ -1905,20 +2154,25 @@ static int
 guid_to_name_cb(zfs_handle_t *zhp, void *arg)
 {
        guid_to_name_data_t *gtnd = arg;
+       const char *slash;
        int err;
 
        if (gtnd->skip != NULL &&
-           strcmp(zhp->zfs_name, gtnd->skip) == 0) {
+           (slash = strrchr(zhp->zfs_name, '/')) != NULL &&
+           strcmp(slash + 1, gtnd->skip) == 0) {
+               zfs_close(zhp);
                return (0);
        }
 
-       if (zhp->zfs_dmustats.dds_guid == gtnd->guid) {
+       if (zfs_prop_get_int(zhp, ZFS_PROP_GUID) == gtnd->guid) {
                (void) strcpy(gtnd->name, zhp->zfs_name);
                zfs_close(zhp);
                return (EEXIST);
        }
 
        err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
+       if (err != EEXIST && gtnd->bookmark_ok)
+               err = zfs_iter_bookmarks(zhp, guid_to_name_cb, gtnd);
        zfs_close(zhp);
        return (err);
 }
@@ -1932,45 +2186,48 @@ guid_to_name_cb(zfs_handle_t *zhp, void 
  */
 static int
 guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
-    char *name)
+    boolean_t bookmark_ok, char *name)
 {
-       /* exhaustive search all local snapshots */
        char pname[ZFS_MAXNAMELEN];
        guid_to_name_data_t gtnd;
-       int err = 0;
-       zfs_handle_t *zhp;
-       char *cp;
 
        gtnd.guid = guid;
+       gtnd.bookmark_ok = bookmark_ok;
        gtnd.name = name;
        gtnd.skip = NULL;
 
-       (void) strlcpy(pname, parent, sizeof (pname));
-
        /*
-        * Search progressively larger portions of the hierarchy.  This will
+        * Search progressively larger portions of the hierarchy, starting
+        * with the filesystem specified by 'parent'.  This will
         * select the "most local" version of the origin snapshot in the case
         * that there are multiple matching snapshots in the system.
         */
-       while ((cp = strrchr(pname, '/')) != NULL) {
-
+       (void) strlcpy(pname, parent, sizeof (pname));
+       char *cp = strrchr(pname, '@');
+       if (cp == NULL)
+               cp = strchr(pname, '\0');
+       for (; cp != NULL; cp = strrchr(pname, '/')) {
                /* Chop off the last component and open the parent */
                *cp = '\0';
-               zhp = make_dataset_handle(hdl, pname);
+               zfs_handle_t *zhp = make_dataset_handle(hdl, pname);
 
                if (zhp == NULL)
                        continue;
-
-               err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
+               int err = guid_to_name_cb(zfs_handle_dup(zhp), &gtnd);
+               if (err != EEXIST)
+                       err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
+               if (err != EEXIST && bookmark_ok)
+                       err = zfs_iter_bookmarks(zhp, guid_to_name_cb, &gtnd);
                zfs_close(zhp);
                if (err == EEXIST)
                        return (0);
 
                /*
-                * Remember the dataset that we already searched, so we
-                * skip it next time through.
+                * Remember the last portion of the dataset so we skip it next
+                * time through (as we've already searched that portion of the
+                * hierarchy).
                 */
-               gtnd.skip = pname;
+               gtnd.skip = strrchr(pname, '/') + 1;
        }
 
        return (ENOENT);
@@ -2568,11 +2825,9 @@ recv_skip(libzfs_handle_t *hdl, int fd, 
 
                switch (drr->drr_type) {
                case DRR_BEGIN:
-                       /* NB: not to be used on v2 stream packages */
                        if (drr->drr_payloadlen != 0) {
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "invalid substream header"));
-                               return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
+                               (void) recv_read(hdl, fd, buf,
+                                   drr->drr_payloadlen, B_FALSE, NULL);
                        }
                        break;
 
@@ -2633,6 +2888,40 @@ recv_skip(libzfs_handle_t *hdl, int fd, 
        return (-1);
 }
 
+static void
+recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap,
+    boolean_t resumable)
+{
+       char target_fs[ZFS_MAXNAMELEN];

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to