podman

Форк
0
315 строк · 7.3 Кб
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

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.