github.com/meteocima/afero@v1.7.1-0.20200716093009-d7b0954360d0/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) Link(name, targetDir string) error {
    49  	panic("not implemented")
    50  }
    51  
    52  func (u *CopyOnWriteFs) copyToLayer(name string) error {
    53  	return copyToLayer(u.base, u.layer, name)
    54  }
    55  
    56  func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
    57  	b, err := u.isBaseFile(name)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	if b {
    62  		if err := u.copyToLayer(name); err != nil {
    63  			return err
    64  		}
    65  	}
    66  	return u.layer.Chtimes(name, atime, mtime)
    67  }
    68  
    69  func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
    70  	b, err := u.isBaseFile(name)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	if b {
    75  		if err := u.copyToLayer(name); err != nil {
    76  			return err
    77  		}
    78  	}
    79  	return u.layer.Chmod(name, mode)
    80  }
    81  
    82  func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
    83  	fi, err := u.layer.Stat(name)
    84  	if err != nil {
    85  		isNotExist := u.isNotExist(err)
    86  		if isNotExist {
    87  			return u.base.Stat(name)
    88  		}
    89  		return nil, err
    90  	}
    91  	return fi, nil
    92  }
    93  
    94  func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
    95  	llayer, ok1 := u.layer.(Lstater)
    96  	lbase, ok2 := u.base.(Lstater)
    97  
    98  	if ok1 {
    99  		fi, b, err := llayer.LstatIfPossible(name)
   100  		if err == nil {
   101  			return fi, b, nil
   102  		}
   103  
   104  		if !u.isNotExist(err) {
   105  			return nil, b, err
   106  		}
   107  	}
   108  
   109  	if ok2 {
   110  		fi, b, err := lbase.LstatIfPossible(name)
   111  		if err == nil {
   112  			return fi, b, nil
   113  		}
   114  		if !u.isNotExist(err) {
   115  			return nil, b, err
   116  		}
   117  	}
   118  
   119  	fi, err := u.Stat(name)
   120  
   121  	return fi, false, err
   122  }
   123  
   124  func (u *CopyOnWriteFs) SymlinkIfPossible(oldname, newname string) error {
   125  	if slayer, ok := u.layer.(Linker); ok {
   126  		return slayer.SymlinkIfPossible(oldname, newname)
   127  	}
   128  
   129  	return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
   130  }
   131  
   132  func (u *CopyOnWriteFs) ReadlinkIfPossible(name string) (string, error) {
   133  	if rlayer, ok := u.layer.(LinkReader); ok {
   134  		return rlayer.ReadlinkIfPossible(name)
   135  	}
   136  
   137  	if rbase, ok := u.base.(LinkReader); ok {
   138  		return rbase.ReadlinkIfPossible(name)
   139  	}
   140  
   141  	return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
   142  }
   143  
   144  func (u *CopyOnWriteFs) isNotExist(err error) bool {
   145  	if e, ok := err.(*os.PathError); ok {
   146  		err = e.Err
   147  	}
   148  	if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
   149  		return true
   150  	}
   151  	return false
   152  }
   153  
   154  // Renaming files present only in the base layer is not permitted
   155  func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
   156  	b, err := u.isBaseFile(oldname)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	if b {
   161  		return syscall.EPERM
   162  	}
   163  	return u.layer.Rename(oldname, newname)
   164  }
   165  
   166  // Removing files present only in the base layer is not permitted. If
   167  // a file is present in the base layer and the overlay, only the overlay
   168  // will be removed.
   169  func (u *CopyOnWriteFs) Remove(name string) error {
   170  	err := u.layer.Remove(name)
   171  	switch err {
   172  	case syscall.ENOENT:
   173  		_, err = u.base.Stat(name)
   174  		if err == nil {
   175  			return syscall.EPERM
   176  		}
   177  		return syscall.ENOENT
   178  	default:
   179  		return err
   180  	}
   181  }
   182  
   183  func (u *CopyOnWriteFs) RemoveAll(name string) error {
   184  	err := u.layer.RemoveAll(name)
   185  	switch err {
   186  	case syscall.ENOENT:
   187  		_, err = u.base.Stat(name)
   188  		if err == nil {
   189  			return syscall.EPERM
   190  		}
   191  		return syscall.ENOENT
   192  	default:
   193  		return err
   194  	}
   195  }
   196  
   197  func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
   198  	b, err := u.isBaseFile(name)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
   204  		if b {
   205  			if err = u.copyToLayer(name); err != nil {
   206  				return nil, err
   207  			}
   208  			return u.layer.OpenFile(name, flag, perm)
   209  		}
   210  
   211  		dir := filepath.Dir(name)
   212  		isaDir, err := IsDir(u.base, dir)
   213  		if err != nil && !os.IsNotExist(err) {
   214  			return nil, err
   215  		}
   216  		if isaDir {
   217  			if err = u.layer.MkdirAll(dir, 0777); err != nil {
   218  				return nil, err
   219  			}
   220  			return u.layer.OpenFile(name, flag, perm)
   221  		}
   222  
   223  		isaDir, err = IsDir(u.layer, dir)
   224  		if err != nil {
   225  			return nil, err
   226  		}
   227  		if isaDir {
   228  			return u.layer.OpenFile(name, flag, perm)
   229  		}
   230  
   231  		return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
   232  	}
   233  	if b {
   234  		return u.base.OpenFile(name, flag, perm)
   235  	}
   236  	return u.layer.OpenFile(name, flag, perm)
   237  }
   238  
   239  // This function handles the 9 different possibilities caused
   240  // by the union which are the intersection of the following...
   241  //  layer: doesn't exist, exists as a file, and exists as a directory
   242  //  base:  doesn't exist, exists as a file, and exists as a directory
   243  func (u *CopyOnWriteFs) Open(name string) (File, error) {
   244  	// Since the overlay overrides the base we check that first
   245  	b, err := u.isBaseFile(name)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	// If overlay doesn't exist, return the base (base state irrelevant)
   251  	if b {
   252  		return u.base.Open(name)
   253  	}
   254  
   255  	// If overlay is a file, return it (base state irrelevant)
   256  	dir, err := IsDir(u.layer, name)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  	if !dir {
   261  		return u.layer.Open(name)
   262  	}
   263  
   264  	// Overlay is a directory, base state now matters.
   265  	// Base state has 3 states to check but 2 outcomes:
   266  	// A. It's a file or non-readable in the base (return just the overlay)
   267  	// B. It's an accessible directory in the base (return a UnionFile)
   268  
   269  	// If base is file or nonreadable, return overlay
   270  	dir, err = IsDir(u.base, name)
   271  	if !dir || err != nil {
   272  		return u.layer.Open(name)
   273  	}
   274  
   275  	// Both base & layer are directories
   276  	// Return union file (if opens are without error)
   277  	bfile, bErr := u.base.Open(name)
   278  	lfile, lErr := u.layer.Open(name)
   279  
   280  	// If either have errors at this point something is very wrong. Return nil and the errors
   281  	if bErr != nil || lErr != nil {
   282  		return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
   283  	}
   284  
   285  	return &UnionFile{Base: bfile, Layer: lfile}, nil
   286  }
   287  
   288  func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
   289  	dir, err := IsDir(u.base, name)
   290  	if err != nil {
   291  		return u.layer.MkdirAll(name, perm)
   292  	}
   293  	if dir {
   294  		return ErrFileExists
   295  	}
   296  	return u.layer.MkdirAll(name, perm)
   297  }
   298  
   299  func (u *CopyOnWriteFs) Name() string {
   300  	return "CopyOnWriteFs"
   301  }
   302  
   303  func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
   304  	dir, err := IsDir(u.base, name)
   305  	if err != nil {
   306  		return u.layer.MkdirAll(name, perm)
   307  	}
   308  	if dir {
   309  		// This is in line with how os.MkdirAll behaves.
   310  		return nil
   311  	}
   312  	return u.layer.MkdirAll(name, perm)
   313  }
   314  
   315  func (u *CopyOnWriteFs) Create(name string) (File, error) {
   316  	return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
   317  }