OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / spf13 / afero / cacheOnReadFs.go
1 package afero
2
3 import (
4         "os"
5         "syscall"
6         "time"
7 )
8
9 // If the cache duration is 0, cache time will be unlimited, i.e. once
10 // a file is in the layer, the base will never be read again for this file.
11 //
12 // For cache times greater than 0, the modification time of a file is
13 // checked. Note that a lot of file system implementations only allow a
14 // resolution of a second for timestamps... or as the godoc for os.Chtimes()
15 // states: "The underlying filesystem may truncate or round the values to a
16 // less precise time unit."
17 //
18 // This caching union will forward all write calls also to the base file
19 // system first. To prevent writing to the base Fs, wrap it in a read-only
20 // filter - Note: this will also make the overlay read-only, for writing files
21 // in the overlay, use the overlay Fs directly, not via the union Fs.
22 type CacheOnReadFs struct {
23         base      Fs
24         layer     Fs
25         cacheTime time.Duration
26 }
27
28 func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
29         return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
30 }
31
32 type cacheState int
33
34 const (
35         // not present in the overlay, unknown if it exists in the base:
36         cacheMiss cacheState = iota
37         // present in the overlay and in base, base file is newer:
38         cacheStale
39         // present in the overlay - with cache time == 0 it may exist in the base,
40         // with cacheTime > 0 it exists in the base and is same age or newer in the
41         // overlay
42         cacheHit
43         // happens if someone writes directly to the overlay without
44         // going through this union
45         cacheLocal
46 )
47
48 func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {
49         var lfi, bfi os.FileInfo
50         lfi, err = u.layer.Stat(name)
51         if err == nil {
52                 if u.cacheTime == 0 {
53                         return cacheHit, lfi, nil
54                 }
55                 if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {
56                         bfi, err = u.base.Stat(name)
57                         if err != nil {
58                                 return cacheLocal, lfi, nil
59                         }
60                         if bfi.ModTime().After(lfi.ModTime()) {
61                                 return cacheStale, bfi, nil
62                         }
63                 }
64                 return cacheHit, lfi, nil
65         }
66
67         if err == syscall.ENOENT || os.IsNotExist(err) {
68                 return cacheMiss, nil, nil
69         }
70
71         return cacheMiss, nil, err
72 }
73
74 func (u *CacheOnReadFs) copyToLayer(name string) error {
75         return copyToLayer(u.base, u.layer, name)
76 }
77
78 func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
79         st, _, err := u.cacheStatus(name)
80         if err != nil {
81                 return err
82         }
83         switch st {
84         case cacheLocal:
85         case cacheHit:
86                 err = u.base.Chtimes(name, atime, mtime)
87         case cacheStale, cacheMiss:
88                 if err := u.copyToLayer(name); err != nil {
89                         return err
90                 }
91                 err = u.base.Chtimes(name, atime, mtime)
92         }
93         if err != nil {
94                 return err
95         }
96         return u.layer.Chtimes(name, atime, mtime)
97 }
98
99 func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
100         st, _, err := u.cacheStatus(name)
101         if err != nil {
102                 return err
103         }
104         switch st {
105         case cacheLocal:
106         case cacheHit:
107                 err = u.base.Chmod(name, mode)
108         case cacheStale, cacheMiss:
109                 if err := u.copyToLayer(name); err != nil {
110                         return err
111                 }
112                 err = u.base.Chmod(name, mode)
113         }
114         if err != nil {
115                 return err
116         }
117         return u.layer.Chmod(name, mode)
118 }
119
120 func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
121         st, fi, err := u.cacheStatus(name)
122         if err != nil {
123                 return nil, err
124         }
125         switch st {
126         case cacheMiss:
127                 return u.base.Stat(name)
128         default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
129                 return fi, nil
130         }
131 }
132
133 func (u *CacheOnReadFs) Rename(oldname, newname string) error {
134         st, _, err := u.cacheStatus(oldname)
135         if err != nil {
136                 return err
137         }
138         switch st {
139         case cacheLocal:
140         case cacheHit:
141                 err = u.base.Rename(oldname, newname)
142         case cacheStale, cacheMiss:
143                 if err := u.copyToLayer(oldname); err != nil {
144                         return err
145                 }
146                 err = u.base.Rename(oldname, newname)
147         }
148         if err != nil {
149                 return err
150         }
151         return u.layer.Rename(oldname, newname)
152 }
153
154 func (u *CacheOnReadFs) Remove(name string) error {
155         st, _, err := u.cacheStatus(name)
156         if err != nil {
157                 return err
158         }
159         switch st {
160         case cacheLocal:
161         case cacheHit, cacheStale, cacheMiss:
162                 err = u.base.Remove(name)
163         }
164         if err != nil {
165                 return err
166         }
167         return u.layer.Remove(name)
168 }
169
170 func (u *CacheOnReadFs) RemoveAll(name string) error {
171         st, _, err := u.cacheStatus(name)
172         if err != nil {
173                 return err
174         }
175         switch st {
176         case cacheLocal:
177         case cacheHit, cacheStale, cacheMiss:
178                 err = u.base.RemoveAll(name)
179         }
180         if err != nil {
181                 return err
182         }
183         return u.layer.RemoveAll(name)
184 }
185
186 func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
187         st, _, err := u.cacheStatus(name)
188         if err != nil {
189                 return nil, err
190         }
191         switch st {
192         case cacheLocal, cacheHit:
193         default:
194                 if err := u.copyToLayer(name); err != nil {
195                         return nil, err
196                 }
197         }
198         if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
199                 bfi, err := u.base.OpenFile(name, flag, perm)
200                 if err != nil {
201                         return nil, err
202                 }
203                 lfi, err := u.layer.OpenFile(name, flag, perm)
204                 if err != nil {
205                         bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
206                         return nil, err
207                 }
208                 return &UnionFile{base: bfi, layer: lfi}, nil
209         }
210         return u.layer.OpenFile(name, flag, perm)
211 }
212
213 func (u *CacheOnReadFs) Open(name string) (File, error) {
214         st, fi, err := u.cacheStatus(name)
215         if err != nil {
216                 return nil, err
217         }
218
219         switch st {
220         case cacheLocal:
221                 return u.layer.Open(name)
222
223         case cacheMiss:
224                 bfi, err := u.base.Stat(name)
225                 if err != nil {
226                         return nil, err
227                 }
228                 if bfi.IsDir() {
229                         return u.base.Open(name)
230                 }
231                 if err := u.copyToLayer(name); err != nil {
232                         return nil, err
233                 }
234                 return u.layer.Open(name)
235
236         case cacheStale:
237                 if !fi.IsDir() {
238                         if err := u.copyToLayer(name); err != nil {
239                                 return nil, err
240                         }
241                         return u.layer.Open(name)
242                 }
243         case cacheHit:
244                 if !fi.IsDir() {
245                         return u.layer.Open(name)
246                 }
247         }
248         // the dirs from cacheHit, cacheStale fall down here:
249         bfile, _ := u.base.Open(name)
250         lfile, err := u.layer.Open(name)
251         if err != nil && bfile == nil {
252                 return nil, err
253         }
254         return &UnionFile{base: bfile, layer: lfile}, nil
255 }
256
257 func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
258         err := u.base.Mkdir(name, perm)
259         if err != nil {
260                 return err
261         }
262         return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
263 }
264
265 func (u *CacheOnReadFs) Name() string {
266         return "CacheOnReadFs"
267 }
268
269 func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
270         err := u.base.MkdirAll(name, perm)
271         if err != nil {
272                 return err
273         }
274         return u.layer.MkdirAll(name, perm)
275 }
276
277 func (u *CacheOnReadFs) Create(name string) (File, error) {
278         bfh, err := u.base.Create(name)
279         if err != nil {
280                 return nil, err
281         }
282         lfh, err := u.layer.Create(name)
283         if err != nil {
284                 // oops, see comment about OS_TRUNC above, should we remove? then we have to
285                 // remember if the file did not exist before
286                 bfh.Close()
287                 return nil, err
288         }
289         return &UnionFile{base: bfh, layer: lfh}, nil
290 }