mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
Implement bookmark copying
This feature allows copying existing bookmarks using
zfs bookmark fs#target fs#newbookmark
There are some niche use cases for such functionality,
e.g. when using bookmarks as markers for replication progress.
Copying redaction bookmarks produces a normal bookmark that
cannot be used for redacted send (we are not duplicating
the redaction object).
ZCP support for bookmarking (both creation and copying) will be
implemented in a separate patch based on this work.
Overview:
- Terminology:
- source = existing snapshot or bookmark
- new/bmark = new bookmark
- Implement bookmark copying in `dsl_bookmark.c`
- create new bookmark node
- copy source's `zbn_phys` to new's `zbn_phys`
- zero-out redaction object id in copy
- Extend existing bookmark ioctl nvlist schema to accept
bookmarks as sources
- => `dsl_bookmark_create_nvl_validate` is authoritative
- use `dsl_dataset_is_before` check for both snapshot
and bookmark sources
- Adjust CLI
- refactor shortname expansion logic in `zfs_do_bookmark`
- Update man pages
- warn about redaction bookmark handling
- Add test cases
- CLI
- pyyzfs libzfs_core bindings
Reviewed-by: Matt Ahrens <matt@delphix.com>
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Christian Schwarz <me@cschwarz.com>
Closes #9571
This commit is contained in:
committed by
Brian Behlendorf
parent
7b49bbc816
commit
a73f361fdb
@@ -26,4 +26,6 @@
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
log_must zfs destroy "$TESTPOOL/$TESTFS/child"
|
||||
log_must zfs destroy "$TESTPOOL/${TESTFS}_with_suffix"
|
||||
default_cleanup
|
||||
|
||||
@@ -28,4 +28,8 @@
|
||||
|
||||
DISK=${DISKS%% *}
|
||||
|
||||
default_volume_setup $DISK
|
||||
default_setup_noexit $DISK
|
||||
log_must zfs create "$TESTPOOL/$TESTFS/child"
|
||||
log_must zfs create "$TESTPOOL/${TESTFS}_with_suffix"
|
||||
log_must zfs create "$TESTPOOL/$TESTFS/recv"
|
||||
log_pass
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#
|
||||
# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
|
||||
# Copyright 2019, 2020 by Christian Schwarz. All rights reserved.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
@@ -32,12 +33,22 @@
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Create initial snapshot
|
||||
#
|
||||
# 2. Verify we can create a bookmark specifying snapshot and bookmark full paths
|
||||
# 3. Verify we can create a bookmark specifying the snapshot name
|
||||
# 4. Verify we can create a bookmark specifying the bookmark name
|
||||
# 3. Verify we can create a bookmark specifying the short snapshot name
|
||||
# 4. Verify we can create a bookmark specifying the short bookmark name
|
||||
# 5. Verify at least a full dataset path is required and both snapshot and
|
||||
# bookmark name must be valid
|
||||
#
|
||||
# 6. Verify we can copy a bookmark by specifying the source bookmark and new
|
||||
# bookmark full paths.
|
||||
# 7. Verify we can copy a bookmark specifying the short source name
|
||||
# 8. Verify we can copy a bookmark specifying the short new name
|
||||
# 9. Verify two short paths are not allowed, and test empty paths
|
||||
# 10. Verify we cannot copy a bookmark if the new bookmark already exists
|
||||
# 11. Verify that copying a bookmark only works if new and source name
|
||||
# have the same dataset
|
||||
#
|
||||
|
||||
verify_runnable "both"
|
||||
|
||||
@@ -49,18 +60,29 @@ function cleanup
|
||||
if bkmarkexists "$DATASET#$TESTBM"; then
|
||||
log_must zfs destroy "$DATASET#$TESTBM"
|
||||
fi
|
||||
if bkmarkexists "$DATASET#$TESTBMCOPY"; then
|
||||
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
||||
fi
|
||||
}
|
||||
|
||||
log_assert "'zfs bookmark' should work only when passed valid arguments."
|
||||
log_onexit cleanup
|
||||
|
||||
DATASET="$TESTPOOL/$TESTFS"
|
||||
DATASET_TWO="$TESTPOOL/${TESTFS}_two"
|
||||
TESTSNAP='snapshot'
|
||||
TESTSNAP2='snapshot2'
|
||||
TESTBM='bookmark'
|
||||
TESTBMCOPY='bookmark_copy'
|
||||
|
||||
|
||||
# Create initial snapshot
|
||||
log_must zfs snapshot "$DATASET@$TESTSNAP"
|
||||
|
||||
#
|
||||
# Bookmark creation tests
|
||||
#
|
||||
|
||||
# Verify we can create a bookmark specifying snapshot and bookmark full paths
|
||||
log_must zfs bookmark "$DATASET@$TESTSNAP" "$DATASET#$TESTBM"
|
||||
log_must eval "bkmarkexists $DATASET#$TESTBM"
|
||||
@@ -97,4 +119,120 @@ log_mustnot zfs bookmark "$TESTSNAP" "$DATASET#"
|
||||
log_mustnot zfs bookmark "$TESTSNAP" "$DATASET"
|
||||
log_mustnot eval "bkmarkexists $DATASET#$TESTBM"
|
||||
|
||||
log_pass "'zfs bookmark' works as expected only when passed valid arguments."
|
||||
# Verify that we can create a bookmarks on another origin filesystem
|
||||
log_must zfs clone "$DATASET@$TESTSNAP" "$DATASET_TWO"
|
||||
log_must zfs bookmark "$DATASET@$TESTSNAP" "$DATASET_TWO#$TESTBM"
|
||||
log_must eval "destroy_dataset $DATASET_TWO"
|
||||
|
||||
# Verify that we can cannot create bookmarks on a non-origin filesystem
|
||||
log_must zfs create "$DATASET_TWO"
|
||||
log_mustnot_expect "source is not an ancestor of the new bookmark's dataset" zfs bookmark "$DATASET@$TESTSNAP" "$DATASET_TWO#$TESTBM"
|
||||
log_must zfs destroy "$DATASET_TWO"
|
||||
|
||||
# Verify that we can create bookmarks of snapshots on the pool dataset
|
||||
log_must zfs snapshot "$TESTPOOL@$TESTSNAP"
|
||||
log_must zfs bookmark "$TESTPOOL@$TESTSNAP" "$TESTPOOL#$TESTBM"
|
||||
log_must zfs destroy "$TESTPOOL#$TESTBM"
|
||||
log_must zfs destroy "$TESTPOOL@$TESTSNAP"
|
||||
|
||||
#
|
||||
# Bookmark copying tests
|
||||
#
|
||||
|
||||
# create the source bookmark
|
||||
log_must zfs bookmark "$DATASET@$TESTSNAP" "$DATASET#$TESTBM"
|
||||
|
||||
# Verify we can copy a bookmark by specifying the source bookmark
|
||||
# and new bookmark full paths.
|
||||
log_must eval "bkmarkexists $DATASET#$TESTBM"
|
||||
log_must zfs bookmark "$DATASET#$TESTBM" "$DATASET#$TESTBMCOPY"
|
||||
log_must eval "bkmarkexists $DATASET#$TESTBMCOPY"
|
||||
## validate destroy once (should be truly independent bookmarks)
|
||||
log_must zfs destroy "$DATASET#$TESTBM"
|
||||
log_mustnot eval "bkmarkexists $DATASET#$TESTBM"
|
||||
log_must eval "bkmarkexists $DATASET#$TESTBMCOPY"
|
||||
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
||||
log_mustnot eval "bkmarkexists $DATASET#$TESTBMCOPY"
|
||||
log_mustnot eval "bkmarkexists $DATASET#$TESTBM"
|
||||
## recreate the source bookmark
|
||||
log_must zfs bookmark "$DATASET@$TESTSNAP" "$DATASET#$TESTBM"
|
||||
|
||||
# Verify we can copy a bookmark specifying the short source name
|
||||
log_must zfs bookmark "#$TESTBM" "$DATASET#$TESTBMCOPY"
|
||||
log_must eval "bkmarkexists $DATASET#$TESTBMCOPY"
|
||||
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
||||
|
||||
# Verify we can copy a bookmark specifying the short bookmark name
|
||||
log_must zfs bookmark "$DATASET#$TESTBM" "#$TESTBMCOPY"
|
||||
log_must eval "bkmarkexists $DATASET#$TESTBMCOPY"
|
||||
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
||||
|
||||
# Verify two short paths are not allowed, and test empty paths
|
||||
log_mustnot zfs bookmark "#$TESTBM" "#$TESTBMCOPY"
|
||||
log_mustnot zfs bookmark "#$TESTBM" "#"
|
||||
log_mustnot zfs bookmark "#" "#$TESTBMCOPY"
|
||||
log_mustnot zfs bookmark "#" "#"
|
||||
log_mustnot zfs bookmark "#" ""
|
||||
log_mustnot zfs bookmark "" "#"
|
||||
log_mustnot zfs bookmark "" ""
|
||||
|
||||
# Verify that we can copy bookmarks on another origin filesystem
|
||||
log_must zfs clone "$DATASET@$TESTSNAP" "$DATASET_TWO"
|
||||
log_must zfs bookmark "$DATASET#$TESTBM" "$DATASET_TWO#$TESTBMCOPY"
|
||||
log_must zfs destroy "$DATASET_TWO"
|
||||
|
||||
# Verify that we can cannot create bookmarks on another non-origin filesystem
|
||||
log_must zfs create "$DATASET_TWO"
|
||||
log_mustnot_expect "source is not an ancestor of the new bookmark's dataset" zfs bookmark "$DATASET#$TESTBM" "$DATASET_TWO#$TESTBMCOPY"
|
||||
log_must zfs destroy "$DATASET_TWO"
|
||||
|
||||
# Verify that we can copy bookmarks on the pool dataset
|
||||
log_must zfs snapshot "$TESTPOOL@$TESTSNAP"
|
||||
log_must zfs bookmark "$TESTPOOL@$TESTSNAP" "$TESTPOOL#$TESTBM"
|
||||
log_must zfs bookmark "$TESTPOOL#$TESTBM" "$TESTPOOL#$TESTBMCOPY"
|
||||
log_must zfs destroy "$TESTPOOL#$TESTBM"
|
||||
log_must zfs destroy "$TESTPOOL#$TESTBMCOPY"
|
||||
log_must zfs destroy "$TESTPOOL@$TESTSNAP"
|
||||
|
||||
# Verify that copied 'normal' bookmarks are independent of the source bookmark
|
||||
log_must zfs bookmark "$DATASET#$TESTBM" "$DATASET#$TESTBMCOPY"
|
||||
log_must zfs destroy "$DATASET#$TESTBM"
|
||||
log_must eval "zfs send $DATASET@$TESTSNAP > $TEST_BASE_DIR/zfstest_datastream.$$"
|
||||
log_must eval "destroy_dataset $TESTPOOL/$TESTFS/recv"
|
||||
log_must eval "zfs recv -o mountpoint=none $TESTPOOL/$TESTFS/recv < $TEST_BASE_DIR/zfstest_datastream.$$"
|
||||
log_must zfs snapshot "$DATASET@$TESTSNAP2"
|
||||
log_must eval "zfs send -i \#$TESTBMCOPY $DATASET@$TESTSNAP2 > $TEST_BASE_DIR/zfstest_datastream.$$"
|
||||
log_must eval "zfs recv $TESTPOOL/$TESTFS/recv < $TEST_BASE_DIR/zfstest_datastream.$$"
|
||||
# cleanup
|
||||
log_must eval "destroy_dataset $DATASET@$TESTSNAP2"
|
||||
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
||||
log_must zfs bookmark "$DATASET@$TESTSNAP" "$DATASET#$TESTBM"
|
||||
|
||||
# Verify that copied redaction bookmarks are independent of the source bookmark
|
||||
## create redaction bookmark
|
||||
log_must zfs destroy "$DATASET#$TESTBM"
|
||||
log_must zfs destroy "$DATASET@$TESTSNAP"
|
||||
log_must eval "echo secret > $TESTDIR/secret"
|
||||
log_must zfs snapshot "$DATASET@$TESTSNAP"
|
||||
log_must eval "echo redacted > $TESTDIR/secret"
|
||||
log_must zfs snapshot "$DATASET@$TESTSNAP2" # TESTSNAP2 is the redaction snapshot
|
||||
log_must zfs list -t all -o name,createtxg,guid,mountpoint,written
|
||||
log_must zfs redact "$DATASET@$TESTSNAP" "$TESTBM" "$DATASET@$TESTSNAP2"
|
||||
# ensure our primitive for testing whether a bookmark is a redaction bookmark works
|
||||
log_must eval "zfs get all $DATASET#$TESTBM | grep redact_snaps"
|
||||
## copy the redaction bookmark
|
||||
log_must zfs bookmark "$DATASET#$TESTBM" "#$TESTBMCOPY"
|
||||
log_must eval "zfs send --redact "$TESTBMCOPY" -i $DATASET@$TESTSNAP $DATASET@$TESTSNAP2 2>&1 | head -n 100 | grep 'internal error: Invalid argument'"
|
||||
log_mustnot eval "zfs get all $DATASET#$TESTBMCOPY | grep redact_snaps"
|
||||
# try the above again after destroying the source bookmark, preventive measure for future work
|
||||
log_must zfs destroy "$DATASET#$TESTBM"
|
||||
log_must eval "zfs send --redact "$TESTBMCOPY" -i $DATASET@$TESTSNAP $DATASET@$TESTSNAP2 2>&1 | head -n 100 | grep 'internal error: Invalid argument'"
|
||||
log_mustnot eval "zfs get all $DATASET#$TESTBMCOPY | grep redact_snaps"
|
||||
## cleanup
|
||||
log_must eval "destroy_dataset $DATASET@$TESTSNAP2"
|
||||
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
||||
log_must eval "destroy_dataset $DATASET@$TESTSNAP"
|
||||
log_must zfs snapshot "$DATASET@$TESTSNAP"
|
||||
log_must zfs bookmark "$DATASET@$TESTSNAP" "$DATASET#$TESTBM"
|
||||
|
||||
log_pass "'zfs bookmark' works as expected"
|
||||
|
||||
Reference in New Issue
Block a user