Files
at-container-registry/pkg/appview/overlay.go

92 lines
2.1 KiB
Go

package appview
import (
"io/fs"
"sort"
)
// overlayFS layers two fs.FS instances: upper wins, lower is fallback.
// ReadDir merges entries from both layers (upper entries replace lower).
type overlayFS struct {
upper fs.FS
lower fs.FS
}
// newOverlayFS creates a layered filesystem. Files in upper take priority over lower.
func newOverlayFS(upper, lower fs.FS) fs.FS {
return &overlayFS{upper: upper, lower: lower}
}
func (o *overlayFS) Open(name string) (fs.File, error) {
f, err := o.upper.Open(name)
if err == nil {
return f, nil
}
return o.lower.Open(name)
}
// ReadFile implements fs.ReadFileFS for efficient file reads.
func (o *overlayFS) ReadFile(name string) ([]byte, error) {
if rfs, ok := o.upper.(fs.ReadFileFS); ok {
data, err := rfs.ReadFile(name)
if err == nil {
return data, nil
}
}
if rfs, ok := o.lower.(fs.ReadFileFS); ok {
return rfs.ReadFile(name)
}
// Fallback: open and read
f, err := o.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
return nil, err
}
buf := make([]byte, stat.Size())
_, err = f.(interface{ Read([]byte) (int, error) }).Read(buf)
return buf, err
}
// ReadDir implements fs.ReadDirFS with merge semantics: entries from upper
// replace entries from lower with the same name.
func (o *overlayFS) ReadDir(name string) ([]fs.DirEntry, error) {
merged := make(map[string]fs.DirEntry)
// Read lower first
if rdfs, ok := o.lower.(fs.ReadDirFS); ok {
entries, err := rdfs.ReadDir(name)
if err == nil {
for _, e := range entries {
merged[e.Name()] = e
}
}
}
// Upper entries override lower
if rdfs, ok := o.upper.(fs.ReadDirFS); ok {
entries, err := rdfs.ReadDir(name)
if err == nil {
for _, e := range entries {
merged[e.Name()] = e
}
}
}
if len(merged) == 0 {
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrNotExist}
}
result := make([]fs.DirEntry, 0, len(merged))
for _, e := range merged {
result = append(result, e)
}
sort.Slice(result, func(i, j int) bool {
return result[i].Name() < result[j].Name()
})
return result, nil
}