Skip to content

Add snapshot_delete_dir option to remove directories from snapshots#648

Open
mnikulin wants to merge 2 commits into
digint:masterfrom
mnikulin:master
Open

Add snapshot_delete_dir option to remove directories from snapshots#648
mnikulin wants to merge 2 commits into
digint:masterfrom
mnikulin:master

Conversation

@mnikulin

Copy link
Copy Markdown

Summary

Adds a new configuration option snapshot_delete_dir that allows specifying a list of directories to be deleted inside newly created snapshots before they are set read-only.

When snapshot_delete_dir is configured, btrbk:

  1. Creates the snapshot as read-write (instead of read-only)
  2. Deletes all configured directories inside the snapshot
  3. Sets the snapshot to read-only using btrfs property set ro true

If any deletion fails, the snapshot is removed and the subvolume section is skipped (same error handling as a failed snapshot creation).

Configuration Example

subvolume /home/user
    snapshot_dir /mnt/snapshots
    snapshot_delete_dir .cache
    snapshot_delete_dir .config/google-chrome
    snapshot_delete_dir .local/share/virtualenvs
    snapshot_delete_dir .local/share/Trash
    snapshot_delete_dir Downloads

Motivation

This is an implementation of the approach discussed in issue #258 — a simple, explicit list of directories to exclude from snapshots, without complex include/exclude glob patterns.

This also partially addresses #238: users who want backups to contain only certain parts of a subvolume can now delete the unwanted directories from snapshots after creation.

Security

The path argument is validated with check_file(..., relative => 1, sanitize => 1) to prevent path traversal attacks (e.g. a misconfigured ../../../../home).

When using backend_remote btrfs-progs-sudo, btrbk invokes commands via sudo -n. For snapshot_delete_dir, two additional commands are executed on the source/snapshot host:

  • sudo -n btrfs property set <snapshot_path> ro true
  • sudo -n rm -rf <snapshot_path>/<dir>

To follow the principle of least privilege, you can restrict rm -rf in /etc/sudoers to the exact snapshot directory prefix instead of allowing arbitrary paths:

# /etc/sudoers.d/btrbk
btrbk_user ALL=(root) NOPASSWD: \
    /usr/bin/btrfs, \
    /usr/bin/readlink, \
    /bin/rm -rf /mnt/snapshots/home/user.*

This limits rm -rf to paths matching the snapshot naming pattern (e.g. /mnt/snapshots/home/user.20260627T0000+0300), preventing accidental or malicious deletion of unrelated paths.

Implementation Notes

  • btrfs property set ro true is used to make the snapshot read-only after cleanup. This approach preserves the parent_uuid relationship, enabling incremental send/receive to work correctly.
  • ssh_filter_btrbk.sh is updated to allow btrfs property set <path> ro true and rm -rf <path> when --source or --snapshot mode is used.
  • The option supports global, volume, and subvolume context and can be specified multiple times.

@dcolascione

Copy link
Copy Markdown

Don't you want to put the snapshot in a temporary directory first, then do atomic rename?

@mnikulin

Copy link
Copy Markdown
Author

Don't you want to put the snapshot in a temporary directory first, then do atomic rename?

This seems to add quite a bit of complexity compared to the original snapshot creation flow before this PR. Also, mv is not very easy to constrain precisely in sudoers: paths can be limited with regexes, but reliably expressing "only rename this expected temporary snapshot to its matching final name" would likely require a dedicated wrapper.

@dcolascione

Copy link
Copy Markdown

Deleting directories doesn't provide enough power anyway. You want something at least as powerful as rsync pattern files, but really, to just punt on the whole thing to a script, which can then be as simple as deleting directories or as complex as reading Borg- or rsync-style pattern descriptions

@mnikulin

Copy link
Copy Markdown
Author

A script-based approach may be a good solution for a more general feature, but I am not sure it is in the spirit of btrbk. It is also a broader design with different trade-offs than what I am trying to solve here.

My goal was even simpler: get a backup without a few configurable directories. This is what I have been missing in btrbk for years, and even this already runs into some limitations of btrfs snapshots. I do not need rsync- or Borg-style pattern handling for my use case.

@dcolascione

Copy link
Copy Markdown

You want to omit a few directories. Others want to omit a few files. Others might want to do size-based cleaning or something. You can't make everyone happy with a static approach, and I can't see an a priori reason to prefer directory exclusion over other kinds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants