Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / 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 }