aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/spf13/afero/copyOnWriteFs.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/spf13/afero/copyOnWriteFs.go')
-rw-r--r--vendor/github.com/spf13/afero/copyOnWriteFs.go293
1 files changed, 293 insertions, 0 deletions
diff --git a/vendor/github.com/spf13/afero/copyOnWriteFs.go b/vendor/github.com/spf13/afero/copyOnWriteFs.go
new file mode 100644
index 0000000..e8108a8
--- /dev/null
+++ b/vendor/github.com/spf13/afero/copyOnWriteFs.go
@@ -0,0 +1,293 @@
1package afero
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "syscall"
8 "time"
9)
10
11var _ Lstater = (*CopyOnWriteFs)(nil)
12
13// The CopyOnWriteFs is a union filesystem: a read only base file system with
14// a possibly writeable layer on top. Changes to the file system will only
15// be made in the overlay: Changing an existing file in the base layer which
16// is not present in the overlay will copy the file to the overlay ("changing"
17// includes also calls to e.g. Chtimes() and Chmod()).
18//
19// Reading directories is currently only supported via Open(), not OpenFile().
20type CopyOnWriteFs struct {
21 base Fs
22 layer Fs
23}
24
25func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
26 return &CopyOnWriteFs{base: base, layer: layer}
27}
28
29// Returns true if the file is not in the overlay
30func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
31 if _, err := u.layer.Stat(name); err == nil {
32 return false, nil
33 }
34 _, err := u.base.Stat(name)
35 if err != nil {
36 if oerr, ok := err.(*os.PathError); ok {
37 if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
38 return false, nil
39 }
40 }
41 if err == syscall.ENOENT {
42 return false, nil
43 }
44 }
45 return true, err
46}
47
48func (u *CopyOnWriteFs) copyToLayer(name string) error {
49 return copyToLayer(u.base, u.layer, name)
50}
51
52func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
53 b, err := u.isBaseFile(name)
54 if err != nil {
55 return err
56 }
57 if b {
58 if err := u.copyToLayer(name); err != nil {
59 return err
60 }
61 }
62 return u.layer.Chtimes(name, atime, mtime)
63}
64
65func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
66 b, err := u.isBaseFile(name)
67 if err != nil {
68 return err
69 }
70 if b {
71 if err := u.copyToLayer(name); err != nil {
72 return err
73 }
74 }
75 return u.layer.Chmod(name, mode)
76}
77
78func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
79 fi, err := u.layer.Stat(name)
80 if err != nil {
81 isNotExist := u.isNotExist(err)
82 if isNotExist {
83 return u.base.Stat(name)
84 }
85 return nil, err
86 }
87 return fi, nil
88}
89
90func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
91 llayer, ok1 := u.layer.(Lstater)
92 lbase, ok2 := u.base.(Lstater)
93
94 if ok1 {
95 fi, b, err := llayer.LstatIfPossible(name)
96 if err == nil {
97 return fi, b, nil
98 }
99
100 if !u.isNotExist(err) {
101 return nil, b, err
102 }
103 }
104
105 if ok2 {
106 fi, b, err := lbase.LstatIfPossible(name)
107 if err == nil {
108 return fi, b, nil
109 }
110 if !u.isNotExist(err) {
111 return nil, b, err
112 }
113 }
114
115 fi, err := u.Stat(name)
116
117 return fi, false, err
118}
119
120func (u *CopyOnWriteFs) isNotExist(err error) bool {
121 if e, ok := err.(*os.PathError); ok {
122 err = e.Err
123 }
124 if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
125 return true
126 }
127 return false
128}
129
130// Renaming files present only in the base layer is not permitted
131func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
132 b, err := u.isBaseFile(oldname)
133 if err != nil {
134 return err
135 }
136 if b {
137 return syscall.EPERM
138 }
139 return u.layer.Rename(oldname, newname)
140}
141
142// Removing files present only in the base layer is not permitted. If
143// a file is present in the base layer and the overlay, only the overlay
144// will be removed.
145func (u *CopyOnWriteFs) Remove(name string) error {
146 err := u.layer.Remove(name)
147 switch err {
148 case syscall.ENOENT:
149 _, err = u.base.Stat(name)
150 if err == nil {
151 return syscall.EPERM
152 }
153 return syscall.ENOENT
154 default:
155 return err
156 }
157}
158
159func (u *CopyOnWriteFs) RemoveAll(name string) error {
160 err := u.layer.RemoveAll(name)
161 switch err {
162 case syscall.ENOENT:
163 _, err = u.base.Stat(name)
164 if err == nil {
165 return syscall.EPERM
166 }
167 return syscall.ENOENT
168 default:
169 return err
170 }
171}
172
173func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
174 b, err := u.isBaseFile(name)
175 if err != nil {
176 return nil, err
177 }
178
179 if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
180 if b {
181 if err = u.copyToLayer(name); err != nil {
182 return nil, err
183 }
184 return u.layer.OpenFile(name, flag, perm)
185 }
186
187 dir := filepath.Dir(name)
188 isaDir, err := IsDir(u.base, dir)
189 if err != nil && !os.IsNotExist(err) {
190 return nil, err
191 }
192 if isaDir {
193 if err = u.layer.MkdirAll(dir, 0777); err != nil {
194 return nil, err
195 }
196 return u.layer.OpenFile(name, flag, perm)
197 }
198
199 isaDir, err = IsDir(u.layer, dir)
200 if err != nil {
201 return nil, err
202 }
203 if isaDir {
204 return u.layer.OpenFile(name, flag, perm)
205 }
206
207 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
208 }
209 if b {
210 return u.base.OpenFile(name, flag, perm)
211 }
212 return u.layer.OpenFile(name, flag, perm)
213}
214
215// This function handles the 9 different possibilities caused
216// by the union which are the intersection of the following...
217// layer: doesn't exist, exists as a file, and exists as a directory
218// base: doesn't exist, exists as a file, and exists as a directory
219func (u *CopyOnWriteFs) Open(name string) (File, error) {
220 // Since the overlay overrides the base we check that first
221 b, err := u.isBaseFile(name)
222 if err != nil {
223 return nil, err
224 }
225
226 // If overlay doesn't exist, return the base (base state irrelevant)
227 if b {
228 return u.base.Open(name)
229 }
230
231 // If overlay is a file, return it (base state irrelevant)
232 dir, err := IsDir(u.layer, name)
233 if err != nil {
234 return nil, err
235 }
236 if !dir {
237 return u.layer.Open(name)
238 }
239
240 // Overlay is a directory, base state now matters.
241 // Base state has 3 states to check but 2 outcomes:
242 // A. It's a file or non-readable in the base (return just the overlay)
243 // B. It's an accessible directory in the base (return a UnionFile)
244
245 // If base is file or nonreadable, return overlay
246 dir, err = IsDir(u.base, name)
247 if !dir || err != nil {
248 return u.layer.Open(name)
249 }
250
251 // Both base & layer are directories
252 // Return union file (if opens are without error)
253 bfile, bErr := u.base.Open(name)
254 lfile, lErr := u.layer.Open(name)
255
256 // If either have errors at this point something is very wrong. Return nil and the errors
257 if bErr != nil || lErr != nil {
258 return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
259 }
260
261 return &UnionFile{Base: bfile, Layer: lfile}, nil
262}
263
264func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
265 dir, err := IsDir(u.base, name)
266 if err != nil {
267 return u.layer.MkdirAll(name, perm)
268 }
269 if dir {
270 return ErrFileExists
271 }
272 return u.layer.MkdirAll(name, perm)
273}
274
275func (u *CopyOnWriteFs) Name() string {
276 return "CopyOnWriteFs"
277}
278
279func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
280 dir, err := IsDir(u.base, name)
281 if err != nil {
282 return u.layer.MkdirAll(name, perm)
283 }
284 if dir {
285 // This is in line with how os.MkdirAll behaves.
286 return nil
287 }
288 return u.layer.MkdirAll(name, perm)
289}
290
291func (u *CopyOnWriteFs) Create(name string) (File, error) {
292 return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
293}