Bug #5824
zfs_ioc_clone() return EINVAL where EXDEV is expected
0%
Description
dmu_objset_clone_check() has the following code:
error = dsl_dir_hold(dp, doca->doca_clone, FTAG, &pdd, &tail); ... /* You can't clone across pools. */ if (pdd->dd_pool != dp) { dsl_dir_rele(pdd, FTAG); return (SET_ERROR(EXDEV)); }
But, in fact, that code is NOP, because the synctask is assigned to the clone's pool, so dp is that pool and dsl_dir_hold() is called on the clone as well.
Then there is another check:
error = dsl_dataset_hold(dp, doca->doca_origin, FTAG, &origin); if (error != 0) return (error); /* You can't clone across pools. */ if (origin->ds_dir->dd_pool != dp) { dsl_dataset_rele(origin, FTAG); return (SET_ERROR(EXDEV)); }
But the check is unreachable in exactly the scenario it is supposed to check: dsl_dataset_hold() calls dsl_dir_hold() before doing anything else, and if it is called with a dataset name that does not belong to the pool, then the function returns EINVAL.
err = getcomponent(name, buf, &next); if (err != 0) return (err); /* Make sure the name is in the specified pool. */ spaname = spa_name(dp->dp_spa); if (strcmp(buf, spaname) != 0) return (SET_ERROR(EINVAL));
I see two possibilities:
- dmu_objset_clone_check() can convert EINVAL returned from dsl_dataset_hold() to EXDEV
- dsl_dir_hold() could return EXDEV when the name is not in the pool
Related issues
Updated by Andriy Gapon about 6 years ago
Yes, this is it.
I would close this issue as a dup, but it looks like I can't.