]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/spf13/afero/copyOnWriteFs.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / spf13 / afero / copyOnWriteFs.go
1 package afero
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "syscall"
8 "time"
9 )
10
11 var _ 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().
20 type CopyOnWriteFs struct {
21 base Fs
22 layer Fs
23 }
24
25 func 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
30 func (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
48 func (u *CopyOnWriteFs) copyToLayer(name string) error {
49 return copyToLayer(u.base, u.layer, name)
50 }
51
52 func (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
65 func (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
78 func (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
90 func (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
120 func (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
131 func (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.
145 func (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
159 func (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
173 func (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
219 func (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
264 func (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
275 func (u *CopyOnWriteFs) Name() string {
276 return "CopyOnWriteFs"
277 }
278
279 func (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
291 func (u *CopyOnWriteFs) Create(name string) (File, error) {
292 return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
293 }