aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-07-23 17:10:22 +0900
committerOphestra <cat@gensokyo.uk>2026-07-23 17:10:22 +0900
commite0eec632d0a7cca2fb09e026997fa9a39dcd7504 (patch)
tree705ecc285e4fe05c3bba126a276c2e9e28ae9ec8 /internal/pkg
parent67dbb68818026f3d7a64e9519cf1aac36468b7c9 (diff)
internal/pkg: optional executable files
This enables a FileArtifact implementation to specify whether its resulting file should be executable. This change also implements shebang and ELF magic checks in the file artifact. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg')
-rw-r--r--internal/pkg/archive_test.go2
-rw-r--r--internal/pkg/clean_test.go14
-rw-r--r--internal/pkg/compress.go3
-rw-r--r--internal/pkg/file.go15
-rw-r--r--internal/pkg/file_test.go31
-rw-r--r--internal/pkg/ir_test.go3
-rw-r--r--internal/pkg/net.go3
-rw-r--r--internal/pkg/pkg.go20
-rw-r--r--internal/pkg/pkg_test.go32
9 files changed, 90 insertions, 33 deletions
diff --git a/internal/pkg/archive_test.go b/internal/pkg/archive_test.go
index 726c1b6d..1cb4bea9 100644
--- a/internal/pkg/archive_test.go
+++ b/internal/pkg/archive_test.go
@@ -202,7 +202,7 @@ func TestArchiveArtifact(t *testing.T) {
"checksum/CBPcoVHuVUTVRCMbRl8J30RSSzm_tyfuXaZ-HlZsanY1sY50meOVmgaWDrGKbx9F/devices/uevent": {Mode: 0600, Data: []byte("add")},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/5woPJTvOnyFA0LpN6fMlw5Z6d3-cfpfNnmEwy5uV8cJi66xQKcfoGNCdVPApuVPW": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CBPcoVHuVUTVRCMbRl8J30RSSzm_tyfuXaZ-HlZsanY1sY50meOVmgaWDrGKbx9F")},
+ "identifier/-zXKfphyWM2Ko7VMVURaEMSuixNokFSe0xFbnwR2RtRtvMrcPGWalV0LIn45PXTY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CBPcoVHuVUTVRCMbRl8J30RSSzm_tyfuXaZ-HlZsanY1sY50meOVmgaWDrGKbx9F")},
"substitute": {Mode: fs.ModeDir | 0700},
"work": {Mode: fs.ModeDir | 0700},
diff --git a/internal/pkg/clean_test.go b/internal/pkg/clean_test.go
index c67916c3..67dceb09 100644
--- a/internal/pkg/clean_test.go
+++ b/internal/pkg/clean_test.go
@@ -48,7 +48,7 @@ func TestClean(t *testing.T) {
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/H3WYfuZx1w6bGLUmsSJ35TueazNi8YIsUGTPt4c0ln1rMmZgoDX-LCQt-l0-6UXX": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
+ "identifier/h6qx7m0vsyhO6oLEJgY2rKa1c6RZuIZ4QXxZSh__NZJ9VU0MIj5bFB-KuaPcsfpE": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
"lock": {Mode: 0644},
"variant": {Mode: 0400},
@@ -69,7 +69,7 @@ func TestClean(t *testing.T) {
"checksum/KgZ-FjbGuU-XP2QEHInpgv-2Zn0cTH5NqFMgTU0XrSdKmSwyC-3baVs1BMCP5spk": {Mode: 0400, Data: []byte("\xfd")},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/vvEdeSwVJ-N39XjgWwXYcd_M1BgoGKLnNqFL6VF3CM1lNAhDHyKu5WcDdf1U-8iO": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/KgZ-FjbGuU-XP2QEHInpgv-2Zn0cTH5NqFMgTU0XrSdKmSwyC-3baVs1BMCP5spk")},
+ "identifier/tkrOYhQfTRaArk4nNYNjTmHQUmuJMVIoDeAKIQBm3I4uuMimBKXKMIY2gGZqwQg4": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/KgZ-FjbGuU-XP2QEHInpgv-2Zn0cTH5NqFMgTU0XrSdKmSwyC-3baVs1BMCP5spk")},
"lock": {Mode: 0644},
"variant": {Mode: 0400},
@@ -122,8 +122,8 @@ func TestClean(t *testing.T) {
"checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE/result": {Mode: 0444},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/nYVS5wRWFw6kro44ZpRRfxduteOY36xu0jHuT4qE3l9rPiFCNxy4RMOnwTLErDx7": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")},
- "identifier/-p_RKDFOHplXBB2S6wuf2CkA82u6G3jb_IfEi29KB_vi0wnzcGvRjpTx0989-JxJ": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
+ "identifier/KpxTUsUfGbsX6C6HU2j9cd9He3_tt2o2m2MgH4m0zial8lmoqSWY-Gc3KrS0k-UK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")},
+ "identifier/xxQ43VMIS1WHMSEFnUdBNd6pulbtgiTorP_mtJpEsZENYhGBiDEL6Y2D8yqn0Sra": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"lock": {Mode: 0644},
"variant": {Mode: 0400},
@@ -199,14 +199,14 @@ func TestClean(t *testing.T) {
"checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv": {Mode: 0400, Data: []byte("kept")},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/nYVS5wRWFw6kro44ZpRRfxduteOY36xu0jHuT4qE3l9rPiFCNxy4RMOnwTLErDx7": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")},
- "identifier/-p_RKDFOHplXBB2S6wuf2CkA82u6G3jb_IfEi29KB_vi0wnzcGvRjpTx0989-JxJ": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CyDnDvF-LaeGPcSW70tPosNCoclByWkTjznUUF1DcgzlIwkN9yzz1ZFME1TlPj6W")},
+ "identifier/KpxTUsUfGbsX6C6HU2j9cd9He3_tt2o2m2MgH4m0zial8lmoqSWY-Gc3KrS0k-UK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/H-eSiCo227-xdqyNl2R-5G3eqXPtbb8XegAB70I5OQb2majeZXJoCxTq9wJy5qqv")},
+ "identifier/xxQ43VMIS1WHMSEFnUdBNd6pulbtgiTorP_mtJpEsZENYhGBiDEL6Y2D8yqn0Sra": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CyDnDvF-LaeGPcSW70tPosNCoclByWkTjznUUF1DcgzlIwkN9yzz1ZFME1TlPj6W")},
"lock": {Mode: 0644},
"variant": {Mode: 0400},
"status": {Mode: fs.ModeDir | 0700},
- "status/-p_RKDFOHplXBB2S6wuf2CkA82u6G3jb_IfEi29KB_vi0wnzcGvRjpTx0989-JxJ": {Mode: 0400, Data: []byte(statusHeader + "kept")},
+ "status/xxQ43VMIS1WHMSEFnUdBNd6pulbtgiTorP_mtJpEsZENYhGBiDEL6Y2D8yqn0Sra": {Mode: 0400, Data: []byte(statusHeader + "kept")},
"status/td1tf1kb3z8iUFg2k1xlHeTTEz3xR4e77WSWdv2JhjmZyY6iuvodhdT8BblDOi0E": {Mode: 0400, Data: []byte(statusHeader + "kept")},
"substitute": {Mode: fs.ModeDir | 0700},
diff --git a/internal/pkg/compress.go b/internal/pkg/compress.go
index 50303419..9a0c1152 100644
--- a/internal/pkg/compress.go
+++ b/internal/pkg/compress.go
@@ -91,6 +91,9 @@ func (c compoundCloser) Close() error {
return err
}
+// IsExecutable returns false.
+func (*decompressArtifact) IsExecutable() bool { return false }
+
// Cure returns a decompressor [io.ReadCloser].
func (a *decompressArtifact) Cure(r *RContext) (io.ReadCloser, error) {
sr, err := r.Open(a.f)
diff --git a/internal/pkg/file.go b/internal/pkg/file.go
index 1b6b02f7..14467153 100644
--- a/internal/pkg/file.go
+++ b/internal/pkg/file.go
@@ -12,6 +12,7 @@ type fileArtifact []byte
var _ KnownChecksum = new(fileArtifact)
var _ CuresExempt = new(fileArtifact)
+var _ RevisionArtifact = new(fileArtifact)
// fileArtifactNamed embeds fileArtifact alongside a caller-supplied name.
type fileArtifactNamed struct {
@@ -76,6 +77,20 @@ func (a *fileArtifact) Checksum() Checksum {
return Checksum(h.Sum(nil))
}
+// Revision satisfies [RevisionArtifact] for incompatible IsExecutable behaviour.
+func (*fileArtifact) Revision() uint64 { return 0 }
+
+// IsExecutable returns whether the contents begin with shebang or the ELF
+// magic number.
+func (a *fileArtifact) IsExecutable() bool {
+ return a != nil &&
+ (bytes.HasPrefix(
+ *a, []byte{'#', '!'},
+ ) || bytes.HasPrefix(
+ *a, []byte{0x7f, 'E', 'L', 'F'},
+ ))
+}
+
// Cure returns the caller-supplied data.
func (a *fileArtifact) Cure(*RContext) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(*a)), nil
diff --git a/internal/pkg/file_test.go b/internal/pkg/file_test.go
index 0111faa8..f6fcc96f 100644
--- a/internal/pkg/file_test.go
+++ b/internal/pkg/file_test.go
@@ -12,22 +12,41 @@ func TestFile(t *testing.T) {
t.Parallel()
want := expectsFile{0}
+ const ident = "N7M6W8-e55Wky_1q1a6qRKFVDw1S5XtPdB4HE2YZObG8OFum8HittyJgZUG5Ru2u"
+ wantShebang := expectsFile("#!/bin/sh\n")
+ const identShebang = "oKTGZb_TRtJf5G1iGGj4SOx1WUbI6YhmsUeEsCNvmK9tZYRwdwQf-DLFUxRPx5qm"
+ wantELF := expectsFile("\x7fELF\xde\xad\xbe\xef")
+ const identELF = "LjeHmIJgD1SiXIH8ewpGRed2_LU84NLapstnOAa_0hxLd8YY7x4KM0aApsj-RwCh"
checkWithCache(t, []cacheTestCase{
{"file", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
cureMany(t, c, []cureStep{
- {"short", pkg.NewFile("null", []byte{0}), base.Append(
+ {"short", pkg.NewFile("null", want), base.Append(
"identifier",
- "J3quqJoHNQID_k-amRdanjl0nXynlU7wzVJUx25BNvL229wxPyQ9s2Td9VbnRP6s",
+ ident,
), want, pkg.WNew, nil},
+
+ {"shebang", pkg.NewFile("shebang", wantShebang), base.Append(
+ "identifier",
+ identShebang,
+ ), wantShebang, pkg.WNew, nil},
+
+ {"elf", pkg.NewFile("ELF", wantELF), base.Append(
+ "identifier",
+ identELF,
+ ), wantELF, pkg.WNew, nil},
})
}, expectsFS{
".": {Mode: fs.ModeDir | 0700},
- "checksum": {Mode: fs.ModeDir | 0700},
- "checksum/" + pkg.Encode(want.hash()): {Mode: 0400, Data: []byte{0}},
+ "checksum": {Mode: fs.ModeDir | 0700},
+ "checksum/" + pkg.Encode(want.hash()): {Mode: 0400, Data: want},
+ "checksum/" + pkg.Encode(wantShebang.hash()): {Mode: 0500, Data: wantShebang},
+ "checksum/" + pkg.Encode(wantELF.hash()): {Mode: 0500, Data: wantELF},
- "identifier": {Mode: fs.ModeDir | 0700},
- "identifier/J3quqJoHNQID_k-amRdanjl0nXynlU7wzVJUx25BNvL229wxPyQ9s2Td9VbnRP6s": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
+ "identifier": {Mode: fs.ModeDir | 0700},
+ "identifier/" + ident: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + pkg.Encode(want.hash()))},
+ "identifier/" + identShebang: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + pkg.Encode(wantShebang.hash()))},
+ "identifier/" + identELF: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + pkg.Encode(wantELF.hash()))},
"substitute": {Mode: fs.ModeDir | 0700},
diff --git a/internal/pkg/ir_test.go b/internal/pkg/ir_test.go
index 963cdc04..312474bc 100644
--- a/internal/pkg/ir_test.go
+++ b/internal/pkg/ir_test.go
@@ -4,7 +4,6 @@ import (
"bytes"
"io"
"io/fs"
- "math"
"reflect"
"testing"
@@ -154,7 +153,7 @@ func TestRevision(t *testing.T) {
p := buf.Bytes()
p[8] = 0xfd
- wantErr := pkg.RevisionError{math.MaxUint64 - 2, math.MaxUint64}
+ wantErr := pkg.RevisionError{0xfd, 0}
if _, err := c.NewDecoder(
bytes.NewReader(p),
).Decode(); !reflect.DeepEqual(err, wantErr) {
diff --git a/internal/pkg/net.go b/internal/pkg/net.go
index 412dd2a7..cd30e3bb 100644
--- a/internal/pkg/net.go
+++ b/internal/pkg/net.go
@@ -74,6 +74,9 @@ func (e ResponseStatusError) Error() string {
return "the requested URL returned non-OK status: " + http.StatusText(int(e))
}
+// IsExecutable returns false.
+func (*httpArtifact) IsExecutable() bool { return false }
+
// Cure sends the http request and returns the resulting response body reader
// wrapped to perform checksum validation. It is valid but not encouraged to
// close the resulting [io.ReadCloser] before it is read to EOF, as that causes
diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go
index ae3a8185..f837fade 100644
--- a/internal/pkg/pkg.go
+++ b/internal/pkg/pkg.go
@@ -478,6 +478,8 @@ type KnownIdent interface {
// [Artifact]. This value must be known ahead of time and guaranteed to be
// unique without having obtained the full contents of the [Artifact].
ID() ID
+
+ Artifact
}
// KnownChecksum is optionally implemented by [Artifact] for an artifact with
@@ -489,6 +491,8 @@ type KnownChecksum interface {
//
// Result must remain identical across multiple invocations.
Checksum() Checksum
+
+ Artifact
}
// CuresExempt is optionally implemented for an artifact exempt to the
@@ -506,6 +510,12 @@ type CuresExempt interface {
// FileArtifact does not support fine-grained cancellation. Its context is
// inherited from the first [TrivialArtifact] or [FloodArtifact] that opens it.
type FileArtifact interface {
+ // IsExecutable returns whether the resulting filesystem entry should be made
+ // executable, if the [FileArtifact] is cured to the on-disk cache.
+ //
+ // Result must remain identical across multiple invocations.
+ IsExecutable() bool
+
// Cure returns [io.ReadCloser] of the full contents of [FileArtifact]. If
// [FileArtifact] implements [KnownChecksum], Cure is responsible for
// validating any data it produces and must return [ChecksumMismatchError]
@@ -515,7 +525,8 @@ type FileArtifact interface {
//
// Callers are responsible for closing the resulting [io.ReadCloser].
//
- // Result must remain identical across multiple invocations.
+ // The resulting [io.ReadCloser] across multiple invocations must have
+ // identical behaviour.
Cure(r *RContext) (io.ReadCloser, error)
Artifact
@@ -2208,12 +2219,17 @@ func (c *Cache) cure(a Artifact, curesExempt, rebuild bool) (
return
}
+ perm := os.FileMode(0400)
+ if f.IsExecutable() {
+ perm = 0500
+ }
+
work := c.base.Append(dirWork, ids)
var w *os.File
if w, err = os.OpenFile(
work.String(),
os.O_CREATE|os.O_EXCL|os.O_WRONLY,
- 0400,
+ perm,
); err != nil {
return
}
diff --git a/internal/pkg/pkg_test.go b/internal/pkg/pkg_test.go
index b9098c0c..1313d42c 100644
--- a/internal/pkg/pkg_test.go
+++ b/internal/pkg/pkg_test.go
@@ -170,6 +170,8 @@ type stubFile struct {
stubArtifact
}
+func (*stubFile) IsExecutable() bool { return false }
+
func (a *stubFile) Cure(*pkg.RContext) (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(a.data)), a.err
}
@@ -1510,14 +1512,14 @@ func TestCache(t *testing.T) {
"checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV": {Mode: 0400, Data: []byte("destroyed")},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/2vZYAE7AVme2W3EgcDP1QUfwrmgFWIswUQBd6ZIBtiCal-MLiCtAg940Jzed5w4_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
- "identifier/42lkARs2-xl_7wXfjPB0DaOXH8U2BKimaDYX-POJv5l01ArTKMbABt_kUMp24F1U": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")},
- "identifier/H5pfX9loHFLORmMrMFCDbVUGg0RtxUDj4GHs21Q6bD8PTiAaFVGLnTQlhbMC5Z0G": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
- "identifier/sZVN1nOxhdWvZnus3nPTe6q0SqqzObfNBsJNnVu6Co6ajKA9wCiUP33MOdaaEt7K": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")},
+ "identifier/CLyyxJWn1yUGFs_0KnJ4ZSU2hH4-elvGFRB36x14LrNrKBJPxvORhL9O4Rc7oeYU": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")},
+ "identifier/arv8a6z4i0x6pUb5Z2T-3iE4ETvyZj-uReHGKsS7uiBgMgKNEOzTGG3LIZb8vp0L": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/wILUy2izpj2sgKJVhGUGIAde1XVuqvp5BpFMIQHanT5Q8R6jK4QPVSrJsjZh-njV")},
+ "identifier/heRgWhNJmhm898V68kw0ta76t4rP1o29tYW_TrdyBWXGfo37K3QWA7Z-JkLXAwcn": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
+ "identifier/q-VoVBI3IjcWTqwIokpi3y05CP113CHgHslc-XWnhOvDULwzVc3q9Y5xOCm_YyLZ": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UjZSrgz7_B7XMd9fHU7jM33UZhWlFgX0rz7JZbCBYR28bCS7jr_CAJdcDhi52ruE")},
"status": {Mode: fs.ModeDir | 0700},
- "status/H5pfX9loHFLORmMrMFCDbVUGg0RtxUDj4GHs21Q6bD8PTiAaFVGLnTQlhbMC5Z0G": {Mode: fs.ModeSymlink | 0777, Data: []byte("dP-6_wIDRRouOaOF-nkBy-IaUbLdYHbOUTrptu7j_qfW01mnHSjYJ0oykUmvUd2x")},
- "status/2vZYAE7AVme2W3EgcDP1QUfwrmgFWIswUQBd6ZIBtiCal-MLiCtAg940Jzed5w4_": {Mode: 0400, Data: []byte(statusHeader + "destroyed")},
+ "status/q-VoVBI3IjcWTqwIokpi3y05CP113CHgHslc-XWnhOvDULwzVc3q9Y5xOCm_YyLZ": {Mode: fs.ModeSymlink | 0777, Data: []byte("dP-6_wIDRRouOaOF-nkBy-IaUbLdYHbOUTrptu7j_qfW01mnHSjYJ0oykUmvUd2x")},
+ "status/heRgWhNJmhm898V68kw0ta76t4rP1o29tYW_TrdyBWXGfo37K3QWA7Z-JkLXAwcn": {Mode: 0400, Data: []byte(statusHeader + "destroyed")},
"status/dP-6_wIDRRouOaOF-nkBy-IaUbLdYHbOUTrptu7j_qfW01mnHSjYJ0oykUmvUd2x": {Mode: 0400, Data: []byte(statusHeader + "destroyed")},
"substitute": {Mode: fs.ModeDir | 0700},
@@ -1613,10 +1615,10 @@ func TestCache(t *testing.T) {
"checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl/result": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent")},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/PITQy8S3pqQRTJOE9lPJVjUMri6jSuknElXX7SaUVtUTQqByg79dI-kpc6J2GNeY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
- "identifier/TI4SrHTEYRZ9yeiJzPqPULx4pl1hVSFgZ7DTweQSwk49k8lAIC1A5UrPs45P3Hi6": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
- "identifier/xCv2q_KJpNoTkXI9ua5UMAC1ndSrCeAhlQ9njUcwXCSd_qAnSXkgOy7uluHWWD3o": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
- "identifier/DyLrgassC_E06vPgPzQFuCOxwoccc08C04FSuI6aliWWXdJLWnJt9R9OXwoakhhm": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
+ "identifier/k8yyhlBV3JpYzzywAnrRWkfp2TmgIDNC6FqMFwrrR2x0C3unBLveDNKgigOjG9MM": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
+ "identifier/Xys8hPpJsKirZmTTuT7SwQ6661TCp_ZRu8iUjPPD7pC5E3ll7xw4zhNlZ2PVcz_-": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
+ "identifier/8sInO_dhPW9kweit7jVuH_vtMkRxlZT7XE4_yEKPZmpGkgVxkmxz0OpXgbMsH6ET": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
+ "identifier/ofaXbB5gIza_7W4Rzo5XDe_8FwlCPKWn3TBcmGgFAkkTWMl5GXGmOzX89Jd2Mk2A": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
"identifier/8VL-egZySw-RbnHZtxGWdf5Z2v9S9CihrdFwa54Bc5EYMQcEgWEem7QrdCTWlCb8": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"status": {Mode: fs.ModeDir | 0700},
@@ -1756,12 +1758,12 @@ func TestCache(t *testing.T) {
"checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl/result": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent")},
"identifier": {Mode: fs.ModeDir | 0700},
- "identifier/xCv2q_KJpNoTkXI9ua5UMAC1ndSrCeAhlQ9njUcwXCSd_qAnSXkgOy7uluHWWD3o": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
- "identifier/uaLFpraDJ8JOCFT94FhrVCNn9R2iXozSw3W4Rt_zdAKhlViF36CGjjwL6IJoe4fU": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
- "identifier/aCVm-QVKHmWPyHu02CjnJ5xXlIr78nNQRv1x9HhFRamQkaYesdubZFy-m7JxI38v": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
- "identifier/PITQy8S3pqQRTJOE9lPJVjUMri6jSuknElXX7SaUVtUTQqByg79dI-kpc6J2GNeY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
+ "identifier/3fYJjSiWhklt7TK9MPVLGk5_8AIU8ostTcFPXULJf_qeAVG6pmC-Cqqu4ZhEXRxn": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"identifier/8VL-egZySw-RbnHZtxGWdf5Z2v9S9CihrdFwa54Bc5EYMQcEgWEem7QrdCTWlCb8": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
- "identifier/TI4SrHTEYRZ9yeiJzPqPULx4pl1hVSFgZ7DTweQSwk49k8lAIC1A5UrPs45P3Hi6": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
+ "identifier/8sInO_dhPW9kweit7jVuH_vtMkRxlZT7XE4_yEKPZmpGkgVxkmxz0OpXgbMsH6ET": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
+ "identifier/Xys8hPpJsKirZmTTuT7SwQ6661TCp_ZRu8iUjPPD7pC5E3ll7xw4zhNlZ2PVcz_-": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
+ "identifier/k8yyhlBV3JpYzzywAnrRWkfp2TmgIDNC6FqMFwrrR2x0C3unBLveDNKgigOjG9MM": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
+ "identifier/mXlNz_v77nW7jstn_rJn4RL7dCQlkakM0y-HjO6tkDubtzaj-wCmW1hIr2Gk64_O": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fHkl_RuHOoc4rso__nV-qreikovd6Yhrq5mpBlkf5hmPGaxDlik2bYOQ4dhUQjtl")},
"status": {Mode: fs.ModeDir | 0700},
"status/8VL-egZySw-RbnHZtxGWdf5Z2v9S9CihrdFwa54Bc5EYMQcEgWEem7QrdCTWlCb8": {Mode: 0400, Data: []byte("\x00")},