podman

Форк
0
330 строк · 6.9 Кб
1
package afero
2

3
import (
4
	"io"
5
	"os"
6
	"path/filepath"
7
	"syscall"
8
)
9

10
// The UnionFile implements the afero.File interface and will be returned
11
// when reading a directory present at least in the overlay or opening a file
12
// for writing.
13
//
14
// The calls to
15
// Readdir() and Readdirnames() merge the file os.FileInfo / names from the
16
// base and the overlay - for files present in both layers, only those
17
// from the overlay will be used.
18
//
19
// When opening files for writing (Create() / OpenFile() with the right flags)
20
// the operations will be done in both layers, starting with the overlay. A
21
// successful read in the overlay will move the cursor position in the base layer
22
// by the number of bytes read.
23
type UnionFile struct {
24
	Base   File
25
	Layer  File
26
	Merger DirsMerger
27
	off    int
28
	files  []os.FileInfo
29
}
30

31
func (f *UnionFile) Close() error {
32
	// first close base, so we have a newer timestamp in the overlay. If we'd close
33
	// the overlay first, we'd get a cacheStale the next time we access this file
34
	// -> cache would be useless ;-)
35
	if f.Base != nil {
36
		f.Base.Close()
37
	}
38
	if f.Layer != nil {
39
		return f.Layer.Close()
40
	}
41
	return BADFD
42
}
43

44
func (f *UnionFile) Read(s []byte) (int, error) {
45
	if f.Layer != nil {
46
		n, err := f.Layer.Read(s)
47
		if (err == nil || err == io.EOF) && f.Base != nil {
48
			// advance the file position also in the base file, the next
49
			// call may be a write at this position (or a seek with SEEK_CUR)
50
			if _, seekErr := f.Base.Seek(int64(n), io.SeekCurrent); seekErr != nil {
51
				// only overwrite err in case the seek fails: we need to
52
				// report an eventual io.EOF to the caller
53
				err = seekErr
54
			}
55
		}
56
		return n, err
57
	}
58
	if f.Base != nil {
59
		return f.Base.Read(s)
60
	}
61
	return 0, BADFD
62
}
63

64
func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
65
	if f.Layer != nil {
66
		n, err := f.Layer.ReadAt(s, o)
67
		if (err == nil || err == io.EOF) && f.Base != nil {
68
			_, err = f.Base.Seek(o+int64(n), io.SeekStart)
69
		}
70
		return n, err
71
	}
72
	if f.Base != nil {
73
		return f.Base.ReadAt(s, o)
74
	}
75
	return 0, BADFD
76
}
77

78
func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
79
	if f.Layer != nil {
80
		pos, err = f.Layer.Seek(o, w)
81
		if (err == nil || err == io.EOF) && f.Base != nil {
82
			_, err = f.Base.Seek(o, w)
83
		}
84
		return pos, err
85
	}
86
	if f.Base != nil {
87
		return f.Base.Seek(o, w)
88
	}
89
	return 0, BADFD
90
}
91

92
func (f *UnionFile) Write(s []byte) (n int, err error) {
93
	if f.Layer != nil {
94
		n, err = f.Layer.Write(s)
95
		if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
96
			_, err = f.Base.Write(s)
97
		}
98
		return n, err
99
	}
100
	if f.Base != nil {
101
		return f.Base.Write(s)
102
	}
103
	return 0, BADFD
104
}
105

106
func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
107
	if f.Layer != nil {
108
		n, err = f.Layer.WriteAt(s, o)
109
		if err == nil && f.Base != nil {
110
			_, err = f.Base.WriteAt(s, o)
111
		}
112
		return n, err
113
	}
114
	if f.Base != nil {
115
		return f.Base.WriteAt(s, o)
116
	}
117
	return 0, BADFD
118
}
119

120
func (f *UnionFile) Name() string {
121
	if f.Layer != nil {
122
		return f.Layer.Name()
123
	}
124
	return f.Base.Name()
125
}
126

127
// DirsMerger is how UnionFile weaves two directories together.
128
// It takes the FileInfo slices from the layer and the base and returns a
129
// single view.
130
type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)
131

132
var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
133
	files := make(map[string]os.FileInfo)
134

135
	for _, fi := range lofi {
136
		files[fi.Name()] = fi
137
	}
138

139
	for _, fi := range bofi {
140
		if _, exists := files[fi.Name()]; !exists {
141
			files[fi.Name()] = fi
142
		}
143
	}
144

145
	rfi := make([]os.FileInfo, len(files))
146

147
	i := 0
148
	for _, fi := range files {
149
		rfi[i] = fi
150
		i++
151
	}
152

153
	return rfi, nil
154
}
155

