github.com/hbbio/afero@v1.2.3-0.20190412153849-bd67867be27b/copyOnWriteFs.go (about)

     1  package afero
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"syscall"
     8  	"time"
     9  )
    10  
    11  var _ Lstater = (*CopyOnWriteFs)(nil)
    12  
    13  // The CopyOnWriteFs is a union filesystem: a read only base file system with
    14  // a possibly writeable layer on top. Changes to the file system will only
    15  // be made in the overlay: Changing an existing file in the base layer which
    16  // is not present in the overlay will copy the file to the overlay ("changing"
    17  // includes also calls to e.g. Chtimes() and Chmod()).
    18  //
    19  // Reading directories is currently only supported via Open(), not OpenFile().
    20  type CopyOnWriteFs struct {
    21  	base  Fs
    22  	layer Fs
    23  }
    24  
    25  func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
    26  	return &CopyOnWriteFs{base: base, layer: layer}
    27  }
    28  
    29  // Returns true if the file is not in the overlay
    30  func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
    31  	if _, err := u.layer.Stat(name); err == nil {
    32  		return false, nil
    33  	}
    34  	_, err := u.base.Stat(name)
    35  	if err != nil {
    36  		if oerr, ok := err.(*os.PathError); ok {
    37  			if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
    38  				return false, nil
    39  			}
    40  		}
    41  		if err == syscall.ENOENT {
    42  			return false, nil
    43  		}
    44  	}
    45  	return true, err
    46  }
    47  
    48  func (u *CopyOnWriteFs) copyToLayer(name string) error {
    49  	return copyToLayer(u.base, u.layer, name)
    50  }
    51  
    52  func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
    53  	b, err := u.isBaseFile(name)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	if b {
    58  		if err := u.copyToLayer(name); err != nil {
    59  			return err
    60  		}
    61  	}
    62  	return u.layer.Chtimes(name, atime, mtime)
    63  }
    64  
    65  func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
    66  	b, err := u.isBaseFile(name)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	if b {
    71  		if err := u.copyToLayer(name); err != nil {
    72  			return err
    73  		}
    74  	}
    75  	return u.layer.Chmod(name, mode)
    76  }
    77  
    78  func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
    79  	fi, err := u.layer.Stat(name)
    80  	if err != nil {
    81  		isNotExist := u.isNotExist(err)
    82  		if isNotExist {
    83  			return u.base.Stat(name)
    84  		}
    85  		return nil, err
    86  	}
    87  	return fi, nil
    88  }
    89  
    90  func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
    91  	llayer, ok1 := u.layer.(Lstater)
    92  	lbase, ok2 := u.base.(Lstater)
    93  
    94  	if ok1 {
    95  		fi, b, err := llayer.LstatIfPossible(name)
    96  		if err == nil {
    97  			return fi, b, nil
    98  		}
    99  
   100  		if !u.isNotExist(err) {
   101  			return nil, b, err
   102  		}
   103  	}
   104  
   105  	if ok2 {
   106  		fi, b, err := lbase.LstatIfPossible(name)
   107  		if err == nil {
   108  			return fi, b, nil
   109  		}
   110  		if !u.isNotExist(err) {
   111  			return nil, b, err
   112  		}
   113  	}
   114  
   115  	fi, err := u.Stat(name)
   116  
   117  	return fi, false, err
   118  }
   119  
   120  func (u *CopyOnWriteFs) isNotExist(err error) bool {
   121  	if e, ok := err.(*os.PathError); ok {
   122  		err = e.Err
   123  	}
   124  	if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
   125  		return true
   126  	}
   127  	return false
   128  }
   129  
   130  // Renaming files present only in the base layer is not permitted
   131  func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
   132  	b, err := u.isBaseFile(oldname)
   133  	if err != nil {
   134  		return err
   135  	}
   136  	if b {
   137  		return syscall.EPERM
   138  	}
   139  	return u.layer.Rename(oldname, newname)
   140  }
   141  
   142  // Removing files present only in the base layer is not permitted. If
   143  // a file is present in the base layer and the overlay, only the overlay
   144  // will be removed.
   145  func (u *CopyOnWriteFs) Remove(name string) error {
   146  	err := u.layer.Remove(name)
   147  	switch err {
   148  	case syscall.ENOENT:
   149  		_, err = u.base.Stat(name)
   150  		if err == nil {
   151  			return syscall.EPERM
   152  		}
   153  		return syscall.ENOENT
   154  	default:
   155  		return err
   156  	}
   157  }
   158  
   159  func (u *CopyOnWriteFs) RemoveAll(name string) error {
   160  	err := u.layer.RemoveAll(name)
   161  	switch err {
   162  	case syscall.ENOENT:
   163  		_, err = u.base.Stat(name)
   164  		if err == nil {
   165  			return syscall.EPERM
   166  		}
   167  		return syscall.ENOENT
   168  	default:
   169  		return err
   170  	}
   171  }
   172  
   173  func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
   174  	b, err := u.isBaseFile(name)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
   180  		if b {
   181  			if err = u.copyToLayer(name); err != nil {
   182  				return nil, err
   183  			}
   184  			return u.layer.OpenFile(name, flag, perm)
   185  		}
   186  
   187  		dir := filepath.Dir(name)
   188  		isaDir, err := IsDir(u.base, dir)
   189  		if err != nil && !os.IsNotExist(err) {
   190  			return nil, err
   191  		}
   192  		if isaDir {
   193  			if err = u.layer.MkdirAll(dir, 0777); err != nil {
   194  				return nil, err
   195  			}
   196  			return u.layer.OpenFile(name, flag, perm)
   197  		}
   198  
   199  		isaDir, err = IsDir(u.layer, dir)
   200  		if err != nil {
   201  			return nil, err
   202  		}
   203  		if isaDir {
   204  			return u.layer.OpenFile(name, flag, perm)
   205  		}
   206  
   207  		return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
   208  	}
   209  	if b {
   210  		return u.base.OpenFile(name, flag, perm)
   211  	}
   212  	return u.layer.OpenFile(name, flag, perm)
   213  }
   214  
   215  // This function handles the 9 different possibilities caused
   216  // by the union which are the intersection of the following...
   217  //  layer: doesn't exist, exists as a file, and exists as a directory
   218  //  base:  doesn't exist, exists as a file, and exists as a directory
   219  func (u *CopyOnWriteFs) Open(name string) (File, error) {
   220  	// Since the overlay overrides the base we check that first
   221  	b, err := u.isBaseFile(name)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	// If overlay doesn't exist, return the base (base state irrelevant)
   227  	if b {
   228  		return u.base.Open(name)
   229  	}
   230  
   231  	// If overlay is a file, return it (base state irrelevant)
   232  	dir, err := IsDir(u.layer, name)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  	if !dir {
   237  		return u.layer.Open(name)
   238  	}
   239  
   240  	// Overlay is a directory, base state now matters.
   241  	// Base state has 3 states to check but 2 outcomes:
   242  	// A. It's a file or non-readable in the base (return just the overlay)
   243  	// B. It's an accessible directory in the base (return a UnionFile)
   244  
   245  	// If base is file or nonreadable, return overlay
   246  	dir, err = IsDir(u.base, name)
   247  	if !dir || err != nil {
   248  		return u.layer.Open(name)
   249  	}
   250  
   251  	// Both base & layer are directories
   252  	// Return union file (if opens are without error)
   253  	bfile, bErr := u.base.Open(name)
   254  	lfile, lErr := u.layer.Open(name)
   255  
   256  	// If either have errors at this point something is very wrong. Return nil and the errors
   257  	if bErr != nil || lErr != nil {
   258  		return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
   259  	}
   260  
   261  	return &UnionFile{Base: bfile, Layer: lfile}, nil
   262  }
   263  
   264  func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
   265  	dir, err := IsDir(u.base, name)
   266  	if err != nil {
   267  		return u.layer.MkdirAll(name, perm)
   268  	}
   269  	if dir {
   270  		return ErrFileExists
   271  	}
   272  	return u.layer.MkdirAll(name, perm)
   273  }
   274  
   275  func (u *CopyOnWriteFs) Name() string {
   276  	return "CopyOnWriteFs"
   277  }
   278  
   279  func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
   280  	dir, err := IsDir(u.base, name)
   281  	if err != nil {
   282  		return u.layer.MkdirAll(name, perm)
   283  	}
   284  	if dir {
   285  		// This is in line with how os.MkdirAll behaves.
   286  		return nil
   287  	}
   288  	return u.layer.MkdirAll(name, perm)
   289  }
   290  
   291  func (u *CopyOnWriteFs) Create(name string) (File, error) {
   292  	return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
   293  }