If the plugin's .extents made progress before failing, retrying the call with the original offset that differs from the offset expected by extents will cause an assertion failure. We have to perform each retry iteration with a fresh extents object, and copy it on success.
The sh plugin could trigger this with an extents callback that produces valid data followed by garbage, as will be shown in the next patch. Fixes: f0f0ec49 Signed-off-by: Eric Blake <[email protected]> --- filters/retry/retry.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/filters/retry/retry.c b/filters/retry/retry.c index 00dbd091..9747b4eb 100644 --- a/filters/retry/retry.c +++ b/filters/retry/retry.c @@ -322,17 +322,39 @@ retry_extents (struct nbdkit_next_ops *next_ops, void *nxdata, { struct retry_handle *h = handle; struct retry_data data = {0}; + CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL; int r; + size_t i; again: if (next_ops->can_extents (nxdata) != 1) { *err = EIO; r = -1; } - else - r = next_ops->extents (nxdata, count, offset, flags, extents, err); + else { + /* Each retry must begin with extents reset to the right beginning. */ + nbdkit_extents_free (extents2); + extents2 = nbdkit_extents_new (offset, next_ops->get_size (nxdata)); + if (extents2 == NULL) { + *err = errno; + return -1; /* Not worth a retry after ENOMEM. */ + } + r = next_ops->extents (nxdata, count, offset, flags, extents2, err); + } if (r == -1 && do_retry (h, &data, next_ops, nxdata, err)) goto again; + if (r == 0) { + /* Transfer the successful extents back to the caller. */ + for (i = 0; i < nbdkit_extents_count (extents2); ++i) { + struct nbdkit_extent e = nbdkit_get_extent (extents2, i); + + if (nbdkit_add_extent (extents, e.offset, e.length, e.type) == -1) { + *err = errno; + return -1; + } + } + } + return r; } -- 2.21.0 _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
