legacy: extract RepoObj1, update all imports, drop re-export stubs

This commit is contained in:
Mrityunjay Raj 2026-04-04 03:09:31 +05:30
parent 4db927c62e
commit 15bec44c76
13 changed files with 129 additions and 112 deletions

View file

@ -13,11 +13,12 @@ from ..helpers.argparsing import SUPPRESS, PositiveInt
from ..helpers.nanorst import rst_to_terminal
from ..manifest import Manifest, AI_HUMAN_SORT_KEYS
from ..patterns import PatternMatcher
from ..legacyremote import LegacyRemoteRepository
from ..legacy.remote import LegacyRemoteRepository
from ..remote import RemoteRepository
from ..legacyrepository import LegacyRepository
from ..legacy.repository import LegacyRepository
from ..repository import Repository
from ..repoobj import RepoObj, RepoObj1
from ..repoobj import RepoObj
from ..legacy.repoobj import RepoObj1
from ..patterns import (
ArgparsePatternAction,
ArgparseExcludeFileAction,

View file

@ -10,7 +10,7 @@ from ..helpers import ChunkerParams, ChunkIteratorFileWrapper, CompressionSpec
from ..helpers.argparsing import ArgumentParser, ArgumentTypeError
from ..item import ChunkListEntry
from ..manifest import Manifest
from ..legacyrepository import LegacyRepository
from ..legacy.repository import LegacyRepository
from ..repository import Repository
from ..logger import create_logger

View file

@ -487,7 +487,7 @@ def safe_unlink(path):
Use this when deleting potentially large files when recovering
from a VFS error such as ENOSPC. It can help a full file system
recover. Refer to the "File system interaction" section
in legacyrepository.py for further explanations.
in legacy/repository.py for further explanations.
"""
path_obj = Path(path)
try:

View file

@ -1335,9 +1335,9 @@ def ellipsis_truncate(msg, space):
class BorgJsonEncoder(json.JSONEncoder):
def default(self, o):
from ..legacyrepository import LegacyRepository
from ..legacy.repository import LegacyRepository
from ..repository import Repository
from ..legacyremote import LegacyRemoteRepository
from ..legacy.remote import LegacyRemoteRepository
from ..remote import RemoteRepository
from ..archive import Archive
from ..cache import AdHocWithFilesCache

View file

@ -1,5 +1,5 @@
"""
Borg Legacy Package \u2014 Borg 1.x compatibility layer.
Borg Legacy Package Borg 1.x compatibility layer.
This package contains all code specific to reading/writing Borg 1.x repositories.
It is required for ``borg transfer --from-borg1`` and ``borg serve`` (serving v1 clients).

View file

@ -16,24 +16,24 @@ from subprocess import Popen, PIPE
from xxhash import xxh64
from . import __version__
from .compress import Compressor
from .constants import * # NOQA
from .helpers import Error, ErrorWithTraceback, IntegrityError
from .helpers import bin_to_hex
from .helpers import get_limited_unpacker
from .helpers import replace_placeholders
from .helpers import format_file_size
from .helpers import safe_unlink
from .helpers import prepare_subprocess_env, ignore_sigint
from .helpers import get_socket_filename
from .fslocking import LockTimeout, NotLocked, NotMyLock, LockFailed
from .logger import create_logger
from .helpers import msgpack
from .legacyrepository import LegacyRepository
from .version import parse_version, format_version
from .helpers.datastruct import EfficientCollectionQueue
from .platform import is_win32
from .. import __version__
from ..compress import Compressor
from ..constants import * # NOQA
from ..helpers import Error, ErrorWithTraceback, IntegrityError
from ..helpers import bin_to_hex
from ..helpers import get_limited_unpacker
from ..helpers import replace_placeholders
from ..helpers import format_file_size
from ..helpers import safe_unlink
from ..helpers import prepare_subprocess_env, ignore_sigint
from ..helpers import get_socket_filename
from ..fslocking import LockTimeout, NotLocked, NotMyLock, LockFailed
from ..logger import create_logger
from ..helpers import msgpack
from .repository import LegacyRepository
from ..version import parse_version, format_version
from ..helpers.datastruct import EfficientCollectionQueue
from ..platform import is_win32
logger = create_logger(__name__)

View file

@ -0,0 +1,75 @@
"""Legacy RepoObj1 — Borg 1.x repository object format.
Moved from borg.repoobj as part of the legacy code separation.
"""
from ..constants import * # NOQA
from ..helpers import workarounds
from ..compress import Compressor, get_compressor
# Workaround for lost passphrase or key in "authenticated" or "authenticated-blake2" mode
AUTHENTICATED_NO_KEY = "authenticated_no_key" in workarounds
class RepoObj1: # legacy
@classmethod
def extract_crypted_data(cls, data: bytes) -> bytes:
# used for crypto type detection
return data
def __init__(self, key):
self.key = key
self.compressor = get_compressor("lz4", legacy_mode=True)
def id_hash(self, data: bytes) -> bytes:
return self.key.id_hash(data)
def format(
self,
id: bytes,
meta: dict,
data: bytes,
compress: bool = True,
size: int = None,
ctype: int = None,
clevel: int = None,
ro_type: str = None,
) -> bytes:
assert isinstance(id, bytes)
assert meta == {}
assert isinstance(data, (bytes, memoryview))
assert ro_type is not None
assert compress or size is not None and ctype is not None and clevel is not None
if compress:
assert size is None or size == len(data)
meta, data_compressed = self.compressor.compress(meta, data)
else:
assert isinstance(size, int)
data_compressed = data # is already compressed, must include type/level bytes
data_encrypted = self.key.encrypt(id, data_compressed)
return data_encrypted
def parse_meta(self, id: bytes, cdata: bytes) -> dict:
raise NotImplementedError("parse_meta is not available for RepoObj1")
def parse(
self, id: bytes, cdata: bytes, decompress: bool = True, want_compressed: bool = False, ro_type: str = None
) -> tuple[dict, bytes]:
assert not (not decompress and not want_compressed), "invalid parameter combination!"
assert isinstance(id, bytes)
assert isinstance(cdata, bytes)
assert ro_type is not None
data_compressed = self.key.decrypt(id, cdata)
compressor_cls, compression_level = Compressor.detect(data_compressed[:2])
compressor = compressor_cls(level=compression_level, legacy_mode=True)
meta_compressed = {}
meta_compressed["ctype"] = compressor.ID
meta_compressed["clevel"] = compressor.level
meta_compressed["csize"] = len(data_compressed)
if decompress:
meta, data = compressor.decompress(None, data_compressed)
if not AUTHENTICATED_NO_KEY:
self.key.assert_id(id, data)
else:
meta, data = None, None
return meta_compressed if want_compressed else meta, data_compressed if want_compressed else data

View file

@ -14,22 +14,22 @@ from collections.abc import Callable
import xxhash
from .constants import * # NOQA
from .hashindex import NSIndex1Entry, NSIndex1
from .helpers import Error, ErrorWithTraceback, IntegrityError, format_file_size, parse_file_size
from .helpers import Location
from .helpers import ProgressIndicatorPercent
from .helpers import bin_to_hex, hex_to_bin
from .helpers import secure_erase, safe_unlink
from .helpers import msgpack
from .helpers.lrucache import LRUCache
from .fslocking import Lock, LockError, LockErrorT
from .logger import create_logger
from .manifest import Manifest, NoManifestError
from .platform import SaveFile, SyncFile, sync_dir, safe_fadvise
from .repoobj import RepoObj
from .checksums import crc32
from .crypto.file_integrity import IntegrityCheckedFile, FileIntegrityError
from ..constants import * # NOQA
from ..hashindex import NSIndex1Entry, NSIndex1
from ..helpers import Error, ErrorWithTraceback, IntegrityError, format_file_size, parse_file_size
from ..helpers import Location
from ..helpers import ProgressIndicatorPercent
from ..helpers import bin_to_hex, hex_to_bin
from ..helpers import secure_erase, safe_unlink
from ..helpers import msgpack
from ..helpers.lrucache import LRUCache
from ..fslocking import Lock, LockError, LockErrorT
from ..logger import create_logger
from ..manifest import Manifest, NoManifestError
from ..platform import SaveFile, SyncFile, sync_dir, safe_fadvise
from ..repoobj import RepoObj
from ..checksums import crc32
from ..crypto.file_integrity import IntegrityCheckedFile, FileIntegrityError
logger = create_logger(__name__)

View file

@ -117,7 +117,7 @@ class Archives:
def _get_archive_meta(self, id: bytes) -> dict:
# get all metadata directly from the ArchiveItem in the repo.
from .legacyrepository import LegacyRepository
from .legacy.repository import LegacyRepository
from .repository import Repository
try:

View file

@ -36,7 +36,7 @@ from .fslocking import LockTimeout, NotLocked, NotMyLock, LockFailed
from .logger import create_logger, borg_serve_log_queue
from .manifest import NoManifestError
from .helpers import msgpack
from .legacyrepository import LegacyRepository
from .legacy.repository import LegacyRepository
from .repository import Repository, StoreObjectNotFound
from .version import parse_version, format_version
from .helpers.datastruct import EfficientCollectionQueue

View file

@ -6,7 +6,7 @@ from xxhash import xxh64
from .constants import * # NOQA
from .helpers import msgpack, workarounds
from .helpers.errors import IntegrityError
from .compress import Compressor, LZ4_COMPRESSOR, get_compressor
from .compress import Compressor, LZ4_COMPRESSOR
# Workaround for lost passphrase or key in "authenticated" or "authenticated-blake2" mode
AUTHENTICATED_NO_KEY = "authenticated_no_key" in workarounds
@ -139,65 +139,5 @@ class RepoObj:
return meta_compressed if want_compressed else meta, data_compressed if want_compressed else data
class RepoObj1: # legacy
@classmethod
def extract_crypted_data(cls, data: bytes) -> bytes:
# used for crypto type detection
return data
def __init__(self, key):
self.key = key
self.compressor = get_compressor("lz4", legacy_mode=True)
def id_hash(self, data: bytes) -> bytes:
return self.key.id_hash(data)
def format(
self,
id: bytes,
meta: dict,
data: bytes,
compress: bool = True,
size: int = None,
ctype: int = None,
clevel: int = None,
ro_type: str = None,
) -> bytes:
assert isinstance(id, bytes)
assert meta == {}
assert isinstance(data, (bytes, memoryview))
assert ro_type is not None
assert compress or size is not None and ctype is not None and clevel is not None
if compress:
assert size is None or size == len(data)
meta, data_compressed = self.compressor.compress(meta, data)
else:
assert isinstance(size, int)
data_compressed = data # is already compressed, must include type/level bytes
data_encrypted = self.key.encrypt(id, data_compressed)
return data_encrypted
def parse_meta(self, id: bytes, cdata: bytes) -> dict:
raise NotImplementedError("parse_meta is not available for RepoObj1")
def parse(
self, id: bytes, cdata: bytes, decompress: bool = True, want_compressed: bool = False, ro_type: str = None
) -> tuple[dict, bytes]:
assert not (not decompress and not want_compressed), "invalid parameter combination!"
assert isinstance(id, bytes)
assert isinstance(cdata, bytes)
assert ro_type is not None
data_compressed = self.key.decrypt(id, cdata)
compressor_cls, compression_level = Compressor.detect(data_compressed[:2])
compressor = compressor_cls(level=compression_level, legacy_mode=True)
meta_compressed = {}
meta_compressed["ctype"] = compressor.ID
meta_compressed["clevel"] = compressor.level
meta_compressed["csize"] = len(data_compressed)
if decompress:
meta, data = compressor.decompress(None, data_compressed)
if not AUTHENTICATED_NO_KEY:
self.key.assert_id(id, data)
else:
meta, data = None, None
return meta_compressed if want_compressed else meta, data_compressed if want_compressed else data
# Backward compatibility: RepoObj1 has moved to borg.legacy.repoobj
from .legacy.repoobj import RepoObj1 # noqa: F401

View file

@ -13,9 +13,9 @@ from ..helpers import IntegrityError
from ..helpers import msgpack
from ..fslocking import Lock, LockFailed
from ..platformflags import is_win32
from ..legacyremote import LegacyRemoteRepository, InvalidRPCMethod, PathNotAllowed
from ..legacyrepository import LegacyRepository, LoggedIO
from ..legacyrepository import MAGIC, MAX_DATA_SIZE, TAG_DELETE, TAG_PUT2, TAG_PUT, TAG_COMMIT
from ..legacy.remote import LegacyRemoteRepository, InvalidRPCMethod, PathNotAllowed
from ..legacy.repository import LegacyRepository, LoggedIO
from ..legacy.repository import MAGIC, MAX_DATA_SIZE, TAG_DELETE, TAG_PUT2, TAG_PUT, TAG_COMMIT
from ..repoobj import RepoObj
from .hashindex_test import H
@ -664,7 +664,7 @@ def test_subtly_corrupted_hints_without_integrity(repository, caplog):
repository.put(H(3), fchunk(b"1234"))
# Do a compaction run.
# The corrupted refcount is detected and logged as a warning, but compaction proceeds.
caplog.set_level(logging.WARNING, logger="borg.legacyrepository")
caplog.set_level(logging.WARNING, logger="borg.legacy.repository")
repository.commit(compact=True)
assert "Corrupted segment reference count" in caplog.text
# We verify that the repository is still consistent.

View file

@ -4,7 +4,8 @@ from ..constants import ROBJ_FILE_STREAM, ROBJ_MANIFEST, ROBJ_ARCHIVE_META
from ..crypto.key import PlaintextKey
from ..helpers.errors import IntegrityError
from ..repository import Repository
from ..repoobj import RepoObj, RepoObj1
from ..repoobj import RepoObj
from ..legacy.repoobj import RepoObj1
from ..compress import LZ4