Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4a1b788
Add baksnapperd-denotebak.sh [#45]
plattfot Oct 18, 2025
5c3c9b8
Add baksnapperd command `snapshot-type` [#45]
plattfot Oct 18, 2025
976b428
Add support to backup DeNotebak type snapshots
plattfot Oct 18, 2025
d2c78a3
Install baksnapperd-denotebak backend [#45]
plattfot Oct 25, 2025
b11303b
Add support for DeNotebak in baksnapper [#45]
plattfot Nov 1, 2025
7f1ba5b
test: Use make-snapshot-from when reading in input for sender
plattfot Oct 25, 2025
084cbad
test: Move `create-snapshot` out from `create-snapper-snapshot`
plattfot Oct 25, 2025
a2d4ed1
test: Add tags field to `<snapshot>`
plattfot Oct 25, 2025
25d50a3
test: Add support for DeNotebak snapshots in runner
plattfot Oct 25, 2025
46ccda1
test: Add `make-snapshot-from-denotebak-read` [#45]
plattfot Nov 10, 2025
7af0b60
test: Fix mock btrfs command to work with both snapper and DeNotebak …
plattfot Nov 10, 2025
7d2b2b8
test: Initial test for DeNotebak [#45]
plattfot Oct 25, 2025
60e02c5
test: Add support to switch the backend for reading in snapshots [#45]
plattfot Nov 10, 2025
0b0920f
test: Add `create-denotebak-snapshot` procedure [#45]
plattfot Nov 13, 2025
bb88df4
test: Swap the order for `conf` and `test_entry` in the meson tests
plattfot Nov 13, 2025
9e88ba0
test: Move `baksnapperd` dependency to `test_entry` [#45]
plattfot Nov 13, 2025
c64ca30
test: Move `env` to `test_entry` [#45]
plattfot Nov 13, 2025
ae5113f
test: Add test configurations for DeNotebak [#45]
plattfot Nov 13, 2025
ea8ab6a
test: Skip `--config` tests for DeNotebak tests [#45]
plattfot Nov 13, 2025
d99eb83
test: Add `denotebak_test_env` [#45]
plattfot Nov 13, 2025
2841cf3
test: Add DeNotebak tests to `test_entry` [#45]
plattfot Nov 13, 2025
b131231
changelog: add DeNotebak backend
plattfot Nov 17, 2025
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
4 changes: 4 additions & 0 deletions CHANGELOG.d/20251116T170044--add-denotebak-backend__added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!-- SPDX-FileCopyrightText: 2025 Fredrik Salomonsson <plattfot@posteo.net> -->
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
### Added
- [DeNotebak](https://sr.ht/~plattfot/DeNotebak/) backend support. [#45](https://github.com/plattfot/baksnapper/issues/45)
160 changes: 98 additions & 62 deletions baksnapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,16 @@ then
fi
fi

if [[ $sender_version -ge 4 && $($sender snapshot-type) != "snapper" ]]
then
error "config interface only supports snapper"
fi

if [[ $receiver_version -ge 4 && $($receiver snapshot-type) != "snapper" ]]
then
error "config interface only supports snapper"
fi

src_root=${subvolume:?}/.snapshots
dest_root="$dest/$p_config"
elif [[ -n "$p_src" && -n "$p_dest" ]]
Expand Down Expand Up @@ -523,6 +533,40 @@ else
error "You need to specify the SOURCE and DEST or the config name to backup!"
fi

# Get what snapshot type the daemon supports
# 1 [out]: variable where it will save the snapshot type.
# 2 [in]: variable that contains the baksnapperd backend
# 3 [in]: the version of the baksnapperd backend
function get-snapshot-type {
declare -n snapshot_type=$1
if [[ $3 -ge 4 ]]
then
snapshot_type=$(${!2} snapshot-type)
else
# shellcheck disable=SC2034
backend=snapper
fi
}
get-snapshot-type sender_snapshot_type sender "$sender_version"
get-snapshot-type receiver_snapshot_type receiver "$receiver_version"

# shellcheck disable=SC2154
if [[ "$sender_snapshot_type" != "$receiver_snapshot_type" ]]
then
error "Sender and receiver expects different snapshot types: sender: \
$sender_snapshot_type, receiver: $receiver_snapshot_type"
fi

snapshot_type=$sender_snapshot_type

# Using a bitmap to represent where a snapshot is located:
# 0b01 → exist at the source
# 0b10 → exist at the destination
# 0b11 → exist at both locations, i.e. common
declare -A common
only_in_src=()
only_in_dest=()

## Setup signals ###############################################################
# EXIT --> if notify-send is installed, inform the user; always cleanup
if hash notify-send 2> /dev/null
Expand Down Expand Up @@ -597,28 +641,20 @@ function gather-receiver-snapshots {
# only_in_src: only exist at the source
# only_in_dest: only exist at the destination
function compare-snapshots {

idx_src=0
idx_dest=0

# Using a bitmap to represent where a snapshot is located:
# 0b01 → exist at the source
# 0b10 → exist at the destination
# 0b11 → exist at both locations, i.e. common
common=()
only_in_src=()
only_in_dest=()

# NOTE: the snapshots must be sorted in ascending order hence the
# "sort -g" when getting the snapshots.
while (( idx_src < num_src_snapshots && idx_dest < num_dest_snapshots ))
do
if [ "${src_snapshots[idx_src]}" -eq "${dest_snapshots[idx_dest]}" ]
if [[ "${src_snapshots[idx_src]}" == "${dest_snapshots[idx_dest]}" ]]
then
common["${src_snapshots[idx_src]}"]=0b11
((++idx_src))
((++idx_dest))
elif [ "${src_snapshots[idx_src]}" -lt "${dest_snapshots[idx_dest]}" ]
elif [[ "${src_snapshots[idx_src]}" < "${dest_snapshots[idx_dest]}" ]]
then
only_in_src+=("${src_snapshots[idx_src]}")
((++idx_src))
Expand All @@ -645,15 +681,17 @@ function single-backup {
local start_time
start_time=$(date +%s)

$receiver create-snapshot "$dest_root" "$1" ||
error "Failed to create snapshot at backup location!"

if ! $sender send-info "$src_root" "$1" | $receiver receive-info "$dest_root" "$1"
if [[ "$snapshot_type" == "snapper" ]]
then
$receiver remove-broken-snapshot "$dest_root" "$1"
error "Failed to send snapshot info!"
fi
$receiver create-snapshot "$dest_root" "$1" ||
error "Failed to create snapshot at backup location!"

if ! $sender send-info "$src_root" "$1" | $receiver receive-info "$dest_root" "$1"
then
$receiver remove-broken-snapshot "$dest_root" "$1"
error "Failed to send snapshot info!"
fi
fi
printf "%s\t\t%s\t" "${dest_root}" "${1}" >>"${p_summary}"
exec 4>"${p_temp_dir}/${1}"
if ! $sender send-snapshot "$src_root" "$1" | tee >( wc -c >&4 ) | $receiver receive-snapshot "$dest_root" "$1"
Expand All @@ -676,16 +714,17 @@ function incremental-backup {
start_time=$(date +%s)

echo "Incremental backup $1 $2"

$receiver create-snapshot "$dest_root" "$2" ||
error "Failed to create snapshot at backup location!"

if ! $sender send-info "$src_root" "$2" | $receiver receive-info "$dest_root" "$2"
if [[ "$snapshot_type" == "snapper" ]]
then
$receiver remove-broken-snapshot "$dest_root" "$1"
error "Failed to send snapshot info!"
fi
$receiver create-snapshot "$dest_root" "$2" ||
error "Failed to create snapshot at backup location!"

if ! $sender send-info "$src_root" "$2" | $receiver receive-info "$dest_root" "$2"
then
$receiver remove-broken-snapshot "$dest_root" "$1"
error "Failed to send snapshot info!"
fi
fi
printf "%s\t%s\t%s\t" "${dest_root}" "${1}" "${2}" >>"${p_summary}"
exec 4>"${p_temp_dir}/${2}"
if ! $sender send-incremental-snapshot "$src_root/"{"$1","$2"} \
Expand Down Expand Up @@ -767,62 +806,59 @@ function backup {
return 0
fi

# Find the first common snapshot that is lower than the specified
# snapshot.
#
# 1 [out]: parent snapshot
# 2 [in]: snapshot
function find-parent {
declare -n out=$1
local snapshot=$2
local idx=${num_src_snapshots}-1
for (( ; idx >= 0; --idx ))
do
if [[ "${src_snapshots[idx]}" < "$snapshot" && \
${common[${src_snapshots[idx]}]} == 0b11 ]]
then
break
fi
done
out=${src_snapshots[idx]}
}

if [[ $p_all == 0 ]]
then
local common_ids=("${!common[@]}")
local common_last=${common_ids[-1]}
# Check that it's not already synced
if [[ "$common_last" == "$p_snapshot" ]]
if [[ ${common[$p_snapshot]} == 0b11 ]]
then
error "Already synced the last snapshot."
error "Already synced snapshot $p_snapshot."
fi
incremental-backup "$common_last" "$p_snapshot"
local parent
find-parent parent "$p_snapshot"
incremental-backup "$parent" "$p_snapshot"
return 0
else
local -a src_and_common
for snapshot in "${!common[@]}"
do
# These exist at both locations → 0b01
src_and_common["$snapshot"]=0b11
done
for snapshot in "${only_in_src[@]}"
do
# These only exist at the source → 0b01
src_and_common["$snapshot"]=0b01
done
local num_snapshots=${#src_and_common[@]}
# Find the first common snapshot that is lower than the first
# source only snapshot. This will be the start of the incremental
# backup. If no one is found it will use the lowest common one.
local first_src_snapshot=${only_in_src[0]}
local snapshot_ids=("${!src_and_common[@]}")
local idx=${num_snapshots}-1
for (( ; idx >= 0; --idx ))
do
if [[ ${snapshot_ids[idx]} -lt $first_src_snapshot ]]
then
if [[ ${src_and_common[${snapshot_ids[idx]}]} == 0b11 ]]
then
break
fi
fi
done
incremental-backup "${snapshot_ids[idx]}" "$first_src_snapshot"
src_and_common["${snapshot_ids[idx]}"]=0b11
first_src_snapshot="${only_in_src[0]}"
find-parent parent "$first_src_snapshot"
incremental-backup "$parent" "$first_src_snapshot"
common["${src_snapshots[idx]}"]=0b11
for (( src_idx = 1; src_idx < num_src_only; ++src_idx ))
do
# Find the source only snapshot in the snapshot_ids
# Find the next source snapshot to send
local src_snapshot=${only_in_src[src_idx]}
for (( ; idx < num_snapshots; ++idx ))
for (( ; idx < num_src_snapshots; ++idx ))
do
if [[ ${snapshot_ids[idx]} -eq $src_snapshot ]]
if [[ "${src_snapshots[idx]}" == "$src_snapshot" ]]
then
break
fi
done
# Use the common snapshot previous to it as the parent
incremental-backup "${snapshot_ids[idx-1]}" "$src_snapshot"
src_and_common["$src_snapshot"]=0b11
incremental-backup "${src_snapshots[idx-1]}" "$src_snapshot"
common["$src_snapshot"]=0b11
done
fi
}
Expand Down
Loading