Skip to content

Channel programs: add zfs.sync.clone() #17426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cmd/ztest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion include/sys/dmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions include/sys/dsl_dataset.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
30 changes: 29 additions & 1 deletion man/man8/zfs-program.8
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
.\" Copyright (c) 2016, 2019 by Delphix. All Rights Reserved.
.\" Copyright (c) 2019, 2020 by Christian Schwarz. All Rights Reserved.
.\" Copyright 2020 Joyent, Inc.
.\" Copyright (c) 2025, Rob Norris <[email protected]>
.\"
.Dd May 27, 2021
.Dd June 5, 2025
.Dt ZFS-PROGRAM 8
.Os
.
Expand Down Expand Up @@ -347,6 +348,32 @@ They are executed in "syncing context".
.Pp
The available sync submodule functions are as follows:
.Bl -tag -width "xx"
.It Fn zfs.sync.clone snapshot newdataset
Create a new filesystem from a snapshot.
Returns 0 if the filesystem was successfully created,
and a nonzero error code otherwise.
.Pp
Note: Due to general limitations in channel programs, a filesystem created
this way will not be mounted, regardless of the value of the
.Sy mountpoint
and
.Sy canmount
properties.
This limitation may be removed in the future,
so it is recommended that you set
.Sy mountpoint Ns = Ns Sy none
or
.Sy canmount Ns = Ns Sy off
or
.Sy noauto
to avoid surprises.
.Pp
.Bl -tag -compact -width "newbookmark (string)"
.It Ar snapshot Pq string
Name of the source snapshot to clone.
.It Ar newdataset Pq string
Name of the target dataset to create.
.El
.It Sy zfs.sync.destroy Ns Pq Ar dataset , Op Ar defer Ns = Ns Sy true Ns | Ns Sy false
Destroy the given dataset.
Returns 0 on successful destroy, or a nonzero error code if the dataset could
Expand Down Expand Up @@ -474,6 +501,7 @@ The available
.Sy zfs.check
functions are:
.Bl -tag -compact -width "xx"
.It Fn zfs.check.clone snapshot newdataset
.It Sy zfs.check.destroy Ns Pq Ar dataset , Op Ar defer Ns = Ns Sy true Ns | Ns Sy false
.It Fn zfs.check.promote dataset
.It Fn zfs.check.rollback filesystem
Expand Down
107 changes: 0 additions & 107 deletions module/zfs/dmu_objset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down
100 changes: 100 additions & 0 deletions module/zfs/dsl_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading