Skip to content

Add 'zfs send --saved' flag #9007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ get_usage(zfs_help_t idx)
"<filesystem|volume|snapshot>\n"
"\tsend [-DnPpvLec] [-i bookmark|snapshot] "
"--redact <bookmark> <snapshot>\n"
"\tsend [-nvPe] -t <receive_resume_token>\n"));
"\tsend [-nvPe] -t <receive_resume_token>\n"
"\tsend [-Pnv] --saved filesystem\n"));
case HELP_SET:
return (gettext("\tset <property=value> ... "
"<filesystem|volume|snapshot> ...\n"));
Expand Down Expand Up @@ -4207,11 +4208,12 @@ zfs_do_send(int argc, char **argv)
{"raw", no_argument, NULL, 'w'},
{"backup", no_argument, NULL, 'b'},
{"holds", no_argument, NULL, 'h'},
{"saved", no_argument, NULL, 'S'},
{0, 0, 0, 0}
};

/* check options */
while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLeht:cwbd:",
while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLeht:cwbd:S",
long_options, NULL)) != -1) {
switch (c) {
case 'i':
Expand Down Expand Up @@ -4271,6 +4273,9 @@ zfs_do_send(int argc, char **argv)
flags.embed_data = B_TRUE;
flags.largeblock = B_TRUE;
break;
case 'S':
flags.saved = B_TRUE;
break;
case ':':
/*
* If a parameter was not passed, optopt contains the
Expand Down Expand Up @@ -4321,7 +4326,7 @@ zfs_do_send(int argc, char **argv)
if (resume_token != NULL) {
if (fromname != NULL || flags.replicate || flags.props ||
flags.backup || flags.dedup || flags.holds ||
redactbook != NULL) {
flags.saved || redactbook != NULL) {
(void) fprintf(stderr,
gettext("invalid flags combined with -t\n"));
usage(B_FALSE);
Expand All @@ -4342,6 +4347,23 @@ zfs_do_send(int argc, char **argv)
}
}

if (flags.saved) {
if (fromname != NULL || flags.replicate || flags.props ||
flags.doall || flags.backup || flags.dedup ||
flags.holds || flags.largeblock || flags.embed_data ||
flags.compress || flags.raw || redactbook != NULL) {
(void) fprintf(stderr, gettext("incompatible flags "
"combined with saved send flag\n"));
usage(B_FALSE);
}
if (strchr(argv[0], '@') != NULL) {
(void) fprintf(stderr, gettext("saved send must "
"specify the dataset with partially-received "
"state\n"));
usage(B_FALSE);
}
}

if (flags.raw && redactbook != NULL) {
(void) fprintf(stderr,
gettext("Error: raw sends may not be redacted.\n"));
Expand All @@ -4355,7 +4377,16 @@ zfs_do_send(int argc, char **argv)
return (1);
}

if (resume_token != NULL) {
if (flags.saved) {
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
if (zhp == NULL)
return (1);

err = zfs_send_saved(zhp, &flags, STDOUT_FILENO,
resume_token);
zfs_close(zhp);
return (err != 0);
} else if (resume_token != NULL) {
return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,
resume_token));
}
Expand Down
4 changes: 4 additions & 0 deletions include/libzfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,9 @@ typedef struct sendflags {

/* include snapshot holds in send stream */
boolean_t holds;

/* stream represents a partially received dataset */
boolean_t saved;
} sendflags_t;

typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
Expand All @@ -688,6 +691,7 @@ extern int zfs_send_one(zfs_handle_t *, const char *, int, sendflags_t *,
extern int zfs_send_progress(zfs_handle_t *, int, uint64_t *, uint64_t *);
extern int zfs_send_resume(libzfs_handle_t *, sendflags_t *, int outfd,
const char *);
extern int zfs_send_saved(zfs_handle_t *, sendflags_t *, int, const char *);
extern nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl,
const char *token);

Expand Down
1 change: 1 addition & 0 deletions include/libzfs_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ enum lzc_send_flags {
LZC_SEND_FLAG_LARGE_BLOCK = 1 << 1,
LZC_SEND_FLAG_COMPRESS = 1 << 2,
LZC_SEND_FLAG_RAW = 1 << 3,
LZC_SEND_FLAG_SAVED = 1 << 4,
};

int lzc_send(const char *, const char *, int, enum lzc_send_flags);
Expand Down
10 changes: 6 additions & 4 deletions include/sys/dmu_send.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ struct dmu_send_outparams;
int
dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
boolean_t large_block_ok, boolean_t compressok, boolean_t rawok,
uint64_t resumeobj, uint64_t resumeoff, const char *redactbook, int outfd,
offset_t *off, struct dmu_send_outparams *dsop);
boolean_t savedok, uint64_t resumeobj, uint64_t resumeoff,
const char *redactbook, int outfd, offset_t *off,
struct dmu_send_outparams *dsop);
int dmu_send_estimate_fast(struct dsl_dataset *ds, struct dsl_dataset *fromds,
zfs_bookmark_phys_t *frombook, boolean_t stream_compressed,
uint64_t *sizep);
boolean_t saved, uint64_t *sizep);
int dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap,
boolean_t embedok, boolean_t large_block_ok, boolean_t compressok,
boolean_t rawok, int outfd, offset_t *off, struct dmu_send_outparams *dso);
boolean_t rawok, boolean_t savedok, int outfd, offset_t *off,
struct dmu_send_outparams *dso);

