-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathoverlay_fs.go
More file actions
102 lines (85 loc) · 2.14 KB
/
overlay_fs.go
File metadata and controls
102 lines (85 loc) · 2.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package vuego
import (
"io/fs"
"sort"
)
// OverlayFS overlays two filesystems.
//
// This allows extension of the lower filesystem with modified files,
// new files and encourages composition of the contents of a `fs.FS`.
type OverlayFS struct {
chainFS []fs.FS
}
// NewOverlayFS will create a new *OverlayFS.
func NewOverlayFS(upper fs.FS, lower ...fs.FS) *OverlayFS {
chainFS := append([]fs.FS{upper}, lower...)
return &OverlayFS{
chainFS: chainFS,
}
}
// Open opens a file in the overlaid filesystem.
func (o *OverlayFS) Open(name string) (fs.File, error) {
for _, chainfs := range o.chainFS {
if chainfs == nil {
continue
}
f, err := chainfs.Open(name)
if err == nil {
return f, nil
}
}
return nil, fs.ErrNotExist
}
// ReadDir implements combined FS reading.
func (o *OverlayFS) ReadDir(name string) ([]fs.DirEntry, error) {
merged := make(map[string]fs.DirEntry)
var lastErr error
// Iterate through chain (upper layers first) so upper layers override lower
for _, chainfs := range o.chainFS {
if chainfs == nil {
continue
}
entries, err := fs.ReadDir(chainfs, name)
if err == nil {
for _, e := range entries {
// Only add if not already present (upper layers take precedence)
if _, exists := merged[e.Name()]; !exists {
merged[e.Name()] = e
}
}
} else {
lastErr = err
}
}
// If no filesystem had this directory, return error
if len(merged) == 0 && lastErr != nil {
return nil, lastErr
}
entries := make([]fs.DirEntry, 0, len(merged))
for _, e := range merged {
entries = append(entries, e)
}
sort.Slice(entries, func(i, j int) bool {
return entries[i].Name() < entries[j].Name()
})
return entries, nil
}
// Glob implements combined FS reading.
func (o *OverlayFS) Glob(pattern string) ([]string, error) {
matchMap := make(map[string]struct{})
for _, chainfs := range o.chainFS {
if chainfs == nil {
continue
}
matches, _ := fs.Glob(chainfs, pattern)
for _, m := range matches {
matchMap[m] = struct{}{}
}
}
results := make([]string, 0, len(matchMap))
for m := range matchMap {
results = append(results, m)
}
sort.Strings(results)
return results, nil
}