github.com/listspa/afero@v1.2.3-0.20200303091702-462b969194ee/unionFile.go (about)

     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 if c > 0.
   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 c <= 0 && len(f.files) == 0 {
   191  		return f.files, nil
   192  	}
   193  
   194  	if f.off >= len(f.files) {
   195  		return nil, io.EOF
   196  	}
   197  
   198  	if c <= 0 {
   199  		return f.files[f.off:], nil
   200  	}
   201  
   202  	if c > len(f.files) {
   203  		c = len(f.files)
   204  	}
   205  
   206  	defer func() { f.off += c }()
   207  	return f.files[f.off:c], nil
   208  }
   209  
   210  func (f *UnionFile) Readdirnames(c int) ([]string, error) {
   211  	rfi, err := f.Readdir(c)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	var names []string
   216  	for _, fi := range rfi {
   217  		names = append(names, fi.Name())
   218  	}
   219  	return names, nil
   220  }
   221  
   222  func (f *UnionFile) Stat() (os.FileInfo, error) {
   223  	if f.Layer != nil {
   224  		return f.Layer.Stat()
   225  	}
   226  	if f.Base != nil {
   227  		return f.Base.Stat()
   228  	}
   229  	return nil, BADFD
   230  }
   231  
   232  func (f *UnionFile) Sync() (err error) {
   233  	if f.Layer != nil {
   234  		err = f.Layer.Sync()
   235  		if err == nil && f.Base != nil {
   236  			err = f.Base.Sync()
   237  		}
   238  		return err
   239  	}
   240  	if f.Base != nil {
   241  		return f.Base.Sync()
   242  	}
   243  	return BADFD
   244  }
   245  
   246  func (f *UnionFile) Truncate(s int64) (err error) {
   247  	if f.Layer != nil {
   248  		err = f.Layer.Truncate(s)
   249  		if err == nil && f.Base != nil {
   250  			err = f.Base.Truncate(s)
   251  		}
   252  		return err
   253  	}
   254  	if f.Base != nil {
   255  		return f.Base.Truncate(s)
   256  	}
   257  	return BADFD
   258  }
   259  
   260  func (f *UnionFile) WriteString(s string) (n int, err error) {
   261  	if f.Layer != nil {
   262  		n, err = f.Layer.WriteString(s)
   263  		if err == nil && f.Base != nil {
   264  			_, err = f.Base.WriteString(s)
   265  		}
   266  		return n, err
   267  	}
   268  	if f.Base != nil {
   269  		return f.Base.WriteString(s)
   270  	}
   271  	return 0, BADFD
   272  }
   273  
   274  func copyToLayer(base Fs, layer Fs, name string) error {
   275  	bfh, err := base.Open(name)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	defer bfh.Close()
   280  
   281  	// First make sure the directory exists
   282  	exists, err := Exists(layer, filepath.Dir(name))
   283  	if err != nil {
   284  		return err
   285  	}
   286  	if !exists {
   287  		err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
   288  		if err != nil {
   289  			return err
   290  		}
   291  	}
   292  
   293  	// Create the file on the overlay
   294  	lfh, err := layer.Create(name)
   295  	if err != nil {
   296  		return err
   297  	}
   298  	n, err := io.Copy(lfh, bfh)
   299  	if err != nil {
   300  		// If anything fails, clean up the file
   301  		layer.Remove(name)
   302  		lfh.Close()
   303  		return err
   304  	}
   305  
   306  	bfi, err := bfh.Stat()
   307  	if err != nil || bfi.Size() != n {
   308  		layer.Remove(name)
   309  		lfh.Close()
   310  		return syscall.EIO
   311  	}
   312  
   313  	err = lfh.Close()
   314  	if err != nil {
   315  		layer.Remove(name)
   316  		lfh.Close()
   317  		return err
   318  	}
   319  	return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
   320  }