156
// Readdir will weave the two directories together and
157
// return a single view of the overlayed directories.
158
// At the end of the directory view, the error is io.EOF if c > 0.
159
func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
160
	var merge DirsMerger = f.Merger
161
	if merge == nil {
162
		merge = defaultUnionMergeDirsFn
163
	}
164

165
	if f.off == 0 {
166
		var lfi []os.FileInfo
167
		if f.Layer != nil {
168
			lfi, err = f.Layer.Readdir(-1)
169
			if err != nil {
170
				return nil, err
171
			}
172
		}
173

174
		var bfi []os.FileInfo
175
		if f.Base != nil {
176
			bfi, err = f.Base.Readdir(-1)
177
			if err != nil {
178
				return nil, err
179
			}
180

181
		}
182
		merged, err := merge(lfi, bfi)
183
		if err != nil {
184
			return nil, err
185
		}
186
		f.files = append(f.files, merged...)
187
	}
188
	files := f.files[f.off:]
189

190
	if c <= 0 {
191
		return files, nil
192
	}
193

194
	if len(files) == 0 {
195
		return nil, io.EOF
196
	}
197

198
	if c > len(files) {
199
		c = len(files)
200
	}
201

202
	defer func() { f.off += c }()
203
	return files[:c], nil
204
}
205

206
func (f *UnionFile) Readdirnames(c int) ([]string, error) {
207
	rfi, err := f.Readdir(c)
208
	if err != nil {
209
		return nil, err
210
	}
211
	var names []string
212
	for _, fi := range rfi {
213
		names = append(names, fi.Name())
214
	}
215
	return names, nil
216
}
217

218
func (f *UnionFile) Stat() (os.FileInfo, error) {
219
	if f.Layer != nil {
220
		return f.Layer.Stat()
221
	}
222
	if f.Base != nil {
223
		return f.Base.Stat()
224
	}
225
	return nil, BADFD
226
}
227

228
func (f *UnionFile) Sync() (err error) {
229
	if f.Layer != nil {
230
		err = f.Layer.Sync()
231
		if err == nil && f.Base != nil {
232
			err = f.Base.Sync()
233
		}
234
		return err
235
	}
236
	if f.Base != nil {
237
		return f.Base.Sync()
238
	}
239
	return BADFD
240
}
241

242
func (f *UnionFile) Truncate(s int64) (err error) {
243
	if f.Layer != nil {
244
		err = f.Layer.Truncate(s)
245
		if err == nil && f.Base != nil {
246
			err = f.Base.Truncate(s)
247
		}
248
		return err
249
	}
250
	if f.Base != nil {
251
		return f.Base.Truncate(s)
252
	}
253
	return BADFD
254
}
255

256
func (f *UnionFile) WriteString(s string) (n int, err error) {
257
	if f.Layer != nil {
258
		n, err = f.Layer.WriteString(s)
259
		if err == nil && f.Base != nil {
260
			_, err = f.Base.WriteString(s)
261
		}
262
		return n, err
263
	}
264
	if f.Base != nil {
265
		return f.Base.WriteString(s)
266
	}
267
	return 0, BADFD
268
}
269

270
func copyFile(base Fs, layer Fs, name string, bfh File) error {
271
	// First make sure the directory exists
272
	exists, err := Exists(layer, filepath.Dir(name))
273
	if err != nil {
274
		return err
275
	}
276
	if !exists {
277
		err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME?
278
		if err != nil {
279
			return err
280
		}
281
	}
282

283
	// Create the file on the overlay
284
	lfh, err := layer.Create(name)
285
	if err != nil {
286
		return err
287
	}
288
	n, err := io.Copy(lfh, bfh)
289
	if err != nil {
290
		// If anything fails, clean up the file
291
		layer.Remove(name)
292
		lfh.Close()
293
		return err
294
	}
295

296
	bfi, err := bfh.Stat()
297
	if err != nil || bfi.Size() != n {
298
		layer.Remove(name)
299
		lfh.Close()
300
		return syscall.EIO
301
	}
302

303
	err = lfh.Close()
304
	if err != nil {
305
		layer.Remove(name)
306
		lfh.Close()
307
		return err
308
	}
309
	return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
310
}
311

312
func copyToLayer(base Fs, layer Fs, name string) error {
313
	bfh, err := base.Open(name)
314
	if err != nil {
315
		return err
316
	}
317
	defer bfh.Close()
318

319
	return copyFile(base, layer, name, bfh)
320
}
321

322
func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error {
323
	bfh, err := base.OpenFile(name, flag, perm)
324
	if err != nil {
325
		return err
326
	}
327
	defer bfh.Close()
328

329
	return copyFile(base, layer, name, bfh)
330
}
331

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

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

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

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