Fix `zfs send -v` failing to send from filesystem or volume

Sends from filesystems or volumes (as opposed to snapshots) with
the verbose flag set currently fail with an 'Invalid argument'
error. This is caused by `dmu_send_estimate_fast()` failing to
handle non-snapshot sends properly. Fix it by owning the dataset
while we are estimating the send size.

Signed-off-by: Attila Fülöp <attila@fueloep.org>
This commit is contained in:
Attila Fülöp 2022-10-28 19:12:01 +02:00
parent 0b2428da20
commit 5b9afc16cf
3 changed files with 28 additions and 7 deletions

View File

@ -54,7 +54,7 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
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,
zfs_bookmark_phys_t *frombook, boolean_t compressok, boolean_t rawok,
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,

View File

@ -3001,16 +3001,21 @@ dmu_adjust_send_estimate_for_indirects(dsl_dataset_t *ds, uint64_t uncompressed,
int
dmu_send_estimate_fast(dsl_dataset_t *origds, dsl_dataset_t *fromds,
zfs_bookmark_phys_t *frombook, boolean_t stream_compressed,
zfs_bookmark_phys_t *frombook, boolean_t compressok, boolean_t rawok,
boolean_t saved, uint64_t *sizep)
{
int err;
dsl_dataset_t *ds = origds;
ds_hold_flags_t dsflags;
uint64_t uncomp, comp;
boolean_t owned = B_FALSE;
boolean_t stream_compressed = compressok || rawok;
ASSERT(dsl_pool_config_held(origds->ds_dir->dd_pool));
ASSERT(fromds == NULL || frombook == NULL);
dsflags = (rawok) ? DS_HOLD_FLAG_NONE : DS_HOLD_FLAG_DECRYPT;
/*
* If this is a saved send we may actually be sending
* from the %recv clone used for resuming.
@ -3049,10 +3054,22 @@ dmu_send_estimate_fast(dsl_dataset_t *origds, dsl_dataset_t *fromds,
}
}
/* tosnap must be a snapshot or the target of a saved send */
if (!ds->ds_is_snapshot && ds == origds)
return (SET_ERROR(EINVAL));
/*
* If we are sending a filesystem or volume, ensure
* that it doesn't change by owning the dataset.
*/
if (!ds->ds_is_snapshot && ds == origds) {
char dsname[ZFS_MAX_DATASET_NAME_LEN];
dsl_dataset_name(ds, dsname);
err = dsl_dataset_own(ds->ds_dir->dd_pool, dsname, dsflags,
FTAG, &ds);
if (err != 0)
return (err);
owned = B_TRUE;
}
if (fromds != NULL) {
uint64_t used;
if (!fromds->ds_is_snapshot) {
@ -3090,6 +3107,10 @@ dmu_send_estimate_fast(dsl_dataset_t *origds, dsl_dataset_t *fromds,
out:
if (ds != origds)
dsl_dataset_rele(ds, FTAG);
if (owned == B_TRUE)
dsl_dataset_disown(ds->ds_dir->dd_pool, dsflags, FTAG);
return (err);
}

View File

@ -5573,7 +5573,7 @@ zfs_ioc_send(zfs_cmd_t *zc)
}
error = dmu_send_estimate_fast(tosnap, fromsnap, NULL,
compressok || rawok, savedok, &zc->zc_objset_type);
compressok, rawok, savedok, &zc->zc_objset_type);
if (fromsnap != NULL)
dsl_dataset_rele(fromsnap, FTAG);
@ -6751,7 +6751,7 @@ zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
} else {
error = dmu_send_estimate_fast(tosnap, fromsnap,
(from && strchr(fromname, '#') != NULL ? &zbm : NULL),
compressok || rawok, savedok, &space);
compressok, rawok, savedok, &space);
space -= resume_bytes;
if (fromsnap != NULL)
dsl_dataset_rele(fromsnap, FTAG);