...
1
2 package union
3
4 import (
5 "fmt"
6 "io"
7 "net/http"
8 "os"
9 "strings"
10 "time"
11 )
12
13
14
15
16 func New(mapping map[string]http.FileSystem) http.FileSystem {
17 u := &unionFS{
18 ns: make(map[string]http.FileSystem),
19 root: &dirInfo{
20 name: "/",
21 },
22 }
23 for mountPoint, fs := range mapping {
24 u.bind(mountPoint, fs)
25 }
26 return u
27 }
28
29 type unionFS struct {
30 ns map[string]http.FileSystem
31 root *dirInfo
32 }
33
34
35
36 func (u *unionFS) bind(mountPoint string, fs http.FileSystem) {
37 u.ns[mountPoint] = fs
38 u.root.entries = append(u.root.entries, &dirInfo{
39 name: mountPoint[1:],
40 })
41 }
42
43
44 func (u *unionFS) Open(path string) (http.File, error) {
45
46 if path == "/" {
47 return &dir{
48 dirInfo: u.root,
49 }, nil
50 }
51 for prefix, fs := range u.ns {
52 if path == prefix || strings.HasPrefix(path, prefix+"/") {
53 innerPath := path[len(prefix):]
54 if innerPath == "" {
55 innerPath = "/"
56 }
57 return fs.Open(innerPath)
58 }
59 }
60 return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
61 }
62
63
64 type dirInfo struct {
65 name string
66 entries []os.FileInfo
67 }
68
69 func (d *dirInfo) Read([]byte) (int, error) {
70 return 0, fmt.Errorf("cannot Read from directory %s", d.name)
71 }
72 func (d *dirInfo) Close() error { return nil }
73 func (d *dirInfo) Stat() (os.FileInfo, error) { return d, nil }
74
75 func (d *dirInfo) Name() string { return d.name }
76 func (d *dirInfo) Size() int64 { return 0 }
77 func (d *dirInfo) Mode() os.FileMode { return 0755 | os.ModeDir }
78 func (d *dirInfo) ModTime() time.Time { return time.Time{} }
79 func (d *dirInfo) IsDir() bool { return true }
80 func (d *dirInfo) Sys() interface{} { return nil }
81
82
83 type dir struct {
84 *dirInfo
85 pos int
86 }
87
88 func (d *dir) Seek(offset int64, whence int) (int64, error) {
89 if offset == 0 && whence == io.SeekStart {
90 d.pos = 0
91 return 0, nil
92 }
93 return 0, fmt.Errorf("unsupported Seek in directory %s", d.dirInfo.name)
94 }
95
96 func (d *dir) Readdir(count int) ([]os.FileInfo, error) {
97 if d.pos >= len(d.dirInfo.entries) && count > 0 {
98 return nil, io.EOF
99 }
100 if count <= 0 || count > len(d.dirInfo.entries)-d.pos {
101 count = len(d.dirInfo.entries) - d.pos
102 }
103 e := d.dirInfo.entries[d.pos : d.pos+count]
104 d.pos += count
105 return e, nil
106 }
107
View as plain text