Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/spf13/afero/unionFile.go
2875 views
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 &&
96
f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
97
_, err = f.Base.Write(s)
98
}
99
return n, err
100
}
101
if f.Base != nil {
102
return f.Base.Write(s)
103
}
104
return 0, BADFD
105
}
106
107
func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
108
if f.Layer != nil {
109
n, err = f.Layer.WriteAt(s, o)
110
if err == nil && f.Base != nil {
111
_, err = f.Base.WriteAt(s, o)
112
}
113
return n, err
114
}
115
if f.Base != nil {
116
return f.Base.WriteAt(s, o)
117
}
118
return 0, BADFD
119
}
120
121
func (f *UnionFile) Name() string {
122
if f.Layer != nil {
123
return f.Layer.Name()
124
}
125
return f.Base.Name()
126
}
127
128
// DirsMerger is how UnionFile weaves two directories together.
129
// It takes the FileInfo slices from the layer and the base and returns a
130
// single view.
131
type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)
132
133
var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
134
files := make(map[string]os.FileInfo)
135
136
for _, fi := range lofi {
137
files[fi.Name()] = fi
138
}
139
140
for _, fi := range bofi {
141
if _, exists := files[fi.Name()]; !exists {
142
files[fi.Name()] = fi
143
}
144
}
145
146
rfi := make([]os.FileInfo, len(files))
147
148
i := 0
149
for _, fi := range files {
150
rfi[i] = fi
151
i++
152
}
153
154
return rfi, nil
155
}
156
157
// Readdir will weave the two directories together and
158
// return a single view of the overlayed directories.
159
// At the end of the directory view, the error is io.EOF if c > 0.
160
func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
161
merge := f.Merger
162
if merge == nil {
163
merge = defaultUnionMergeDirsFn
164
}
165
166
if f.off == 0 {
167
var lfi []os.FileInfo
168
if f.Layer != nil {
169
lfi, err = f.Layer.Readdir(-1)
170
if err != nil {
171
return nil, err
172
}
173
}
174
175
var bfi []os.FileInfo
176
if f.Base != nil {
177
bfi, err = f.Base.Readdir(-1)
178
if err != nil {
179
return nil, err
180
}
181
182
}
183
merged, err := merge(lfi, bfi)
184
if err != nil {
185
return nil, err
186
}
187
f.files = append(f.files, merged...)
188
}
189
files := f.files[f.off:]
190
191
if c <= 0 {
192
return files, nil
193
}
194
195
if len(files) == 0 {
196
return nil, io.EOF
197
}
198
199
if c > len(files) {
200
c = len(files)
201
}
202
203
defer func() { f.off += c }()
204
return files[:c], nil
205
}
206
207
func (f *UnionFile) Readdirnames(c int) ([]string, error) {
208
rfi, err := f.Readdir(c)
209
if err != nil {
210
return nil, err
211
}
212
var names []string
213
for _, fi := range rfi {
214
names = append(names, fi.Name())
215
}
216
return names, nil
217
}
218
219
func (f *UnionFile) Stat() (os.FileInfo, error) {
220
if f.Layer != nil {
221
return f.Layer.Stat()
222
}
223
if f.Base != nil {
224
return f.Base.Stat()
225
}
226
return nil, BADFD
227
}
228
229
func (f *UnionFile) Sync() (err error) {
230
if f.Layer != nil {
231
err = f.Layer.Sync()
232
if err == nil && f.Base != nil {
233
err = f.Base.Sync()
234
}
235
return err
236
}
237
if f.Base != nil {
238
return f.Base.Sync()
239
}
240
return BADFD
241
}
242
243
func (f *UnionFile) Truncate(s int64) (err error) {
244
if f.Layer != nil {
245
err = f.Layer.Truncate(s)
246
if err == nil && f.Base != nil {
247
err = f.Base.Truncate(s)
248
}
249
return err
250
}
251
if f.Base != nil {
252
return f.Base.Truncate(s)
253
}
254
return BADFD
255
}
256
257
func (f *UnionFile) WriteString(s string) (n int, err error) {
258
if f.Layer != nil {
259
n, err = f.Layer.WriteString(s)
260
if err == nil && f.Base != nil {
261
_, err = f.Base.WriteString(s)
262
}
263
return n, err
264
}
265
if f.Base != nil {
266
return f.Base.WriteString(s)
267
}
268
return 0, BADFD
269
}
270
271
func copyFile(base Fs, layer Fs, name string, bfh File) error {
272
// First make sure the directory exists
273
exists, err := Exists(layer, filepath.Dir(name))
274
if err != nil {
275
return err
276
}
277
if !exists {
278
err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME?
279
if err != nil {
280
return err
281
}
282
}
283
284
// Create the file on the overlay
285
lfh, err := layer.Create(name)
286
if err != nil {
287
return err
288
}
289
n, err := io.Copy(lfh, bfh)
290
if err != nil {
291
// If anything fails, clean up the file
292
layer.Remove(name)
293
lfh.Close()
294
return err
295
}
296
297
bfi, err := bfh.Stat()
298
if err != nil || bfi.Size() != n {
299
layer.Remove(name)
300
lfh.Close()
301
return syscall.EIO
302
}
303
304
err = lfh.Close()
305
if err != nil {
306
layer.Remove(name)
307
lfh.Close()
308
return err
309
}
310
return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
311
}
312
313
func copyToLayer(base Fs, layer Fs, name string) error {
314
bfh, err := base.Open(name)
315
if err != nil {
316
return err
317
}
318
defer bfh.Close()
319
320
return copyFile(base, layer, name, bfh)
321
}
322
323
func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error {
324
bfh, err := base.OpenFile(name, flag, perm)
325
if err != nil {
326
return err
327
}
328
defer bfh.Close()
329
330
return copyFile(base, layer, name, bfh)
331
}
332
333