diff options
Diffstat (limited to 'vendor/github.com/spf13/afero/unionFile.go')
-rw-r--r-- | vendor/github.com/spf13/afero/unionFile.go | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go new file mode 100644 index 00000000..99f9e5db --- /dev/null +++ b/vendor/github.com/spf13/afero/unionFile.go @@ -0,0 +1,274 @@ +package afero + +import ( + "io" + "os" + "path/filepath" + "syscall" +) + +// The UnionFile implements the afero.File interface and will be returned +// when reading a directory present at least in the overlay or opening a file +// for writing. +// +// The calls to +// Readdir() and Readdirnames() merge the file os.FileInfo / names from the +// base and the overlay - for files present in both layers, only those +// from the overlay will be used. +// +// When opening files for writing (Create() / OpenFile() with the right flags) +// the operations will be done in both layers, starting with the overlay. A +// successful read in the overlay will move the cursor position in the base layer +// by the number of bytes read. +type UnionFile struct { + base File + layer File + off int + files []os.FileInfo +} + +func (f *UnionFile) Close() error { + // first close base, so we have a newer timestamp in the overlay. If we'd close + // the overlay first, we'd get a cacheStale the next time we access this file + // -> cache would be useless ;-) + if f.base != nil { + f.base.Close() + } + if f.layer != nil { + return f.layer.Close() + } + return BADFD +} + +func (f *UnionFile) Read(s []byte) (int, error) { + if f.layer != nil { + n, err := f.layer.Read(s) + if (err == nil || err == io.EOF) && f.base != nil { + // advance the file position also in the base file, the next + // call may be a write at this position (or a seek with SEEK_CUR) + if _, seekErr := f.base.Seek(int64(n), os.SEEK_CUR); seekErr != nil { + // only overwrite err in case the seek fails: we need to + // report an eventual io.EOF to the caller + err = seekErr + } + } + return n, err + } + if f.base != nil { + return f.base.Read(s) + } + return 0, BADFD +} + +func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) { + if f.layer != nil { + n, err := f.layer.ReadAt(s, o) + if (err == nil || err == io.EOF) && f.base != nil { + _, err = f.base.Seek(o+int64(n), os.SEEK_SET) + } + return n, err + } + if f.base != nil { + return f.base.ReadAt(s, o) + } + return 0, BADFD +} + +func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) { + if f.layer != nil { + pos, err = f.layer.Seek(o, w) + if (err == nil || err == io.EOF) && f.base != nil { + _, err = f.base.Seek(o, w) + } + return pos, err + } + if f.base != nil { + return f.base.Seek(o, w) + } + return 0, BADFD +} + +func (f *UnionFile) Write(s []byte) (n int, err error) { + if f.layer != nil { + n, err = f.layer.Write(s) + if err == nil && f.base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark? + _, err = f.base.Write(s) + } + return n, err + } + if f.base != nil { + return f.base.Write(s) + } + return 0, BADFD +} + +func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) { + if f.layer != nil { + n, err = f.layer.WriteAt(s, o) + if err == nil && f.base != nil { + _, err = f.base.WriteAt(s, o) + } + return n, err + } + if f.base != nil { + return f.base.WriteAt(s, o) + } + return 0, BADFD +} + +func (f *UnionFile) Name() string { + if f.layer != nil { + return f.layer.Name() + } + return f.base.Name() +} + +// Readdir will weave the two directories together and +// return a single view of the overlayed directories +func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) { + if f.off == 0 { + var files = make(map[string]os.FileInfo) + var rfi []os.FileInfo + if f.layer != nil { + rfi, err = f.layer.Readdir(-1) + if err != nil { + return nil, err + } + for _, fi := range rfi { + files[fi.Name()] = fi + } + } + + if f.base != nil { + rfi, err = f.base.Readdir(-1) + if err != nil { + return nil, err + } + for _, fi := range rfi { + if _, exists := files[fi.Name()]; !exists { + files[fi.Name()] = fi + } + } + } + for _, fi := range files { + f.files = append(f.files, fi) + } + } + if c == -1 { + return f.files[f.off:], nil + } + defer func() { f.off += c }() + return f.files[f.off:c], nil +} + +func (f *UnionFile) Readdirnames(c int) ([]string, error) { + rfi, err := f.Readdir(c) + if err != nil { + return nil, err + } + var names []string + for _, fi := range rfi { + names = append(names, fi.Name()) + } + return names, nil +} + +func (f *UnionFile) Stat() (os.FileInfo, error) { + if f.layer != nil { + return f.layer.Stat() + } + if f.base != nil { + return f.base.Stat() + } + return nil, BADFD +} + +func (f *UnionFile) Sync() (err error) { + if f.layer != nil { + err = f.layer.Sync() + if err == nil && f.base != nil { + err = f.base.Sync() + } + return err + } + if f.base != nil { + return f.base.Sync() + } + return BADFD +} + +func (f *UnionFile) Truncate(s int64) (err error) { + if f.layer != nil { + err = f.layer.Truncate(s) + if err == nil && f.base != nil { + err = f.base.Truncate(s) + } + return err + } + if f.base != nil { + return f.base.Truncate(s) + } + return BADFD +} + +func (f *UnionFile) WriteString(s string) (n int, err error) { + if f.layer != nil { + n, err = f.layer.WriteString(s) + if err == nil && f.base != nil { + _, err = f.base.WriteString(s) + } + return n, err + } + if f.base != nil { + return f.base.WriteString(s) + } + return 0, BADFD +} + +func copyToLayer(base Fs, layer Fs, name string) error { + bfh, err := base.Open(name) + if err != nil { + return err + } + defer bfh.Close() + + // First make sure the directory exists + exists, err := Exists(layer, filepath.Dir(name)) + if err != nil { + return err + } + if !exists { + err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME? + if err != nil { + return err + } + } + + // Create the file on the overlay + lfh, err := layer.Create(name) + if err != nil { + return err + } + n, err := io.Copy(lfh, bfh) + if err != nil { + // If anything fails, clean up the file + layer.Remove(name) + lfh.Close() + return err + } + + bfi, err := bfh.Stat() + if err != nil || bfi.Size() != n { + layer.Remove(name) + lfh.Close() + return syscall.EIO + } + + err = lfh.Close() + if err != nil { + layer.Remove(name) + lfh.Close() + return err + } + return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime()) +} |