Source file
src/io/fs/sub.go
1
2
3
4
5 package fs
6
7 import (
8 "errors"
9 "path"
10 )
11
12
13 type SubFS interface {
14 FS
15
16
17 Sub(dir string) (FS, error)
18 }
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 func Sub(fsys FS, dir string) (FS, error) {
36 if !ValidPath(dir) {
37 return nil, &PathError{Op: "sub", Path: dir, Err: ErrInvalid}
38 }
39 if dir == "." {
40 return fsys, nil
41 }
42 if fsys, ok := fsys.(SubFS); ok {
43 return fsys.Sub(dir)
44 }
45 return &subFS{fsys, dir}, nil
46 }
47
48 var _ FS = (*subFS)(nil)
49 var _ ReadDirFS = (*subFS)(nil)
50 var _ ReadFileFS = (*subFS)(nil)
51 var _ ReadLinkFS = (*subFS)(nil)
52 var _ GlobFS = (*subFS)(nil)
53
54 type subFS struct {
55 fsys FS
56 dir string
57 }
58
59
60 func (f *subFS) fullName(op string, name string) (string, error) {
61 if !ValidPath(name) {
62 return "", &PathError{Op: op, Path: name, Err: ErrInvalid}
63 }
64 return path.Join(f.dir, name), nil
65 }
66
67
68 func (f *subFS) shorten(name string) (rel string, ok bool) {
69 if name == f.dir {
70 return ".", true
71 }
72 if len(name) >= len(f.dir)+2 && name[len(f.dir)] == '/' && name[:len(f.dir)] == f.dir {
73 return name[len(f.dir)+1:], true
74 }
75 return "", false
76 }
77
78
79 func (f *subFS) fixErr(err error) error {
80 if e, ok := err.(*PathError); ok {
81 if short, ok := f.shorten(e.Path); ok {
82 e.Path = short
83 }
84 }
85 return err
86 }
87
88 func (f *subFS) Open(name string) (File, error) {
89 full, err := f.fullName("open", name)
90 if err != nil {
91 return nil, err
92 }
93 file, err := f.fsys.Open(full)
94 return file, f.fixErr(err)
95 }
96
97 func (f *subFS) ReadDir(name string) ([]DirEntry, error) {
98 full, err := f.fullName("read", name)
99 if err != nil {
100 return nil, err
101 }
102 dir, err := ReadDir(f.fsys, full)
103 return dir, f.fixErr(err)
104 }
105
106 func (f *subFS) ReadFile(name string) ([]byte, error) {
107 full, err := f.fullName("read", name)
108 if err != nil {
109 return nil, err
110 }
111 data, err := ReadFile(f.fsys, full)
112 return data, f.fixErr(err)
113 }
114
115 func (f *subFS) ReadLink(name string) (string, error) {
116 full, err := f.fullName("readlink", name)
117 if err != nil {
118 return "", err
119 }
120 target, err := ReadLink(f.fsys, full)
121 if err != nil {
122 return "", f.fixErr(err)
123 }
124 return target, nil
125 }
126
127 func (f *subFS) Lstat(name string) (FileInfo, error) {
128 full, err := f.fullName("lstat", name)
129 if err != nil {
130 return nil, err
131 }
132 info, err := Lstat(f.fsys, full)
133 if err != nil {
134 return nil, f.fixErr(err)
135 }
136 return info, nil
137 }
138
139 func (f *subFS) Glob(pattern string) ([]string, error) {
140
141 if _, err := path.Match(pattern, ""); err != nil {
142 return nil, err
143 }
144 if pattern == "." {
145 return []string{"."}, nil
146 }
147
148 full := f.dir + "/" + pattern
149 list, err := Glob(f.fsys, full)
150 for i, name := range list {
151 name, ok := f.shorten(name)
152 if !ok {
153 return nil, errors.New("invalid result from inner fsys Glob: " + name + " not in " + f.dir)
154 }
155 list[i] = name
156 }
157 return list, f.fixErr(err)
158 }
159
160 func (f *subFS) Sub(dir string) (FS, error) {
161 if dir == "." {
162 return f, nil
163 }
164 full, err := f.fullName("sub", dir)
165 if err != nil {
166 return nil, err
167 }
168 return &subFS{f.fsys, full}, nil
169 }
170
View as plain text