diff options
| author | Craig Jennings <c@cjennings.net> | 2026-02-12 09:01:19 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-02-12 09:01:19 -0600 |
| commit | ac950facc08dbaf8bdda7431d434a16f4d02b222 (patch) | |
| tree | 7d59de88b56446c652dd3e6d0644eda523f090cc | |
| parent | 819a8e36263bbbed182f20040555ba2644dba1be (diff) | |
| download | rsyncshot-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-x | rsyncshot | 25 |
1 files changed, 22 insertions, 3 deletions
@@ -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'" |
