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