mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 10:54:35 +03:00
OpenZFS 2605, 6980, 6902
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> Ported-by: kernelOfTruth <kerneloftruth@gmail.com> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> OpenZFS-issue: https://www.illumos.org/issues/2605 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/9c3fd12 6980 6902 causes zfs send to break due to 32-bit/64-bit struct mismatch Reviewed by: Paul Dagnelie <pcd@delphix.com> Reviewed by: George Wilson <george.wilson@delphix.com> Approved by: Robert Mustacchi <rm@joyent.com> Ported by: Brian Behlendorf <behlendorf1@llnl.gov> OpenZFS-issue: https://www.illumos.org/issues/6980 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/ea4a67f Porting notes: - All rsend and snapshop tests enabled and updated for Linux. - Fix misuse of input argument in traverse_visitbp(). - Fix ISO C90 warnings and errors. - Fix gcc 'missing braces around initializer' in 'struct send_thread_arg to_arg =' warning. - Replace 4 argument fletcher_4_native() with 3 argument version, this change was made in OpenZFS 4185 which has not been ported. - Part of the sections for 'zfs receive' and 'zfs send' was rewritten and reordered to approximate upstream. - Fix mktree xattr creation, 'user.' prefix required. - Minor fixes to newly enabled test cases - Long holds for volumes allowed during receive for minor registration.
This commit is contained in:
committed by
Brian Behlendorf
parent
669cf0ab29
commit
47dfff3b86
+12
-15
@@ -1865,22 +1865,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 = "";
|
||||
}
|
||||
|
||||
@@ -2301,7 +2300,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);
|
||||
|
||||
@@ -2407,14 +2406,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:
|
||||
@@ -2596,8 +2591,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:
|
||||
|
||||
@@ -1051,6 +1051,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);
|
||||
|
||||
+377
-72
@@ -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 <pawel@dawidek.net>.
|
||||
* All rights reserved
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "zfs_prop.h"
|
||||
#include "zfs_fletcher.h"
|
||||
#include "libzfs_impl.h"
|
||||
#include <zlib.h>
|
||||
#include <sys/zio_checksum.h>
|
||||
#include <sys/ddt.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -66,6 +67,8 @@ 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 *);
|
||||
static int guid_to_name(libzfs_handle_t *, const char *,
|
||||
uint64_t, boolean_t, char *);
|
||||
|
||||
static const zio_cksum_t zero_cksum = { { 0 } };
|
||||
|
||||
@@ -234,7 +237,7 @@ cksummer(void *arg)
|
||||
{
|
||||
dedup_arg_t *dda = arg;
|
||||
char *buf = zfs_alloc(dda->dedup_hdl, SPA_MAXBLOCKSIZE);
|
||||
dmu_replay_record_t thedrr;
|
||||
dmu_replay_record_t thedrr = { 0 };
|
||||
dmu_replay_record_t *drr = &thedrr;
|
||||
FILE *ofp;
|
||||
int outfd;
|
||||
@@ -283,8 +286,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) {
|
||||
@@ -1013,17 +1015,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)
|
||||
@@ -1056,6 +1055,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)
|
||||
{
|
||||
@@ -1135,37 +1179,14 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
|
||||
(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) {
|
||||
@@ -1376,6 +1397,231 @@ again:
|
||||
return (0);
|
||||
}
|
||||
|
||||
nvlist_t *
|
||||
zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
|
||||
{
|
||||
unsigned int version;
|
||||
int nread, i;
|
||||
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 (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:
|
||||
case ENOSTR:
|
||||
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.
|
||||
*
|
||||
@@ -1451,7 +1697,9 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
||||
dmu_replay_record_t drr = { 0 };
|
||||
char *packbuf = NULL;
|
||||
size_t buflen = 0;
|
||||
zio_cksum_t zc = { { 0 } };
|
||||
zio_cksum_t zc;
|
||||
|
||||
ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
|
||||
|
||||
if (flags->replicate || flags->props) {
|
||||
nvlist_t *hdrnv;
|
||||
@@ -1913,6 +2161,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
|
||||
|
||||
typedef struct guid_to_name_data {
|
||||
uint64_t guid;
|
||||
boolean_t bookmark_ok;
|
||||
char *name;
|
||||
char *skip;
|
||||
} guid_to_name_data_t;
|
||||
@@ -1921,20 +2170,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);
|
||||
}
|
||||
@@ -1948,45 +2202,48 @@ guid_to_name_cb(zfs_handle_t *zhp, void *arg)
|
||||
*/
|
||||
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, >nd);
|
||||
int err = guid_to_name_cb(zfs_handle_dup(zhp), >nd);
|
||||
if (err != EEXIST)
|
||||
err = zfs_iter_children(zhp, guid_to_name_cb, >nd);
|
||||
if (err != EEXIST && bookmark_ok)
|
||||
err = zfs_iter_bookmarks(zhp, guid_to_name_cb, >nd);
|
||||
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);
|
||||
@@ -2586,11 +2843,9 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
|
||||
|
||||
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;
|
||||
|
||||
@@ -2651,6 +2906,40 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap,
|
||||
boolean_t resumable)
|
||||
{
|
||||
char target_fs[ZFS_MAXNAMELEN];
|
||||
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"checksum mismatch or incomplete stream"));
|
||||
|
||||
if (!resumable)
|
||||
return;
|
||||
(void) strlcpy(target_fs, target_snap, sizeof (target_fs));
|
||||
*strchr(target_fs, '@') = '\0';
|
||||
zfs_handle_t *zhp = zfs_open(hdl, target_fs,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
||||
if (zhp == NULL)
|
||||
return;
|
||||
|
||||
char token_buf[ZFS_MAXPROPLEN];
|
||||
int error = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
|
||||
token_buf, sizeof (token_buf),
|
||||
NULL, NULL, 0, B_TRUE);
|
||||
if (error == 0) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"checksum mismatch or incomplete stream.\n"
|
||||
"Partially received snapshot is saved.\n"
|
||||
"A resuming stream can be generated on the sending "
|
||||
"system by running:\n"
|
||||
" zfs send -t %s"),
|
||||
token_buf);
|
||||
}
|
||||
zfs_close(zhp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restores a backup of tosnap from the file descriptor specified by infd.
|
||||
*/
|
||||
@@ -2799,7 +3088,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
*/
|
||||
if (drrb->drr_flags & DRR_FLAG_CLONE) {
|
||||
if (guid_to_name(hdl, zc.zc_value,
|
||||
drrb->drr_fromguid, zc.zc_string) != 0) {
|
||||
drrb->drr_fromguid, B_FALSE, zc.zc_string) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"local origin for clone %s does not exist"),
|
||||
@@ -2815,8 +3104,10 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
zc.zc_string);
|
||||
}
|
||||
|
||||
boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
||||
DMU_BACKUP_FEATURE_RESUMING;
|
||||
stream_wantsnewfs = (drrb->drr_fromguid == 0 ||
|
||||
(drrb->drr_flags & DRR_FLAG_CLONE) || originsnap);
|
||||
(drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming;
|
||||
|
||||
if (stream_wantsnewfs) {
|
||||
/*
|
||||
@@ -2835,7 +3126,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
char suffix[ZFS_MAXNAMELEN];
|
||||
(void) strcpy(suffix, strrchr(zc.zc_value, '/'));
|
||||
if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
|
||||
zc.zc_value) == 0) {
|
||||
B_FALSE, zc.zc_value) == 0) {
|
||||
*strchr(zc.zc_value, '@') = '\0';
|
||||
(void) strcat(zc.zc_value, suffix);
|
||||
}
|
||||
@@ -2862,7 +3153,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
char snap[ZFS_MAXNAMELEN];
|
||||
(void) strcpy(snap, strchr(zc.zc_value, '@'));
|
||||
if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
|
||||
zc.zc_value) == 0) {
|
||||
B_FALSE, zc.zc_value) == 0) {
|
||||
*strchr(zc.zc_value, '@') = '\0';
|
||||
(void) strcat(zc.zc_value, snap);
|
||||
}
|
||||
@@ -2876,11 +3167,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
zfs_handle_t *zhp;
|
||||
|
||||
/*
|
||||
* Destination fs exists. Therefore this should either
|
||||
* be an incremental, or the stream specifies a new fs
|
||||
* (full stream or clone) and they want us to blow it
|
||||
* away (and have therefore specified -F and removed any
|
||||
* snapshots).
|
||||
* Destination fs exists. It must be one of these cases:
|
||||
* - an incremental send stream
|
||||
* - the stream specifies a new fs (full stream or clone)
|
||||
* and they want us to blow away the existing fs (and
|
||||
* have therefore specified -F and removed any snapshots)
|
||||
* - we are resuming a failed receive.
|
||||
*/
|
||||
if (stream_wantsnewfs) {
|
||||
if (!flags->force) {
|
||||
@@ -2935,6 +3227,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
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
|
||||
* did the first recv, we would have received it into
|
||||
* .../%recv).
|
||||
*/
|
||||
if (resuming && zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT))
|
||||
newfs = B_TRUE;
|
||||
|
||||
zfs_close(zhp);
|
||||
} else {
|
||||
/*
|
||||
@@ -2967,9 +3271,10 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
newfs = B_TRUE;
|
||||
}
|
||||
|
||||
zc.zc_begin_record = drr_noswap->drr_u.drr_begin;
|
||||
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",
|
||||
@@ -3106,8 +3411,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
||||
break;
|
||||
case ECKSUM:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"invalid stream (checksum mismatch)"));
|
||||
recv_ecksum_set_aux(hdl, zc.zc_value, flags->resumable);
|
||||
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
||||
break;
|
||||
case ENOTSUP:
|
||||
@@ -3309,7 +3613,8 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
|
||||
* Restores a backup of tosnap from the file descriptor specified by infd.
|
||||
* Return 0 on total success, -2 if some things couldn't be
|
||||
* destroyed/renamed/promoted, -1 if some things couldn't be received.
|
||||
* (-1 will override -2).
|
||||
* (-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,
|
||||
|
||||
@@ -467,6 +467,13 @@ lzc_get_holds(const char *snapname, nvlist_t **holdsp)
|
||||
int
|
||||
lzc_send(const char *snapname, const char *from, int fd,
|
||||
enum lzc_send_flags flags)
|
||||
{
|
||||
return (lzc_send_resume(snapname, from, fd, flags, 0, 0));
|
||||
}
|
||||
|
||||
int
|
||||
lzc_send_resume(const char *snapname, const char *from, int fd,
|
||||
enum lzc_send_flags flags, uint64_t resumeobj, uint64_t resumeoff)
|
||||
{
|
||||
nvlist_t *args;
|
||||
int err;
|
||||
@@ -479,6 +486,10 @@ lzc_send(const char *snapname, const char *from, int fd,
|
||||
fnvlist_add_boolean(args, "largeblockok");
|
||||
if (flags & LZC_SEND_FLAG_EMBED_DATA)
|
||||
fnvlist_add_boolean(args, "embedok");
|
||||
if (resumeobj != 0 || resumeoff != 0) {
|
||||
fnvlist_add_uint64(args, "resume_object", resumeobj);
|
||||
fnvlist_add_uint64(args, "resume_offset", resumeoff);
|
||||
}
|
||||
err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
|
||||
nvlist_free(args);
|
||||
return (err);
|
||||
@@ -536,22 +547,9 @@ recv_read(int fd, void *buf, int ilen)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The simplest receive case: receive from the specified fd, creating the
|
||||
* specified snapshot. Apply the specified properties a "received" properties
|
||||
* (which can be overridden by locally-set properties). If the stream is a
|
||||
* clone, its origin snapshot must be specified by 'origin'. The 'force'
|
||||
* flag will cause the target filesystem to be rolled back or destroyed if
|
||||
* necessary to receive.
|
||||
*
|
||||
* Return 0 on success or an errno on failure.
|
||||
*
|
||||
* Note: this interface does not work on dedup'd streams
|
||||
* (those with DMU_BACKUP_FEATURE_DEDUP).
|
||||
*/
|
||||
int
|
||||
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, int fd)
|
||||
static int
|
||||
lzc_receive_impl(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, boolean_t resumable, int fd)
|
||||
{
|
||||
/*
|
||||
* The receive ioctl is still legacy, so we need to construct our own
|
||||
@@ -561,7 +559,6 @@ lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
char *atp;
|
||||
char *packed = NULL;
|
||||
size_t size;
|
||||
dmu_replay_record_t drr;
|
||||
int error;
|
||||
|
||||
ASSERT3S(g_refcount, >, 0);
|
||||
@@ -597,10 +594,9 @@ lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
(void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string));
|
||||
|
||||
/* zc_begin_record is non-byteswapped BEGIN record */
|
||||
error = recv_read(fd, &drr, sizeof (drr));
|
||||
error = recv_read(fd, &zc.zc_begin_record, sizeof (zc.zc_begin_record));
|
||||
if (error != 0)
|
||||
goto out;
|
||||
zc.zc_begin_record = drr.drr_u.drr_begin;
|
||||
|
||||
/* zc_cookie is fd to read from */
|
||||
zc.zc_cookie = fd;
|
||||
@@ -608,6 +604,8 @@ lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
/* zc guid is force flag */
|
||||
zc.zc_guid = force;
|
||||
|
||||
zc.zc_resumable = resumable;
|
||||
|
||||
/* zc_cleanup_fd is unused */
|
||||
zc.zc_cleanup_fd = -1;
|
||||
|
||||
@@ -622,6 +620,39 @@ out:
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* The simplest receive case: receive from the specified fd, creating the
|
||||
* specified snapshot. Apply the specified properties as "received" properties
|
||||
* (which can be overridden by locally-set properties). If the stream is a
|
||||
* clone, its origin snapshot must be specified by 'origin'. The 'force'
|
||||
* flag will cause the target filesystem to be rolled back or destroyed if
|
||||
* necessary to receive.
|
||||
*
|
||||
* Return 0 on success or an errno on failure.
|
||||
*
|
||||
* Note: this interface does not work on dedup'd streams
|
||||
* (those with DMU_BACKUP_FEATURE_DEDUP).
|
||||
*/
|
||||
int
|
||||
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, int fd)
|
||||
{
|
||||
return (lzc_receive_impl(snapname, props, origin, force, B_FALSE, fd));
|
||||
}
|
||||
|
||||
/*
|
||||
* Like lzc_receive, but if the receive fails due to premature stream
|
||||
* termination, the intermediate state will be preserved on disk. In this
|
||||
* case, ECKSUM will be returned. The receive may subsequently be resumed
|
||||
* with a resuming send stream generated by lzc_send_resume().
|
||||
*/
|
||||
int
|
||||
lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, int fd)
|
||||
{
|
||||
return (lzc_receive_impl(snapname, props, origin, force, B_TRUE, fd));
|
||||
}
|
||||
|
||||
/*
|
||||
* Roll back this filesystem or volume to its most recent snapshot.
|
||||
* If snapnamebuf is not NULL, it will be filled in with the name
|
||||
|
||||
@@ -1408,6 +1408,8 @@ spl_fstrans_check(void)
|
||||
return (0);
|
||||
}
|
||||
|
||||
void *zvol_tag = "zvol_tag";
|
||||
|
||||
void
|
||||
zvol_create_minors(spa_t *spa, const char *name, boolean_t async)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user