]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/spf13/afero/unionFile.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / spf13 / afero / unionFile.go
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), os.SEEK_CUR); 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), os.SEEK_SET)
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 var 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
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.
160 func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
161 var merge DirsMerger = 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
190 if f.off >= len(f.files) {
191 return nil, io.EOF
192 }
193
194 if c == -1 {
195 return f.files[f.off:], nil
196 }
197
198 if c > len(f.files) {
199 c = len(f.files)
200 }
201
202 defer func() { f.off += c }()
203 return f.files[f.off: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 copyToLayer(base Fs, layer Fs, name string) error {
271 bfh, err := base.Open(name)
272 if err != nil {
273 return err
274 }
275 defer bfh.Close()
276
277 // First make sure the directory exists
278 exists, err := Exists(layer, filepath.Dir(name))
279 if err != nil {
280 return err
281 }
282 if !exists {
283 err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
284 if err != nil {
285 return err
286 }
287 }
288
289 // Create the file on the overlay
290 lfh, err := layer.Create(name)
291 if err != nil {
292 return err
293 }
294 n, err := io.Copy(lfh, bfh)
295 if err != nil {
296 // If anything fails, clean up the file
297 layer.Remove(name)
298 lfh.Close()
299 return err
300 }
301
302 bfi, err := bfh.Stat()
303 if err != nil || bfi.Size() != n {
304 layer.Remove(name)
305 lfh.Close()
306 return syscall.EIO
307 }
308
309 err = lfh.Close()
310 if err != nil {
311 layer.Remove(name)
312 lfh.Close()
313 return err
314 }
315 return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
316 }