Documentation
¶
Overview ¶
Package datastore provides chunk storage, indexing, and backup catalog management for pxar archives.
The package implements the Proxmox Backup Server data model: backup data is split into chunks, each chunk is stored as a DataBlob (with optional zstd compression, AES-256-GCM encryption, and CRC32 verification), and chunk references are tracked in dynamic or fixed index files.
Chunk Store ¶
ChunkStore manages chunk storage on the local filesystem. Each chunk is identified by its SHA-256 digest and stored under a .chunks directory:
store, _ := datastore.NewChunkStore("/backup/datastore")
inserted, size, _ := store.InsertChunk(digest, blobData)
blobData, _ := store.LoadChunk(digest)
Data Blobs ¶
All chunk data is wrapped in a DataBlob envelope containing a magic number and CRC32 checksum:
blob, _ := datastore.EncodeBlob(rawChunk) decoded, _ := datastore.DecodeBlob(blob.Bytes())
Use EncodeCompressedBlob for zstd compression, EncodeEncryptedBlob for AES-256-GCM encryption.
Index Files ¶
Dynamic indexes (.didx) map variable-size chunks (from buzhash chunking) to their digests and offsets:
writer := datastore.NewDynamicIndexWriter(time.Now().Unix()) writer.Add(offset, digest) indexData, _ := writer.Finish() reader, _ := datastore.ParseDynamicIndex(indexData) info, _ := reader.ChunkInfo(0) // info.Start, info.End, info.Digest
Fixed indexes (.fidx) are used for fixed-size chunks (e.g., raw disk images).
Restoration ¶
Restorer reconstructs files from chunk indexes using a ChunkSource:
restorer := datastore.NewRestorer(chunkSource) restorer.RestoreFile(idx, writer) restorer.RestoreRange(idx, offset, length, writer)
Backup Catalogs ¶
BuildCatalogFast performs parallel catalog extraction from a DIDX. BuildDirIndex builds a directory index from goodbye table entries. OnDemandCatalog provides lazy catalog loading from chunked data.
BackupType, BackupGroup, BackupDir, and BackupInfo model the PBS backup namespace hierarchy (type/id/timestamp). Manifest tracks all files in a backup snapshot.
Index ¶
- Constants
- Variables
- func BlobHeaderSizeFor(magic [8]byte) (int, error)
- func CreateRandomKey() ([32]byte, error)
- func DecodeBlob(raw []byte) ([]byte, error)
- func DecodeBlobInto(dst []byte, raw []byte) ([]byte, error)
- func DecodeEncryptedBlob(raw []byte, cc *CryptConfig) ([]byte, error)
- func EncodeBlobTo(dst []byte, data []byte) ([]byte, error)
- func EncodeCompressedBlobTo(dst []byte, data []byte) ([]byte, error)
- func EncodeEncryptedBlobTo(dst []byte, data []byte, cc *CryptConfig, compress bool) ([]byte, error)
- func FormatFingerprint(fp [32]byte) string
- func GenerateKeyFile(password string) ([]byte, error)
- func IsCompressedMagic(magic [8]byte) bool
- func IsEncryptedMagic(magic [8]byte) bool
- func PutBlobBuf(bp *[]byte)
- func SignManifest(manifest *Manifest, cc *CryptConfig) error
- func VerifyManifestSignature(manifest *Manifest, cc *CryptConfig) error
- type BackupDir
- type BackupFileInfo
- type BackupGroup
- type BackupInfo
- type BackupType
- type BlobHeader
- type BuildResult
- type Catalog
- type CatalogChild
- type CatalogEntryType
- type CatalogOptions
- type CatalogReader
- type CatalogTreeEntry
- type CatalogWriter
- func (cw *CatalogWriter) AddBlockDevice(name string)
- func (cw *CatalogWriter) AddCharDevice(name string)
- func (cw *CatalogWriter) AddFIFO(name string)
- func (cw *CatalogWriter) AddFile(name string, size uint64, mtime int64)
- func (cw *CatalogWriter) AddHardlink(name string)
- func (cw *CatalogWriter) AddSocket(name string)
- func (cw *CatalogWriter) AddSymlink(name string)
- func (cw *CatalogWriter) EndDirectory()
- func (cw *CatalogWriter) Finish() error
- func (cw *CatalogWriter) StartDirectory(name string)
- type ChunkInfo
- type ChunkResult
- type ChunkSource
- type ChunkStore
- type ChunkStoreSource
- type CryptConfig
- type CryptMode
- type DataBlob
- type DirIndex
- type DynamicEntry
- type DynamicIndexHeader
- type DynamicIndexReader
- func (r *DynamicIndexReader) CTime() int64
- func (r *DynamicIndexReader) ChunkFromOffset(offset uint64) (int, bool)
- func (r *DynamicIndexReader) ChunkInfo(pos int) (ChunkInfo, bool)
- func (r *DynamicIndexReader) ComputeCsum() ([32]byte, uint64)
- func (r *DynamicIndexReader) Count() int
- func (r *DynamicIndexReader) Entry(i int) DynamicEntry
- func (r *DynamicIndexReader) IndexBytes() uint64
- func (r *DynamicIndexReader) IndexDigest(pos int) ([32]byte, bool)
- type DynamicIndexWriter
- type EncryptedBlobHeader
- type FixedIndexHeader
- type FixedIndexReader
- func (r *FixedIndexReader) CTime() int64
- func (r *FixedIndexReader) ChunkFromOffset(offset uint64) (int, bool)
- func (r *FixedIndexReader) ChunkInfo(pos int) (ChunkInfo, bool)
- func (r *FixedIndexReader) ComputeCsum() ([32]byte, uint64)
- func (r *FixedIndexReader) Count() int
- func (r *FixedIndexReader) IndexBytes() uint64
- func (r *FixedIndexReader) IndexDigest(pos int) ([32]byte, bool)
- type FixedIndexWriter
- type KeyConfig
- type KeyDerivationConfig
- type Manifest
- type OnDemandCatalog
- type Restorer
- type StoreChunker
- type UnprotectedInfo
Constants ¶
const ( BlobHeaderSize = 12 // magic(8) + crc32(4) EncryptedBlobHeaderSize = 44 // magic(8) + crc32(4) + iv(16) + tag(16) IndexHeaderSize = 4096 DynamicEntrySize = 40 // end_offset(8) + digest(32) FixedDigestSize = 32 MaxBlobSize = 128 * 1024 * 1024 // 128MB )
Variables ¶
var ( MagicUncompressedBlob = [8]byte{66, 171, 56, 7, 190, 131, 112, 161} MagicCompressedBlob = [8]byte{49, 185, 88, 66, 111, 182, 163, 127} MagicEncryptedBlob = [8]byte{123, 103, 133, 190, 34, 45, 76, 240} MagicEncrComprBlob = [8]byte{230, 89, 27, 191, 11, 191, 216, 11} MagicFixedChunkIndex = [8]byte{47, 127, 65, 237, 145, 253, 15, 205} MagicDynamicChunkIndex = [8]byte{28, 145, 78, 165, 25, 186, 179, 205} )
Magic numbers from Proxmox Backup Server (file_formats.rs).
var BlobBufPool = sync.Pool{ New: func() any { buf := make([]byte, 0, 4<<20) return &buf }, }
var CatalogMagic = [8]byte{145, 253, 96, 249, 196, 103, 88, 213}
var ErrUnknownBlobMagic = fmt.Errorf("unknown blob magic")
ErrUnknownBlobMagic is returned when blob data has an unrecognized magic number.
Functions ¶
func BlobHeaderSizeFor ¶
BlobHeaderSizeFor returns the header size for the given blob magic. Returns ErrUnknownBlobMagic for unknown magic values.
func CreateRandomKey ¶ added in v0.6.0
CreateRandomKey generates a random 32-byte encryption key.
func DecodeBlob ¶
DecodeBlob decodes a raw blob, verifies CRC, and returns the payload data. For encrypted blobs, use DecodeEncryptedBlob with a CryptConfig.
func DecodeBlobInto ¶ added in v0.3.2
DecodeBlobInto decodes a raw blob into dst, verifying CRC. For compressed blobs, dst is used as the decompression output buffer (grown if needed). For uncompressed blobs, returns a slice into raw (zero allocation). For encrypted blobs, use DecodeEncryptedBlob with a CryptConfig.
func DecodeEncryptedBlob ¶ added in v0.6.0
func DecodeEncryptedBlob(raw []byte, cc *CryptConfig) ([]byte, error)
DecodeEncryptedBlob decodes an encrypted blob and returns the decrypted payload.
func EncodeBlobTo ¶ added in v0.3.2
EncodeBlobTo encodes data as an uncompressed blob into dst, which must have capacity of at least BlobHeaderSize+len(data). Returns the slice of dst containing the encoded blob. This avoids the DataBlob wrapper allocation.
func EncodeCompressedBlobTo ¶ added in v0.3.2
EncodeCompressedBlobTo encodes data as a compressed blob into dst. If compression doesn't reduce size, falls back to uncompressed format. Returns the slice of dst containing the encoded blob.
func EncodeEncryptedBlobTo ¶ added in v0.6.0
EncodeEncryptedBlobTo encodes an encrypted blob into dst.
func FormatFingerprint ¶ added in v0.6.0
FormatFingerprint formats a 32-byte fingerprint as colon-separated hex, matching PBS's fingerprint format (uppercase hex with colons).
func GenerateKeyFile ¶ added in v0.6.0
GenerateKeyFile creates a new encrypted key file protected by a password. This generates a random 32-byte key and encrypts it with AES-256-GCM using a key derived from the password via PBKDF2.
func IsCompressedMagic ¶
IsCompressedMagic returns true for compressed blob types.
func IsEncryptedMagic ¶
IsEncryptedMagic returns true for encrypted blob types.
func PutBlobBuf ¶ added in v0.13.0
func PutBlobBuf(bp *[]byte)
PutBlobBuf resets buf and returns it to the shared pool.
func SignManifest ¶ added in v0.6.0
func SignManifest(manifest *Manifest, cc *CryptConfig) error
SignManifest signs a manifest JSON using HMAC-SHA256 with the id_key. The signature is computed over the canonical JSON with "signature" and "unprotected" fields removed, matching PBS behavior.
func VerifyManifestSignature ¶ added in v0.6.0
func VerifyManifestSignature(manifest *Manifest, cc *CryptConfig) error
VerifyManifestSignature verifies the manifest signature.
Types ¶
type BackupDir ¶
type BackupDir struct {
Timestamp time.Time
Group BackupGroup
}
BackupDir represents a single backup snapshot.
func (BackupDir) Info ¶
func (d BackupDir) Info() (*BackupInfo, error)
Info returns detailed information about this backup snapshot.
type BackupFileInfo ¶ added in v0.24.0
type BackupFileInfo struct {
Filename string `json:"filename"`
CryptMode string `json:"crypt-mode,omitempty"`
CSum string `json:"csum"`
Size uint64 `json:"size"`
}
BackupFileInfo describes a file in a backup manifest.
type BackupGroup ¶
type BackupGroup struct {
ID string
Base string
Type BackupType
}
BackupGroup represents a collection of backup snapshots (e.g., vm/100).
func (BackupGroup) Destroy ¶
func (g BackupGroup) Destroy() error
Destroy removes the backup group directory.
func (BackupGroup) FullPath ¶
func (g BackupGroup) FullPath() string
FullPath returns the absolute path under the base directory.
func (BackupGroup) ListSnapshots ¶
func (g BackupGroup) ListSnapshots(fn func(BackupDir) error) error
ListSnapshots invokes fn for each backup snapshot in this group.
func (BackupGroup) Path ¶
func (g BackupGroup) Path() string
Path returns the relative path for this group (e.g., "vm/100").
type BackupInfo ¶
BackupInfo holds metadata about a backup snapshot.
type BackupType ¶
type BackupType int
BackupType identifies the kind of backup.
const ( BackupVM BackupType = iota BackupCT BackupHost )
func ParseBackupType ¶
func ParseBackupType(s string) (BackupType, error)
ParseBackupType parses a backup type string.
func (BackupType) String ¶
func (bt BackupType) String() string
type BlobHeader ¶
BlobHeader is the 12-byte header for uncompressed and compressed blobs.
func UnmarshalBlobHeader ¶
func UnmarshalBlobHeader(data []byte) (BlobHeader, error)
UnmarshalBlobHeader parses a BlobHeader from raw bytes.
func (*BlobHeader) MarshalTo ¶
func (h *BlobHeader) MarshalTo(buf []byte)
MarshalTo writes the header to buf (must be at least BlobHeaderSize bytes).
type BuildResult ¶ added in v0.12.1
type BuildResult struct {
Index *DirIndex
RootChildren []CatalogChild
}
BuildResult is returned by BuildDirIndex. It contains the lightweight DirIndex and, as a free side effect of the scan, the root directory's direct children.
func BuildDirIndex ¶ added in v0.12.0
func BuildDirIndex( metaIdx *DynamicIndexReader, source ChunkSource, opts CatalogOptions, ) (*BuildResult, error)
BuildDirIndex builds a lightweight directory location index by scanning metadata chunks on demand. It fetches and processes one chunk at a time with a background prefetch goroutine, keeping at most 2-3 decoded chunks in memory regardless of total metadata size.
Peak memory: O(directory_count × avg_path_length) for the index + 2–3 chunks (~8–12 MB for 4 MB chunks) during the scan.
type Catalog ¶ added in v0.11.0
type Catalog struct {
Dirs map[string][]CatalogChild
// contains filtered or unexported fields
}
Catalog is a lightweight directory tree: parentPath → children.
The chunks field retains decoded chunk data so that filename strings (created via unsafe.String during parsing) remain valid for the Catalog's lifetime. Callers should not retain a Catalog longer than needed.
func BuildCatalogFast ¶ added in v0.11.0
func BuildCatalogFast( metaIdx *DynamicIndexReader, source ChunkSource, opts CatalogOptions, ) (*Catalog, error)
BuildCatalogFast downloads metadata chunks in parallel and performs a single sequential pass to build a directory catalog — no concatenation. Chunks are parsed in-place; filename strings are zero-copy views into chunk data kept alive by Catalog.chunks.
type CatalogChild ¶ added in v0.11.0
CatalogChild is a lightweight directory entry. Field order: largest first to minimize padding (Size int64, Name string, Kind pxar.EntryKind). Total: 8 + 16 + 8 = 32 bytes.
type CatalogEntryType ¶ added in v0.6.0
type CatalogEntryType int
const ( CatalogEntryTypeFile CatalogEntryType = iota CatalogEntryTypeDir CatalogEntryTypeSymlink CatalogEntryTypeHardlink CatalogEntryTypeBlockDev CatalogEntryTypeCharDev CatalogEntryTypeFifo CatalogEntryTypeSocket )
type CatalogOptions ¶ added in v0.11.0
type CatalogOptions struct {
MaxWorkers int // parallel chunk downloads (default 4)
}
CatalogOptions configures BuildCatalogFast behavior.
type CatalogReader ¶ added in v0.6.0
type CatalogReader struct {
// contains filtered or unexported fields
}
func NewCatalogReader ¶ added in v0.6.0
func NewCatalogReader(data []byte) *CatalogReader
type CatalogTreeEntry ¶ added in v0.6.0
type CatalogTreeEntry struct {
Name string
Children []CatalogTreeEntry
EntryType CatalogEntryType
Size uint64
Mtime int64
}
func ReadCatalogTree ¶ added in v0.6.0
func ReadCatalogTree(data []byte) (*CatalogTreeEntry, error)
type CatalogWriter ¶ added in v0.6.0
type CatalogWriter struct {
// contains filtered or unexported fields
}
CatalogWriter produces a pcat1 binary catalog stream compatible with Proxmox Backup Server. Directories are written bottom-up: leaf directories before their parents. Each directory block contains entries followed by a trailer with metadata. The stream ends with an 8-byte little-endian root directory offset.
Block format: [entries...][entry_count(u64)][name_len(u64)][name(bytes)] Entry format: [name_len(u64)][type(1)][name(bytes)][type-specific data...] File extra: [size(u64)][mtime(i64)] Dir extra: [offset(u64)] (offset from dir start in the stream)
func NewCatalogWriter ¶ added in v0.6.0
func NewCatalogWriter(w io.Writer) *CatalogWriter
NewCatalogWriter creates a catalog writer and writes the pcat1 magic header.
func (*CatalogWriter) AddBlockDevice ¶ added in v0.6.0
func (cw *CatalogWriter) AddBlockDevice(name string)
func (*CatalogWriter) AddCharDevice ¶ added in v0.6.0
func (cw *CatalogWriter) AddCharDevice(name string)
func (*CatalogWriter) AddFIFO ¶ added in v0.18.0
func (cw *CatalogWriter) AddFIFO(name string)
func (*CatalogWriter) AddFile ¶ added in v0.6.0
func (cw *CatalogWriter) AddFile(name string, size uint64, mtime int64)
func (*CatalogWriter) AddHardlink ¶ added in v0.6.0
func (cw *CatalogWriter) AddHardlink(name string)
func (*CatalogWriter) AddSocket ¶ added in v0.6.0
func (cw *CatalogWriter) AddSocket(name string)
func (*CatalogWriter) AddSymlink ¶ added in v0.6.0
func (cw *CatalogWriter) AddSymlink(name string)
func (*CatalogWriter) EndDirectory ¶ added in v0.6.0
func (cw *CatalogWriter) EndDirectory()
EndDirectory encodes the current directory block and adds a Dir entry to the parent directory.
func (*CatalogWriter) Finish ¶ added in v0.6.0
func (cw *CatalogWriter) Finish() error
Finish encodes the root directory block and writes the 8-byte little-endian root directory offset at the end of the stream.
func (*CatalogWriter) StartDirectory ¶ added in v0.6.0
func (cw *CatalogWriter) StartDirectory(name string)
type ChunkResult ¶
type ChunkResult struct {
Digest [32]byte // SHA-256 of raw chunk data
Offset uint64 // start offset in the original stream
Size int // chunk data size in bytes
Exists bool // true if chunk was already in the store
}
ChunkResult describes a single chunk produced by the chunker pipeline.
type ChunkSource ¶
type ChunkSource interface {
// GetChunk retrieves a chunk by its SHA-256 digest.
// Returns the raw chunk data (not decoded/blob-wrapped).
GetChunk(digest [32]byte) ([]byte, error)
}
ChunkSource provides access to chunks by their digest.
type ChunkStore ¶
type ChunkStore struct {
// contains filtered or unexported fields
}
ChunkStore manages chunk storage on the filesystem. Chunks are stored under base/.chunks/XXXX/XXXXYY... where XXXX are the first four hex characters of the SHA-256 digest (PBS-compatible layout).
func NewChunkStore ¶
func NewChunkStore(base string) (*ChunkStore, error)
NewChunkStore creates a ChunkStore rooted at base, creating the .chunks directory if needed.
func (*ChunkStore) ChunkPath ¶
func (cs *ChunkStore) ChunkPath(digest [32]byte) string
ChunkPath returns the filesystem path for a chunk identified by digest.
func (*ChunkStore) InsertChunk ¶
InsertChunk stores a chunk. Returns (exists, size, error). If the chunk already exists, returns (true, existingSize, nil).
func (*ChunkStore) LoadChunk ¶
func (cs *ChunkStore) LoadChunk(digest [32]byte) ([]byte, error)
LoadChunk reads a chunk from disk.
func (*ChunkStore) TouchChunk ¶
func (cs *ChunkStore) TouchChunk(digest [32]byte) error
TouchChunk updates the access time of a chunk file.
type ChunkStoreSource ¶
type ChunkStoreSource struct {
// contains filtered or unexported fields
}
ChunkStoreSource adapts a ChunkStore to the ChunkSource interface.
func NewChunkStoreSource ¶
func NewChunkStoreSource(store *ChunkStore) *ChunkStoreSource
NewChunkStoreSource creates a chunk source from a local chunk store.
type CryptConfig ¶ added in v0.6.0
type CryptConfig struct {
// contains filtered or unexported fields
}
CryptConfig holds the derived keys needed for encryption, signing, and fingerprinting.
func LoadKeyFile ¶ added in v0.6.0
func LoadKeyFile(data []byte, password string) (*CryptConfig, error)
LoadKeyFile decrypts a key file using a password and returns the raw encryption key.
func LoadKeyFileNoPassword ¶ added in v0.6.0
func LoadKeyFileNoPassword(data []byte) (*CryptConfig, error)
LoadKeyFileNoPassword loads a key file with "none" KDF (no encryption).
func NewCryptConfig ¶ added in v0.6.0
func NewCryptConfig(encKey [32]byte) (*CryptConfig, error)
NewCryptConfig derives signing and fingerprint keys from a raw 32-byte encryption key.
func (*CryptConfig) AuthTag ¶ added in v0.6.0
func (c *CryptConfig) AuthTag(data []byte) [32]byte
AuthTag computes an HMAC-SHA256 authentication tag over data using the id_key.
func (*CryptConfig) ComputeDigest ¶ added in v0.6.0
func (c *CryptConfig) ComputeDigest(data []byte) [32]byte
ComputeDigest computes SHA-256(data || id_key), matching PBS compute_digest.
func (*CryptConfig) Decrypt ¶ added in v0.6.0
func (c *CryptConfig) Decrypt(data []byte) ([]byte, error)
Decrypt decrypts data encrypted by Encrypt. Input format: nonce || ciphertext (with GCM tag appended).
func (*CryptConfig) Encrypt ¶ added in v0.6.0
func (c *CryptConfig) Encrypt(plaintext []byte) ([]byte, error)
Encrypt encrypts plaintext using AES-256-GCM with a random nonce. Returns nonce + ciphertext (ciphertext includes the GCM tag).
func (*CryptConfig) Fingerprint ¶ added in v0.6.0
func (c *CryptConfig) Fingerprint() [32]byte
Fingerprint computes SHA-256(fingerprintInput || id_key), matching PBS key fingerprint.
type CryptMode ¶ added in v0.6.0
type CryptMode string
CryptMode represents the encryption mode for a backup.
type DataBlob ¶
type DataBlob struct {
// contains filtered or unexported fields
}
DataBlob represents a stored data blob with optional compression. The raw data contains the magic, CRC, and payload.
func EncodeBlob ¶
EncodeBlob creates an uncompressed blob from data.
func EncodeCompressedBlob ¶
EncodeCompressedBlob creates a compressed blob. Falls back to uncompressed if compression doesn't reduce size.
func EncodeEncryptedBlob ¶ added in v0.6.0
func EncodeEncryptedBlob(data []byte, cc *CryptConfig, compress bool) (*DataBlob, error)
EncodeEncryptedBlob creates an encrypted blob from data using AES-256-GCM. If compress is true and the data compresses well, it is compressed before encryption (producing an EncrCompr blob). Otherwise, an Encrypted blob is produced.
func (*DataBlob) IsCompressed ¶
IsCompressed returns true if the blob uses compression.
func (*DataBlob) IsEncrypted ¶
IsEncrypted returns true if the blob uses encryption.
type DirIndex ¶ added in v0.12.0
type DirIndex struct {
// contains filtered or unexported fields
}
DirIndex is a lightweight mapping from directory paths to their positions in the metadata stream. Use with OnDemandCatalog for lazy directory loading.
Memory: ~80 bytes per directory (path string + location struct). For 1M directories: ~80 MB (vs 2+ GB for a full Catalog).
func (*DirIndex) DirPaths ¶ added in v0.12.0
DirPaths invokes fn for each known directory path in unspecified order.
type DynamicEntry ¶
DynamicEntry is a single entry in a dynamic index (40 bytes).
type DynamicIndexHeader ¶
DynamicIndexHeader is the 4096-byte header for dynamic chunk index files.
func UnmarshalDynamicIndexHeader ¶
func UnmarshalDynamicIndexHeader(data []byte) (DynamicIndexHeader, error)
UnmarshalDynamicIndexHeader parses a DynamicIndexHeader from raw bytes.
func (*DynamicIndexHeader) MarshalTo ¶
func (h *DynamicIndexHeader) MarshalTo(buf []byte)
MarshalTo writes the header to buf (must be at least IndexHeaderSize bytes).
type DynamicIndexReader ¶
type DynamicIndexReader struct {
// contains filtered or unexported fields
}
DynamicIndexReader reads a dynamic chunk index.
func ParseDynamicIndex ¶ added in v0.18.0
func ParseDynamicIndex(data []byte) (*DynamicIndexReader, error)
ParseDynamicIndex parses a dynamic index from raw bytes.
func (*DynamicIndexReader) CTime ¶
func (r *DynamicIndexReader) CTime() int64
CTime returns the creation timestamp.
func (*DynamicIndexReader) ChunkFromOffset ¶
func (r *DynamicIndexReader) ChunkFromOffset(offset uint64) (int, bool)
ChunkFromOffset returns the chunk index containing the given byte offset. Uses binary search for O(log n) lookup.
func (*DynamicIndexReader) ChunkInfo ¶
func (r *DynamicIndexReader) ChunkInfo(pos int) (ChunkInfo, bool)
ChunkInfo returns the chunk info at position i.
func (*DynamicIndexReader) ComputeCsum ¶
func (r *DynamicIndexReader) ComputeCsum() ([32]byte, uint64)
ComputeCsum computes the SHA-256 checksum over all entry data.
func (*DynamicIndexReader) Count ¶
func (r *DynamicIndexReader) Count() int
Count returns the number of entries.
func (*DynamicIndexReader) Entry ¶
func (r *DynamicIndexReader) Entry(i int) DynamicEntry
Entry returns the entry at position i.
func (*DynamicIndexReader) IndexBytes ¶
func (r *DynamicIndexReader) IndexBytes() uint64
IndexBytes returns the total virtual size (end offset of last entry).
func (*DynamicIndexReader) IndexDigest ¶
func (r *DynamicIndexReader) IndexDigest(pos int) ([32]byte, bool)
IndexDigest returns the digest at position pos.
type DynamicIndexWriter ¶
type DynamicIndexWriter struct {
// contains filtered or unexported fields
}
DynamicIndexWriter builds a dynamic chunk index.
func NewDynamicIndexWriter ¶
func NewDynamicIndexWriter(ctime int64) *DynamicIndexWriter
NewDynamicIndexWriter creates a new writer with the given creation time.
func (*DynamicIndexWriter) Add ¶
func (w *DynamicIndexWriter) Add(endOffset uint64, digest [32]byte)
Add appends an entry with the given end offset and digest.
func (*DynamicIndexWriter) Csum ¶ added in v0.3.0
func (w *DynamicIndexWriter) Csum() [32]byte
Csum returns the SHA-256 checksum over all entry data (end_offset || digest pairs). This matches PBS's compute_csum() and is the checksum stored in the manifest. The result is cached and invalidated by Add().
func (*DynamicIndexWriter) Finish ¶
func (w *DynamicIndexWriter) Finish() ([]byte, error)
Finish writes the complete index and returns the raw bytes.
func (*DynamicIndexWriter) LastEndOffset ¶ added in v0.27.2
func (w *DynamicIndexWriter) LastEndOffset() uint64
LastEndOffset returns the end offset of the last entry, which is the total virtual size of the indexed stream. Returns 0 if there are no entries.
type EncryptedBlobHeader ¶
EncryptedBlobHeader is the 48-byte header for encrypted blobs.
func UnmarshalEncryptedBlobHeader ¶
func UnmarshalEncryptedBlobHeader(data []byte) (EncryptedBlobHeader, error)
UnmarshalEncryptedBlobHeader parses an EncryptedBlobHeader from raw bytes.
func (*EncryptedBlobHeader) MarshalTo ¶
func (h *EncryptedBlobHeader) MarshalTo(buf []byte)
MarshalTo writes the header to buf (must be at least EncryptedBlobHeaderSize bytes).
type FixedIndexHeader ¶
type FixedIndexHeader struct {
Magic [8]byte
UUID [16]byte
Ctime int64
IndexCsum [32]byte
Size uint64
ChunkSize uint64
}
FixedIndexHeader is the 4096-byte header for fixed chunk index files.
func UnmarshalFixedIndexHeader ¶
func UnmarshalFixedIndexHeader(data []byte) (FixedIndexHeader, error)
UnmarshalFixedIndexHeader parses a FixedIndexHeader from raw bytes.
func (*FixedIndexHeader) MarshalTo ¶
func (h *FixedIndexHeader) MarshalTo(buf []byte)
MarshalTo writes the header to buf (must be at least IndexHeaderSize bytes).
type FixedIndexReader ¶
type FixedIndexReader struct {
// contains filtered or unexported fields
}
FixedIndexReader reads a fixed-size chunk index.
func ParseFixedIndex ¶ added in v0.18.0
func ParseFixedIndex(data []byte) (*FixedIndexReader, error)
ParseFixedIndex parses a fixed index from raw bytes.
func (*FixedIndexReader) CTime ¶
func (r *FixedIndexReader) CTime() int64
CTime returns the creation timestamp.
func (*FixedIndexReader) ChunkFromOffset ¶
func (r *FixedIndexReader) ChunkFromOffset(offset uint64) (int, bool)
ChunkFromOffset returns the chunk index for the given byte offset.
func (*FixedIndexReader) ChunkInfo ¶
func (r *FixedIndexReader) ChunkInfo(pos int) (ChunkInfo, bool)
ChunkInfo returns chunk info at position pos.
func (*FixedIndexReader) ComputeCsum ¶
func (r *FixedIndexReader) ComputeCsum() ([32]byte, uint64)
ComputeCsum computes the SHA-256 checksum over all digests.
func (*FixedIndexReader) Count ¶
func (r *FixedIndexReader) Count() int
Count returns the number of chunks.
func (*FixedIndexReader) IndexBytes ¶
func (r *FixedIndexReader) IndexBytes() uint64
IndexBytes returns the total virtual size.
func (*FixedIndexReader) IndexDigest ¶
func (r *FixedIndexReader) IndexDigest(pos int) ([32]byte, bool)
IndexDigest returns the digest at position pos.
type FixedIndexWriter ¶
type FixedIndexWriter struct {
// contains filtered or unexported fields
}
FixedIndexWriter builds a fixed-size chunk index.
func NewFixedIndexWriter ¶
func NewFixedIndexWriter(ctime int64, size, chunkSize uint64) (*FixedIndexWriter, error)
NewFixedIndexWriter creates a writer. ChunkSize must be a power of 2.
func (*FixedIndexWriter) Finish ¶
func (w *FixedIndexWriter) Finish() ([]byte, error)
Finish writes the complete index and returns raw bytes.
func (*FixedIndexWriter) Set ¶
func (w *FixedIndexWriter) Set(i int, digest [32]byte)
Set sets the digest for chunk at index i.
type KeyConfig ¶ added in v0.6.0
type KeyConfig struct {
Created string `json:"created,omitempty"`
Modified string `json:"modified,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
Data []byte `json:"data"`
Kdf KeyDerivationConfig `json:"kdf"`
}
KeyConfig represents an encryption key file that can be stored on disk.
type KeyDerivationConfig ¶ added in v0.6.0
type KeyDerivationConfig struct {
Type string `json:"type"` // "scrypt" or "pbkdf2" or "none"
Salt []byte `json:"salt,omitempty"`
N int `json:"n,omitempty"` // scrypt: CPU cost
R int `json:"r,omitempty"` // scrypt: block size
P int `json:"p,omitempty"` // scrypt: parallelism
Iter int `json:"iter,omitempty"` // pbkdf2: iterations
}
KeyDerivationConfig specifies the key derivation function parameters.
type Manifest ¶
type Manifest struct {
BackupType string `json:"backup-type"`
BackupID string `json:"backup-id"`
CryptMode string `json:"crypt-mode,omitempty"`
Signature string `json:"signature,omitempty"`
Files []BackupFileInfo `json:"files"`
Unprotected json.RawMessage `json:"unprotected,omitempty"`
BackupTime int64 `json:"backup-time"`
}
Manifest represents a backup manifest (index.json).
func UnmarshalManifest ¶
UnmarshalManifest parses a manifest from JSON.
type OnDemandCatalog ¶ added in v0.12.0
type OnDemandCatalog struct {
// contains filtered or unexported fields
}
OnDemandCatalog provides lazy directory listing over a metadata stream. Only the lightweight DirIndex (~80 MB for 1M dirs) is held in memory; chunks are fetched from the source when ListDir is called. An LRU cache of decoded chunks avoids re-fetching recently used chunks.
Safe for concurrent use. Internal LRU cache is mutex-protected.
func NewOnDemandCatalog ¶ added in v0.12.0
func NewOnDemandCatalog(index *DirIndex, metaIdx *DynamicIndexReader, source ChunkSource) *OnDemandCatalog
NewOnDemandCatalog creates a lazy catalog backed by the given index and source.
func (*OnDemandCatalog) DirPaths ¶ added in v0.12.0
func (c *OnDemandCatalog) DirPaths(fn func(string) error) error
DirPaths invokes fn for each known directory path.
func (*OnDemandCatalog) HasDir ¶ added in v0.12.0
func (c *OnDemandCatalog) HasDir(path string) bool
HasDir reports whether a directory exists in the index.
func (*OnDemandCatalog) ListDir ¶ added in v0.12.0
func (c *OnDemandCatalog) ListDir(path string, fn func(CatalogChild) error) error
ListDir fetches and parses a single directory's children on demand. Only the chunk(s) containing that directory's entries are fetched. Returned children own their data — safe to retain across calls.
If the directory has nested subdirectories, their subtrees are skipped using end offsets from the DirIndex (when available) or depth tracking (fallback). Only the direct children of the requested directory are returned. For each child, fn is called with a CatalogChild; if fn returns a non-nil error, iteration stops and the error is returned.
func (*OnDemandCatalog) NumDirs ¶ added in v0.12.0
func (c *OnDemandCatalog) NumDirs() int
NumDirs returns the total number of directories.
type Restorer ¶
type Restorer struct {
// contains filtered or unexported fields
}
Restorer reconstructs files from dynamic indexes using a chunk source.
func NewRestorer ¶
func NewRestorer(source ChunkSource) *Restorer
NewRestorer creates a new restorer with the given chunk source.
func (*Restorer) FileSize ¶
func (r *Restorer) FileSize(idx *DynamicIndexReader) uint64
FileSize returns the total size of the file represented by the index.
func (*Restorer) RestoreFile ¶
func (r *Restorer) RestoreFile(idx *DynamicIndexReader, w io.Writer) error
RestoreFile reconstructs a complete file from a dynamic index. Writes the reconstructed file content to w.
func (*Restorer) RestoreRange ¶
RestoreRange reconstructs a specific byte range from a dynamic index. Useful for partial reads without downloading the entire file.
type StoreChunker ¶
type StoreChunker struct {
// contains filtered or unexported fields
}
StoreChunker splits a data stream into variable-size chunks using buzhash content-defined chunking, computes digests, stores chunks via ChunkStore, and builds a DynamicIndexWriter.
func NewStoreChunker ¶
func NewStoreChunker(store *ChunkStore, config buzhash.Config, compress bool) *StoreChunker
NewStoreChunker creates a chunker pipeline. If compress is true, chunks are stored as compressed DataBlobs; otherwise as uncompressed blobs.
func (*StoreChunker) ChunkStream ¶
func (sc *StoreChunker) ChunkStream(r io.Reader, fn func(ChunkResult) error) ([]ChunkResult, *DynamicIndexWriter, error)
ChunkStream reads all data from r, stores each chunk, and builds an index. fn is called for each chunk after it is stored; if fn returns a non-nil error, chunking stops and the error is returned. If fn is nil, no callback is made.
type UnprotectedInfo ¶ added in v0.6.0
type UnprotectedInfo struct {
KeyFingerprint string `json:"key-fingerprint"`
}
UnprotectedInfo holds the unprotected key info in a manifest.