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
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.
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 {
30 func (f *UnionFile) Close() error {
31 // first close base, so we have a newer timestamp in the overlay. If we'd close
32 // the overlay first, we'd get a cacheStale the next time we access this file
33 // -> cache would be useless ;-)
38 return f.layer.Close()
43 func (f *UnionFile) Read(s []byte) (int, error) {
45 n, err := f.layer.Read(s)
46 if (err == nil || err == io.EOF) && f.base != nil {
47 // advance the file position also in the base file, the next
48 // call may be a write at this position (or a seek with SEEK_CUR)
49 if _, seekErr := f.base.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
50 // only overwrite err in case the seek fails: we need to
51 // report an eventual io.EOF to the caller
63 func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
65 n, err := f.layer.ReadAt(s, o)
66 if (err == nil || err == io.EOF) && f.base != nil {
67 _, err = f.base.Seek(o+int64(n), os.SEEK_SET)
72 return f.base.ReadAt(s, o)
77 func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
79 pos, err = f.layer.Seek(o, w)
80 if (err == nil || err == io.EOF) && f.base != nil {
81 _, err = f.base.Seek(o, w)
86 return f.base.Seek(o, w)
91 func (f *UnionFile) Write(s []byte) (n int, err error) {
93 n, err = f.layer.Write(s)
94 if err == nil && f.base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
95 _, err = f.base.Write(s)
100 return f.base.Write(s)
105 func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
107 n, err = f.layer.WriteAt(s, o)
108 if err == nil && f.base != nil {
109 _, err = f.base.WriteAt(s, o)
114 return f.base.WriteAt(s, o)
119 func (f *UnionFile) Name() string {
121 return f.layer.Name()
126 // Readdir will weave the two directories together and
127 // return a single view of the overlayed directories
128 func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
130 var files = make(map[string]os.FileInfo)
131 var rfi []os.FileInfo
133 rfi, err = f.layer.Readdir(-1)
137 for _, fi := range rfi {
138 files[fi.Name()] = fi
143 rfi, err = f.base.Readdir(-1)
147 for _, fi := range rfi {
148 if _, exists := files[fi.Name()]; !exists {
149 files[fi.Name()] = fi
153 for _, fi := range files {
154 f.files = append(f.files, fi)
158 return f.files[f.off:], nil
160 defer func() { f.off += c }()
161 return f.files[f.off:c], nil
164 func (f *UnionFile) Readdirnames(c int) ([]string, error) {
165 rfi, err := f.Readdir(c)
170 for _, fi := range rfi {
171 names = append(names, fi.Name())
176 func (f *UnionFile) Stat() (os.FileInfo, error) {
178 return f.layer.Stat()
186 func (f *UnionFile) Sync() (err error) {
189 if err == nil && f.base != nil {
200 func (f *UnionFile) Truncate(s int64) (err error) {
202 err = f.layer.Truncate(s)
203 if err == nil && f.base != nil {
204 err = f.base.Truncate(s)
209 return f.base.Truncate(s)
214 func (f *UnionFile) WriteString(s string) (n int, err error) {
216 n, err = f.layer.WriteString(s)
217 if err == nil && f.base != nil {
218 _, err = f.base.WriteString(s)
223 return f.base.WriteString(s)
228 func copyToLayer(base Fs, layer Fs, name string) error {
229 bfh, err := base.Open(name)
235 // First make sure the directory exists
236 exists, err := Exists(layer, filepath.Dir(name))
241 err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
247 // Create the file on the overlay
248 lfh, err := layer.Create(name)
252 n, err := io.Copy(lfh, bfh)
254 // If anything fails, clean up the file
260 bfi, err := bfh.Stat()
261 if err != nil || bfi.Size() != n {
273 return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())