github.com/LandonTClipp/afero@v1.3.6-0.20200907052150-97f9d166c7a3/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) SymlinkIfPossible(oldname, newname string) error {
   121  	if slayer, ok := u.layer.(Linker); ok {
   122  		return slayer.SymlinkIfPossible(oldname, newname)
   123  	}
   124  
   125  	return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
   126  }
   127  
   128  func (u *CopyOnWriteFs) ReadlinkIfPossible(name string) (string, error) {
   129  	if rlayer, ok := u.layer.(LinkReader); ok {
   130  		return rlayer.ReadlinkIfPossible(name)
   131  	}
   132  
   133  	if rbase, ok := u.base.(LinkReader); ok {
   134  		return rbase.ReadlinkIfPossible(name)
   135  	}
   136  
   137  	return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
   138  }
   139  
   140  func (u *CopyOnWriteFs) isNotExist(err error) bool {
   141  	if e, ok := err.(*os.PathError); ok {
   142  		err = e.Err
   143  	}
   144  	if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
   145  		return true
   146  	}
   147  	return false
   148  }
   149  
   150  // Renaming files present only in the base layer is not permitted
   151  func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
   152  	b, err := u.isBaseFile(oldname)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	if b {
   157  		return syscall.EPERM
   158  	}
   159  	return u.layer.Rename(oldname, newname)
   160  }
   161  
   162  // Removing files present only in the base layer is not permitted. If
   163  // a file is present in the base layer and the overlay, only the overlay
   164  // will be removed.
   165  func (u *CopyOnWriteFs) Remove(name string) error {
   166  	err := u.layer.Remove(name)
   167  	switch err {
   168  	case syscall.ENOENT:
   169  		_, err = u.base.Stat(name)
   170  		if err == nil {
   171  			return syscall.EPERM
   172  		}
   173  		return syscall.ENOENT
   174  	default:
   175  		return err
   176  	}
   177  }
   178  
   179  func (u *CopyOnWriteFs) RemoveAll(name string) error {
   180  	err := u.layer.RemoveAll(name)
   181  	switch err {
   182  	case syscall.ENOENT:
   183  		_, err = u.base.Stat(name)
   184  		if err == nil {
   185  			return syscall.EPERM
   186  		}
   187  		return syscall.ENOENT
   188  	default:
   189  		return err
   190  	}
   191  }
   192  
   193  func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
   194  	b, err := u.isBaseFile(name)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
   200  		if b {
   201  			if err = u.copyToLayer(name); err != nil {
   202  				return nil, err
   203  			}
   204  			return u.layer.OpenFile(name, flag, perm)
   205  		}
   206  
   207  		dir := filepath.Dir(name)
   208  		isaDir, err := IsDir(u.base, dir)
   209  		if err != nil && !os.IsNotExist(err) {
   210  			return nil, err
   211  		}
   212  		if isaDir {
   213  			if err = u.layer.MkdirAll(dir, 0777); err != nil {
   214  				return nil, err
   215  			}
   216  			return u.layer.OpenFile(name, flag, perm)
   217  		}
   218  
   219  		isaDir, err = IsDir(u.layer, dir)
   220  		if err != nil {
   221  			return nil, err
   222  		}
   223  		if isaDir {
   224  			return u.layer.OpenFile(name, flag, perm)
   225  		}
   226  
   227  		return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
   228  	}
   229  	if b {
   230  		return u.base.OpenFile(name, flag, perm)
   231  	}
   232  	return u.layer.OpenFile(name, flag, perm)
   233  }
   234  
   235  // This function handles the 9 different possibilities caused
   236  // by the union which are the intersection of the following...
   237  //  layer: doesn't exist, exists as a file, and exists as a directory
   238  //  base:  doesn't exist, exists as a file, and exists as a directory
   239  func (u *CopyOnWriteFs) Open(name string) (File, error) {
   240  	// Since the overlay overrides the base we check that first
   241  	b, err := u.isBaseFile(name)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	// If overlay doesn't exist, return the base (base state irrelevant)
   247  	if b {
   248  		return u.base.Open(name)
   249  	}
   250  
   251  	// If overlay is a file, return it (base state irrelevant)
   252  	dir, err := IsDir(u.layer, name)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	if !dir {
   257  		return u.layer.Open(name)
   258  	}
   259  
   260  	// Overlay is a directory, base state now matters.
   261  	// Base state has 3 states to check but 2 outcomes:
   262  	// A. It's a file or non-readable in the base (return just the overlay)
   263  	// B. It's an accessible directory in the base (return a UnionFile)
   264  
   265  	// If base is file or nonreadable, return overlay
   266  	dir, err = IsDir(u.base, name)
   267  	if !dir || err != nil {
   268  		return u.layer.Open(name)
   269  	}
   270  
   271  	// Both base & layer are directories
   272  	// Return union file (if opens are without error)
   273  	bfile, bErr := u.base.Open(name)
   274  	lfile, lErr := u.layer.Open(name)
   275  
   276  	// If either have errors at this point something is very wrong. Return nil and the errors
   277  	if bErr != nil || lErr != nil {
   278  		return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
   279  	}
   280  
   281  	return &UnionFile{Base: bfile, Layer: lfile}, nil
   282  }
   283  
   284  func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
   285  	dir, err := IsDir(u.base, name)
   286  	if err != nil {
   287  		return u.layer.MkdirAll(name, perm)
   288  	}
   289  	if dir {
   290  		return ErrFileExists
   291  	}
   292  	return u.layer.MkdirAll(name, perm)
   293  }
   294  
   295  func (u *CopyOnWriteFs) Name() string {
   296  	return "CopyOnWriteFs"
   297  }
   298  
   299  func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
   300  	dir, err := IsDir(u.base, name)
   301  	if err != nil {
   302  		return u.layer.MkdirAll(name, perm)
   303  	}
   304  	if dir {
   305  		// This is in line with how os.MkdirAll behaves.
   306  		return nil
   307  	}
   308  	return u.layer.MkdirAll(name, perm)
   309  }
   310  
   311  func (u *CopyOnWriteFs) Create(name string) (File, error) {
   312  	return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
   313  }