aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-02-12 09:01:19 -0600
committerCraig Jennings <c@cjennings.net>2026-02-12 09:01:19 -0600
commitac950facc08dbaf8bdda7431d434a16f4d02b222 (patch)
tree7d59de88b56446c652dd3e6d0644eda523f090cc
parent819a8e36263bbbed182f20040555ba2644dba1be (diff)
downloadrsyncshot-ac950facc08dbaf8bdda7431d434a16f4d02b222.tar.gz
rsyncshot-ac950facc08dbaf8bdda7431d434a16f4d02b222.zip
Fix snapshot rotation failure on remote filesystems
Snapshots with read-only permissions (from chmod -w at creation) and internal dirs with restrictive modes (e.g. /etc at 555) prevented rm -rf from deleting old snapshots during rotation. This caused cascading mv failures and silent rotation breakage. - Add chmod -R u+w before rm -rf of oldest snapshot - Clean up orphan snapshots beyond retention count - Add error checking on rm, mv, and cp -al operations
-rwxr-xr-xrsyncshot25
1 files changed, 22 insertions, 3 deletions
diff --git a/rsyncshot b/rsyncshot
index 5b5820b..8194d20 100755
--- a/rsyncshot
+++ b/rsyncshot
@@ -940,9 +940,24 @@ fi
log "Rotating snapshots..."
# --- Delete oldest snapshot if it exceeds retention count ---
+# Restore write permission recursively first — snapshots contain directories
+# preserved with their original permissions (e.g. /etc dirs with mode 555),
+# and chmod -w (applied at creation) removes owner write on the top level.
+# Without u+w, rm -rf fails on remote filesystems where we run as a normal user.
if run_cmd "[ -d '$BASE_PATH/$TYPE.$MAX' ]"; then
log "Deleting oldest snapshot: $TYPE.$MAX"
- run_cmd "$RM -rf '$BASE_PATH/$TYPE.$MAX'"
+ run_cmd "chmod -R u+w '$BASE_PATH/$TYPE.$MAX'"
+ if ! run_cmd "$RM -rf '$BASE_PATH/$TYPE.$MAX'"; then
+ error "Snapshot rotation failed: could not delete $TYPE.$MAX"
+ fi
+fi
+
+# --- Clean up orphan snapshot beyond retention (from previous failures) ---
+BEYOND=$((MAX+1))
+if run_cmd "[ -d '$BASE_PATH/$TYPE.$BEYOND' ]"; then
+ log "Cleaning up orphaned snapshot: $TYPE.$BEYOND"
+ run_cmd "chmod -R u+w '$BASE_PATH/$TYPE.$BEYOND'"
+ run_cmd "$RM -rf '$BASE_PATH/$TYPE.$BEYOND'"
fi
# --- Rotate existing snapshots (newest to oldest to avoid overwriting) ---
@@ -950,7 +965,9 @@ for (( start=$((MAX)); start>=0; start-- )); do
end=$((start+1))
if run_cmd "[ -d '$BASE_PATH/$TYPE.$start' ]"; then
log "Rotating: $TYPE.$start -> $TYPE.$end"
- run_cmd "$MV '$BASE_PATH/$TYPE.$start' '$BASE_PATH/$TYPE.$end'"
+ if ! run_cmd "$MV '$BASE_PATH/$TYPE.$start' '$BASE_PATH/$TYPE.$end'"; then
+ error "Snapshot rotation failed: could not move $TYPE.$start to $TYPE.$end"
+ fi
fi
done
@@ -962,7 +979,9 @@ run_cmd "touch '$BASE_PATH/latest'"
# This is instant and uses no additional disk space for unchanged files.
# Only files that differ between snapshots consume extra space.
log "Creating new snapshot: $TYPE.0"
-run_cmd "$CP -al '$BASE_PATH/latest' '$BASE_PATH/$TYPE.0'"
+if ! run_cmd "$CP -al '$BASE_PATH/latest' '$BASE_PATH/$TYPE.0'"; then
+ error "Snapshot creation failed: could not hard-link latest to $TYPE.0"
+fi
# --- Make snapshot read-only to prevent accidental modification ---
run_cmd "chmod -w '$BASE_PATH/$TYPE.0'"