copyOnWriteFs.go raw

   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, 0o777); 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  //
 251  //	layer: doesn't exist, exists as a file, and exists as a directory
 252  //	base:  doesn't exist, exists as a file, and exists as a directory
 253  func (u *CopyOnWriteFs) Open(name string) (File, error) {
 254  	// Since the overlay overrides the base we check that first
 255  	b, err := u.isBaseFile(name)
 256  	if err != nil {
 257  		return nil, err
 258  	}
 259  
 260  	// If overlay doesn't exist, return the base (base state irrelevant)
 261  	if b {
 262  		return u.base.Open(name)
 263  	}
 264  
 265  	// If overlay is a file, return it (base state irrelevant)
 266  	dir, err := IsDir(u.layer, name)
 267  	if err != nil {
 268  		return nil, err
 269  	}
 270  	if !dir {
 271  		return u.layer.Open(name)
 272  	}
 273  
 274  	// Overlay is a directory, base state now matters.
 275  	// Base state has 3 states to check but 2 outcomes:
 276  	// A. It's a file or non-readable in the base (return just the overlay)
 277  	// B. It's an accessible directory in the base (return a UnionFile)
 278  
 279  	// If base is file or nonreadable, return overlay
 280  	dir, err = IsDir(u.base, name)
 281  	if !dir || err != nil {
 282  		return u.layer.Open(name)
 283  	}
 284  
 285  	// Both base & layer are directories
 286  	// Return union file (if opens are without error)
 287  	bfile, bErr := u.base.Open(name)
 288  	lfile, lErr := u.layer.Open(name)
 289  
 290  	// If either have errors at this point something is very wrong. Return nil and the errors
 291  	if bErr != nil || lErr != nil {
 292  		return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
 293  	}
 294  
 295  	return &UnionFile{Base: bfile, Layer: lfile}, nil
 296  }
 297  
 298  func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
 299  	dir, err := IsDir(u.base, name)
 300  	if err != nil {
 301  		return u.layer.MkdirAll(name, perm)
 302  	}
 303  	if dir {
 304  		return ErrFileExists
 305  	}
 306  	return u.layer.MkdirAll(name, perm)
 307  }
 308  
 309  func (u *CopyOnWriteFs) Name() string {
 310  	return "CopyOnWriteFs"
 311  }
 312  
 313  func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
 314  	dir, err := IsDir(u.base, name)
 315  	if err != nil {
 316  		return u.layer.MkdirAll(name, perm)
 317  	}
 318  	if dir {
 319  		// This is in line with how os.MkdirAll behaves.
 320  		return nil
 321  	}
 322  	return u.layer.MkdirAll(name, perm)
 323  }
 324  
 325  func (u *CopyOnWriteFs) Create(name string) (File, error) {
 326  	return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666)
 327  }
 328