mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-25 03:37:45 +03:00
Implement a new type of zfs receive: corrective receive (-c)
This type of recv is used to heal corrupted data when a replica of the data already exists (in the form of a send file for example). With the provided send stream, corrective receive will read from disk blocks described by the WRITE records. When any of the reads come back with ECKSUM we use the data from the corresponding WRITE record to rewrite the corrupted block. Reviewed-by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Paul Zuchowski <pzuchowski@datto.com> Signed-off-by: Alek Pinchuk <apinchuk@axcient.com> Closes #9372
This commit is contained in:
@@ -436,6 +436,29 @@ send_iterate_prop(zfs_handle_t *zhp, boolean_t received_only, nvlist_t *nv)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* returns snapshot guid
|
||||
* and returns 0 if the snapshot does not exist
|
||||
*/
|
||||
static uint64_t
|
||||
get_snap_guid(libzfs_handle_t *hdl, const char *fs, const char *snap)
|
||||
{
|
||||
char name[MAXPATHLEN + 1];
|
||||
uint64_t guid = 0;
|
||||
|
||||
if (fs == NULL || fs[0] == '\0' || snap == NULL || snap[0] == '\0')
|
||||
return (guid);
|
||||
|
||||
(void) snprintf(name, sizeof (name), "%s@%s", fs, snap);
|
||||
zfs_handle_t *zhp = zfs_open(hdl, name, ZFS_TYPE_SNAPSHOT);
|
||||
if (zhp != NULL) {
|
||||
guid = zfs_prop_get_int(zhp, ZFS_PROP_GUID);
|
||||
zfs_close(zhp);
|
||||
}
|
||||
|
||||
return (guid);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns snapshot creation txg
|
||||
* and returns 0 if the snapshot does not exist
|
||||
@@ -4541,9 +4564,34 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
redacted = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
||||
DMU_BACKUP_FEATURE_REDACTED;
|
||||
|
||||
if (zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
|
||||
if (flags->heal) {
|
||||
if (flags->isprefix || flags->istail || flags->force ||
|
||||
flags->canmountoff || flags->resumable || flags->nomount ||
|
||||
flags->skipholds) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"corrective recv can not be used when combined with"
|
||||
" this flag"));
|
||||
err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
||||
goto out;
|
||||
}
|
||||
uint64_t guid =
|
||||
get_snap_guid(hdl, name, strchr(destsnap, '@') + 1);
|
||||
if (guid == 0) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"corrective recv must specify an existing snapshot"
|
||||
" to heal"));
|
||||
err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
||||
goto out;
|
||||
} else if (guid != drrb->drr_toguid) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"local snapshot doesn't match the snapshot"
|
||||
" in the provided stream"));
|
||||
err = zfs_error(hdl, EZFS_WRONG_PARENT, errbuf);
|
||||
goto out;
|
||||
}
|
||||
} else if (zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
zfs_handle_t *zhp;
|
||||
zfs_handle_t *zhp = NULL;
|
||||
boolean_t encrypted;
|
||||
|
||||
(void) strcpy(zc.zc_name, name);
|
||||
@@ -4737,8 +4785,9 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
}
|
||||
|
||||
if (flags->verbose) {
|
||||
(void) printf("%s %s stream of %s into %s\n",
|
||||
(void) printf("%s %s%s stream of %s into %s\n",
|
||||
flags->dryrun ? "would receive" : "receiving",
|
||||
flags->heal ? " corrective" : "",
|
||||
drrb->drr_fromguid ? "incremental" : "full",
|
||||
drrb->drr_toname, destsnap);
|
||||
(void) fflush(stdout);
|
||||
@@ -4808,10 +4857,17 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops,
|
||||
oxprops, wkeydata, wkeylen, origin, flags->force, flags->resumable,
|
||||
raw, infd, drr_noswap, -1, &read_bytes, &errflags,
|
||||
NULL, &prop_errors);
|
||||
if (flags->heal) {
|
||||
err = ioctl_err = lzc_receive_with_heal(destsnap, rcvprops,
|
||||
oxprops, wkeydata, wkeylen, origin, flags->force,
|
||||
flags->heal, flags->resumable, raw, infd, drr_noswap, -1,
|
||||
&read_bytes, &errflags, NULL, &prop_errors);
|
||||
} else {
|
||||
err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops,
|
||||
oxprops, wkeydata, wkeylen, origin, flags->force,
|
||||
flags->resumable, raw, infd, drr_noswap, -1, &read_bytes,
|
||||
&errflags, NULL, &prop_errors);
|
||||
}
|
||||
ioctl_errno = ioctl_err;
|
||||
prop_errflags = errflags;
|
||||
|
||||
@@ -4933,7 +4989,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
||||
break;
|
||||
case EACCES:
|
||||
if (raw && stream_wantsnewfs) {
|
||||
if (flags->heal) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"key must be loaded to do a non-raw "
|
||||
"corrective recv on an encrypted "
|
||||
"dataset."));
|
||||
} else if (raw && stream_wantsnewfs) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"failed to create encryption key"));
|
||||
} else if (raw && !stream_wantsnewfs) {
|
||||
@@ -4973,8 +5034,14 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
break;
|
||||
case ECKSUM:
|
||||
case ZFS_ERR_STREAM_TRUNCATED:
|
||||
recv_ecksum_set_aux(hdl, destsnap, flags->resumable,
|
||||
ioctl_err == ECKSUM);
|
||||
if (flags->heal)
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"corrective receive was not able to "
|
||||
"reconstruct the data needed for "
|
||||
"healing."));
|
||||
else
|
||||
recv_ecksum_set_aux(hdl, destsnap,
|
||||
flags->resumable, ioctl_err == ECKSUM);
|
||||
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
||||
break;
|
||||
case ZFS_ERR_STREAM_LARGE_BLOCK_MISMATCH:
|
||||
@@ -4984,8 +5051,14 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
||||
break;
|
||||
case ENOTSUP:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"pool must be upgraded to receive this stream."));
|
||||
if (flags->heal)
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"stream is not compatible with the "
|
||||
"data in the pool."));
|
||||
else
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"pool must be upgraded to receive this "
|
||||
"stream."));
|
||||
(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
|
||||
break;
|
||||
case EDQUOT:
|
||||
|
||||
Reference in New Issue
Block a user