diff --git a/cmd/ztest.c b/cmd/ztest.c index 9191c9d70..3b63ad43f 100644 --- a/cmd/ztest.c +++ b/cmd/ztest.c @@ -4916,7 +4916,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id) fatal(B_FALSE, "dmu_take_snapshot(%s) = %d", snap1name, error); } - error = dmu_objset_clone(clone1name, snap1name); + error = dsl_dataset_clone(clone1name, snap1name); if (error) { if (error == ENOSPC) { ztest_record_enospc(FTAG); @@ -4943,7 +4943,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id) fatal(B_FALSE, "dmu_open_snapshot(%s) = %d", snap3name, error); } - error = dmu_objset_clone(clone2name, snap3name); + error = dsl_dataset_clone(clone2name, snap3name); if (error) { if (error == ENOSPC) { ztest_record_enospc(FTAG); @@ -6334,13 +6334,13 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id) fatal(B_FALSE, "dmu_objset_snapshot(%s) = %d", fullname, error); } - error = dmu_objset_clone(clonename, fullname); + error = dsl_dataset_clone(clonename, fullname); if (error) { if (error == ENOSPC) { - ztest_record_enospc("dmu_objset_clone"); + ztest_record_enospc("dsl_dataset_clone"); goto out; } - fatal(B_FALSE, "dmu_objset_clone(%s) = %d", clonename, error); + fatal(B_FALSE, "dsl_dataset_clone(%s) = %d", clonename, error); } error = dsl_destroy_snapshot(fullname, B_TRUE); diff --git a/include/sys/dmu.h b/include/sys/dmu.h index bc2aea71e..fec7714fd 100644 --- a/include/sys/dmu.h +++ b/include/sys/dmu.h @@ -360,7 +360,6 @@ void dmu_objset_evict_dbufs(objset_t *os); int dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags, struct dsl_crypto_params *dcp, dmu_objset_create_sync_func_t func, void *arg); -int dmu_objset_clone(const char *name, const char *origin); int dsl_destroy_snapshots_nvl(struct nvlist *snaps, boolean_t defer, struct nvlist *errlist); int dmu_objset_snapshot_one(const char *fsname, const char *snapname); diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h index 681294593..2e1f9847f 100644 --- a/include/sys/dsl_dataset.h +++ b/include/sys/dsl_dataset.h @@ -276,6 +276,12 @@ dsl_dataset_phys(dsl_dataset_t *ds) return ((dsl_dataset_phys_t *)ds->ds_dbuf->db_data); } +typedef struct dsl_dataset_clone_arg_t { + const char *ddca_clone; + const char *ddca_origin; + cred_t *ddca_cred; +} dsl_dataset_clone_arg_t; + typedef struct dsl_dataset_promote_arg { const char *ddpa_clonename; dsl_dataset_t *ddpa_clone; @@ -364,6 +370,9 @@ uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, void dsl_dataset_snapshot_sync(void *arg, dmu_tx_t *tx); int dsl_dataset_snapshot_check(void *arg, dmu_tx_t *tx); int dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors); +void dsl_dataset_clone_sync(void *arg, dmu_tx_t *tx); +int dsl_dataset_clone_check(void *arg, dmu_tx_t *tx); +int dsl_dataset_clone(const char *clone, const char *origin); void dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx); int dsl_dataset_promote_check(void *arg, dmu_tx_t *tx); int dsl_dataset_promote(const char *name, char *conflsnap); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index d08252e3c..b3f792e4a 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -1383,112 +1383,6 @@ dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags, return (rv); } -typedef struct dmu_objset_clone_arg { - const char *doca_clone; - const char *doca_origin; - cred_t *doca_cred; -} dmu_objset_clone_arg_t; - -static int -dmu_objset_clone_check(void *arg, dmu_tx_t *tx) -{ - dmu_objset_clone_arg_t *doca = arg; - dsl_dir_t *pdd; - const char *tail; - int error; - dsl_dataset_t *origin; - dsl_pool_t *dp = dmu_tx_pool(tx); - - if (strchr(doca->doca_clone, '@') != NULL) - return (SET_ERROR(EINVAL)); - - if (strlen(doca->doca_clone) >= ZFS_MAX_DATASET_NAME_LEN) - return (SET_ERROR(ENAMETOOLONG)); - - error = dsl_dir_hold(dp, doca->doca_clone, FTAG, &pdd, &tail); - if (error != 0) - return (error); - if (tail == NULL) { - dsl_dir_rele(pdd, FTAG); - return (SET_ERROR(EEXIST)); - } - - error = dsl_fs_ss_limit_check(pdd, 1, ZFS_PROP_FILESYSTEM_LIMIT, NULL, - doca->doca_cred); - if (error != 0) { - dsl_dir_rele(pdd, FTAG); - return (SET_ERROR(EDQUOT)); - } - - error = dsl_dataset_hold(dp, doca->doca_origin, FTAG, &origin); - if (error != 0) { - dsl_dir_rele(pdd, FTAG); - return (error); - } - - /* You can only clone snapshots, not the head datasets. */ - if (!origin->ds_is_snapshot) { - dsl_dataset_rele(origin, FTAG); - dsl_dir_rele(pdd, FTAG); - return (SET_ERROR(EINVAL)); - } - - dsl_dataset_rele(origin, FTAG); - dsl_dir_rele(pdd, FTAG); - - return (0); -} - -static void -dmu_objset_clone_sync(void *arg, dmu_tx_t *tx) -{ - dmu_objset_clone_arg_t *doca = arg; - dsl_pool_t *dp = dmu_tx_pool(tx); - dsl_dir_t *pdd; - const char *tail; - dsl_dataset_t *origin, *ds; - uint64_t obj; - char namebuf[ZFS_MAX_DATASET_NAME_LEN]; - - VERIFY0(dsl_dir_hold(dp, doca->doca_clone, FTAG, &pdd, &tail)); - VERIFY0(dsl_dataset_hold(dp, doca->doca_origin, FTAG, &origin)); - - obj = dsl_dataset_create_sync(pdd, tail, origin, 0, - doca->doca_cred, NULL, tx); - - VERIFY0(dsl_dataset_hold_obj(pdd->dd_pool, obj, FTAG, &ds)); - dsl_dataset_name(origin, namebuf); - spa_history_log_internal_ds(ds, "clone", tx, - "origin=%s (%llu)", namebuf, (u_longlong_t)origin->ds_object); - dsl_dataset_rele(ds, FTAG); - dsl_dataset_rele(origin, FTAG); - dsl_dir_rele(pdd, FTAG); -} - -int -dmu_objset_clone(const char *clone, const char *origin) -{ - dmu_objset_clone_arg_t doca; - - cred_t *cr = CRED(); - crhold(cr); - - doca.doca_clone = clone; - doca.doca_origin = origin; - doca.doca_cred = cr; - - int rv = dsl_sync_task(clone, - dmu_objset_clone_check, dmu_objset_clone_sync, &doca, - 6, ZFS_SPACE_CHECK_NORMAL); - - if (rv == 0) - zvol_create_minor(clone); - - crfree(cr); - - return (rv); -} - int dmu_objset_snapshot_one(const char *fsname, const char *snapname) { @@ -3165,7 +3059,6 @@ EXPORT_SYMBOL(dmu_objset_rele_flags); EXPORT_SYMBOL(dmu_objset_disown); EXPORT_SYMBOL(dmu_objset_from_ds); EXPORT_SYMBOL(dmu_objset_create); -EXPORT_SYMBOL(dmu_objset_clone); EXPORT_SYMBOL(dmu_objset_stats); EXPORT_SYMBOL(dmu_objset_fast_stat); EXPORT_SYMBOL(dmu_objset_spa); diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index ae55cf8b8..c0a7872c4 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -3320,6 +3320,106 @@ dsl_dataset_rollback(const char *fsname, const char *tosnap, void *owner, 1, ZFS_SPACE_CHECK_RESERVED)); } +int +dsl_dataset_clone_check(void *arg, dmu_tx_t *tx) +{ + dsl_dataset_clone_arg_t *ddca = arg; + dsl_dir_t *pdd; + const char *tail; + int error; + dsl_dataset_t *origin; + dsl_pool_t *dp = dmu_tx_pool(tx); + + if (strchr(ddca->ddca_clone, '@') != NULL) + return (SET_ERROR(EINVAL)); + + if (strlen(ddca->ddca_clone) >= ZFS_MAX_DATASET_NAME_LEN) + return (SET_ERROR(ENAMETOOLONG)); + + error = dsl_dir_hold(dp, ddca->ddca_clone, FTAG, &pdd, &tail); + if (error != 0) + return (error); + if (tail == NULL) { + dsl_dir_rele(pdd, FTAG); + return (SET_ERROR(EEXIST)); + } + + error = dsl_fs_ss_limit_check(pdd, 1, ZFS_PROP_FILESYSTEM_LIMIT, NULL, + ddca->ddca_cred); + if (error != 0) { + dsl_dir_rele(pdd, FTAG); + return (SET_ERROR(EDQUOT)); + } + + error = dsl_dataset_hold(dp, ddca->ddca_origin, FTAG, &origin); + if (error != 0) { + dsl_dir_rele(pdd, FTAG); + return (error); + } + + /* You can only clone snapshots, not the head datasets. */ + if (!origin->ds_is_snapshot) { + dsl_dataset_rele(origin, FTAG); + dsl_dir_rele(pdd, FTAG); + return (SET_ERROR(EINVAL)); + } + + dsl_dataset_rele(origin, FTAG); + dsl_dir_rele(pdd, FTAG); + + return (0); +} + +void +dsl_dataset_clone_sync(void *arg, dmu_tx_t *tx) +{ + dsl_dataset_clone_arg_t *ddca = arg; + dsl_pool_t *dp = dmu_tx_pool(tx); + dsl_dir_t *pdd; + const char *tail; + dsl_dataset_t *origin, *ds; + uint64_t obj; + char namebuf[ZFS_MAX_DATASET_NAME_LEN]; + + VERIFY0(dsl_dir_hold(dp, ddca->ddca_clone, FTAG, &pdd, &tail)); + VERIFY0(dsl_dataset_hold(dp, ddca->ddca_origin, FTAG, &origin)); + + obj = dsl_dataset_create_sync(pdd, tail, origin, 0, + ddca->ddca_cred, NULL, tx); + + VERIFY0(dsl_dataset_hold_obj(pdd->dd_pool, obj, FTAG, &ds)); + dsl_dataset_name(origin, namebuf); + spa_history_log_internal_ds(ds, "clone", tx, + "origin=%s (%llu)", namebuf, (u_longlong_t)origin->ds_object); + dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele(origin, FTAG); + dsl_dir_rele(pdd, FTAG); +} + +int +dsl_dataset_clone(const char *clone, const char *origin) +{ + dsl_dataset_clone_arg_t ddca; + + cred_t *cr = CRED(); + crhold(cr); + + ddca.ddca_clone = clone; + ddca.ddca_origin = origin; + ddca.ddca_cred = cr; + + int rv = dsl_sync_task(clone, + dsl_dataset_clone_check, dsl_dataset_clone_sync, &ddca, + 6, ZFS_SPACE_CHECK_NORMAL); + + if (rv == 0) + zvol_create_minor(clone); + + crfree(cr); + + return (rv); +} + struct promotenode { list_node_t link; dsl_dataset_t *ds; diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index c37d8a1c0..ebb1cfd07 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -3730,7 +3730,7 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) if (dataset_namecheck(origin_name, NULL, NULL) != 0) return (SET_ERROR(EINVAL)); - error = dmu_objset_clone(fsname, origin_name); + error = dsl_dataset_clone(fsname, origin_name); /* * It would be nice to do this atomically.