Skip to content

store-relocated caches incomplete fallback pours and skips repair/probing on reinstall #356

Description

@StayBlue

Environment

  • nanobrew: v0.1.205
  • macOS: 26.5.1
  • arch: arm64
  • /opt/nb: absent

Summary

Nanobrew saves a keg into store-relocated/<bottle-sha> even when the whole-file relocation could not run. That incomplete snapshot is then treated as fully relocated: nb reinstall restores it, skips relocation, and returns before the post-install probe.

This means that creating /opt/nb later does not heal the package. The user must know to delete store-relocated manually. This is both the pre-v0.1.204 cache-invalidation follow-up mentioned in #347 and an ongoing way for v0.1.205 itself to create new poisoned snapshots.

Reproduction

With /opt/nb absent:

nb install perl

The first install warns:

nb: note: /opt/nb short-prefix symlink unavailable — Mach-O .rodata not relocated; run `sudo nb init` and reinstall affected packages

The resulting libperl.dylib still contains its Homebrew @INC paths:

strings -a /opt/nanobrew/prefix/Cellar/perl/5.42.2/lib/perl5/5.42/darwin-thread-multi-2level/CORE/libperl.dylib \
  | rg '/opt/homebrew'

The same paths are saved in the relocated snapshot:

/opt/nanobrew/store-relocated/11266b9a2528911df242d82ec041ee91a50c3374d03c9401b558e521b5569916/lib/perl5/5.42/darwin-thread-multi-2level/CORE/libperl.dylib

Reinstalling the same artifact:

nb reinstall perl

completed in 179 ms with no short-prefix warning and no failed post-install probe:

    ✓ perl
==> Done in 179.4ms

Afterward, libperl.dylib still contained /opt/homebrew, and Config.pm was still loaded from Homebrew.

Scope observed locally

After removing the temporary Perl test snapshot, the existing installation still had:

  • 107 store-relocated snapshots containing /opt/homebrew
  • 10 affected snapshots created after v0.1.205 was released
  • 324 foreign-prefix files in currently selected kegs across 45 formulae

So this is not limited to pre-fix cache entries.

Relevant implementation

  • src/main.zig fast-paths on hasRelocatedEntry(bottle_sha256), materializes it, links it, runs postinstall, and returns before relocation and probing.
  • The normal path calls saveRelocatedEntry after relocateKeg, even though the short-prefix fallback may have knowingly left .rodata unrelocated.
  • src/store/store.zig keys snapshots only by artifact SHA. It stores no relocation schema/version, target prefix, short-prefix availability, or completeness marker.

Expected

  • Do not save a relocated snapshot when required relocation was incomplete.
  • Version or validate relocated-cache entries before reuse.
  • Invalidate snapshots produced by pre-fix versions or incompatible relocation modes.
  • A cached reinstall should still run appropriate health/probe validation.
  • Once /opt/nb becomes available, reinstalling should repair rather than perpetuate the foreign-prefix keg.

Refs #347, #355, and #317.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions