diff options
Diffstat (limited to 'module')
-rw-r--r-- | module/zfs/dmu_send.c | 1 | ||||
-rw-r--r-- | module/zfs/dsl_userhold.c | 24 |
2 files changed, 23 insertions, 2 deletions
diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index 9c0ad406b..43e19ecbc 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -1282,7 +1282,6 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, err = dsl_pool_hold(tosnap, FTAG, &dp); if (err != 0) return (err); - if (strchr(tosnap, '@') == NULL && spa_writeable(dp->dp_spa)) { /* * We are sending a filesystem or volume. Ensure diff --git a/module/zfs/dsl_userhold.c b/module/zfs/dsl_userhold.c index c80b35d48..638805d0b 100644 --- a/module/zfs/dsl_userhold.c +++ b/module/zfs/dsl_userhold.c @@ -83,6 +83,7 @@ dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) { dsl_dataset_user_hold_arg_t *dduha = arg; dsl_pool_t *dp = dmu_tx_pool(tx); + nvlist_t *tmp_holds; if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) return (SET_ERROR(ENOTSUP)); @@ -90,6 +91,26 @@ dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) if (!dmu_tx_is_syncing(tx)) return (0); + /* + * Ensure the list has no duplicates by copying name/values from + * non-unique dduha_holds to unique tmp_holds, and comparing counts. + */ + tmp_holds = fnvlist_alloc(); + for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); + pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { + size_t len = strlen(nvpair_name(pair)) + + strlen(fnvpair_value_string(pair)); + char *nameval = kmem_zalloc(len + 2, KM_SLEEP); + (void) strcpy(nameval, nvpair_name(pair)); + (void) strcat(nameval, "@"); + (void) strcat(nameval, fnvpair_value_string(pair)); + fnvlist_add_string(tmp_holds, nameval, ""); + kmem_free(nameval, len + 2); + } + size_t tmp_count = fnvlist_num_pairs(tmp_holds); + fnvlist_free(tmp_holds); + if (tmp_count != fnvlist_num_pairs(dduha->dduha_holds)) + return (SET_ERROR(EEXIST)); for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { dsl_dataset_t *ds; @@ -312,7 +333,8 @@ dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist) return (0); dduha.dduha_holds = holds; - dduha.dduha_chkholds = fnvlist_alloc(); + /* chkholds can have non-unique name */ + VERIFY(0 == nvlist_alloc(&dduha.dduha_chkholds, 0, KM_SLEEP)); dduha.dduha_errlist = errlist; dduha.dduha_minor = cleanup_minor; |