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