cache

package
v1.29.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Index

Constants

View Source
const ReservedPrefix = ".lmm-"

ReservedPrefix marks lmm's own bookkeeping entries inside a cache version directory. Nothing under it is mod content: reserved entries are excluded from every enumerator of a version directory (ListFiles, Size) so they can never be deployed, counted, checksummed, or conflict-matched.

It is exported so the archive extractor (internal/core) can enforce the same namespace from the other side: mod archives are untrusted content, and a member shipped under this prefix could otherwise forge a completion marker (making the cache-first guard skip a real download) or hide itself from deploy. One constant, both sides.

Variables

This section is empty.

Functions

func HasRetainedSource added in v1.29.0

func HasRetainedSource(versionDir string) (bool, error)

HasRetainedSource reports whether versionDir contains at least one retained compile source (any entry named by RetainedSourceName, for any fileID). It is deployableFiles' narrowing gate (#210): a retained source is the validate+retain compile model's signature, present in every real #210 entry (a merged-pak deploy that keeps its .exmodz for offline recompile) and the same signal verify's retained-source carve-out trusts. Its absence means unattributed content on disk cannot be distinguished from an unmanifested contributor (#144, e.g. `lmm import`), so callers must fall back to the full union rather than narrow.

A missing versionDir is not an error - it reports (false, nil), same as an empty directory.

func MarkFileComplete added in v1.25.0

func MarkFileComplete(versionDir, fileID string) error

MarkFileComplete writes the zero-byte completion marker for fileID into versionDir, recording that this source file's content has been fully committed to that cache entry. It is the write side of HasFileIDs.

A bare (zero-byte) marker vouches for completion but records no member manifest - FileManifests reports it as Recorded=false. Production commits go through MarkFileCompleteWithMembers instead; this remains the legacy shape every pre-manifest cache entry already has on disk.

