Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to restrict rsync to one file system #1600

Merged
merged 16 commits into from
Feb 4, 2024
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Back In Time

Version 1.4.4-dev (development of upcoming release)
* Feature: Support rsync '--one-file-system' in Expert Options (#1598)
* Build: Fix bash-completion symlink creation while installing & adding --diagnostics (#1615)
* Build: Activate PyLint error E602 (undefined-variable)
* Build: TravisCI use PyQt (except arch "ppc64le")
Expand Down
10 changes: 9 additions & 1 deletion common/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1266,13 +1266,21 @@ def setCopyUnsafeLinks(self, value, profile_id = None):
return self.setProfileBoolValue('snapshots.copy_unsafe_links', value, profile_id)

def copyLinks(self, profile_id = None):
#?When symlinks are encountered, the item that they point to
#?When symlinks are encountered, the item that they point to
#?(the reference) is copied, rather than the symlink.
return self.profileBoolValue('snapshots.copy_links', False, profile_id)

def setCopyLinks(self, value, profile_id = None):
return self.setProfileBoolValue('snapshots.copy_links', value, profile_id)

def oneFileSystem(self, profile_id = None):
#?Use rsync's "--one-file-system" to avoid crossing filesystem
#?boundaries when recursing.
return self.profileBoolValue('snapshots.one_file_system', False, profile_id)

def setOneFileSystem(self, value, profile_id = None):
return self.setProfileBoolValue('snapshots.one_file_system', value, profile_id)

def rsyncOptionsEnabled(self, profile_id = None):
#?Past additional options to rsync
return self.profileBoolValue('snapshots.rsync_options.enabled', False, profile_id)
Expand Down
Empty file modified common/create-manpage-backintime-config.py
100644 → 100755
Empty file.
13 changes: 11 additions & 2 deletions common/man/C/backintime-config.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH backintime-config 1 "Jan 2024" "version 1.4.4-dev" "USER COMMANDS"
.TH backintime-config 1 "Feb 2024" "version 1.4.4-dev" "USER COMMANDS"
.SH NAME
config \- BackInTime configuration files.
.SH SYNOPSIS
Expand Down Expand Up @@ -202,7 +202,7 @@ Default: true
.RS
Type: bool Allowed Values: true|false
.br
When symlinks are encountered, the item that they point to (the reference) is copied, rather than the symlink.
When symlinks are encountered, the item that they point to (the reference) is copied, rather than the symlink.
.PP
Default: false
.RE
Expand Down Expand Up @@ -458,6 +458,15 @@ Display notifications (errors, warnings) through libnotify.
Default: true
.RE

.IP "\fIprofile<N>.snapshots.one_file_system\fR" 6
.RS
Type: bool Allowed Values: true|false
.br
Use rsync's "--one-file-system" to avoid crossing filesystem boundaries when recursing.
.PP
Default: false
.RE

.IP "\fIprofile<N>.snapshots.path\fR" 6
.RS
Type: str Allowed Values: absolute path
Expand Down
5 changes: 4 additions & 1 deletion common/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,9 @@ def rsyncPrefix(config,
else:
cmd.append('--links')

if config.oneFileSystem():
cmd.append('--one-file-system')

if config.preserveAcl() and "ACLs" in caps:
cmd.append('--acls') # preserve ACLs (implies --perms)
no_perms = False
Expand Down Expand Up @@ -1283,7 +1286,7 @@ def mountArgs(path):
list: mount args
"""
mp = mountpoint(path)

with open('/etc/mtab', 'r') as mounts:

for line in mounts:
Expand Down
25 changes: 25 additions & 0 deletions qt/settingsdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import datetime
import copy
import re
import textwrap

from PyQt5.QtGui import QIcon, QFont, QPalette, QBrush, QColor
from PyQt5.QtWidgets import (QDialog,
Expand Down Expand Up @@ -67,6 +68,9 @@
import logger
from exceptions import MountException, NoPubKeyLogin, KnownHost

# That value is used to wrap tooltip strings (inserting newline characters).
_TOOLTIP_WRAP_LENGTH = 72


class SettingsDialog(QDialog):
def __init__(self, parent):
Expand Down Expand Up @@ -1018,6 +1022,25 @@ def __init__(self, parent):
)
layout.addWidget(self.cbCopyLinks)

# one file system option
self.cbOneFileSystem = QCheckBox(
_('Restrict to one file system'), self)
self.cbOneFileSystem.setToolTip(
'uses \'rsync --one-file-system\'\n'
'From \'man rsync\':\n'
+ '\n'.join(textwrap.wrap(
'This tells rsync to avoid crossing a filesystem boundary '
'when recursing. This does not limit the user\'s ability '
'to specify items to copy from multiple filesystems, just '
'rsync\'s recursion through the hierarchy of each directory '
'that the user specified, and also the analogous recursion '
'on the receiving side during deletion. Also keep in mind '
'that rsync treats a "bind" mount to the same device as '
'being on the same filesystem.',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

longest tooltip I ever saw ;-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are much more of this from-rsync-manpage-copy-and-pasted-tooltips. That is why I introduced the wrapping, to prevent translators from dealing with line breaks them selfs.

_TOOLTIP_WRAP_LENGTH))
)
layout.addWidget(self.cbOneFileSystem)

# additional rsync options
hlayout = QHBoxLayout()
layout.addLayout(hlayout)
Expand Down Expand Up @@ -1393,6 +1416,7 @@ def updateProfile(self):
self.cbPreserveXattr.setChecked(self.config.preserveXattr())
self.cbCopyUnsafeLinks.setChecked(self.config.copyUnsafeLinks())
self.cbCopyLinks.setChecked(self.config.copyLinks())
self.cbOneFileSystem.setChecked(self.config.oneFileSystem())
self.cbRsyncOptions.setChecked(self.config.rsyncOptionsEnabled())
self.txtRsyncOptions.setText(self.config.rsyncOptions())
self.cbSshPrefix.setChecked(self.config.sshPrefixEnabled())
Expand Down Expand Up @@ -1587,6 +1611,7 @@ def saveProfile(self):
self.config.setPreserveXattr(self.cbPreserveXattr.isChecked())
self.config.setCopyUnsafeLinks(self.cbCopyUnsafeLinks.isChecked())
self.config.setCopyLinks(self.cbCopyLinks.isChecked())
self.config.setOneFileSystem(self.cbOneFileSystem.isChecked())
self.config.setRsyncOptions(self.cbRsyncOptions.isChecked(),
self.txtRsyncOptions.text())
self.config.setSshPrefix(self.cbSshPrefix.isChecked(),
Expand Down