-r/--repo and --other-repo completed local directories even when the value
already started with a URL scheme like ssh:// or rclone:. In fish, this
offered e.g. "ssh://etc/" (fish completes the part after the ":" as a path),
tcsh printed "ssh:// not found".
Now, a URL scheme other than file: (the ones Location parses as a URL) gets
no completion in any shell, while local paths still complete directories:
- bash, zsh, fish: new _borg_complete_repo_dirs helper
- tcsh: a leading completion rule completing nothing for a URL, it is used
before the directory rules of the repository options.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
msgpack 1.2.3 brings fixes relevant to borg's usage of the C extension:
memory leaks when map key validation or container hooks fail
(GHSA-j586-36cw-2gc2) and a use-after-free when cleaning up an
incomplete Unpacker.skip().
The C Unpacker now raises ValueError when switching between unpack,
skip, header readers or read_bytes() while an object is incomplete.
borg only uses feed() plus unpack()/iteration (and RobustUnpacker
creates a fresh Unpacker on each resync attempt), so this does not
affect borg.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reaching the attribute directory of a file requires opening the file.
For a block or character device node, that runs the open routine of
its driver, which might fail (ENXIO if there is no such device, as
for the device nodes created by the archiver tests when run as root),
block or have side effects (e.g. a tape rewinding on close).
Treat device nodes as not supporting extended attributes instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After resync(), __next__ joined all data fed so far and searched it from
offset 0, advancing with data = data[1:], which copies the remaining
data for every byte. Resyncing over a big item's chunk list did not
finish.
Search a bytearray via a memoryview, continue at the offset where the
last search stopped and drop the searched data. Offsets where the fed
data ends within the msgpacked object (incomplete item starts) are tried
again after each feed(), the last MAX_ITEM_HEADER_LEN bytes are checked
again by valid_msgpacked_dict after the next feed().
Incomplete item starts more than MAX_PENDING_ITEM_LEN (4 MiB) before the
end of the fed data are dropped, so an item > 4 MiB starting right at a
resync point is not recovered.
Try item starts with unpackb on a memoryview slice, its max_*_len limits
default to the data length, so a bogus array32/map32 length is not
preallocated.
LegacyRemoteRepository.call_many took the next call from the front of a
list (calls.pop(0)), which moves all the remaining entries each time.
get_many() passes all ids at once, so borg transfer --from-borg1
--chunker-params (which fetches all chunks of an item via fetch_many)
from a remote borg 1.x repository was quadratic in the chunk count of
the item.
Use collections.deque with popleft() for calls and waiting_for.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Correct what borg compact reclaims after a crash, how compact/repo-compress
rewrite index/, when the index is rebuilt from the packs and when the repair
walk is used. Add pack writer and read path docs.
'authenticated-*' in the help text started an unterminated inline emphasis
in the generated rst usage docs and man page.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SALVAGE_READ_ERROR now covers OSError, BackendConnectionError and
ReadRangeError. Other store backend errors, e.g. BackendMustBeOpen,
PermissionDenied or QuotaExceeded, propagate.
Without the object, the commands silently fell back to the built-in defaults, so
anybody with write access to the store could remove a repository default (e.g. an
"obfuscate" compression) unnoticed: the key protects the content of the object,
not its presence.
Now "borg repo-create" always writes config/defaults (empty if no default was
given), and a missing object is an error (Repository.DefaultsMissing, rc 34) like
one that fails the authentication: the commands using the defaults refuse to run.
"borg check" reports such an object, "borg check --repair" replaces it by empty
defaults, so the repository can be used again (with the built-in defaults).
with_repository reads the defaults only after the security checks (assert_secure).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
On Windows, repo-info output lines end with CRLF, so the assertions
expecting a trailing "\n" failed there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The moto S3 test server install already pulls in the cryptography
manylinux wheel, so a plain "pip install --no-binary cryptography"
kept that wheel (statically linked OpenSSL) and the dynamic libcrypto
check failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
"borg repo-create --compression SPEC --chunker-params PARAMS" stores repository
defaults in the new config/defaults store object.
Compression: the commands that compress data (create, recreate, import-tar,
transfer, repo-compress, debug put-obj) use the repository default if no
compression was given via the command line, the environment or default.yaml;
without a repository default, they use lz4 as before.
Chunker params: "--chunker-params default" now means the repository default,
else the built-in CHUNKER_PARAMS: ChunkerParams("default") returns the
DEFAULT_CHUNKER_PARAMS marker and with_repository replaces it by the real
chunker params. borg create and borg import-tar use the marker as their parser
default, so they use the repository default if no chunker params were given.
borg recreate and borg transfer still only rechunk if --chunker-params is
given; with "default", they rechunk to the repository default.
config/defaults is stored in the key's store object envelope, so it is
authenticated: nobody without the key can change the defaults (e.g. remove an
"obfuscate" compression) without being noticed. config/config can not hold
them, as it is read before the key is known and is not authenticated.
config/defaults is read at most once per Repository object.
borg repo-info shows the defaults (also in --json).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
If an archive's metadata object is missing or invalid while its archives/ entry
still exists, Archives.list() yields a placeholder ArchiveInfo for it, but
opening it as an Archive raises Archive.DoesNotExist.
- ArchiveFormatter: use the placeholder values of the ArchiveInfo (and defaults)
if the archive can not be opened, so repo-list (and prune --list) go on.
- compact: skip such a soft-deleted archive with the "cannot be fully preserved"
warning, as documented in mark_soft_deleted_used(), instead of aborting.
- Archive.DoesNotExist: show a binary archive id as hex.
- check --format: keep using the fallback format for such archives.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Previously, "borg tag" always rewrote the archive metadata (and thus the
archive entry in the repository), even when just listing the tags or when
the requested change was a no-op (including a refused --set).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Previously, borg silently did not set the tags, so it looked like the
given tags were just ignored, and the return code was 0.
Now borg emits a warning naming the special tags that would be removed
and exits with rc 1 (warning).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without NAME or archive filter options, "borg tag --set/--clear/--add/--remove"
silently changed the tags of all archives in the repository.
Now it aborts like "borg delete" does, unless archives are selected
explicitly, e.g. using -a 'sh:*' for all archives. Just showing the
tags of all archives still works without a selection.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
--tags used nargs="+" and thus swallowed the archive NAME if it was given
after it, e.g. "borg create --tags foo NAME PATH" failed.
Now --tag takes exactly one tag and can be given multiple times,
e.g. "borg create --tag foo --tag bar NAME PATH".
Co-Authored-By: Claude Opus 5.5 <[email protected]>