Skip to content

Commit 671c935

Browse files
Dan McDonaldbehlendorf
authored andcommitted
OpenZFS 4986 - receiving replication stream fails if any snapshot exceeds refquota
Authored by: Dan McDonald <danmcd@omniti.com> Reviewed by: John Kennedy <john.kennedy@delphix.com> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Approved by: Gordon Ross <gordon.ross@nexenta.com> Ported-by: Brian Behlendorf <behlendorf1@llnl.gov> OpenZFS-issue: https://www.illumos.org/issues/4986 OpenZFS-commit: openzfs/openzfs@5878fad
1 parent f8866f8 commit 671c935

File tree

2 files changed

+121
-19
lines changed

2 files changed

+121
-19
lines changed

lib/libzfs/libzfs_sendrecv.c

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
2727
* All rights reserved
2828
* Copyright (c) 2013 Steven Hartland. All rights reserved.
29+
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
2930
*/
3031

3132
#include <assert.h>
@@ -66,7 +67,7 @@ extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
6667

6768
static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
6869
recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
69-
uint64_t *);
70+
uint64_t *, const char *);
7071
static int guid_to_name(libzfs_handle_t *, const char *,
7172
uint64_t, boolean_t, char *);
7273

@@ -2621,6 +2622,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
26212622
nvlist_t *stream_nv = NULL;
26222623
avl_tree_t *stream_avl = NULL;
26232624
char *fromsnap = NULL;
2625+
char *sendsnap = NULL;
26242626
char *cp;
26252627
char tofs[ZFS_MAXNAMELEN];
26262628
char sendfs[ZFS_MAXNAMELEN];
@@ -2769,8 +2771,16 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
27692771
*/
27702772
(void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname,
27712773
ZFS_MAXNAMELEN);
2772-
if ((cp = strchr(sendfs, '@')) != NULL)
2774+
if ((cp = strchr(sendfs, '@')) != NULL) {
27732775
*cp = '\0';
2776+
/*
2777+
* Find the "sendsnap", the final snapshot in a replication
2778+
* stream. zfs_receive_one() handles certain errors
2779+
* differently, depending on if the contained stream is the
2780+
* last one or not.
2781+
*/
2782+
sendsnap = (cp + 1);
2783+
}
27742784

27752785
/* Finally, receive each contained stream */
27762786
do {
@@ -2783,7 +2793,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
27832793
*/
27842794
error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
27852795
sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
2786-
action_handlep);
2796+
action_handlep, sendsnap);
27872797
if (error == ENODATA) {
27882798
error = 0;
27892799
break;
@@ -2948,7 +2958,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
29482958
const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
29492959
dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
29502960
avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
2951-
uint64_t *action_handlep)
2961+
uint64_t *action_handlep, const char *finalsnap)
29522962
{
29532963
zfs_cmd_t zc = {"\0"};
29542964
time_t begin_time;
@@ -2965,6 +2975,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
29652975
nvlist_t *snapprops_nvlist = NULL;
29662976
zprop_errflags_t prop_errflags;
29672977
boolean_t recursive;
2978+
char *snapname = NULL;
29682979

29692980
begin_time = time(NULL);
29702981

@@ -2975,7 +2986,6 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
29752986
ENOENT);
29762987

29772988
if (stream_avl != NULL) {
2978-
char *snapname;
29792989
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
29802990
&snapname);
29812991
nvlist_t *props;
@@ -3316,7 +3326,21 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
33163326
ZPROP_N_MORE_ERRORS) == 0) {
33173327
trunc_prop_errs(intval);
33183328
break;
3319-
} else {
3329+
} else if (snapname == NULL || finalsnap == NULL ||
3330+
strcmp(finalsnap, snapname) == 0 ||
3331+
strcmp(nvpair_name(prop_err),
3332+
zfs_prop_to_name(ZFS_PROP_REFQUOTA)) != 0) {
3333+
/*
3334+
* Skip the special case of, for example,
3335+
* "refquota", errors on intermediate
3336+
* snapshots leading up to a final one.
3337+
* That's why we have all of the checks above.
3338+
*
3339+
* See zfs_ioctl.c's extract_delay_props() for
3340+
* a list of props which can fail on
3341+
* intermediate snapshots, but shouldn't
3342+
* affect the overall receive.
3343+
*/
33203344
(void) snprintf(tbuf, sizeof (tbuf),
33213345
dgettext(TEXT_DOMAIN,
33223346
"cannot receive %s property on %s"),
@@ -3501,7 +3525,7 @@ static int
35013525
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
35023526
const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
35033527
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
3504-
uint64_t *action_handlep)
3528+
uint64_t *action_handlep, const char *finalsnap)
35053529
{
35063530
int err;
35073531
dmu_replay_record_t drr, drr_noswap;
@@ -3597,10 +3621,11 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
35973621
if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
35983622
*cp = '\0';
35993623
sendfs = nonpackage_sendfs;
3624+
VERIFY(finalsnap == NULL);
36003625
}
36013626
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
36023627
&drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
3603-
cleanup_fd, action_handlep));
3628+
cleanup_fd, action_handlep, finalsnap));
36043629
} else {
36053630
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
36063631
DMU_COMPOUNDSTREAM);
@@ -3678,7 +3703,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
36783703
VERIFY(cleanup_fd >= 0);
36793704

36803705
err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
3681-
stream_avl, &top_zfs, cleanup_fd, &action_handle);
3706+
stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL);
36823707

36833708
VERIFY(0 == close(cleanup_fd));
36843709

