Skip to content

Commit abff184

Browse files
archive: preserve pre-open original atime, fixes #6194
When creating an archive with --atime on platforms without O_NOATIME, opening a file for reading could update atime before we recorded it (thus we archived the updated atime, not the original one). Capture pre-open path-based-stat atime and use it if it pre-dates the atime we got from the fd AND if it refers to same fs object (avoid race condition).
1 parent d680ee0 commit abff184

File tree

1 file changed

+13
-3
lines changed

1 file changed

+13
-3
lines changed

src/borg/archive.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,15 +1088,21 @@ def __init__(self, *, noatime, noctime, nobirthtime, numeric_ids, noflags, noacl
10881088
self.noxattrs = noxattrs
10891089
self.nobirthtime = nobirthtime
10901090

1091-
def stat_simple_attrs(self, st, path, fd=None):
1091+
def stat_simple_attrs(self, st, path, fd=None, *, st_before_open=None):
10921092
attrs = {}
10931093
attrs["mode"] = st.st_mode
10941094
# borg can work with archives only having mtime (very old borg archives do not have
10951095
# atime/ctime). it can be useful to omit atime/ctime, if they change without the
10961096
# file content changing - e.g. to get better metadata deduplication.
10971097
attrs["mtime"] = safe_ns(st.st_mtime_ns)
10981098
if not self.noatime:
1099-
attrs["atime"] = safe_ns(st.st_atime_ns)
1099+
# If a pre-open stat result was provided and it refers to same fs object,
1100+
# then preserve the original atime. Avoid race conditions.
1101+
if st_before_open is not None and st_before_open.st_ino == st.st_ino:
1102+
atime = min(st_before_open.st_atime_ns, st.st_atime_ns)
1103+
else:
1104+
atime = st.st_atime_ns
1105+
attrs["atime"] = safe_ns(atime)
11001106
if not self.noctime:
11011107
attrs["ctime"] = safe_ns(st.st_ctime_ns)
11021108
if not self.nobirthtime:
@@ -1391,10 +1397,14 @@ def process_file(self, *, path, parent_fd, name, st, cache, flags=flags_normal,
13911397
): # no status yet
13921398
if item is None:
13931399
return status
1400+
# Remember the filename-based stat result we obtained before opening the file.
1401+
# On platforms without O_NOATIME, merely opening a file for reading can update
1402+
# the atime. We want to store the original atime as seen before opening.
1403+
st_before_open = st
13941404
with OsOpen(path=path, parent_fd=parent_fd, name=name, flags=flags, noatime=True) as fd:
13951405
with backup_io("fstat"):
13961406
st = stat_update_check(st, os.fstat(fd))
1397-
item.update(self.metadata_collector.stat_simple_attrs(st, path, fd=fd))
1407+
item.update(self.metadata_collector.stat_simple_attrs(st, path, fd=fd, st_before_open=st_before_open))
13981408
item.update(self.metadata_collector.stat_ext_attrs(st, path, fd=fd))
13991409
maybe_exclude_by_attr(item) # check early, before processing all the file content
14001410
is_special_file = is_special(st.st_mode)

0 commit comments

Comments
 (0)