Merge pull request #8971 from ThomasWaldmann/files-changed-option-master

create --files-changed=MODE option
This commit is contained in:
TW 2025-08-02 11:09:19 +02:00 committed by GitHub
commit 547f7ec338
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 46 additions and 11 deletions

View file

@ -1215,6 +1215,7 @@ class FilesystemObjectProcessors:
log_json,
iec,
file_status_printer=None,
files_changed="ctime",
):
self.metadata_collector = metadata_collector
self.cache = cache
@ -1223,6 +1224,7 @@ class FilesystemObjectProcessors:
self.process_file_chunks = process_file_chunks
self.show_progress = show_progress
self.print_file_status = file_status_printer or (lambda *args: None)
self.files_changed = files_changed
self.hlm = HardLinkManager(id_type=tuple, info_type=(list, type(None))) # (dev, ino) -> chunks or None
self.stats = Statistics(output_json=log_json, iec=iec) # threading: done by cache (including progress)
@ -1445,21 +1447,37 @@ class FilesystemObjectProcessors:
if not is_win32: # TODO for win32
with backup_io("fstat2"):
st2 = os.fstat(fd)
if is_special_file:
if self.files_changed == "disabled" or is_special_file:
# special files:
# - fifos change naturally, because they are fed from the other side. no problem.
# - blk/chr devices don't change ctime anyway.
pass
elif st.st_ctime_ns != st2.st_ctime_ns:
# ctime was changed, this is either a metadata or a data change.
changed_while_backup = True
elif start_reading - TIME_DIFFERS1_NS < st2.st_ctime_ns < end_reading + TIME_DIFFERS1_NS:
# this is to treat a very special race condition, see #3536.
# - file was changed right before st.ctime was determined.
# - then, shortly afterwards, but already while we read the file, the
# file was changed again, but st2.ctime is the same due to ctime granularity.
# when comparing file ctime to local clock, widen interval by TIME_DIFFERS1_NS.
changed_while_backup = True
elif self.files_changed == "ctime":
if st.st_ctime_ns != st2.st_ctime_ns:
# ctime was changed, this is either a metadata or a data change.
changed_while_backup = True
elif (
start_reading - TIME_DIFFERS1_NS < st2.st_ctime_ns < end_reading + TIME_DIFFERS1_NS
):
# this is to treat a very special race condition, see #3536.
# - file was changed right before st.ctime was determined.
# - then, shortly afterwards, but already while we read the file, the
# file was changed again, but st2.ctime is the same due to ctime granularity.
# when comparing file ctime to local clock, widen interval by TIME_DIFFERS1_NS.
changed_while_backup = True
elif self.files_changed == "mtime":
if st.st_mtime_ns != st2.st_mtime_ns:
# mtime was changed, this is either a data change.
changed_while_backup = True
elif (
start_reading - TIME_DIFFERS1_NS < st2.st_mtime_ns < end_reading + TIME_DIFFERS1_NS
):
# this is to treat a very special race condition, see #3536.
# - file was changed right before st.mtime was determined.
# - then, shortly afterwards, but already while we read the file, the
# file was changed again, but st2.mtime is the same due to mtime granularity.
# when comparing file mtime to local clock, widen interval by TIME_DIFFERS1_NS.
changed_while_backup = True
if changed_while_backup:
# regular file changed while we backed it up, might be inconsistent/corrupt!
if last_try:

View file

@ -262,6 +262,7 @@ class CreateMixIn:
log_json=args.log_json,
iec=args.iec,
file_status_printer=self.print_file_status,
files_changed=args.files_changed,
)
create_inner(archive, cache, fso)
else:
@ -611,6 +612,13 @@ class CreateMixIn:
it had before a content change happened. This can be used maliciously as well as
well-meant, but in both cases mtime based cache modes can be problematic.
The ``--files-changed`` option controls how Borg detects if a file has changed during backup:
- ctime (default): Use ctime to detect changes. This is the safest option.
- mtime: Use mtime to detect changes.
- disabled: Disable the "file has changed while we backed it up" detection completely.
This is not recommended unless you know what you're doing, as it could lead to
inconsistent backups if files change during the backup process.
The mount points of filesystems or filesystem snapshots should be the same for every
creation of a new archive to ensure fast operation. This is because the file cache that
is used to determine changed files quickly uses absolute filenames.
@ -888,6 +896,15 @@ class CreateMixIn:
default=FILES_CACHE_MODE_UI_DEFAULT,
help="operate files cache in MODE. default: %s" % FILES_CACHE_MODE_UI_DEFAULT,
)
fs_group.add_argument(
"--files-changed",
metavar="MODE",
dest="files_changed",
action=Highlander,
choices=["ctime", "mtime", "disabled"],
default="ctime",
help="specify how to detect if a file has changed during backup (ctime, mtime, disabled). default: ctime",
)
fs_group.add_argument(
"--read-special",
dest="read_special",