module/zfs/zfs_ioctl.c

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
/*
2323
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
2424
* Portions Copyright 2011 Martin Matuska
25+
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
2526
* Portions Copyright 2012 Pawel Jakub Dawidek <pawel@dawidek.net>
2627
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
2728
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
@@ -3990,6 +3991,56 @@ props_reduce(nvlist_t *props, nvlist_t *origprops)
39903991
}
39913992
}
39923993

3994+
/*
3995+
* Extract properties that cannot be set PRIOR to the receipt of a dataset.
3996+
* For example, refquota cannot be set until after the receipt of a dataset,
3997+
* because in replication streams, an older/earlier snapshot may exceed the
3998+
* refquota. We want to receive the older/earlier snapshot, but setting
3999+
* refquota pre-receipt will set the dsl's ACTUAL quota, which will prevent
4000+
* the older/earlier snapshot from being received (with EDQUOT).
4001+
*
4002+
* The ZFS test "zfs_receive_011_pos" demonstrates such a scenario.
4003+
*
4004+
* libzfs will need to be judicious handling errors encountered by props
4005+
* extracted by this function.
4006+
*/
4007+
static nvlist_t *
4008+
extract_delay_props(nvlist_t *props)
4009+
{
4010+
nvlist_t *delayprops;
4011+
nvpair_t *nvp, *tmp;
4012+
static const zfs_prop_t delayable[] = { ZFS_PROP_REFQUOTA, 0 };
4013+
int i;
4014+
4015+
VERIFY(nvlist_alloc(&delayprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
4016+
4017+
for (nvp = nvlist_next_nvpair(props, NULL); nvp != NULL;
4018+
nvp = nvlist_next_nvpair(props, nvp)) {
4019+
/*
4020+
* strcmp() is safe because zfs_prop_to_name() always returns
4021+
* a bounded string.
4022+
*/
4023+
for (i = 0; delayable[i] != 0; i++) {
4024+
if (strcmp(zfs_prop_to_name(delayable[i]),
4025+
nvpair_name(nvp)) == 0) {
4026+
break;
4027+
}
4028+
}
4029+
if (delayable[i] != 0) {
4030+
tmp = nvlist_prev_nvpair(props, nvp);
4031+
VERIFY(nvlist_add_nvpair(delayprops, nvp) == 0);
4032+
VERIFY(nvlist_remove_nvpair(props, nvp) == 0);
4033+
nvp = tmp;
4034+
}
4035+
}
4036+
4037+
if (nvlist_empty(delayprops)) {
4038+
nvlist_free(delayprops);
4039+
delayprops = NULL;
4040+
}
4041+
return (delayprops);
4042+
}
4043+
39934044
#ifdef DEBUG
39944045
static boolean_t zfs_ioc_recv_inject_err;
39954046
#endif
@@ -4026,6 +4077,7 @@ zfs_ioc_recv(zfs_cmd_t *zc)
40264077
offset_t off;
40274078
nvlist_t *props = NULL; /* sent properties */
40284079
nvlist_t *origprops = NULL; /* existing properties */
4080+
nvlist_t *delayprops = NULL; /* sent properties applied post-receive */
40294081
char *origin = NULL;
40304082
char *tosnap;
40314083
char tofs[ZFS_MAXNAMELEN];
@@ -4106,21 +4158,12 @@ zfs_ioc_recv(zfs_cmd_t *zc)
41064158
props_error = dsl_prop_set_hasrecvd(tofs);
41074159

41084160
if (props_error == 0) {
4161+
delayprops = extract_delay_props(props);
41094162
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
41104163
props, errors);
41114164
}
41124165
}
41134166

4114-
if (zc->zc_nvlist_dst_size != 0 &&
4115-
(nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
4116-
put_nvlist(zc, errors) != 0)) {
4117-
/*
4118-
* Caller made zc->zc_nvlist_dst less than the minimum expected
4119-
* size or supplied an invalid address.
4120-
*/
4121-
props_error = SET_ERROR(EINVAL);
4122-
}
4123-
41244167
off = fp->f_offset;
41254168
error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
41264169
&zc->zc_action_handle);
@@ -4145,6 +4188,40 @@ zfs_ioc_recv(zfs_cmd_t *zc)
41454188
} else {
41464189
error = dmu_recv_end(&drc, NULL);
41474190
}
4191+
4192+
/* Set delayed properties now, after we're done receiving. */
4193+
if (delayprops != NULL && error == 0) {
4194+
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
4195+
delayprops, errors);
4196+
}
4197+
}
4198+
4199+
if (delayprops != NULL) {
4200+
/*
4201+
* Merge delayed props back in with initial props, in case
4202+
* we're DEBUG and zfs_ioc_recv_inject_err is set (which means
4203+
* we have to make sure clear_received_props() includes
4204+
* the delayed properties).
4205+
*
4206+
* Since zfs_ioc_recv_inject_err is only in DEBUG kernels,
4207+
* using ASSERT() will be just like a VERIFY.
4208+
*/
4209+
ASSERT(nvlist_merge(props, delayprops, 0) == 0);
4210+
nvlist_free(delayprops);
4211+
}
4212+
4213+
/*
4214+
* Now that all props, initial and delayed, are set, report the prop
4215+
* errors to the caller.
4216+
*/
4217+
if (zc->zc_nvlist_dst_size != 0 &&
4218+
(nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
4219+
put_nvlist(zc, errors) != 0)) {
4220+
/*
4221+
* Caller made zc->zc_nvlist_dst less than the minimum expected
4222+
* size or supplied an invalid address.
4223+
*/
4224+
props_error = SET_ERROR(EINVAL);
41484225
}
41494226

41504227
zc->zc_cookie = off - fp->f_offset;

0 commit comments

Comments
 (0)