#1139322 backupninja: rsync handler: rotate_long cutoff_time and metadata bugs

#1139322#5
Date:
2026-06-08 15:05:08 UTC
From:
To:
Package: backupninja
Version: 1.2.2-1
Severity: important
Tags: patch
--- Problem description

The rsync handler's rotate_long (and rotate_long_remote) functions contain
three bugs that together cause long-format backup rotation to stall silently
after a few cycles.
--- Bug 1 -- cuttoff_time calculation wrong for weekly/monthly rotate_long currently reads: cutoff_time=$(( now - (seconds*(i-1)) )) For daily rotations this is correct (base_age = 0), but for weekly and monthly it does not account for the time needed to fill all preceding tiers. Example with keepdaily=5, keepweekly=3: weekly.1: cutoff = now - 0 (immediately eligible for promotion) monthly.1: cutoff = now - 0 (immediately eligible) Instead they should take, respectively, 5 days and 26 days before being rotated. Fix: add base_age so that weekly/monthly only rotate after lower tiers have been filled: if [ "$rottype" == "daily" ]; then base_age=0 elif [ "$rottype" == "weekly" ]; then base_age=$((keepdaily * 86400)) elif [ "$rottype" == "monthly" ]; then base_age=$(((keepdaily * 86400) + (keepweekly * 604800))) fi cutoff_time=$(( now - (base_age + (i * seconds)) ))
--- Bug 2 -- created file lost on daily/weekly promotion When daily.MAX is promoted to weekly.1 (or weekly.MAX to monthly.1), the original handler discards the "created" metadata file from the source directory and writes only a new "rotated" file: date +%c%n%s > metadata/rotated #if [ -f metadata/daily.$max/created ]; then # mv ... (commented out) #fi Over successive cycles every backup in a tier ends up with the same promotion timestamp instead of the original backup creation time. The age calculation sees all of them as equally new, which produces infinite "skipping rotation" messages. Fix: restore the mv of created into the promoted directory: if [ -f $backuproot/metadata/daily.$max/created ]; then $nice mv $backuproot/metadata/daily.$max/created \ $backuproot/metadata/weekly.1/ fi (Same change needed on the weekly->monthly promotion and in rotate_long_remote.)
--- Bug 3 -- no fallback when only "rotated" exists rotate_long currently reads: if [ -f $metadata.$i/created ]; then $nice mv $metadata.$i/created $metadata.$next fi If a backup directory only has a "rotated" file (common after Bug 2 has corrupted the metadata), the timestamp is silently lost during the shift. The next iteration reads "created=0", triggers "Invalid metadata", and aborts the rotation loop. Fix: if [ -f $metadata.$i/created ]; then $nice mv $metadata.$i/created $metadata.$next elif [ -f $metadata.$i/rotated ]; then $nice mv $metadata.$i/rotated $metadata.$next/created fi
--- Repairing existing corrupted timestamps Backup sets already affected (all monthly.* metadata showing the same date) can be repaired with: for d in /path/to/backup/section/weekly.* /path/to/backup/section/monthly.*; do mtime=$(stat -c '%Y' "$d") printf '%(%c)T\n%s\n' "$mtime" "$mtime" > "$(dirname $d)/metadata/$(basename $d)/created" done
--- Affected versions The bugs affect both local (rotate_long) and remote (rotate_long_remote) code paths.