typedef int (*dmu_send_outfunc_t)(objset_t *os, void *buf, int len, void *arg);
typedef struct dmu_send_outparams {
Expand Down
161 changes: 139 additions & 22 deletions lib/libzfs/libzfs_sendrecv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand All @@ -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"));
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions lib/libzfs_core/libzfs_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,8 @@ lzc_send_resume_redacted(const char *snapname, const char *from, int fd,
fnvlist_add_boolean(args, "compressok");
if (flags & LZC_SEND_FLAG_RAW)
fnvlist_add_boolean(args, "rawok");
if (flags & LZC_SEND_FLAG_SAVED)
fnvlist_add_boolean(args, "savedok");
if (resumeobj != 0 || resumeoff != 0) {
fnvlist_add_uint64(args, "resume_object", resumeobj);
fnvlist_add_uint64(args, "resume_offset", resumeoff);
Expand Down
21 changes: 21 additions & 0 deletions man/man8/zfs-send.8
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
.Fl t
.Ar receive_resume_token
.Nm
.Cm send
.Op Fl Pnv
.Fl S Ar filesystem
.Nm
.Cm redact
.Ar snapshot redaction_bookmark
.Ar redaction_snapshot Ns ...
Expand Down Expand Up @@ -503,6 +507,23 @@ See the documentation for
for more details.
.It Xo
.Nm
.Cm send
.Op Fl Pnv
.Op Fl i Ar snapshot Ns | Ns Ar bookmark
.Fl S
.Ar filesystem
.Xc
Generate a send stream from a dataset that has been partially received.
.Bl -tag -width "-L"
.It Fl S, -saved
This flag requires that the specified filesystem previously received a resumable
send that did not finish and was interrupted. In such scenarios this flag
enables the user to send this partially received state. Using this flag will
always use the last fully received snapshot as the incremental source if it
exists.
.El
.It Xo
.Nm
.Cm redact
.Ar snapshot redaction_bookmark
.Ar redaction_snapshot Ns ...
Expand Down
1 change: 0 additions & 1 deletion module/zfs/dmu_recv.c
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,6 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
drba->drba_cookie->drc_raw = B_TRUE;
}


if (featureflags & DMU_BACKUP_FEATURE_REDACTED) {
uint64_t *redact_snaps;
uint_t numredactsnaps;
Expand Down
Loading