summaryrefslogtreecommitdiffstats
path: root/lib/libzfs/libzfs_sendrecv.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libzfs/libzfs_sendrecv.c')
-rw-r--r--lib/libzfs/libzfs_sendrecv.c161
1 files changed, 139 insertions, 22 deletions
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index 20d29f48c..de3bfdbf4 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -1815,6 +1815,8 @@ lzc_flags_from_sendflags(const sendflags_t *flags)
lzc_flags |= LZC_SEND_FLAG_COMPRESS;
if (flags->raw)
lzc_flags |= LZC_SEND_FLAG_RAW;
+ if (flags->saved)
+ lzc_flags |= LZC_SEND_FLAG_SAVED;
return (lzc_flags);
}
@@ -1981,9 +1983,9 @@ find_redact_book(libzfs_handle_t *hdl, const char *path,
return (0);
}
-int
-zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
- const char *resume_token)
+static int
+zfs_send_resume_impl(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
+ nvlist_t *resume_nvl)
{
char errbuf[1024];
char *toname;
@@ -2001,15 +2003,6 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
(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->verbosity != 0) {
(void) fprintf(fout, dgettext(TEXT_DOMAIN,
"resume token contents:\n"));
@@ -2036,19 +2029,27 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
lzc_flags |= LZC_SEND_FLAG_COMPRESS;
if (flags->raw || nvlist_exists(resume_nvl, "rawok"))
lzc_flags |= LZC_SEND_FLAG_RAW;
+ if (flags->saved || nvlist_exists(resume_nvl, "savedok"))
+ lzc_flags |= LZC_SEND_FLAG_SAVED;
- 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);
+ if (flags->saved) {
+ (void) strcpy(name, toname);
+ } else {
+ error = guid_to_name(hdl, toname, toguid, B_FALSE, name);
+ if (error != 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));
}
- 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,
@@ -2199,6 +2200,122 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
return (error);
}
+int
+zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
+ const char *resume_token)
+{
+ int ret;
+ char errbuf[1024];
+ nvlist_t *resume_nvl;
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot resume send"));
+
+ 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));
+ }
+
+ ret = zfs_send_resume_impl(hdl, flags, outfd, resume_nvl);
+ nvlist_free(resume_nvl);
+
+ return (ret);
+}
+
+int
+zfs_send_saved(zfs_handle_t *zhp, sendflags_t *flags, int outfd,
+ const char *resume_token)
+{
+ int ret;
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ nvlist_t *saved_nvl = NULL, *resume_nvl = NULL;
+ uint64_t saved_guid = 0, resume_guid = 0;
+ uint64_t obj = 0, off = 0, bytes = 0;
+ char token_buf[ZFS_MAXPROPLEN];
+ char errbuf[1024];
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "saved send failed"));
+
+ ret = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
+ token_buf, sizeof (token_buf), NULL, NULL, 0, B_TRUE);
+ if (ret != 0)
+ goto out;
+
+ saved_nvl = zfs_send_resume_token_to_nvlist(hdl, token_buf);
+ if (saved_nvl == NULL) {
+ /*
+ * zfs_error_aux has already been set by
+ * zfs_send_resume_token_to_nvlist()
+ */
+ ret = zfs_error(hdl, EZFS_FAULT, errbuf);
+ goto out;
+ }
+
+ /*
+ * If a resume token is provided we use the object and offset
+ * from that instead of the default, which starts from the
+ * beginning.
+ */
+ if (resume_token != NULL) {
+ resume_nvl = zfs_send_resume_token_to_nvlist(hdl,
+ resume_token);
+ if (resume_nvl == NULL) {
+ ret = zfs_error(hdl, EZFS_FAULT, errbuf);
+ goto out;
+ }
+
+ if (nvlist_lookup_uint64(resume_nvl, "object", &obj) != 0 ||
+ nvlist_lookup_uint64(resume_nvl, "offset", &off) != 0 ||
+ nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 ||
+ nvlist_lookup_uint64(resume_nvl, "toguid",
+ &resume_guid) != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "provided resume token is corrupt"));
+ ret = zfs_error(hdl, EZFS_FAULT, errbuf);
+ goto out;
+ }
+
+ if (nvlist_lookup_uint64(saved_nvl, "toguid",
+ &saved_guid)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "dataset's resume token is corrupt"));
+ ret = zfs_error(hdl, EZFS_FAULT, errbuf);
+ goto out;
+ }
+
+ if (resume_guid != saved_guid) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "provided resume token does not match dataset"));
+ ret = zfs_error(hdl, EZFS_BADBACKUP, errbuf);
+ goto out;
+ }
+ }
+
+ (void) nvlist_remove_all(saved_nvl, "object");
+ fnvlist_add_uint64(saved_nvl, "object", obj);
+
+ (void) nvlist_remove_all(saved_nvl, "offset");
+ fnvlist_add_uint64(saved_nvl, "offset", off);
+
+ (void) nvlist_remove_all(saved_nvl, "bytes");
+ fnvlist_add_uint64(saved_nvl, "bytes", bytes);
+
+ (void) nvlist_remove_all(saved_nvl, "toname");
+ fnvlist_add_string(saved_nvl, "toname", zhp->zfs_name);
+
+ ret = zfs_send_resume_impl(hdl, flags, outfd, saved_nvl);
+
+out:
+ nvlist_free(saved_nvl);
+ nvlist_free(resume_nvl);
+ return (ret);
+}
+
/*
* This function informs the target system that the recursive send is complete.
* The record is also expected in the case of a send -p.