Commit graph

10222 commits

Author SHA1 Message Date
Thomas Waldmann
d5cd72b179 docs: add docs for "key add", "key list" and "key remove"
The multi-key feature (#9743) added the "borg key add", "borg key list"
and "borg key remove" subcommands but never wired up their docs:

- scripts/make.py: map key_add/key_list/key_remove to the "key" usage
  group, so build_man can locate their examples (it previously aborted
  with FileNotFoundError: docs/usage/key_add.rst).
- docs/usage/key.rst: include the three new generated snippets so they
  show up on the HTML key page.
- regenerate the affected docs: new key_{add,list,remove}.rst.inc usage
  snippets, new borg-key-{add,list,remove}.1 man pages, and borg-key.1
  (SYNOPSIS + SEE ALSO now reference the new subcommands).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 21:37:21 +02:00
Thomas Waldmann
8f4231d21b key: allow --key-location for authenticated* modes
The authenticated and authenticated-blake3 modes do not encrypt data, but
they still have a real key (id/auth key material) stored as a key blob.
That blob can live as a keyfile or as a repokey just like the encrypted
modes, so make it configurable instead of always forcing repokey storage.

- AuthenticatedKeyBase: set LOCATION_CONFIGURABLE = True so --key-location
  (at repo-create) and "borg key change-location" apply.
- key change-location: only copy sessionid/cipher when present (those are
  AEAD-only; authenticated keys do not have them).
- repo-info: report the key storage location for authenticated keys too,
  and handle the authenticated-blake3 variant (was only "authenticated").
- repo-create help: stop claiming authenticated* has no keyfile/repokey
  storage; only "none" truly has no key.
- add change-location round-trip tests for authenticated mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:57:26 +02:00
Mrityunjay Raj
e6010b5793 repository: address chunk-index review feedback
PackWriter requires a real/explicit index (no silent private one); PackLocationUnknown is now a loud ErrorWithTraceback and get() raises it unconditionally; close() persists via is_chunk_index_loaded/self.chunks; delete_chunkindex_cache calls invalidate_chunk_index() directly; drop per-access fragment consolidation (compact reclaims instead); capture BORG_TESTONLY_SHA256_PACK_ID at import; remove dead _current_offset; update tests.
2026-06-13 14:47:07 +05:30
Thomas Waldmann
d2bc45f56d
key: unify keyfile/repokey classes, locate key independent of type byte (#9743)
Borg used to read the manifest's key-type byte and then look for the key in
exactly one place (keyfile or repokey) depending on the key class that byte
selected. As a result every crypto suite was duplicated into a keyfile class
and a repokey class that differed only in TYPE, NAME, ARG_NAME and STORAGE.

Now key *location* is independent of the type byte: detection tries keyfiles
first and repokeys afterwards until a passphrase unlocks a key. The type byte
still selects the crypto suite (id hash, MAC, cipher) to instantiate. Where a
key is stored (keyfile vs repokey) is therefore a per-key property
(self.storage), not a separate class, so a repository may even hold a mix of
keyfile- and repo-stored borg keys.

With storage decoupled from class identity, the keyfile/repokey class pairs
collapse into one class per crypto suite:
- modern AEAD: AESOCBKey, CHPOKey, Blake3AESOCBKey, Blake3CHPOKey
- legacy borg 1.x (read-only): AESCTRKey, Blake2AESCTRKey
There is now exactly one type byte per modern crypto suite (the old separate
repokey type bytes 0x11/0x21/0x31/0x41 were removed; borg2 is beta and only
needs to read repos it created). identify_key() matches on TYPES_ACCEPTABLE.

CLI: --encryption selects only the crypto suite (aes-ocb, chacha20-poly1305,
blake3-aes-ocb, blake3-chacha20-poly1305, authenticated*, none); the storage
location is chosen with the new --key-location=repokey|keyfile (default
repokey). The old combined modes (repokey-aes-ocb etc.) were removed.
borg key import also gained --key-location. borg key change-location no longer
swaps key classes or rewrites the manifest; it just re-saves the unlocked key
at the new location.

Keyfile removal (key remove, change-location) now overwrites the keyfile with
random data via secure_erase() before unlinking, consistent with save().

borg 1.x legacy read compatibility is preserved (the legacy class merge is a
behavior-preserving rename; the legacy type bytes incl. PASSPHRASE stay in
TYPES_ACCEPTABLE).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:48:45 +02:00
TW
e0c7a68a3e
Merge pull request #9762 from ThomasWaldmann/multi-keyblob-9743
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
key: multiple keys per repository (#9743)
2026-06-12 23:16:06 +02:00
Thomas Waldmann
af0904f5f0
key: support multiple borg keys per repository, fixes #9743
A repository can now have multiple borg keys protected by individual
(usually different) passphrases, each borg key is stored separately.
Any of the keys can be used to decrypt the repository, all contain
the same secret key material.

Keys now have a LABEL (first key always has "admin" label) for easier
identification. The "admin" key is protected and can't be removed.

Repository now stores N borg keys under keys/ instead of one.
LegacyRepository stays single-key.

To find the right key, borg tries a passphrase against every borg key.

CLI:
- borg key list
- borg key add --label
- borg key remove --label|--key|--passphrase
- borg key export --label|--key

Reading borg 1.x keyfiles and repokey (borg transfer --from-borg1)
keeps working: the legacy key classes share FlexiKey, so enumeration
falls back to load_key() and headerless 1.x repokey blobs still parse.

The keyblob on-disk format is unchanged; multiplicity lives only in the
storage/enumeration layer.

tests: adapt test_repo_key_detect_does_not_raise_integrity_error

Updated to the multiple-borg-keys API: store_key.call_args for the saved blob and
load_keys.return_value to feed it back to detect().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 20:38:42 +02:00
TW
08b1a67928
Merge pull request #9763 from ThomasWaldmann/fix-stale-fuse-mounts
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
testsuite: sweep stale borg FUSE mounts left by aborted runs
2026-06-12 16:03:53 +02:00
Thomas Waldmann
5541121f4e testsuite: sweep stale borg FUSE mounts left by aborted runs
When a test run is aborted (Ctrl-C, a timeout, or a crashed xdist worker),
the fuse_mount context manager's `finally: umount` does not run, leaving a
live FUSE mount under the pytest temp dir. A later run then fails to rm_rf
the old temp tree (Resource busy / Device not configured / Read-only file
system), which surfaces as noisy pytest warnings.

Add pytest_sessionstart/sessionfinish hooks (run once on the xdist
controller) that walk the pytest temp tree for leftover "mountpoint"
directories and unmount them via borg's own cross-platform umount helper,
making runs self-healing across previously aborted sessions.

Discovery uses only the stdlib (os.walk + os.path.ismount, with an
os.listdir fallback for dead/stale mounts) and the mount is named by the
well-known on-disk layout, so there is no OS-specific mount-table parsing or
force-unmount handling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 13:55:02 +02:00
Mrityunjay Raj
445f2605ef repository: make the chunk index single-owned by the repository
PackWriter and the cache now share repository.chunks instead of keeping their own copies; set_chunk_index is gone (use the writable .chunks property). The cache consolidates fragmented cache/chunks.* files so they no longer grow by one per backup until the next check or compact.
2026-06-12 16:22:07 +05:30
Mrityunjay Raj
8b5c2d857e repository: invalidate in-memory chunk index when its cache is deleted
delete_chunkindex_cache() now also drops the repository's in-memory index, so
close() cannot persist a stale copy and callers no longer clear it by hand.
get() now raises a distinct PackLocationUnknown when a chunk is indexed but not
yet flushed, rather than a plain ObjectNotFound.
2026-06-12 12:53:20 +05:30
Mrityunjay Raj
cae28f2dd1 check: drop stale in-memory chunk index after --repair clears the cache
finish() deletes cache/chunks.* and now also invalidates the live index, so close() does not write it back. The next repository access rebuilds it from actual repository contents.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
abb32abf2d repository: persist chunk index to repo cache on close()
close() now writes the in-memory index back so the next session can resolve chunk locations instead of failing with ObjectNotFound. Empty incremental writes are skipped so a deleted index is not recreated as a stale, empty one.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
0161a54a71 ci/tests: exempt BORG_TESTONLY_SHA256_PACK_ID from clean_env; add concurrency to sha256 job
env var was wiped before every test; sha256 job now gets its own concurrency group so it is not cancelled mid-run
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
066004800c repository: simplify chunks property; trigger lazy build before put()
Remove overlay loop: put() accesses self.chunks first so PackWriter.chunks is updated before any write.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
844daf9324 repository: add set_chunk_index() and lazy chunks property; route get() through ChunkIndex
PackWriter now always owns a ChunkIndex; the N=1 fallback in get() is removed.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
05ce183f71 repository: add BORG_TESTONLY_SHA256_PACK_ID to force sha256 pack_ids at N=1
Adds tox env and informational CI job (continue-on-error) to track progress toward full sha256 pack_id adoption, refs #8572
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
67b11f7826 repository: PackWriter now manages ChunkIndex updates internally
On add(), marks chunk with UNKNOWN_BYTES32; on flush(), replaces with real
pack_id. put(), flush(), and set_chunk_index() simplified accordingly.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
1df2065f85 repository: remove N=1 fallback from get(), update _chunks eagerly in put()
get() raises ObjectNotFound when entry is missing or UNKNOWN_BYTES32; put()
marks the id in _chunks immediately so the index is live after each write.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
54adb9b7f6 repository: resolve pack location from ChunkIndex in get(), flush handles update_pack_info
Remove obj_offset/obj_size params from get(); always initialize _chunks to an
empty ChunkIndex so callers never need to guard for None.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
68491c4409 repository: add set_chunk_index() for ChunkIndex-based pack routing, refs #8572
Replace _pack_info (session-scoped dict) with a borrowed ChunkIndex reference.
Cache passes its index via set_chunk_index(); get() routes correctly for all sessions.
2026-06-12 12:15:27 +05:30
Mrityunjay Raj
2553a705cc repository: add obj_offset/obj_size range-load params to Store.load calls
retry_size min() guards against corrupted meta_size, no-op for healthy objects.
2026-06-12 12:15:26 +05:30
TW
c710edf0df
Merge pull request #9754 from ThomasWaldmann/simplify-location-parsing
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
Simplify location parsing, remove socket: remainders
2026-06-11 10:48:23 +02:00
TW
23d5152840
Merge pull request #9756 from ThomasWaldmann/exclude-dataless-master
create: add --exclude-dataless to skip cloud files not materialized locally
2026-06-11 09:39:23 +02:00
Thomas Waldmann
f484741fdd
create: add --exclude-dataless to skip cloud files not materialized locally
Ports #9755 (1.4-maint) to master.

macOS flags files whose content lives in cloud storage (e.g. iCloud Drive)
and is not present locally with SF_DATALESS. Reading such a file triggers
downloading its content.

This adds --exclude-dataless to borg create: the flags are checked right
after stat() and before any open(), so excluded files/dirs never get
materialized. Skipped paths are reported with the usual 'x' status,
analogous to --exclude-nodump.

Notes:
- stat.SF_DATALESS only exists from Python 3.13 on, so there is a fallback
  to the value from macOS' sys/stat.h (0x40000000).
- The check covers directories too (they can also be dataless), and we skip
  before opening/recursing into them.
- Test fakes get_flags via monkeypatch since SF_DATALESS cannot be set from
  userspace.
- Fish shell completion updated; bash/zsh completions don't exist in master.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 07:36:53 +02:00
Thomas Waldmann
5eab183183
remove leftover socket: protocol code
The unix-socket transport (socket:// repositories, the --socket option and
"borg serve" over a socket) was never part of a stable borg 2 release and the
old RPC protocol it relied on is gone, so the remaining code was dead:

- legacy remote: drop the unreachable proto == "socket" connection branch and
  the now-unused self.sock handling, "import socket" and get_socket_filename
  import (LegacyRemoteRepository is only built for proto == "ssh")
- helpers: remove get_socket_filename() and its export
- parseformat: drop "socket" from local_path_re - socket:// is now treated like
  any other unknown scheme (a local path) rather than being special-cased
- tests: drop test_socket and the self.sock check in the legacy reopen helper
- docs: drop the stale --socket entry from the manually maintained
  common-options.rst.inc (the auto-generated usage/man docs are left untouched
  here and will be rebuilt in a separate commit)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 18:51:55 +02:00
TW
86fd77fd33
Merge pull request #9753 from ThomasWaldmann/fix-9748
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
compact: invalidate cached chunk indexes before deleting objects, #9748
2026-06-10 18:37:06 +02:00
Thomas Waldmann
aa9f810453
parseformat: simplify Location parsing/validation, #9678
For sftp/http(s)/s3/b2/rclone repositories, borg only detects the scheme now
and hands the raw URL to borgstore, which parses and validates it - removing
the duplicate parsing borg used to do. Precise field extraction (user/host/
port/path) is kept only for the protocols borg itself reads: file, rest and
legacy ssh.

- drop http_re, s3_re, rclone_re and the sftp arm of the old ssh_or_sftp_re
- add a single scheme-detection pass-through against BORGSTORE_SCHEMES; reject
  unknown schemes (e.g. socket://) as before
- canonical_path() returns the processed URL for the delegated protocols, with
  embedded credentials stripped so secrets never reach the security state file
  or logs
- source local_path_re's scheme exclusions from BORGSTORE_SCHEMES
- create: use proto == "file" instead of "not location.host" for the local
  repo-dir inode skip

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 18:17:40 +02:00
Thomas Waldmann
0b103291b8
compact: invalidate cached chunk indexes before deleting objects, #9748
If `borg compact` was interrupted after deleting repository objects but
before writing the updated chunk index, the still-existing cache/chunks.*
kept claiming the deleted objects were present. A later `borg create` would
trust that stale index, skip re-uploading the affected chunks and silently
create an archive with dangling object references that extracts to zero bytes.

Invalidate all cached chunk indexes via delete_chunkindex_cache() before the
first object is deleted, so an interruption is conservative: the next client
rebuilds the index from actual repository contents and re-uploads any deleted
data. The post-deletion save_chunk_index() still writes a fresh, valid index.

Add a regression test covering both compact paths (default and --stats) that
interrupts compaction right before save_chunk_index() and verifies no cached
chunk index survives and a later create+extract reproduces the original bytes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:45:57 +02:00
TW
8509b3b7fa
Merge pull request #9751 from ThomasWaldmann/fix-9749
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
files cache: don't empty cache on a no-change backup, #9749
2026-06-10 14:48:05 +02:00
TW
d2cdeaff57
Merge pull request #9750 from ThomasWaldmann/remove-xxh64
remove xxhash / xxh64 requirement, mentions
2026-06-10 14:00:23 +02:00
Thomas Waldmann
c08c2ca461
files cache: don't empty cache on a no-change backup, #9749
FilesCacheMixin initialized _newest_cmtime to 0, but _write_files_cache()
only treats None as "no file was chunked this run" (falling back to a
max_time_ns cutoff that keeps all current entries).

When a backup reuses all files from the cache without chunking anything,
_newest_cmtime stayed at 0, so the race-protection cutoff became the unix
epoch and every current (age == 0) entry was discarded. The next backup
then had to re-read, chunk and hash all files again.

Initialize _newest_cmtime to None to match the documented contract in
_write_files_cache(), and make the comparisons in _build_files_cache()
None-safe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 12:39:36 +02:00
TW
5a9855dda3
Merge pull request #9747 from ThomasWaldmann/remove-RepositoryNoCache
Remove cache_if_remote and RepositoryNoCache
2026-06-10 11:48:32 +02:00
Thomas Waldmann
31202ea4f1
remove xxhash / xxh64 requirement, mentions 2026-06-10 00:38:18 +02:00
Thomas Waldmann
c35bc660dd
Remove cache_if_remote and RepositoryNoCache
In modern borg these were just a pass-through repository wrapper (there
is no RepositoryCache), with one variant doing inline decryption and
returning (csize, plaintext) tuples. Drop both and make all consumers
use the raw repository directly:

- fuse.py: ItemCache / FuseOperations / FuseBackend now take the raw
  repository + repo_objs and decrypt via repo_objs.parse(ROBJ_DONTCARE),
  matching hlfuse.py. The csize value was discarded at both call sites.
- mount_cmds.py: drop the cache_if_remote wrapper around FuseOperations.
- archive.py (rebuild_archives / check): drop the pass-through wrapper;
  robust_iterator now uses self.repository directly.
- repository.py: delete the RepositoryNoCache class and cache_if_remote.
- repository_test.py: remove TestCacheIfRemote and orphaned imports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:36:23 +02:00
TW
ac0a643087
Merge pull request #9745 from ThomasWaldmann/remove-preload
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
repository: remove chunk preloading
2026-06-09 22:51:52 +02:00
Thomas Waldmann
c914035d08
repository: remove chunk preloading
Preloading was used only by extract and export-tar, and the modern
borgstore-based Repository.preload() was already a no-op. Remove the
preload calls from both commands and the now-dead supporting code:

- DownloadPipeline.preload_item_chunks / Archive.preload_item_chunks
- hlids_preloaded tracking
- is_preloaded parameter from DownloadPipeline.fetch_many and
  Repository.get_many
- the no-op Repository.preload()

Also: remove preload support from borg.legacy

With no remaining callers, drop the legacy-side preload machinery:

LegacyRemoteRepository:
- preload_ids / chunkid_to_msgids state and the pop_preload_msgid helper
- is_preloaded parameter and handling in call_many() (get requests now
  always go through the normal send path; MAX_INFLIGHT pipelining of
  regular calls is unchanged)
- is_preloaded from get_many() and the preload() method

LegacyRepository:
- is_preloaded from get_many() and the no-op preload() stub

Both legacy repo classes now match the modern Repository interface.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:46:03 +02:00
TW
2600428f81
Merge pull request #9741 from ThomasWaldmann/fix-omnios-tmp-tmpdir 2026-06-09 16:04:18 +02:00
Thomas Waldmann
35eff1c91a
tests: skip test_extract_y2261 when FS rejects post-y2038 timestamps
On omniOS the test data now lives on ZFS (via TMPDIR=/var/tmp), and ZFS
rejects the year-2261 os.utime() with EOVERFLOW. Treat that as an
unsupported-filesystem condition and skip, rather than failing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 13:06:17 +02:00
Thomas Waldmann
b56107dfe4
tests: allow omniOS CI runtime dir in test_get_runtime_dir
With TMPDIR=/var/tmp/borg-ci on omniOS, platformdirs' user_runtime_dir
fallback yields /var/tmp/borg-ci/runtime-<uid>/borg. Add it to the
accepted values, like the existing NetBSD CI entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 13:02:22 +02:00
TW
7cdaebf4f3
Merge pull request #9723 from mr-raj12/pack-files-step5-packwriter
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
repository: add PackWriter and two-phase chunk index update
2026-06-09 12:55:28 +02:00
Thomas Waldmann
f0f40eb936
CI: redirect TMPDIR off tmpfs /tmp on omniOS
On omniOS /tmp is swap-backed tmpfs (small, RAM-bound), so the pip/cargo
build temps and the pytest temp tree quickly exhaust it ("no space left on
device"). Point TMPDIR at disk-backed /var/tmp instead, mirroring what the
NetBSD job already does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 11:29:10 +02:00
Mrityunjay Raj
0395cc1bef hashindex: add UNKNOWN_BYTES32, batch update_pack_info, return None from PackWriter, refs #8572
Fix PackWriter.flush() to use max_count == 1 (not len == 1) for the pack_id hack,
so final partial packs under max_count > 1 correctly use SHA256. Add covering test.
Move sha256 import to module level in repository_test.
2026-06-09 14:49:57 +05:30
Mrityunjay Raj
71bbde208f repository: add PackWriter and two-phase chunk index update, refs #8572
PackWriter buffers (chunk_id, cdata) pairs and flushes as pack files via borgstore.
At N=1 pack_id == chunk_id; UNKNOWN_INT32 (0xFFFFFFFF) placeholders in the index
are replaced by real pack location fields after flush() via update_pack_info().
Update test_chunkindex_add to expect UNKNOWN_INT32 sentinels from add().
2026-06-09 14:49:09 +05:30
TW
9b8fc60430
Merge pull request #9734 from ThomasWaldmann/borg-serve-rest
borg serve --rest: serve rest:// repositories with borg
2026-06-09 11:13:11 +02:00
dependabot[bot]
8c1543fa8b
build(deps): bump codecov/codecov-action in the actions group
Bumps the actions group with 1 update: [codecov/codecov-action](https://github.com/codecov/codecov-action).


Updates `codecov/codecov-action` from 6 to 7
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-09 05:44:05 +00:00
TW
e46b4bf5d1
Merge pull request #9739 from ThomasWaldmann/pdf-docs-master
Some checks are pending
Lint / lint (push) Waiting to run
CI / lint (push) Waiting to run
CI / security (push) Waiting to run
CI / asan_ubsan (push) Blocked by required conditions
CI / native_tests (push) Blocked by required conditions
CI / vm_tests (NetBSD, false, netbsd, 10.1) (push) Blocked by required conditions
CI / vm_tests (OmniOS, false, omnios, r151056) (push) Blocked by required conditions
CI / vm_tests (OpenBSD, false, openbsd, 7.8) (push) Blocked by required conditions
CI / vm_tests (borg-freebsd-14-x86_64-gh, FreeBSD, true, freebsd, 14.3) (push) Blocked by required conditions
CI / windows_tests (push) Blocked by required conditions
CodeQL / Analyze (push) Waiting to run
docs: offer a PDF download and link offline formats in the sidebar
2026-06-09 02:09:24 +02:00
Thomas Waldmann
a5ef482ca8
docs: offer a PDF download and link offline formats in the sidebar, fixes #9731
Enable the pdf output format on Read the Docs (the LaTeX build config
already existed in docs/conf.py) and add a "Downloads" line to the left
sidebar that links the offline formats (PDF, HTML zip, ePub). The links
are populated from the Read the Docs addons data, reusing the same
mechanism as the version selector, so they are version-correct and hidden
when unavailable. The line is left-aligned with the boxes above and the
table of contents below, with separators above and below it.

Also drop the stale 'resources' entry from latex_appendices (the page was
removed in #2088); it broke the now-enabled PDF build with a doctree
KeyError.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 02:08:14 +02:00
Thomas Waldmann
a1e8e538c0 drop unused permissions param from legacy RepositoryServer
RepositoryServer stored self.permissions but never read it: open() builds
a LegacyRepository without any permissions, and legacy (borg 1.x / v1)
repositories have no permission system at all. Remove the dead __init__
parameter and stop forwarding args.permissions from do_serve.

The --permissions CLI option stays - it applies to the non-legacy
"borg serve --rest" path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 00:55:50 +02:00
Thomas Waldmann
83b0dd66c7
improve docs / comments 2026-06-09 00:49:50 +02:00
Thomas Waldmann
3f31abe22a
serve --rest: let client choose backend under forced-command restrictions
Remote access is restricted via an SSH forced command in authorized_keys
that hardcodes the restriction, e.g.
  command="borg serve --rest --restrict-to-path=/srv/repos"
get_args() merges the forced command with the client's intended command
(SSH_ORIGINAL_COMMAND), copying only allowlisted options from the client.

For legacy serve the repo path travels inside the RPC protocol, so the
server enforces restrictions against it. But a rest:// repo passes the
repo as "--backend FILE:<path>" on the command line, and "backend" was in
neither allow- nor denylist, so under a forced command the client's
--backend was dropped: args.backend ended up None and do_serve_rest failed
with "requires --backend" - restrictions for rest were effectively broken.

Add "backend" to the allowlist so the client chooses which repo while the
forced command pins the restriction and the rest mode; do_serve_rest then
validates the client backend against restrict_to_paths/repositories via
check_rest_restrictions. The --rest mode flag stays out of the allowlist
so a forced legacy serve cannot be flipped to rest by the client.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 00:49:48 +02:00