Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/spf13/afero/cacheOnReadFs.go
2875 views
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) copyFileToLayer(name string, flag int, perm os.FileMode) error {
79
return copyFileToLayer(u.base, u.layer, name, flag, perm)
80
}
81
82
func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
83
st, _, err := u.cacheStatus(name)
84
if err != nil {
85
return err
86
}
87
switch st {
88
case cacheLocal:
89
case cacheHit:
90
err = u.base.Chtimes(name, atime, mtime)
91
case cacheStale, cacheMiss:
92
if err := u.copyToLayer(name); err != nil {
93
return err
94
}
95
err = u.base.Chtimes(name, atime, mtime)
96
}
97
if err != nil {
98
return err
99
}
100
return u.layer.Chtimes(name, atime, mtime)
101
}
102
103
func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
104
st, _, err := u.cacheStatus(name)
105
if err != nil {
106
return err
107
}
108
switch st {
109
case cacheLocal:
110
case cacheHit:
111
err = u.base.Chmod(name, mode)
112
case cacheStale, cacheMiss:
113
if err := u.copyToLayer(name); err != nil {
114
return err
115
}
116
err = u.base.Chmod(name, mode)
117
}
118
if err != nil {
119
return err
120
}
121
return u.layer.Chmod(name, mode)
122
}
123
124
func (u *CacheOnReadFs) Chown(name string, uid, gid int) error {
125
st, _, err := u.cacheStatus(name)
126
if err != nil {
127
return err
128
}
129
switch st {
130
case cacheLocal:
131
case cacheHit:
132
err = u.base.Chown(name, uid, gid)
133
case cacheStale, cacheMiss:
134
if err := u.copyToLayer(name); err != nil {
135
return err
136
}
137
err = u.base.Chown(name, uid, gid)
138
}
139
if err != nil {
140
return err
141
}
142
return u.layer.Chown(name, uid, gid)
143
}
144
145
func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
146
st, fi, err := u.cacheStatus(name)
147
if err != nil {
148
return nil, err
149
}
150
switch st {
151
case cacheMiss:
152
return u.base.Stat(name)
153
default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
154
return fi, nil
155
}
156
}
157
158
func (u *CacheOnReadFs) Rename(oldname, newname string) error {
159
st, _, err := u.cacheStatus(oldname)
160
if err != nil {
161
return err
162
}
163
switch st {
164
case cacheLocal:
165
case cacheHit:
166
err = u.base.Rename(oldname, newname)
167
case cacheStale, cacheMiss:
168
if err := u.copyToLayer(oldname); err != nil {
169
return err
170
}
171
err = u.base.Rename(oldname, newname)
172
}
173
if err != nil {
174
return err
175
}
176
return u.layer.Rename(oldname, newname)
177
}
178
179
func (u *CacheOnReadFs) Remove(name string) error {
180
st, _, err := u.cacheStatus(name)
181
if err != nil {
182
return err
183
}
184
switch st {
185
case cacheLocal:
186
case cacheHit, cacheStale, cacheMiss:
187
err = u.base.Remove(name)
188
}
189
if err != nil {
190
return err
191
}
192
return u.layer.Remove(name)
193
}
194
195
func (u *CacheOnReadFs) RemoveAll(name string) error {
196
st, _, err := u.cacheStatus(name)
197
if err != nil {
198
return err
199
}
200
switch st {
201
case cacheLocal:
202
case cacheHit, cacheStale, cacheMiss:
203
err = u.base.RemoveAll(name)
204
}
205
if err != nil {
206
return err
207
}
208
return u.layer.RemoveAll(name)
209
}
210
211
func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
212
st, _, err := u.cacheStatus(name)
213
if err != nil {
214
return nil, err
215
}
216
switch st {
217
case cacheLocal, cacheHit:
218
default:
219
if err := u.copyFileToLayer(name, flag, perm); err != nil {
220
return nil, err
221
}
222
}
223
if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
224
bfi, err := u.base.OpenFile(name, flag, perm)
225
if err != nil {
226
return nil, err
227
}
228
lfi, err := u.layer.OpenFile(name, flag, perm)
229
if err != nil {
230
bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
231
return nil, err
232
}
233
return &UnionFile{Base: bfi, Layer: lfi}, nil
234
}
235
return u.layer.OpenFile(name, flag, perm)
236
}
237
238
func (u *CacheOnReadFs) Open(name string) (File, error) {
239
st, fi, err := u.cacheStatus(name)
240
if err != nil {
241
return nil, err
242
}
243
244
switch st {
245
case cacheLocal:
246
return u.layer.Open(name)
247
248
case cacheMiss:
249
bfi, err := u.base.Stat(name)
250
if err != nil {
251
return nil, err
252
}
253
if bfi.IsDir() {
254
return u.base.Open(name)
255
}
256
if err := u.copyToLayer(name); err != nil {
257
return nil, err
258
}
259
return u.layer.Open(name)
260
261
case cacheStale:
262
if !fi.IsDir() {
263
if err := u.copyToLayer(name); err != nil {
264
return nil, err
265
}
266
return u.layer.Open(name)
267
}
268
case cacheHit:
269
if !fi.IsDir() {
270
return u.layer.Open(name)
271
}
272
}
273
// the dirs from cacheHit, cacheStale fall down here:
274
bfile, _ := u.base.Open(name)
275
lfile, err := u.layer.Open(name)
276
if err != nil && bfile == nil {
277
return nil, err
278
}
279
return &UnionFile{Base: bfile, Layer: lfile}, nil
280
}
281
282
func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
283
err := u.base.Mkdir(name, perm)
284
if err != nil {
285
return err
286
}
287
return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
288
}
289
290
func (u *CacheOnReadFs) Name() string {
291
return "CacheOnReadFs"
292
}
293
294
func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
295
err := u.base.MkdirAll(name, perm)
296
if err != nil {
297
return err
298
}
299
return u.layer.MkdirAll(name, perm)
300
}
301
302
func (u *CacheOnReadFs) Create(name string) (File, error) {
303
bfh, err := u.base.Create(name)
304
if err != nil {
305
return nil, err
306
}
307
lfh, err := u.layer.Create(name)
308
if err != nil {
309
// oops, see comment about OS_TRUNC above, should we remove? then we have to
310
// remember if the file did not exist before
311
bfh.Close()
312
return nil, err
313
}
314
return &UnionFile{Base: bfh, Layer: lfh}, nil
315
}
316
317