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