versionDir is a raw directory path rather than a cache key because the marker is written into the STAGING directory just before it is swapped into place (see internal/core/service.go's commitStagedCacheWithMarker), so the marker and the content it vouches for become visible in the same atomic rename - a marker can never appear without its content.

An unverifiable fileID (blank, or carrying a path separator - see VerifiableFileID) is skipped rather than rejected: HasFileIDs refuses those same IDs, so the entry simply reads as incomplete and costs a redundant re-download, which is the safe direction and never a write outside versionDir.

func MarkFileCompleteWithMembers added in v1.27.0

func MarkFileCompleteWithMembers(versionDir, fileID string, members []string) error

MarkFileCompleteWithMembers is MarkFileComplete plus provenance: the marker body records WHICH versionDir-relative members this file ID contributed (slash-separated, one per line, under the manifestHeader line), so the same-version update path can later undeploy members owned solely by a superseded file (#144 item 4). Read back via FileManifests.

A member name a line-oriented body cannot represent faithfully (embedded newline/carriage return) degrades the WHOLE write to a bare legacy marker: completion is still vouched for, but no manifest is recorded, so consumers fall back to today's union behavior rather than ever trusting a corrupted member list.

func MergeFingerprintPath added in v1.28.0

func MergeFingerprintPath(versionDir string) string

MergeFingerprintPath returns the reserved on-disk path for versionDir's merge-fingerprint marker. Pure naming, like RetainedSourceName - callers (internal/core, which owns the MergedFingerprint type and its JSON encoding) read/write the actual bytes with ordinary file I/O.

func PruneUnclaimed added in v1.29.0

func PruneUnclaimed(versionDir string) error

PruneUnclaimed deletes non-reserved regular files in versionDir that no recorded manifest claims, then removes directories the deletions emptied (#210). It is a no-op unless EVERY marker carries a recorded manifest AND the entry holds a retained source (.lmm-source-*) - the validate+retain model's signature (#210); pruning on anything less could delete legacy content no manifest attributes (#144, e.g. an entry `lmm import` populated directly). A bare marker means unknown provenance - pruning on guesswork could delete a legacy file's live content, so any bare marker anywhere in versionDir makes the whole call a no-op. Reserved (ReservedPrefix) entries are never candidates. Callers invoke it on a STAGING directory at commit time, so a prune can never race a deploy.

func RetainedSourceName added in v1.28.0

func RetainedSourceName(fileID string) string

RetainedSourceName returns the reserved on-disk filename for fileID's retained compile source. It is a pure naming function - like GetFilePath, callers join it against a staging or cache directory themselves and read/write/copy the actual bytes with ordinary file I/O (see internal/core's ingest/merge paths).

fileID is Base'd first (#197 hardening): it is source-controlled (a ModSource's own DownloadableFile.ID, or - for an import - the archive's own filename) exactly like the FileName fields #196's review already found needed filepath.Base sanitization at their own join sites - a fileID containing "../" must not be able to escape the staging/cache directory this name gets joined against downstream.

func VerifiableFileID added in v1.27.0

func VerifiableFileID(fileID string) bool

VerifiableFileID reports whether a source file ID can be round-tripped through a marker filename. A blank ID has nothing to name, and one carrying a path separator would place (or look for) the marker outside the version directory entirely. Both are refused rather than sanitized: two distinct IDs sanitizing to the same marker would let one file's completion vouch for another's.

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache manages the central mod file cache

func New

func New(basePath string) *Cache

New creates a new cache manager for the global cache (basePath/gameID/source-mod/version).

func NewGameScoped added in v1.1.0

func NewGameScoped(basePath string) *Cache

NewGameScoped creates a cache for a per-game cache_path. Paths are basePath/source-mod/version (no gameID); the base is already game-specific.

func (*Cache) CloneMod added in v1.3.3

func (c *Cache) CloneMod(dest *Cache, gameID, sourceID, modID, version string) error

CloneMod copies a cached mod version into another cache.

Unlike every other walker it deliberately INCLUDES lmm's own .lmm-* bookkeeping entries: a clone is meant to reproduce the entry itself, not enumerate its mod content, and the reinstall cache transaction (internal/core/flows.go) round-trips a live entry through a staged/snapshot cache and back. Dropping the completion markers on that round trip would silently downgrade a complete entry to a pre-marker one and cost a redundant redownload on the next cache-first check.

func (*Cache) Delete

func (c *Cache) Delete(gameID, sourceID, modID, version string) error

Delete removes a cached mod version, then removes the mod's per-mod container directory (ModPath's parent - the "<source>-<modID>" directory version subdirectories live under) if this was its last remaining version. The container has no meaning once nothing is left under it, so leaving it behind after every version is gone is just litter (#190 item 4). A container that still holds another version, or that never existed, is left alone either way - never an error.

func (*Cache) Exists

func (c *Cache) Exists(gameID, sourceID, modID, version string) bool

Exists checks if a mod version is cached

func (*Cache) FileManifests added in v1.27.0

func (c *Cache) FileManifests(gameID, sourceID, modID, version string) (map[string]FileManifest, error)

FileManifests reads every completion marker in the version directory and returns each file ID's manifest. A directory (or entry) with no markers returns an empty map, never an error. A marker whose body is empty or carries an unrecognized header parses as Recorded=false - the safe, union-fallback direction - so pre-manifest entries and any future format revision both degrade silently rather than misreport provenance.

func (*Cache) GetFilePath

func (c *Cache) GetFilePath(gameID, sourceID, modID, version, relativePath string) string

GetFilePath returns the full path to a cached file

func (*Cache) HasFileIDs added in v1.25.0

func (c *Cache) HasFileIDs(gameID, sourceID, modID, version string, fileIDs []string) bool

HasFileIDs reports whether every named source file has been fully committed to the given (gameID, sourceID, modID, version) cache entry - a stronger guard than Exists alone, which only checks the version directory's presence and can return true for a PARTIALLY populated entry (e.g. a previous download run that stored file 1 of 2 before failing - each file is committed to the cache individually, so a broken-off run leaves the directory present but incomplete). Callers doing a "cache already has this, skip downloading" check (see internal/core/flows.go's ApplyProfileSwitch and cmd/lmm/profile.go's doProfileApply, both #96) should use this instead of Exists to decide whether a re-download is genuinely unnecessary.

Completeness is judged by the per-file markers MarkFileComplete writes, NOT by on-disk filenames: under the default DeployExtract mode a cache version directory holds an archive's EXTRACTED MEMBERS, whose names have nothing to do with the DownloadableFile's own FileName, so a filename-based check would report false for essentially every archive-based mod and redownload despite a complete cache.

LEGACY ENTRIES: a cache directory populated before markers existed (or by an `lmm import` that could not resolve the archive against its source's file listing - source-linked imports that do resolve stamp the marker at import time, #139) carries no markers and therefore reads as incomplete. That costs exactly one redundant redownload, which commits markers on the way through and makes every later check a hit.

An empty/nil fileIDs degrades to Exists - there is nothing left to verify beyond the directory's presence. An unverifiable ID (blank, or carrying a path separator) reports false rather than being skipped.

func (*Cache) ListFiles

func (c *Cache) ListFiles(gameID, sourceID, modID, version string) ([]string, error)

ListFiles returns all files in a cached mod version.

This is the choke point every consumer of a cache entry's contents goes through - Installer.Install/Replace/Undeploy, DetectConflicts, `lmm verify`'s file-count check, DownloadModResult.FilesExtracted - so lmm's own bookkeeping entries (the .lmm-* completion markers, see MarkFileComplete) are excluded here and never reach any of them. A version directory holding ONLY markers correctly reports zero files.

CloneMod is the sole exception and walks with includeReserved - see there.

func (*Cache) ModPath

func (c *Cache) ModPath(gameID, sourceID, modID, version string) string

ModPath returns the path where a mod version's files are stored

func (*Cache) Size

func (c *Cache) Size(gameID, sourceID, modID, version string) (int64, error)

Size returns the total size of cached files for a mod version. lmm's own .lmm-* bookkeeping entries are excluded, same as in ListFiles.

func (*Cache) Store

func (c *Cache) Store(gameID, sourceID, modID, version, relativePath string, content []byte) error

Store saves a file to the cache

type FileManifest added in v1.27.0

type FileManifest struct {
	// Members holds the version-dir-relative paths (OS separators) this file
	// ID contributed to the cache entry. Meaningful only when Recorded.
	Members []string
	// Recorded is false for a legacy bare marker: the file's completion is
	// vouched for, but WHICH members it contributed was never written down.
	// Consumers must treat that as unknown provenance - never as "none".
	Recorded bool
}

FileManifest is one completion marker's recorded member manifest.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL