github.com/IBM/fsgo@v0.0.0-20220920202152-e16fd2119d49/unionFile.go (about)

     1  // Copyright 2022 IBM Inc. All rights reserved
     2  // Copyright © 2014 Steve Francia <spf@spf13.com>
     3  //
     4  // SPDX-License-Identifier: Apache2.0
     5  package fsgo
     6  
     7  import (
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"syscall"
    12  )
    13  
    14  // The UnionFile implements the fsgo.File interface and will be returned
    15  // when reading a directory present at least in the overlay or opening a file
    16  // for writing.
    17  //
    18  // The calls to
    19  // Readdir() and Readdirnames() merge the file os.FileInfo / names from the
    20  // base and the overlay - for files present in both layers, only those
    21  // from the overlay will be used.
    22  //
    23  // When opening files for writing (Create() / OpenFile() with the right flags)
    24  // the operations will be done in both layers, starting with the overlay. A
    25  // successful read in the overlay will move the cursor position in the base layer
    26  // by the number of bytes read.
    27  type UnionFile struct {
    28  	Base   File
    29  	Layer  File
    30  	Merger DirsMerger
    31  	off    int
    32  	files  []os.FileInfo
    33  }
    34  
    35  func (f *UnionFile) Close() error {
    36  	// first close base, so we have a newer timestamp in the overlay. If we'd close
    37  	// the overlay first, we'd get a cacheStale the next time we access this file
    38  	// -> cache would be useless ;-)
    39  	if f.Base != nil {
    40  		f.Base.Close()
    41  	}
    42  	if f.Layer != nil {
    43  		return f.Layer.Close()
    44  	}
    45  	return BADFD
    46  }
    47  
    48  func (f *UnionFile) Read(s []byte) (int, error) {
    49  	if f.Layer != nil {
    50  		n, err := f.Layer.Read(s)
    51  		if (err == nil || err == io.EOF) && f.Base != nil {
    52  			// advance the file position also in the base file, the next
    53  			// call may be a write at this position (or a seek with SEEK_CUR)
    54  			if _, seekErr := f.Base.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
    55  				// only overwrite err in case the seek fails: we need to
    56  				// report an eventual io.EOF to the caller
    57  				err = seekErr
    58  			}
    59  		}
    60  		return n, err
    61  	}
    62  	if f.Base != nil {
    63  		return f.Base.Read(s)
    64  	}
    65  	return 0, BADFD
    66  }
    67  
    68  func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
    69  	if f.Layer != nil {
    70  		n, err := f.Layer.ReadAt(s, o)
    71  		if (err == nil || err == io.EOF) && f.Base != nil {
    72  			_, err = f.Base.Seek(o+int64(n), io.SeekStart)
    73  		}
    74  		return n, err
    75  	}
    76  	if f.Base != nil {
    77  		return f.Base.ReadAt(s, o)
    78  	}
    79  	return 0, BADFD
    80  }
    81  
    82  func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
    83  	if f.Layer != nil {
    84  		pos, err = f.Layer.Seek(o, w)
    85  		if (err == nil || err == io.EOF) && f.Base != nil {
    86  			_, err = f.Base.Seek(o, w)
    87  		}
    88  		return pos, err
    89  	}
    90  	if f.Base != nil {
    91  		return f.Base.Seek(o, w)
    92  	}
    93  	return 0, BADFD
    94  }
    95  
    96  func (f *UnionFile) Write(s []byte) (n int, err error) {
    97  	if f.Layer != nil {
    98  		n, err = f.Layer.Write(s)
    99  		if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
   100  			_, err = f.Base.Write(s)
   101  		}
   102  		return n, err
   103  	}
   104  	if f.Base != nil {
   105  		return f.Base.Write(s)
   106  	}
   107  	return 0, BADFD
   108  }
   109  
   110  func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
   111  	if f.Layer != nil {
   112  		n, err = f.Layer.WriteAt(s, o)
   113  		if err == nil && f.Base != nil {
   114  			_, err = f.Base.WriteAt(s, o)
   115  		}
   116  		return n, err
   117  	}
   118  	if f.Base != nil {
   119  		return f.Base.WriteAt(s, o)
   120  	}
   121  	return 0, BADFD
   122  }
   123  
   124  func (f *UnionFile) Name() string {
   125  	if f.Layer != nil {
   126  		return f.Layer.Name()
   127  	}
   128  	return f.Base.Name()
   129  }
   130  
   131  // DirsMerger is how UnionFile weaves two directories together.
   132  // It takes the FileInfo slices from the layer and the base and returns a
   133  // single view.
   134  type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)
   135  
   136  var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
   137  	var files = make(map[string]os.FileInfo)
   138  
   139  	for _, fi := range lofi {
   140  		files[fi.Name()] = fi
   141  	}
   142  
   143  	for _, fi := range bofi {
   144  		if _, exists := files[fi.Name()]; !exists {
   145  			files[fi.Name()] = fi
   146  		}
   147  	}
   148  
   149  	rfi := make([]os.FileInfo, len(files))
   150  
   151  	i := 0
   152  	for _, fi := range files {
   153  		rfi[i] = fi
   154  		i++
   155  	}
   156  
   157  	return rfi, nil
   158  
   159  }
   160  
   161  // Readdir will weave the two directories together and
   162  // return a single view of the overlayed directories.
   163  // At the end of the directory view, the error is io.EOF if c > 0.
   164  func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
   165  	var merge DirsMerger = f.Merger
   166  	if merge == nil {
   167  		merge = defaultUnionMergeDirsFn
   168  	}
   169  
   170  	if f.off == 0 {
   171  		var lfi []os.FileInfo
   172  		if f.Layer != nil {
   173  			lfi, err = f.Layer.Readdir(-1)
   174  			if err != nil {
   175  				return nil, err
   176  			}
   177  		}
   178  
   179  		var bfi []os.FileInfo
   180  		if f.Base != nil {
   181  			bfi, err = f.Base.Readdir(-1)
   182  			if err != nil {
   183  				return nil, err
   184  			}
   185  
   186  		}
   187  		merged, err := merge(lfi, bfi)
   188  		if err != nil {
   189  			return nil, err
   190  		}
   191  		f.files = append(f.files, merged...)
   192  	}
   193  	files := f.files[f.off:]
   194  
   195  	if c <= 0 {
   196  		return files, nil
   197  	}
   198  
   199  	if len(files) == 0 {
   200  		return nil, io.EOF
   201  	}
   202  
   203  	if c > len(files) {
   204  		c = len(files)
   205  	}
   206  
   207  	defer func() { f.off += c }()
   208  	return files[:c], nil
   209  }
   210  
   211  func (f *UnionFile) Readdirnames(c int) ([]string, error) {
   212  	rfi, err := f.Readdir(c)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	var names []string
   217  	for _, fi := range rfi {
   218  		names = append(names, fi.Name())
   219  	}
   220  	return names, nil
   221  }
   222  
   223  func (f *UnionFile) Stat() (os.FileInfo, error) {
   224  	if f.Layer != nil {
   225  		return f.Layer.Stat()
   226  	}
   227  	if f.Base != nil {
   228  		return f.Base.Stat()
   229  	}
   230  	return nil, BADFD
   231  }
   232  
   233  func (f *UnionFile) Sync() (err error) {
   234  	if f.Layer != nil {
   235  		err = f.Layer.Sync()
   236  		if err == nil && f.Base != nil {
   237  			err = f.Base.Sync()
   238  		}
   239  		return err
   240  	}
   241  	if f.Base != nil {
   242  		return f.Base.Sync()
   243  	}
   244  	return BADFD
   245  }
   246  
   247  func (f *UnionFile) Truncate(s int64) (err error) {
   248  	if f.Layer != nil {
   249  		err = f.Layer.Truncate(s)
   250  		if err == nil && f.Base != nil {
   251  			err = f.Base.Truncate(s)
   252  		}
   253  		return err
   254  	}
   255  	if f.Base != nil {
   256  		return f.Base.Truncate(s)
   257  	}
   258  	return BADFD
   259  }
   260  
   261  func (f *UnionFile) WriteString(s string) (n int, err error) {
   262  	if f.Layer != nil {
   263  		n, err = f.Layer.WriteString(s)
   264  		if err == nil && f.Base != nil {
   265  			_, err = f.Base.WriteString(s)
   266  		}
   267  		return n, err
   268  	}
   269  	if f.Base != nil {
   270  		return f.Base.WriteString(s)
   271  	}
   272  	return 0, BADFD
   273  }
   274  
   275  func copyFile(base Fs, layer Fs, name string, bfh File) error {
   276  	// First make sure the directory exists
   277  	exists, err := Exists(layer, filepath.Dir(name))
   278  	if err != nil {
   279  		return err
   280  	}
   281  	if !exists {
   282  		err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
   283  		if err != nil {
   284  			return err
   285  		}
   286  	}
   287  
   288  	// Create the file on the overlay
   289  	lfh, err := layer.Create(name)
   290  	if err != nil {
   291  		return err
   292  	}
   293  	n, err := io.Copy(lfh, bfh)
   294  	if err != nil {
   295  		// If anything fails, clean up the file
   296  		layer.Remove(name)
   297  		lfh.Close()
   298  		return err
   299  	}
   300  
   301  	bfi, err := bfh.Stat()
   302  	if err != nil || bfi.Size() != n {
   303  		layer.Remove(name)
   304  		lfh.Close()
   305  		return syscall.EIO
   306  	}
   307  
   308  	err = lfh.Close()
   309  	if err != nil {
   310  		layer.Remove(name)
   311  		lfh.Close()
   312  		return err
   313  	}
   314  	return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
   315  }
   316  
   317  func copyToLayer(base Fs, layer Fs, name string) error {
   318  	bfh, err := base.Open(name)
   319  	if err != nil {
   320  		return err
   321  	}
   322  	defer bfh.Close()
   323  
   324  	return copyFile(base, layer, name, bfh)
   325  }
   326  
   327  func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error {
   328  	bfh, err := base.OpenFile(name, flag, perm)
   329  	if err != nil {
   330  		return err
   331  	}
   332  	defer bfh.Close()
   333  
   334  	return copyFile(base, layer, name, bfh)
   335  }