gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

copyOnWriteFs.go (7763B)


      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 }