zcp: support bookmarks in zfs.sync.destroy

Teach the channel-program synctask destroy path to recognize bookmark
targets and route them through the existing bookmark-destroy
implementation. This enables zfs.sync.destroy() to remove bookmarks from
Lua channel programs, matching the behavior already available for
datasets and snapshots. A functional regression test was added to cover
bookmark destruction from a channel program.

Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Stephen Warren <[email protected]>
Closes #18992
This commit is contained in:
Stephen Warren authored and GitHub committed 2026-09-04 11:38:14 -07:00
1 parent f7b10b66be
commit fd639b075a
7 files changed
+83 -12

No files matched your search

+9
View File
@@ -18,6 +18,7 @@
#include <sys/zfs_context.h>
#include <sys/zfs_refcount.h>
#include <sys/dmu_tx.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_pool.h>
@@ -115,6 +116,12 @@ typedef struct dsl_bookmark_create_redacted_arg {
const void *dbcra_tag;
} dsl_bookmark_create_redacted_arg_t;
typedef struct dsl_bookmark_destroy_arg {
nvlist_t *dbda_bmarks;
nvlist_t *dbda_success;
nvlist_t *dbda_errors;
} dsl_bookmark_destroy_arg_t;
int dsl_bookmark_create(nvlist_t *, nvlist_t *);
int dsl_bookmark_create_nvl_validate(nvlist_t *);
int dsl_bookmark_create_check(void *arg, dmu_tx_t *tx);
@@ -125,6 +132,8 @@ int dsl_get_bookmarks(const char *, nvlist_t *, nvlist_t *);
int dsl_get_bookmarks_impl(dsl_dataset_t *, nvlist_t *, nvlist_t *);
int dsl_get_bookmark_props(const char *, const char *, nvlist_t *);
int dsl_bookmark_destroy(nvlist_t *, nvlist_t *);
int dsl_bookmark_destroy_check(void *, dmu_tx_t *);
void dsl_bookmark_destroy_sync(void *, dmu_tx_t *);
int dsl_bookmark_lookup(struct dsl_pool *, const char *,
struct dsl_dataset *, zfs_bookmark_phys_t *);
int dsl_bookmark_lookup_impl(dsl_dataset_t *, const char *,
+2 -8
View File
@@ -969,12 +969,6 @@ out:
return (err);
}
typedef struct dsl_bookmark_destroy_arg {
nvlist_t *dbda_bmarks;
nvlist_t *dbda_success;
nvlist_t *dbda_errors;
} dsl_bookmark_destroy_arg_t;
static void
dsl_bookmark_destroy_sync_impl(dsl_dataset_t *ds, const char *name,
dmu_tx_t *tx)
@@ -1082,7 +1076,7 @@ dsl_bookmark_destroy_sync_impl(dsl_dataset_t *ds, const char *name,
VERIFY0(zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
}
static int
int
dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
{
dsl_bookmark_destroy_arg_t *dbda = arg;
@@ -1147,7 +1141,7 @@ dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
return (rv);
}
static void
void
dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
{
dsl_bookmark_destroy_arg_t *dbda = arg;
+22 -2
View File
@@ -158,7 +158,8 @@ static const zcp_synctask_info_t zcp_synctask_destroy_info = {
.name = "destroy",
.func = zcp_synctask_destroy,
.pargs = {
{.za_name = "filesystem | snapshot", .za_lua_type = LUA_TSTRING },
{.za_name = "filesystem | snapshot | bookmark",
.za_lua_type = LUA_TSTRING },
{NULL, 0}
},
.kwargs = {
@@ -172,11 +173,11 @@ static const zcp_synctask_info_t zcp_synctask_destroy_info = {
static int
zcp_synctask_destroy(lua_State *state, boolean_t sync, nvlist_t *err_details)
{
(void) err_details;
int err;
const char *dsname = lua_tostring(state, 1);
boolean_t issnap = (strchr(dsname, '@') != NULL);
boolean_t isbookmark = (strchr(dsname, '#') != NULL);
if (!issnap && !lua_isnil(state, 2)) {
return (luaL_error(state,
@@ -195,6 +196,25 @@ zcp_synctask_destroy(lua_State *state, boolean_t sync, nvlist_t *err_details)
err = zcp_sync_task(state, dsl_destroy_snapshot_check,
dsl_destroy_snapshot_sync, &ddsa, sync, dsname);
} else if (isbookmark) {
dsl_bookmark_destroy_arg_t dbda = { 0 };
dbda.dbda_bmarks = fnvlist_alloc();
dbda.dbda_success = fnvlist_alloc();
dbda.dbda_errors = err_details;
fnvlist_add_boolean(dbda.dbda_bmarks, dsname);
zcp_cleanup_handler_t *zch = zcp_register_cleanup(state,
zcp_synctask_cleanup, dbda.dbda_bmarks);
zcp_cleanup_handler_t *zch2 = zcp_register_cleanup(state,
zcp_synctask_cleanup, dbda.dbda_success);
err = zcp_sync_task(state, dsl_bookmark_destroy_check,
dsl_bookmark_destroy_sync, &dbda, sync, dsname);
zcp_deregister_cleanup(state, zch2);
zcp_deregister_cleanup(state, zch);
fnvlist_free(dbda.dbda_success);
fnvlist_free(dbda.dbda_bmarks);
} else {
dsl_destroy_head_arg_t ddha = { 0 };
ddha.ddha_name = dsname;
+2 -1
View File
@@ -133,7 +133,8 @@ tests = ['tst.args_to_lua', 'tst.divide_by_zero', 'tst.exists', 'tst.encryption'
tags = ['functional', 'channel_program', 'lua_core']
[tests/functional/channel_program/synctask_core]
tests = ['tst.destroy_fs', 'tst.destroy_snap', 'tst.get_count_and_limit',
tests = ['tst.destroy_fs', 'tst.destroy_snap',
'tst.destroy_bookmark', 'tst.get_count_and_limit',
'tst.get_index_props', 'tst.get_mountpoint', 'tst.get_neg',
'tst.get_number_props', 'tst.get_string_props', 'tst.get_type',
'tst.get_userquota', 'tst.get_written', 'tst.inherit', 'tst.list_bookmarks',
+2 -1
View File
@@ -74,7 +74,8 @@ tests = ['tst.args_to_lua', 'tst.divide_by_zero', 'tst.exists', 'tst.encryption'
tags = ['functional', 'channel_program', 'lua_core']
[tests/functional/channel_program/synctask_core]
tests = ['tst.destroy_fs', 'tst.destroy_snap', 'tst.get_count_and_limit',
tests = ['tst.destroy_fs', 'tst.destroy_snap',
'tst.destroy_bookmark', 'tst.get_count_and_limit',
'tst.get_index_props', 'tst.get_mountpoint', 'tst.get_neg',
'tst.get_number_props', 'tst.get_string_props', 'tst.get_type',
'tst.get_userquota', 'tst.get_written', 'tst.inherit', 'tst.list_bookmarks',
+1
View File
@@ -642,6 +642,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/channel_program/synctask_core/tst.bookmark.create.ksh \
functional/channel_program/synctask_core/tst.clone.ksh \
functional/channel_program/synctask_core/tst.destroy_fs.ksh \
functional/channel_program/synctask_core/tst.destroy_bookmark.ksh \
functional/channel_program/synctask_core/tst.destroy_snap.ksh \
functional/channel_program/synctask_core/tst.get_count_and_limit.ksh \
functional/channel_program/synctask_core/tst.get_index_props.ksh \
@@ -0,0 +1,45 @@
#!/bin/ksh -p
# SPDX-License-Identifier: CDDL-1.0
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# https://opensource.org/license/CDDL-1.0.
#
. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
verify_runnable "global"
fs=$TESTPOOL/$TESTFS/testchild
snapname=testsnap
bookname=testbookmark
bookmark=$fs#$bookname
snap=$fs@$snapname
function cleanup
{
destroy_dataset $fs "-R"
}
log_onexit cleanup
log_must zfs create $fs
log_must zfs snapshot $snap
log_must zfs bookmark $snap $bookmark
log_must_program_sync $TESTPOOL - $bookmark <<-EOF
arg = ...
bookmark = arg["argv"][1]
err = zfs.sync.destroy(bookmark)
msg = "destroying " .. bookmark .. " err=" .. err
return msg
EOF
log_mustnot zfs list -H -o name -t bookmark $bookmark
log_pass "Destroying bookmark with channel program works."