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