Skip to content

Commit 547f7ec

Browse files
Merge pull request #8971 from ThomasWaldmann/files-changed-option-master
create --files-changed=MODE option
2 parents e40298e + b27df15 commit 547f7ec

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

src/borg/archive.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,7 @@ def __init__(
12151215
log_json,
12161216
iec,
12171217
file_status_printer=None,
1218+
files_changed="ctime",
12181219
):
12191220
self.metadata_collector = metadata_collector
12201221
self.cache = cache
@@ -1223,6 +1224,7 @@ def __init__(
12231224
self.process_file_chunks = process_file_chunks
12241225
self.show_progress = show_progress
12251226
self.print_file_status = file_status_printer or (lambda *args: None)
1227+
self.files_changed = files_changed
12261228

12271229
self.hlm = HardLinkManager(id_type=tuple, info_type=(list, type(None))) # (dev, ino) -> chunks or None
12281230
self.stats = Statistics(output_json=log_json, iec=iec) # threading: done by cache (including progress)
@@ -1445,21 +1447,37 @@ def process_file(self, *, path, parent_fd, name, st, cache, flags=flags_normal,
14451447
if not is_win32: # TODO for win32
14461448
with backup_io("fstat2"):
14471449
st2 = os.fstat(fd)
1448-
if is_special_file:
1450+
if self.files_changed == "disabled" or is_special_file:
14491451
# special files:
14501452
# - fifos change naturally, because they are fed from the other side. no problem.
14511453
# - blk/chr devices don't change ctime anyway.
14521454
pass
1453-
elif st.st_ctime_ns != st2.st_ctime_ns:
1454-
# ctime was changed, this is either a metadata or a data change.
1455-
changed_while_backup = True
1456-
elif start_reading - TIME_DIFFERS1_NS < st2.st_ctime_ns < end_reading + TIME_DIFFERS1_NS:
1457-
# this is to treat a very special race condition, see #3536.
1458-
# - file was changed right before st.ctime was determined.
1459-
# - then, shortly afterwards, but already while we read the file, the
1460-
# file was changed again, but st2.ctime is the same due to ctime granularity.
1461-
# when comparing file ctime to local clock, widen interval by TIME_DIFFERS1_NS.
1462-
changed_while_backup = True
1455+
elif self.files_changed == "ctime":
1456+
if st.st_ctime_ns != st2.st_ctime_ns:
1457+
# ctime was changed, this is either a metadata or a data change.
1458+
changed_while_backup = True
1459+
elif (
1460+
start_reading - TIME_DIFFERS1_NS < st2.st_ctime_ns < end_reading + TIME_DIFFERS1_NS
1461+
):
1462+
# this is to treat a very special race condition, see #3536.
1463+
# - file was changed right before st.ctime was determined.
1464+
# - then, shortly afterwards, but already while we read the file, the
1465+
# file was changed again, but st2.ctime is the same due to ctime granularity.
1466+
# when comparing file ctime to local clock, widen interval by TIME_DIFFERS1_NS.
1467+
changed_while_backup = True
1468+
elif self.files_changed == "mtime":
1469+
if st.st_mtime_ns != st2.st_mtime_ns:
1470+
# mtime was changed, this is either a data change.
1471+
changed_while_backup = True
1472+
elif (
1473+
start_reading - TIME_DIFFERS1_NS < st2.st_mtime_ns < end_reading + TIME_DIFFERS1_NS
1474+
):
1475+
# this is to treat a very special race condition, see #3536.
1476+
# - file was changed right before st.mtime was determined.
1477+
# - then, shortly afterwards, but already while we read the file, the
1478+
# file was changed again, but st2.mtime is the same due to mtime granularity.
1479+
# when comparing file mtime to local clock, widen interval by TIME_DIFFERS1_NS.
1480+
changed_while_backup = True
14631481
if changed_while_backup:
14641482
# regular file changed while we backed it up, might be inconsistent/corrupt!
14651483
if last_try:

src/borg/archiver/create_cmd.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ def create_inner(archive, cache, fso):
262262
log_json=args.log_json,
263263
iec=args.iec,
264264
file_status_printer=self.print_file_status,
265+
files_changed=args.files_changed,
265266
)
266267
create_inner(archive, cache, fso)
267268
else:
@@ -611,6 +612,13 @@ def build_parser_create(self, subparsers, common_parser, mid_common_parser):
611612
it had before a content change happened. This can be used maliciously as well as
612613
well-meant, but in both cases mtime based cache modes can be problematic.
613614
615+
The ``--files-changed`` option controls how Borg detects if a file has changed during backup:
616+
- ctime (default): Use ctime to detect changes. This is the safest option.
617+
- mtime: Use mtime to detect changes.
618+
- disabled: Disable the "file has changed while we backed it up" detection completely.
619+
This is not recommended unless you know what you're doing, as it could lead to
620+
inconsistent backups if files change during the backup process.
621+
614622
The mount points of filesystems or filesystem snapshots should be the same for every
615623
creation of a new archive to ensure fast operation. This is because the file cache that
616624
is used to determine changed files quickly uses absolute filenames.
@@ -888,6 +896,15 @@ def build_parser_create(self, subparsers, common_parser, mid_common_parser):
888896
default=FILES_CACHE_MODE_UI_DEFAULT,
889897
help="operate files cache in MODE. default: %s" % FILES_CACHE_MODE_UI_DEFAULT,
890898
)
899+
fs_group.add_argument(
900+
"--files-changed",
901+
metavar="MODE",
902+
dest="files_changed",
903+
action=Highlander,
904+
choices=["ctime", "mtime", "disabled"],
905+
default="ctime",
906+
help="specify how to detect if a file has changed during backup (ctime, mtime, disabled). default: ctime",
907+
)
891908
fs_group.add_argument(
892909
"--read-special",
893910
dest="read_special",

0 commit comments

Comments
 (0)