From 38450db74abdddfb3e366516f46e0e9c291002b1 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 7 Jun 2026 21:12:15 +0900 Subject: [PATCH] internal/rosa: access backing storage through fs This is more versatile than hardcoding the os.Root implementation. Signed-off-by: Ophestra --- cmd/mbf/main.go | 2 +- internal/rosa/mirror.go | 22 +++++++---- internal/rosa/mirror_test.go | 77 ++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 internal/rosa/mirror_test.go 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), + ) + } +}