aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-06-07 21:12:15 +0900
committerOphestra <cat@gensokyo.uk>2026-06-07 22:08:57 +0900
commit38450db74abdddfb3e366516f46e0e9c291002b1 (patch)
treef985cd99b1be1d094d7465c07132d32f2046a8eb
parent9344f694c7d8ec02f118bffa6f556f5b1cc7395c (diff)
internal/rosa: access backing storage through fs
This is more versatile than hardcoding the os.Root implementation. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--cmd/mbf/main.go2
-rw-r--r--internal/rosa/mirror.go22
-rw-r--r--internal/rosa/mirror_test.go77
3 files changed, 92 insertions, 9 deletions
diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go
index ae835500..8047e0c4 100644
--- a/cmd/mbf/main.go
+++ b/cmd/mbf/main.go
@@ -522,7 +522,7 @@ func main() {
if base, err := os.OpenRoot(cm.base); err != nil {
return err
} else {
- h = rosa.NewMirror(msg, base, key)
+ h = rosa.NewMirror(msg, base.FS(), key)
}
server := http.Server{Addr: args[0], Handler: h}
diff --git a/internal/rosa/mirror.go b/internal/rosa/mirror.go
index 3f7822e5..09419396 100644
--- a/internal/rosa/mirror.go
+++ b/internal/rosa/mirror.go
@@ -204,7 +204,7 @@ func (r Remote) Status(
// NewMirror returns an [http.Handler] for servicing mirror requests.
func NewMirror(
msg message.Msg,
- base *os.Root,
+ fsys fs.FS,
key ed25519.PrivateKey,
) http.Handler {
const identName = "ident"
@@ -224,7 +224,7 @@ func NewMirror(
}
ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
- if linkname, err := base.Readlink(filepath.Join(
+ if linkname, err := fs.ReadLink(fsys, filepath.Join(
"identifier",
ids,
)); err != nil {
@@ -239,7 +239,7 @@ func NewMirror(
(*pkg.Checksum)(buf[len(pkg.Checksum{}):]),
filepath.Base(linkname),
); err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
msg.Verbosef("serving artifact %s", ids)
@@ -276,7 +276,7 @@ func NewMirror(
checksums := pkg.Encode(buf)
rel := filepath.Join("checksum", checksums)
- if _, err := base.Lstat(rel); err != nil {
+ if _, err := fs.Lstat(fsys, rel); err != nil {
if errors.Is(err, os.ErrNotExist) {
w.WriteHeader(http.StatusNotFound)
return
@@ -287,7 +287,7 @@ func NewMirror(
}
msg.Verbosef("serving outcome %s", pkg.Encode(buf))
- fsys, err := fs.Sub(base.FS(), rel)
+ _fsys, err := fs.Sub(fsys, rel)
if err != nil {
msg.GetLogger().Println(err)
w.WriteHeader(http.StatusInternalServerError)
@@ -301,7 +301,7 @@ func NewMirror(
return
}
w.Header().Set("Content-Encoding", "gzip")
- if err = pkg.Write(fsys, ".", gw); err != nil {
+ if err = pkg.Write(_fsys, ".", gw); err != nil {
msg.Verbose(err)
}
if err = gw.Close(); err != nil {
@@ -323,7 +323,7 @@ func NewMirror(
}
ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
- f, err := base.Open(filepath.Join(
+ f, err := fsys.Open(filepath.Join(
"status",
ids,
))
@@ -336,6 +336,12 @@ func NewMirror(
w.WriteHeader(http.StatusInternalServerError)
return
}
+ s, ok := f.(io.Seeker)
+ if !ok {
+ msg.GetLogger().Println("backing filesystem does not support seek")
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
msg.Verbosef("serving status %s", ids)
h := sha512.New384()
@@ -348,7 +354,7 @@ func NewMirror(
if _, err = w.Write(append(ed25519.Sign(key, buf[:]), buf[:]...)); err != nil {
msg.Verbose(err)
return
- } else if _, err = f.Seek(0, io.SeekStart); err != nil {
+ } else if _, err = s.Seek(0, io.SeekStart); err != nil {
msg.GetLogger().Println(err)
return
} else if _, err = io.Copy(w, f); err != nil {
diff --git a/internal/rosa/mirror_test.go b/internal/rosa/mirror_test.go
new file mode 100644
index 00000000..a671cd31
--- /dev/null
+++ b/internal/rosa/mirror_test.go
@@ -0,0 +1,77 @@
+package rosa_test
+
+import (
+ "crypto/ed25519"
+ "io/fs"
+ "log"
+ "net/http/httptest"
+ "os"
+ "testing"
+ "testing/fstest"
+ "unique"
+
+ "hakurei.app/check"
+ "hakurei.app/fhs"
+ "hakurei.app/internal/pkg"
+ "hakurei.app/internal/rosa"
+ "hakurei.app/message"
+)
+
+func TestMirror(t *testing.T) {
+ t.Parallel()
+
+ pub, priv, err := ed25519.GenerateKey(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ base := check.MustAbs(t.TempDir())
+ msg := message.New(log.New(os.Stderr, "mirror: ", 0))
+ msg.SwapVerbose(testing.Verbose())
+
+ var c *pkg.Cache
+ c, err = pkg.Open(t.Context(), msg, 0, 0, 0, base)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(c.Close)
+
+ a := pkg.NewExec(
+ "", "",
+ nil, 0, false, false,
+ fhs.AbsRoot, nil, fhs.AbsRoot, nil,
+ )
+ id := c.Ident(a)
+ ids := pkg.Encode(id.Value())
+
+ var wantChecksum pkg.Checksum
+ if err = pkg.SumFS(&wantChecksum, fstest.MapFS{
+ ".": {Mode: os.ModeDir | 0500},
+ }, "."); err != nil {
+ t.Fatal(err)
+ }
+ wantChecksums := pkg.Encode(wantChecksum)
+
+ server := httptest.NewServer(rosa.NewMirror(msg, fstest.MapFS{
+ "identifier/" + ids: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantChecksums)},
+ "checksum/" + wantChecksums: {Mode: os.ModeDir | 0500},
+ }, priv))
+ t.Cleanup(server.Close)
+
+ var extern pkg.External
+ extern, err = rosa.NewRemote("http://example.com:80", pub, server.Client())
+ if err != nil {
+ t.Fatal(err)
+ }
+ c.SetExternal(extern)
+
+ var got unique.Handle[pkg.Checksum]
+ if _, got, err = c.Cure(a); err != nil {
+ t.Fatal(err)
+ } else if got.Value() != wantChecksum {
+ t.Logf(
+ "Cure: checksum = %s, want %s",
+ pkg.Encode(got.Value()), pkg.Encode(wantChecksum),
+ )
+ }
+}