Skip to content

Comments

cache: add seek()/tell() to SyncFile, use SaveFile in _write_files_cache, fixes #9390#9392

Open
mr-raj12 wants to merge 1 commit intoborgbackup:masterfrom
mr-raj12:fix-syncfile-seek-tell
Open

cache: add seek()/tell() to SyncFile, use SaveFile in _write_files_cache, fixes #9390#9392
mr-raj12 wants to merge 1 commit intoborgbackup:masterfrom
mr-raj12:fix-syncfile-seek-tell

Conversation

@mr-raj12
Copy link
Contributor

Description

  • _write_files_cache() in cache.py has a TODO noting that SaveFile couldn't be used because SyncFile lacks .seek() and .tell()
  • Without SaveFile, the files cache is written directly to the final path — a crash mid-write can leave a corrupted file
  • SaveFile provides atomic writes (temp file + os.replace), but its __enter__ returns a SyncFile, and IntegrityCheckedFile needs .seek()/.tell() on its backing fd for hash finalization (hash_length calls seek(0, SEEK_END) + tell())
  • Fix: add the missing methods to SyncFile, make close() idempotent (needed because the IntegrityCheckedFile → hasher → FileLikeWrapper exit chain closes the SyncFile before SaveFile.__exit__ tries to close it again), then refactor _write_files_cache to use SaveFile

Fixes #9390

Changes

File Change
src/borg/platform/base.py Add seek() and tell() to SyncFile, delegating to self.f; make close() idempotent with if self.f.closed: return guard
src/borg/cache.py Refactor _write_files_cache to wrap writes in SaveFile + IntegrityCheckedFile(override_fd=...) for atomic updates; remove TODO comment
src/borg/testsuite/platform/all_test.py Add tests for seek/tell, idempotent close, and SaveFile + IntegrityCheckedFile integration
src/borg/testsuite/crypto/file_integrity_test.py Add test for IntegrityCheckedFile with SyncFile as override_fd

Checklist

  • PR is against master
  • New code has tests
  • Tests pass
  • Commit messages are clean and reference related issues
  • Code formatted with black

@codecov
Copy link

codecov bot commented Feb 21, 2026

Codecov Report

❌ Patch coverage is 89.65517% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.46%. Comparing base (0354697) to head (6c1017b).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/borg/cache.py 86.36% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9392      +/-   ##
==========================================
+ Coverage   76.43%   76.46%   +0.02%     
==========================================
  Files          85       85              
  Lines       14800    14810      +10     
  Branches     2212     2213       +1     
==========================================
+ Hits        11313    11324      +11     
  Misses       2809     2809              
+ Partials      678      677       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@ThomasWaldmann ThomasWaldmann left a comment

Choose a reason for hiding this comment

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

good change, found some minor things.

sf.seek(0, io.SEEK_END)
assert sf.tell() == 11
sf.seek(5, io.SEEK_SET)
assert sf.tell() == 5
Copy link
Member

Choose a reason for hiding this comment

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

for completeness, do a read after that and assert.


def test_syncfile_close_idempotent():
"""Calling SyncFile.close() twice does not raise."""
with tempfile.TemporaryDirectory() as tmpdir:
Copy link
Member

Choose a reason for hiding this comment

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

use the pytest fixture.

sf.close() # must not raise


def test_savefile_with_integrity_checked_file():
Copy link
Member

Choose a reason for hiding this comment

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

quite similar to test_write_and_verify_with_syncfile.

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.

cache: SyncFile lacks .seek()/.tell(), preventing SaveFile usage in _write_files_cache

2 participants