11579 Commits
Author SHA1 Message Date
TW 15738b8fb5 Merge pull request #10517 from ThomasWaldmann/robustunpacker-pypy-bufferror
RobustUnpacker: release all memoryviews of the resync buffer before resizing it
2026-10-11 21:53:00 +02:00
Thomas WaldmannandClaude Fable 5.1 4da7021dc5 RobustUnpacker: release all memoryviews of the resync buffer before resizing it
Since the PyPy nightly of 2026-10-11 (PyPy 8.0.2-alpha0, rev 544bd2f768f9), 12 tests
in archive_test.py (test_missing_chunk, test_corrupt_chunk, test_resync_*) fail with:

    BufferError: Existing exports of data: object cannot be re-sized

at `del self._buffered_data[:drop_len]` in RobustUnpacker._search().

_search() scanned the buffer inside `with memoryview(self._buffered_data) as data:`
and called valid_msgpacked_dict(data[offset:]) for every offset, which also sliced the
key out of that view. CPython frees these unreferenced child views immediately, so the
bytearray is exportless again as soon as the with block releases the parent view.
PyPy has no refcounting: the child views stay alive until the GC runs. PyPy 8.0.0 did
not count child views as exports of the bytearray, the new nightly does (like CPython
always did), so the resize after the with block fails.

Fix: valid_msgpacked_dict(d, offset=0) only indexes d, it never slices it, and _search()
passes (data, offset) instead of a slice. Together with the explicitly released slice in
_try_item_start(), no unreleased view of _buffered_data exists anymore when it is
resized. The scan loop also gets a bit faster on CPython (no slice object per offset).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-11 19:45:09 +02:00
TW 0ff5aa9d56 Merge pull request #10516 from ThomasWaldmann/same-repo-dir-8188
security: same local repo dir via another path is not a relocation, fixes #8188
2026-10-11 19:39:53 +02:00
Thomas WaldmannandClaude Opus 5.5 a879f5c723 security: same local repo dir via another path is not a relocation, fixes #8188
The "repository was previously located at" check compared path strings only,
so reaching the same local repository directory via a symlink, a bind mount or
a different letter case on a case-insensitive filesystem looked like a
relocation. Answering yes rewrote the stored location, so alternating between
the paths kept triggering the prompt (breaking e.g. unattended prune).

If the strings differ, but both are local paths referring to the same existing
directory (same st_dev/st_ino, st_ino != 0), do not warn. The stored location
and all displayed paths stay as they were. Remote locations are not affected,
and a real move/rename (old path gone) still triggers the warning.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:46:42 +02:00
TW c0fd800f40 Merge pull request #10506 from ThomasWaldmann/extract-continue-dirs-progress
extract --continue: skip complete dirs, count skipped files in --progress
2026-10-11 18:01:37 +02:00
TW e052b0e499 Merge pull request #10509 from ThomasWaldmann/automount-6652
create: back up automounted directories, fixes #6652
2026-10-11 17:57:32 +02:00
Thomas WaldmannandClaude Opus 5.5 dc473d2291 create: do not pre-open directories given via --paths-from-stdin, refs #6652
That cost an extra open/fstat/close per directory (noticeable when e.g.
find feeds millions of entries, and on network filesystems).

It is not needed: if such a directory is an automount point that is not
mounted yet, the race condition check in process_dir triggers a retry,
which then sees the mounted filesystem. Usually, the program producing
the paths has already triggered the mount anyway.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 01:36:57 +02:00
TW 306a3c8e90 Merge pull request #10497 from ThomasWaldmann/encrypt-files-cache
seal the files cache with the repository key, fixes #5041
2026-10-11 01:19:46 +02:00
TW 7d7d74139c Merge pull request #10498 from ThomasWaldmann/stickers-1902
add borg sticker design source, fixes #1902
2026-10-11 00:09:40 +02:00
Thomas WaldmannandClaude Opus 5.5 185c5a3125 cache: seal the files cache with the repository key, fixes #5041
The files cache (file sizes, timestamps, inode numbers, chunk lists and
--digests content digests) was stored as plaintext msgpack in the local
cache directory, protected only by a checksum without a key, so anyone
who could read the cache directory could read it, and anyone who could
write it could make borg skip changed files.

