CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-borgbackup

Deduplicated, encrypted, authenticated and compressed backups

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

mount.mddocs/

Mount and Access

Mounting archives as filesystems for browsing and selective file restoration, providing direct access to backup contents without full extraction.

import subprocess
import os

Capabilities

Archive Mounting

Mount archives as read-only filesystems for browsing and selective file access.

def mount_archive(repo_path: str, mount_point: str, archive_name: str = None,
                 foreground: bool = False, numeric_owner: bool = False,
                 strip_components: int = None, **options) -> None:
    """
    Mount archive as filesystem.
    
    Args:
        repo_path: Path to repository
        mount_point: Directory to mount at
        archive_name: Specific archive to mount (optional, mounts all if not specified)
        foreground: Run in foreground (default: background daemon)
        numeric_owner: Show numeric user/group IDs
        strip_components: Strip N leading path components
        **options: Additional options like first, last, glob_archives
    """
    cmd = ['borg', 'mount']
    if foreground:
        cmd.append('--foreground')
    if numeric_owner:
        cmd.append('--numeric-owner')
    if strip_components:
        cmd.extend(['--strip-components', str(strip_components)])
    if options.get('first'):
        cmd.extend(['--first', str(options['first'])])
    if options.get('last'):
        cmd.extend(['--last', str(options['last'])])
    if options.get('glob_archives'):
        cmd.extend(['--glob-archives', options['glob_archives']])
    
    if archive_name:
        cmd.append(f'{repo_path}::{archive_name}')
    else:
        cmd.append(repo_path)
    cmd.append(mount_point)
    
    subprocess.run(cmd, check=True)

def unmount_archive(mount_point: str) -> None:
    """
    Unmount archive filesystem.
    
    Args:
        mount_point: Directory to unmount
    """
    cmd = ['borg', 'umount', mount_point]
    subprocess.run(cmd, check=True)

Usage example:

import subprocess
import os

# Create mount point
os.makedirs('/mnt/backup', exist_ok=True)

# Mount specific archive
subprocess.run([
    'borg', 'mount', '/backup/repo::documents-2023-12-01', '/mnt/backup'
], check=True)

# Browse mounted archive (now you can use regular file operations)
# files = os.listdir('/mnt/backup')
# with open('/mnt/backup/home/user/important.txt', 'r') as f:
#     content = f.read()

# Unmount when done
subprocess.run(['borg', 'umount', '/mnt/backup'], check=True)

# Mount all archives (creates subdirectories for each archive)
subprocess.run(['borg', 'mount', '/backup/repo', '/mnt/all-backups'], check=True)

FUSE Filesystem Features

Access mounted archives with full filesystem features including browsing, copying, and searching.

def mount_with_options(repo_path: str, mount_point: str, 
                      archive_pattern: str = None, versions_view: bool = False,
                      allow_damaged_files: bool = False) -> None:
    """
    Mount with advanced FUSE options.
    
    Args:
        repo_path: Path to repository
        mount_point: Directory to mount at
        archive_pattern: Glob pattern for archive selection
        versions_view: Enable versions view (shows all versions of files)
        allow_damaged_files: Allow access to damaged files
    """
    cmd = ['borg', 'mount']
    if archive_pattern:
        cmd.extend(['--glob-archives', archive_pattern])
    if versions_view:
        cmd.append('--versions-view')  
    if allow_damaged_files:
        cmd.append('--consider-damaged-chunks')
    
    cmd.extend([repo_path, mount_point])
    subprocess.run(cmd, check=True)

Usage example:

import subprocess
import os
import shutil

# Mount with pattern matching
subprocess.run([
    'borg', 'mount', '--glob-archives=documents-*', 
    '/backup/repo', '/mnt/documents'
], check=True)

# Copy specific files from mounted archive
source_path = '/mnt/documents/documents-2023-12-01/home/user/important.txt'
if os.path.exists(source_path):
    shutil.copy2(source_path, '/restore/important.txt')

# Search for files in mounted archive
def find_files(mount_path, pattern):
    """Find files matching pattern in mounted archive"""
    matches = []
    for root, dirs, files in os.walk(mount_path):
        for file in files:
            if pattern in file:
                matches.append(os.path.join(root, file))
    return matches

# Find all PDF files
pdf_files = find_files('/mnt/documents', '.pdf')

# Unmount
subprocess.run(['borg', 'umount', '/mnt/documents'], check=True)

Repository Web Interface

Serve repository contents via HTTP for web-based browsing (if borg-web or similar tools are available).

