]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/spf13/afero/cacheOnReadFs.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.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 }