It is now written as a sealed stream: encrypted and authenticated with
the repository key (authenticated only in the authenticated-* modes),
bound to the repository id and the file name of the files cache. A files
cache that fails the verification is ignored with a warning (the exit
code is not changed) and rebuilt from the previous archive.

The checksums in the [integrity] section of the cache config are not
needed any more, so the cache config is no longer rewritten on every
cache close().

The FAQ about cache security now describes what is protected and what is not.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 00:06:11 +02:00
Thomas WaldmannandClaude Opus 5.5 8cdc03b67e crypto: add sealed streams (key-sealed frames over a byte stream)
A sealed stream cuts a byte stream into 1 MiB frames and puts every
frame into the key's envelope (encrypted and authenticated in the
encrypting modes, authenticated only in the authenticated-* modes),
so neither the writer nor the reader needs memory for more than a frame.

The AAD of a frame is a caller-given context plus the frame number and
a "last frame" flag, so the reader detects changed, reordered, dropped
or duplicated frames, truncation and trailing data. Every frame is
sealed with encrypt_oneshot(), so sealed streams are thread-safe.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 00:06:04 +02:00
TW 9f581aa0c5 Merge pull request #10508 from ThomasWaldmann/mount-passcommand
mount: add passcommand mount option, fixes #6804
2026-10-10 22:51:26 +02:00
TW a17617fa43 Merge pull request #10505 from ThomasWaldmann/mount-opt-strip-numeric
mount: add strip_components, numeric_ids and archive_dir_format mount options, fixes #4880
2026-10-10 22:46:03 +02:00
TW f9c7518467 Merge pull request #10510 from stanleys12/osc/report-a-lock-object-unreadable-due-to-o-fea0
locking: treat a lock object we are not permitted to read as unreadable
2026-10-10 22:23:38 +02:00
TW 32578a1167 Merge pull request #10511 from ThomasWaldmann/update-changes-master
update CHANGES
2026-10-09 02:52:06 +02:00
Thomas Waldmann d1be425e84 update CHANGES 2026-10-08 19:43:07 +02:00
Stanley Shen ecf6189e9a locking: treat a lock object we are not permitted to read as unreadable
A lock object created by another user (e.g. by borg running as root) made
borg crash with a PermissionError traceback. Treat it like any other
unreadable lock object: it blocks us (fail closed), is named in the error
message and can be removed with borg break-lock.
2026-10-07 16:28:32 -07:00
TW 257212e23d Merge pull request #10488 from mvanhorn/fix/10485-pack-max-env-validation
fix: validate BORG_PACK_MAX_SIZE and BORG_PACK_MAX_COUNT
2026-10-07 17:56:33 +02:00
Matt Van Horn d96475f773 treat empty BORG_PACK_MAX_SIZE/BORG_PACK_MAX_COUNT as unset
Matches BORG_PACK_CACHE_SIZE, where an empty value already means unset.
2026-10-07 06:32:58 -07:00
Thomas WaldmannandClaude Opus 5.5 ee710a414c platform test: only stat mount candidates that exist, refs #6652
OpenBSD has no /proc.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 14:50:35 +02:00
Thomas WaldmannandClaude Opus 5.5 cd7532035e create: back up automounted directories, fixes #6652
stat() does not trigger an automount (autofs, systemd automount units,
ZFS .zfs/snapshot/*), but opening the directory does. borg then saw a
different inode in fstat() than in the stat() before and skipped the
whole tree as a race condition.

Now, if a directory turns into another directory on a different mount
than its parent directory, we accept that as an automount, use the
fstat() result and redo the skip_inodes and --one-file-system decisions.
platform.on_different_mounts() compares the mount ids on Linux (st_dev
is not good enough there, e.g. for btrfs subvolumes) and st_dev elsewhere.

Recursion roots and --paths-from-stdin directories are opened before
taking their stat, so --one-file-system uses the device number of the
mounted filesystem (this also fixes incomplete -x backups of a ZFS
snapshot directory given as a recursion root).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 14:01:51 +02:00
TW b2067a683d Merge pull request #10507 from mr-raj12/docs-repo-corruption-9825
docs: document repo corruption recovery, refs #9825
2026-10-07 12:49:25 +02:00
Thomas WaldmannandClaude Opus 5.5 9c08f32792 mount: add passcommand mount option, fixes #6804
fstab and autofs entries can only give mount options, not environment
variables, so an encrypted repository could not be mounted from there
without setting BORG_PASSCOMMAND for the whole automount daemon.

-o passcommand=CMD works like BORG_PASSCOMMAND=CMD. It is applied before
the repository is opened (the key is loaded before the other mount
options are processed) and is not passed on to libfuse. Like the
passphrase environment variables, it is mutually exclusive with them.

There is deliberately no mount option for the passphrase itself: mount
options are visible to other users (/etc/fstab, ps output, systemd
mount units, autofs logs).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 12:00:39 +02:00
Thomas WaldmannandClaude Opus 5.5 c7f2f2d8ce mount: add archive_dir_format mount option
Same as BORG_MOUNT_ARCHIVE_DIR_FORMAT, but usable from fstab and autofs
entries; if both are given, the mount option is used. As mount options
are separated by commas, the format can not contain a comma.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 11:10:08 +02:00
Mrityunjay Raj fd560dce0b docs: document repo corruption recovery, refs #9825 2026-10-07 13:44:32 +05:30
Thomas WaldmannandClaude Opus 5.5 10bbecb90f extract --continue: skip complete dirs, count skipped files in --progress, refs #6857
Directories: same_item() already considered an existing directory with the archived
mode and mtime as complete, but do_extract never passed continue_extraction when
restoring directory metadata, so all directories got their metadata restored again.

--progress: the total includes the files that --continue skips, but their size was
never added to the progress, so the percentage stayed far below the real one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 23:39:33 +02:00
Thomas WaldmannandClaude Opus 5.5 6a06e4ff85 mount: add numeric_ids mount option
Like strip_components, this makes --numeric-ids usable from fstab and
autofs entries. Also clarify in the docs that numeric-ids was the
--numeric-ids CLI option, not a mount option.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 23:12:51 +02:00
Thomas WaldmannandClaude Opus 5.5 cd3ce39972 mount: add strip_components mount option, fixes #4880
fstab and autofs entries can only give mount options, not borg CLI
options, so -o strip_components=N now does what --strip-components N
does (the mount option wins if both are given).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 23:12:19 +02:00
TW c4c067e0f5 Merge pull request #10503 from ThomasWaldmann/faq-sparse-14
docs: faq: update sparse file support, refs #14
2026-10-06 22:36:48 +02:00
Thomas WaldmannandClaude Opus 5.5 8a84a81788 docs: faq: update sparse file support, refs #14
borg create --sparse seeks over holes, but the archive still does not
record the hole layout, so extract --sparse can not reproduce it exactly.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 19:06:00 +02:00
TW 05302a7995 Merge pull request #10499 from ThomasWaldmann/import-tar-acls
import-tar: convert GNU tar PAX ACLs, exact PAX ns timestamps
2026-10-06 18:05:59 +02:00
TW 25260b950e Merge pull request #10502 from borgbackup/dependabot/github_actions/actions-2a11465d79
build(deps): bump the actions group with 2 updates
2026-10-06 13:01:17 +02:00
TW a3e8cf0aef Merge pull request #10501 from borgbackup/dependabot/pip/requirements.d/pip-dependencies-ff9b7b8c9b
build(deps-dev): bump the pip-dependencies group in /requirements.d with 3 updates
2026-10-06 13:00:53 +02:00
Thomas WaldmannandClaude Opus 5.5 7e3d305a98 test_tar_acl_to_borg: look up the current user, not root
Haiku has no "root" user, so the name lookup fell back to the name and
the test expected uid 0 in vain.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 13:00:14 +02:00
Matt Van HornandClaude Opus 5.5 52aee23df1 fix: validate BORG_PACK_MAX_SIZE and BORG_PACK_MAX_COUNT, fixes #10485
Repository.__init__ now parses and validates both variables, so a bad
value fails before the store is created, opened or locked. Each must be
a positive integer, and BORG_PACK_MAX_SIZE must be below the new
MAX_PACK_SIZE_LIMIT of 2126512128 (2 GiB - 20 MiB). PackWriter.add keeps
the object that crosses the cap, so packs stay below 2 GiB, clear of OS
bugs with files of 2 GiB or more. A rejected value raises Error with the
variable name and the value.

With only BORG_PACK_MAX_COUNT set, the writer still gets
MAX_PACK_SIZE_LIMIT - 1 as its byte ceiling. pack_max_size returns the
user's size, or DEFAULT_PACK_MAX_SIZE when that variable is unset, so
compaction keeps targeting that value.

The environment-variable help and the packs internals page state the
limit, and repository tests cover rejected values (also on repository
creation), the size one below the limit, count-only mode, the unset
defaults, and both variables set together.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019wSaUMZrtm6XSzwEcxavkv
2026-10-05 23:37:11 -07:00
dependabot[bot] 2c9ce3b0de build(deps): bump the actions group with 2 updates
Bumps the actions group with 2 updates: [korthout/backport-action](https://github.com/korthout/backport-action) and [msys2/setup-msys2](https://github.com/msys2/setup-msys2).


Updates `korthout/backport-action` from 4.6.1 to 4.7.0
- [Release notes](https://github.com/korthout/backport-action/releases)
- [Commits](https://github.com/korthout/backport-action/compare/6b65649031ac6d18ffdfd0c0820e9436f3fde22b...8560fb503c275d433c56f05a2f64850093baa9f7)

Updates `msys2/setup-msys2` from 2.32.0 to 2.33.0
- [Release notes](https://github.com/msys2/setup-msys2/releases)
- [Changelog](https://github.com/msys2/setup-msys2/blob/main/CHANGELOG.md)
- [Commits](https://github.com/msys2/setup-msys2/compare/66cd2cce69caa17b53920067426061ca1de3a884...ec48f7c5447b3140e2b088413ae3a55687bccb6e)

---
updated-dependencies:
- dependency-name: korthout/backport-action
  dependency-version: 4.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: msys2/setup-msys2
  dependency-version: 2.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-10-06 05:45:43 +00:00
dependabot[bot] fe2d132cbb build(deps-dev): bump the pip-dependencies group
Bumps the pip-dependencies group in /requirements.d with 3 updates: [build](https://github.com/pypa/build), [tox](https://github.com/tox-dev/tox) and [coverage](https://github.com/coveragepy/coveragepy).


Updates `build` from 1.6.0 to 1.6.1
- [Release notes](https://github.com/pypa/build/releases)
- [Changelog](https://github.com/pypa/build/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/build/compare/1.6.0...1.6.1)

Updates `tox` from 4.61.1 to 4.61.5
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.61.1...4.61.5)

Updates `coverage` from 7.16.0 to 7.16.2
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.16.0...7.16.2)

---
updated-dependencies:
- dependency-name: build
  dependency-version: 1.6.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: pip-dependencies
- dependency-name: tox
  dependency-version: 4.61.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: pip-dependencies
- dependency-name: coverage
  dependency-version: 7.16.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: pip-dependencies
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-10-06 05:43:16 +00:00
Thomas WaldmannandClaude Opus 5.5 6f9f3a2fa8 import-tar: test that invalid PAX timestamps are ignored
Previously, an invalid atime/ctime/mtime PAX header made import-tar crash
with a ValueError from float().

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 02:27:13 +02:00
Thomas WaldmannandClaude Opus 5.5 a7eb51a06f export-tar/import-tar: exact ns timestamps in PAX headers
export-tar wrote the atime/ctime/mtime PAX headers via str(ns / 1e9) and
import-tar parsed them via float(s) * 1e9. A float has ~240 ns resolution
at today's epoch, so the "ns" timestamps of --tar-format=PAX did not
roundtrip exactly (e.g. ...987654321 came back as ...987654400).

Write and parse them as exact decimal strings now.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 02:05:04 +02:00
Thomas WaldmannandClaude Opus 5.5 dda3515d81 import-tar: convert PAX ACLs to borg's ACL format
GNU tar (--format=posix --acls) writes named ACL entries without the
numeric uid/gid, e.g. "user:nobody:rw-". import-tar stored the text
unchanged, but borg's ACL format has the id as a 4th field, so borg
extract crashed in acl_use_local_uid_gid with an IndexError.

Named entries without an id now get it from a local user/group name
lookup (falling back to the name, like borg create does). Comma
separated entries (as star writes them) and comments are handled, too.

system.posix_acl_* xattrs (GNU tar --xattrs-include='*') are skipped,
like borg create does, because the ACLs are stored separately.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 02:04:35 +02:00
TW 2850460ef3 Merge pull request #10496 from ThomasWaldmann/lock-recheck-delay-9899
locking: BORG_LOCK_RECHECK_DELAY sets the race recheck delay, fixes #9899
2026-10-06 00:13:35 +02:00
Thomas WaldmannandClaude Opus 5.5 12ec7e9eb2 add borg sticker design source, fixes #1902
GIMP source (editable text layers) and a preview of the chosen
rectangular design "C" from the ticket, plus a README with the
font, colours and printing notes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 00:09:27 +02:00
Thomas WaldmannandClaude Opus 5.5 e64154bca8 help: plain text substitution for the storelocking reference, refs #9899
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 23:30:18 +02:00
Thomas WaldmannandClaude Opus 5.5 07b8d0271c locking: BORG_LOCK_RECHECK_DELAY sets the race recheck delay, fixes #9899
acquire() creates its lock object, waits for the race recheck delay and
lists the lock objects again to detect other clients racing for the lock.

With list-after-write consistency (local filesystems, sftp, rest, AWS S3,
MinIO), at least the client that created its lock object last sees the
other's lock in its second listing, so the delay is not needed for
correctness and the 0.01s default stays.

Stores that only list a new object after a lag (e.g. NFS clients caching
directory listings, some cloud storages via rclone) need a delay of at
least that lag. That lag is a property of the storage, not of the request
latency, so it is not measured, but set via BORG_LOCK_RECHECK_DELAY.
Invalid values abort with an error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 23:04:09 +02:00
TW ab0e8e9cef Merge pull request #10494 from ThomasWaldmann/fix-xattrs-errors-test
tests: extract --continue in test_extract_xattrs_errors
2026-10-05 17:27:14 +02:00
TW bee7027b1e Merge pull request #10495 from ThomasWaldmann/completion-repo-url-followup
completion: repository directory completion fixes, refs #10460
2026-10-05 16:16:49 +02:00
TW 2ee7d26a4b Merge pull request #10493 from mr-raj12/check-verify-data-other-copy-10491
check: --verify-data --repair indexes an intact other copy of a defect chunk, fixes #10491
2026-10-05 15:37:58 +02:00
Thomas WaldmannandClaude Fable 5.1 f3c82cda58 completion: repository directory completion fixes, refs #10460
- zsh: pass the arguments of _borg_complete_repo_dirs on to _files, so the
  completions are described as REPO (SRC_REPOSITORY) again, not as "file".
- fish: give the value being completed to __fish_complete_directories, so
  directories are also completed for a value attached to the short option
  (-rPATH).
- tests: use TCSH_REPO_URL_RULE instead of a hardcoded list of URL schemes.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-05 15:08:51 +02:00
Mrityunjay Raj 35ecc47403 check: --verify-data --repair does not index a defect copy in a rewritten pack 2026-10-05 18:18:56 +05:30
TW 33ec03c512 Merge pull request #10472 from ThomasWaldmann/completion-repo-url-10460
completion: no directory completion for repository URLs, fixes #10460
2026-10-05 14:41:53 +02:00