def serve_web_interface(repo_path: str, host: str = '127.0.0.1', 
                       port: int = 8080, readonly: bool = True) -> None:
    """
    Serve repository via web interface (requires additional tools).
    
    Args:
        repo_path: Path to repository
        host: Host interface to bind to
        port: Port to serve on
        readonly: Serve in read-only mode
        
    Note: This requires additional tools like borg-web or borgmatic-web
    """
    # This is a conceptual example - actual implementation depends on web interface tool
    cmd = ['borg-web', '--repo', repo_path, '--host', host, '--port', str(port)]
    if readonly:
        cmd.append('--readonly')
    subprocess.run(cmd, check=True)

Archive Browsing Utilities

Utility functions for programmatically browsing mounted archives.

def browse_archive(mount_point: str, callback_func = None) -> dict:
    """
    Programmatically browse mounted archive contents.
    
    Args:
        mount_point: Path to mounted archive
        callback_func: Optional callback for each file/directory
        
    Returns:
        Dictionary with archive structure and file information
    """
    archive_contents = {}
    
    for root, dirs, files in os.walk(mount_point):
        rel_root = os.path.relpath(root, mount_point)
        archive_contents[rel_root] = {
            'directories': dirs,
            'files': []
        }
        
        for file in files:
            file_path = os.path.join(root, file)
            try:
                stat = os.stat(file_path)
                file_info = {
                    'name': file,
                    'size': stat.st_size,
                    'mtime': stat.st_mtime,
                    'mode': stat.st_mode
                }
                archive_contents[rel_root]['files'].append(file_info)
                
                if callback_func:
                    callback_func(file_path, file_info)
            except OSError:
                # Handle potential issues with damaged files
                pass
    
    return archive_contents

def extract_files_from_mount(mount_point: str, file_patterns: list, 
                           destination: str) -> list:
    """
    Extract specific files from mounted archive.
    
    Args:
        mount_point: Path to mounted archive
        file_patterns: List of file patterns to extract
        destination: Destination directory for extracted files
        
    Returns:
        List of successfully extracted file paths
    """
    import fnmatch
    import shutil
    
    extracted_files = []
    os.makedirs(destination, exist_ok=True)
    
    for root, dirs, files in os.walk(mount_point):
        for file in files:
            file_path = os.path.join(root, file)
            rel_path = os.path.relpath(file_path, mount_point)
            
            # Check if file matches any pattern
            for pattern in file_patterns:
                if fnmatch.fnmatch(rel_path, pattern) or fnmatch.fnmatch(file, pattern):
                    dest_path = os.path.join(destination, rel_path)
                    dest_dir = os.path.dirname(dest_path)
                    os.makedirs(dest_dir, exist_ok=True)
                    
                    try:
                        shutil.copy2(file_path, dest_path)
                        extracted_files.append(dest_path)
                    except OSError as e:
                        print(f"Failed to extract {rel_path}: {e}")
                    break
    
    return extracted_files

Usage example:

import subprocess
import os

# Mount archive
subprocess.run(['borg', 'mount', '/backup/repo::backup-2023-12-01', '/mnt/backup'], check=True)

try:
    # Browse archive contents
    def file_callback(path, info):
        if info['size'] > 1000000:  # Files larger than 1MB
            print(f"Large file: {path} ({info['size']} bytes)")
    
    contents = browse_archive('/mnt/backup', file_callback)
    
    # Extract specific file types
    extracted = extract_files_from_mount('/mnt/backup', ['*.pdf', '*.doc*'], '/restore/documents')
    print(f"Extracted {len(extracted)} files")
    
finally:
    # Always unmount
    subprocess.run(['borg', 'umount', '/mnt/backup'], check=True)

Types

class MountOptions:
    """Mount operation options"""
    def __init__(self):
        self.repository: str            # Repository path
        self.archive: str               # Archive name (optional)
        self.mount_point: str           # Mount point directory
        self.foreground: bool           # Run in foreground
        self.numeric_owner: bool        # Show numeric IDs
        self.strip_components: int      # Strip path components
        
class FileInfo:
    """File information from mounted archive"""
    def __init__(self):
        self.name: str                  # File name
        self.path: str                  # Full path
        self.size: int                  # File size in bytes
        self.mtime: float               # Modification time (timestamp)
        self.mode: int                  # File mode/permissions
        self.is_directory: bool         # Is directory flag
        
class ArchiveStructure:
    """Archive directory structure"""
    def __init__(self):
        self.directories: dict          # Directory tree structure
        self.files: list                # List of all files
        self.total_size: int            # Total size of all files
        self.file_count: int            # Total number of files
        self.directory_count: int       # Total number of directories

Install with Tessl CLI

npx tessl i tessl/pypi-borgbackup

docs

archives.md

index.md

maintenance.md

mount.md

repository.md

utilities.md

tile.json