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