Documentation
¶
Index ¶
- Constants
- func FilterFiles(catalog []manifest.FileEntry, patterns []string) []manifest.FileEntry
- func MatchFilePattern(path, pattern string) bool
- func OpenEntryAccessor(repoPath string, backup *manifest.Backup) (manifest.EntryAccessor, io.Closer)
- func OperatorExclusionFor(excludePaths []string, catalogPath string) (string, bool)
- func SampleEntryIndices(backup *manifest.Backup, percent float64, seed uint64) ([]int, error)
- func VerifyWrittenDigest(b *manifest.Backup, r io.Reader) (string, error)
- type FileRestoreResult
- type FileRestorer
- func (r *FileRestorer) ExtractFile(ctx context.Context, backup *manifest.Backup, filePath, outputPath string) (*FileRestoreResult, error)
- func (r *FileRestorer) RestoreFiles(ctx context.Context, backup *manifest.Backup, targetDir string, ...) (*FileRestoreResult, error)
- func (r *FileRestorer) SetNormalizer(n preprocess.Normalizer)
- type RestoreResult
- type Restorer
- func (r *Restorer) Restore(ctx context.Context, backup *manifest.Backup, writer Target) (*RestoreResult, error)
- func (r *Restorer) RestoreEntries(ctx context.Context, backup *manifest.Backup, ea manifest.EntryAccessor, ...) (*RestoreResult, error)
- func (r *Restorer) SetNormalizer(n preprocess.Normalizer)
- type StreamVerify
- type Target
- type VerifyError
- type VerifyResult
- func Verify(ctx context.Context, backup *manifest.Backup, idx *index.DedupIndex, ...) (*VerifyResult, error)
- func VerifySelectedWithNormalizer(ctx context.Context, backup *manifest.Backup, idx *index.DedupIndex, ...) (*VerifyResult, error)
- func VerifyStreamed(ctx context.Context, backup *manifest.Backup, entries manifest.EntryAccessor, ...) (*VerifyResult, error)
- func VerifyWithNormalizer(ctx context.Context, backup *manifest.Backup, idx *index.DedupIndex, ...) (*VerifyResult, error)
Constants ¶
const ( // MaxPrefetchBytes caps the estimated frame bytes held by one window. MaxPrefetchBytes = 16 << 20 // MaxPrefetchChunks caps the chunks in one window, so pathologically small // chunks cannot turn the byte budget into a huge request list. MaxPrefetchChunks = 2048 )
Prefetch window bounds (#204). A file restore used to fetch every chunk on its own — with the S3 backend that is TWO presigned range requests per chunk (header, then payload), so a few hundred chunks meant a thousand round trips. Chunks are now fetched in windows: one pack-grouped, range-coalesced batch covers many chunks at once.
The window is what bounds memory. A restore holds AT MOST one window of raw frames at a time (the previous window is dropped before a new batch is issued), so the extra footprint is capped at MaxPrefetchBytes regardless of how large the file, the pack, or the backup is — nothing here buffers a whole file or a whole pack set. Streaming is otherwise unchanged: bytes are still written out chunk by chunk as the loop walks the entries.
const ( DigestMatch = "match" DigestMismatch = "mismatch" DigestNotVerifiable = "not-verifiable" // pre-#455 backup: no stored digest )
Digest verdicts (#455): what a verify can honestly say about the manifest's whole-stream content digest. "" — no claim — is what a sampled or errored verify reports: a fold over part of the stream can neither match nor honestly mismatch the whole.
Variables ¶
This section is empty.
Functions ¶
func FilterFiles ¶
filterFiles returns catalog entries matching any of the given patterns, plus parent directories needed to contain them.
func MatchFilePattern ¶
matchFilePattern checks if a file path matches a glob pattern.
func OpenEntryAccessor ¶
func OpenEntryAccessor(repoPath string, backup *manifest.Backup) (manifest.EntryAccessor, io.Closer)
openEntryAccessor opens an EntryAccessor for the given backup. It prefers the on-disk .dnm file for O(log n) seeks. If no disk file is available (e.g. in tests) it falls back to the already-loaded backup.Entries slice.
func OperatorExclusionFor ¶
OperatorExclusionFor finds the configured exclusion (canonical `C:\a\b` form) that covers a catalog path (forward-slash, volume-root relative, with or without a leading "./"). Case-insensitive, as NTFS paths are; a match is the path itself or anything under it.
func SampleEntryIndices ¶
SampleEntryIndices deterministically selects a subset of a backup's verifiable (non-excluded) entry indices for --sample verification.
- percent in (0,100]; percent >= 100 selects the whole verifiable population.
- K = ceil(percent/100 * P), clamped to at least 1 (when there is anything to sample) and at most P.
- Selection is seeded by the BackupID (optionally mixed with seed): the same (backup, percent, seed) yields the same subset on every host and rerun, so a flagged sample failure is reproducible.
The returned indices are TRUE manifest indices, sorted ascending (stable report order + better range coalescing).
func VerifyWrittenDigest ¶
VerifyWrittenDigest holds a restored target against the manifest's whole-stream digest (#455 slice 3): a sequential fold over exactly b.TotalBytes of r, compared to b.ContentDigest.
It reads the target back rather than folding at write time because the restorer writes PACK-MAJOR (#83) — every pack fetched exactly once, at the cost of stream order — so the only place the stream exists again is on the medium it was written to. That is also what makes this the STRONGEST check in the product: it judges the bytes on disk, after every buffer, cache and controller between the repo and the medium has had its say. Each chunk was verified as it was written; only this sees that what LANDED, as a whole, is what was captured.
Verdicts are the verify vocabulary: DigestMatch, DigestMismatch (a short read counts — truncation is the likeliest real failure, and "the image ended early" IS a mismatch with the captured stream, reported as one), DigestNotVerifiable for pre-digest backups. The error return is for the reader failing, not for the bytes disagreeing.
Types ¶
type FileRestoreResult ¶
type FileRestoreResult struct {
TotalFiles int
RestoredFiles int
DirsCreated int
SymlinksCreated int
BytesWritten int64
Duration time.Duration
}
FileRestoreResult contains the outcome of a file-mode restore.
type FileRestorer ¶
type FileRestorer struct {
IgnoreErrors bool // if true, log and skip files with incomplete catalog entries rather than aborting
// contains filtered or unexported fields
}
FileRestorer restores files from a file-mode backup.
func NewFileRestorer ¶
func NewFileRestorer(idx *index.DedupIndex, st *store.ChunkStore, repoPath string, logger *slog.Logger) *FileRestorer
NewFileRestorer creates a new file restore engine. repoPath is needed to load referenced manifests for unchanged files in watcher backups.
func (*FileRestorer) ExtractFile ¶
func (r *FileRestorer) ExtractFile(ctx context.Context, backup *manifest.Backup, filePath, outputPath string) (*FileRestoreResult, error)
ExtractFile extracts a single file from a file-mode backup to outputPath. Unlike RestoreFiles, this writes the file directly to outputPath without reconstructing the directory tree.
func (*FileRestorer) RestoreFiles ¶
func (r *FileRestorer) RestoreFiles(ctx context.Context, backup *manifest.Backup, targetDir string, filePatterns []string) (*FileRestoreResult, error)
RestoreFiles restores files from a file-mode backup to targetDir. If filePatterns is non-empty, only matching files (and their parent dirs) are restored.
func (*FileRestorer) SetNormalizer ¶
func (r *FileRestorer) SetNormalizer(n preprocess.Normalizer)
SetNormalizer configures the normalizer used to verify chunk integrity; it MUST match the normalizer the backup was created with. See Restorer.SetNormalizer.
type RestoreResult ¶
type RestoreResult struct {
TotalChunks int64
RestoredChunks int64
ExcludedChunks int64
BytesWritten int64
Duration time.Duration
}
RestoreResult contains the outcome of a restore operation.
type Restorer ¶
type Restorer struct {
// OnProgress (#153): periodic (bytesWritten, totalBytes) during Restore —
// recovery flows render percent/rate/ETA from it.
OnProgress func(done, total int64)
// contains filtered or unexported fields
}
Restorer restores backups from a repository to a target writer.
func NewRestorer ¶
func NewRestorer(idx *index.DedupIndex, st *store.ChunkStore, logger *slog.Logger) *Restorer
NewRestorer creates a new restore engine.
func (*Restorer) Restore ¶
func (r *Restorer) Restore(ctx context.Context, backup *manifest.Backup, writer Target) (*RestoreResult, error)
Restore writes the backup contents to the target writer. It is RestoreEntries over the backup's in-memory entries; a caller holding a DNM (or a chain of them) should pass its accessor to RestoreEntries directly and never hold the entries whole (#506).
func (*Restorer) RestoreEntries ¶
func (r *Restorer) RestoreEntries(ctx context.Context, backup *manifest.Backup, ea manifest.EntryAccessor, writer Target) (*RestoreResult, error)
RestoreEntries writes the backup to the target, reading its entries through ea instead of a materialized []Entry (#506). Two passes:
- stream the entries in batches: write zeros for excluded regions as they are met (one reused buffer, not one per region) and collect a chunkRef for every real chunk — the index lookup happens here;
- sort the refs pack-major (#83: every pack fetched exactly once) and restore each chunk, re-reading only that entry through ea.At, an O(1) seek on a DNM.
Restore is this with a slice accessor over backup.Entries; callers that hold a DNM (or a chain of them — an incremental's parents) pass its accessor and never hold the entries whole.
func (*Restorer) SetNormalizer ¶
func (r *Restorer) SetNormalizer(n preprocess.Normalizer)
SetNormalizer configures the normalizer used to verify chunk integrity. It MUST match the normalizer the backup was created with (reconstructed from the repo config): chunk identity is the hash of the normalized bytes while the stored bytes are the originals, so verification re-normalizes before comparing against the manifest's chunk hash. A nil normalizer (default) verifies the stored bytes directly.
type StreamVerify ¶
type StreamVerify struct {
// contains filtered or unexported fields
}
StreamVerify is VerifyStreamed opened up for span-wise walking (#522 phase 3): the caller may verify the entry stream in consecutive ranges — staging each range's chunks under a disk budget between calls — and the digest fold carries across ranges, because SHA-256 state advanced in stream order is the SAME fold DigestCoversSourceStreamV1 names. Ranges MUST be walked consecutively from zero; Range enforces it, because a gap or overlap would silently fold a stream that is not the source.
func NewStreamVerify ¶
func NewStreamVerify(backup *manifest.Backup, total int64) (*StreamVerify, error)
NewStreamVerify prepares a walk over total entries of backup (METADATA only — Entries may be nil, exactly as VerifyStreamed).
func ResumeStreamVerify ¶
func ResumeStreamVerify(backup *manifest.Backup, total int64, checkpoint []byte) (*StreamVerify, error)
ResumeStreamVerify is NewStreamVerify continued from a Checkpoint. The next Range must start at the checkpoint's offset; Range enforces it.
func (*StreamVerify) Checkpoint ¶
func (sv *StreamVerify) Checkpoint() ([]byte, error)
Checkpoint serializes the walk's position so a later process can continue it (#522: a multi-span verify that outlives one run). It carries the entry offset, the counters, and the SHA-256 fold's own marshaled state — crypto/sha256 implements encoding.BinaryMarshaler, and a fold restored from it produces the digest an uninterrupted fold would, so DigestCoversSourceStreamV1 is untouched: still one sequential fold over the whole stream, across process lifetimes.
A walk that has seen a chunk error makes no stream claim and cannot be resumed: the fold is already aborted, and continuing would only spend hours to report a verdict Finish will refuse anyway.
func (*StreamVerify) Finish ¶
func (sv *StreamVerify) Finish() *VerifyResult
Finish closes the fold and returns the result. The digest verdict is only meaningful when every entry was ranged; Finish refuses a partial walk the same way the range check refuses a gap.
func (*StreamVerify) Next ¶
func (sv *StreamVerify) Next() int64
Next is the entry offset the next Range must start at.
func (*StreamVerify) Range ¶
func (sv *StreamVerify) Range(ctx context.Context, entries manifest.EntryAccessor, lo, hi int64, idx *index.DedupIndex, st *store.ChunkStore, norm preprocess.Normalizer, onProgress func(done, total int64)) error
Range verifies entries [lo, hi), folding them into the stream digest. onProgress reports GLOBAL positions against the full total.
type Target ¶
Target is the destination a volume restore writes into. *volume.Writer satisfies it; the interface lives here so the core engine does not depend on the platform layer.
type VerifyError ¶
VerifyError describes a single verification failure.
func (VerifyError) Error ¶
func (e VerifyError) Error() string
type VerifyResult ¶
type VerifyResult struct {
TotalChunks int64
VerifiedChunks int64
ExcludedChunks int64
Errors []VerifyError
Duration time.Duration
// DigestVerdict is the whole-stream check (#455): DigestMatch,
// DigestMismatch, DigestNotVerifiable, or "" when this verify ran over
// a sample (or hit chunk errors) and therefore makes no stream claim.
// Per-chunk checks prove each chunk matches ITSELF; an entry list that
// lost, duplicated or reordered a record passes every one of them —
// only the fold over the reconstruction can object (#376, one level up).
DigestVerdict string
// DigestExpected/DigestActual are set on a mismatch, so the report
// names both values instead of "they differ".
DigestExpected string
DigestActual string
}
VerifyResult contains the outcome of a verify operation.
func Verify ¶
func Verify(ctx context.Context, backup *manifest.Backup, idx *index.DedupIndex, st *store.ChunkStore) (*VerifyResult, error)
Verify checks that all chunks in a backup are retrievable and match their hashes. Unlike Restore, it reports ALL errors instead of stopping at the first one.
Use VerifyWithNormalizer for repos created with --normalize; a nil normalizer (this function) verifies the stored bytes directly.
func VerifySelectedWithNormalizer ¶
func VerifySelectedWithNormalizer(ctx context.Context, backup *manifest.Backup, idx *index.DedupIndex, st *store.ChunkStore, norm preprocess.Normalizer, indices []int) (*VerifyResult, error)
func VerifyStreamed ¶
func VerifyStreamed(ctx context.Context, backup *manifest.Backup, entries manifest.EntryAccessor, idx *index.DedupIndex, st *store.ChunkStore, norm preprocess.Normalizer, onProgress func(done, total int64)) (*VerifyResult, error)
VerifySelectedWithNormalizer verifies only the entries at the given manifest indices (used by sampled cloud verify). Each VerifyError's ChunkIndex is the TRUE manifest index, so the report is unambiguous regardless of sampling. VerifyStreamed is the full verify over an EntryAccessor (#478): the walk restore-zip's memory lesson (#419) demands — windowed reads off the staged .dnm, never the whole entry list resident. It is by construction the complete in-order walk, so the digest fold applies exactly as in the slice path. backup supplies METADATA only (digest fields, the zero-entries guard); its Entries slice is ignored and may be nil. onProgress, when non-nil, is called every progressStride entries and at each window boundary with (verified so far, total) — the panel's percent for a run that takes minutes. Finer than the read window on purpose (the a00b2ce7 incident: one slow window froze progress for 11 minutes, and a stuck verify was indistinguishable from a slow one).
func VerifyWithNormalizer ¶
func VerifyWithNormalizer(ctx context.Context, backup *manifest.Backup, idx *index.DedupIndex, st *store.ChunkStore, norm preprocess.Normalizer) (*VerifyResult, error)
VerifyWithNormalizer is Verify for repos that used a normalizer. Chunk identity is the hash of the normalized bytes while the stored bytes are the originals, so the retrieved bytes are re-normalized before comparing against the manifest's chunk hash. norm must match the repo config.
func (*VerifyResult) OK ¶
func (r *VerifyResult) OK() bool
OK returns true if the backup verified without errors.