OSDN Git Service

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