unionFile.go raw

   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