gtsocial-umbx

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

cacheOnReadFs.go (7495B)


      1 package afero
      2 
      3 import (
      4 	"os"
      5 	"syscall"
      6 	"time"
      7 )
      8 
      9 // If the cache duration is 0, cache time will be unlimited, i.e. once
     10 // a file is in the layer, the base will never be read again for this file.
     11 //
     12 // For cache times greater than 0, the modification time of a file is
     13 // checked. Note that a lot of file system implementations only allow a
     14 // resolution of a second for timestamps... or as the godoc for os.Chtimes()
     15 // states: "The underlying filesystem may truncate or round the values to a
     16 // less precise time unit."
     17 //
     18 // This caching union will forward all write calls also to the base file
     19 // system first. To prevent writing to the base Fs, wrap it in a read-only
     20 // filter - Note: this will also make the overlay read-only, for writing files
     21 // in the overlay, use the overlay Fs directly, not via the union Fs.
     22 type CacheOnReadFs struct {
     23 	base      Fs
     24 	layer     Fs
     25 	cacheTime time.Duration
     26 }
     27 
     28 func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
     29 	return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
     30 }
     31 
     32 type cacheState int
     33 
     34 const (
     35 	// not present in the overlay, unknown if it exists in the base:
     36 	cacheMiss cacheState = iota
     37 	// present in the overlay and in base, base file is newer:
     38 	cacheStale
     39 	// present in the overlay - with cache time == 0 it may exist in the base,
     40 	// with cacheTime > 0 it exists in the base and is same age or newer in the
     41 	// overlay
     42 	cacheHit
     43 	// happens if someone writes directly to the overlay without
     44 	// going through this union
     45 	cacheLocal
     46 )
     47 
     48 func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {
     49 	var lfi, bfi os.FileInfo
     50 	lfi, err = u.layer.Stat(name)
     51 	if err == nil {
     52 		if u.cacheTime == 0 {
     53 			return cacheHit, lfi, nil
     54 		}
     55 		if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {
     56 			bfi, err = u.base.Stat(name)
     57 			if err != nil {
     58 				return cacheLocal, lfi, nil
     59 			}
     60 			if bfi.ModTime().After(lfi.ModTime()) {
     61 				return cacheStale, bfi, nil
     62 			}
     63 		}
     64 		return cacheHit, lfi, nil
     65 	}
     66 
     67 	if err == syscall.ENOENT || os.IsNotExist(err) {
     68 		return cacheMiss, nil, nil
     69 	}
     70 
     71 	return cacheMiss, nil, err
     72 }
     73 
     74 func (u *CacheOnReadFs) copyToLayer(name string) error {
     75 	return copyToLayer(u.base, u.layer, name)
     76 }
     77 
     78 func (u *CacheOnReadFs) copyFileToLayer(name string, flag int, perm os.FileMode) error {
     79 	return copyFileToLayer(u.base, u.layer, name, flag, perm)
     80 }
     81 
     82 func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
     83 	st, _, err := u.cacheStatus(name)
     84 	if err != nil {
     85 		return err
     86 	}
     87 	switch st {
     88 	case cacheLocal:
     89 	case cacheHit:
     90 		err = u.base.Chtimes(name, atime, mtime)
     91 	case cacheStale, cacheMiss:
     92 		if err := u.copyToLayer(name); err != nil {
     93 			return err
     94 		}
     95 		err = u.base.Chtimes(name, atime, mtime)
     96 	}
     97 	if err != nil {
     98 		return err
     99 	}
    100 	return u.layer.Chtimes(name, atime, mtime)
    101 }
    102 
    103 func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
    104 	st, _, err := u.cacheStatus(name)
    105 	if err != nil {
    106 		return err
    107 	}
    108 	switch st {
    109 	case cacheLocal:
    110 	case cacheHit:
    111 		err = u.base.Chmod(name, mode)
    112 	case cacheStale, cacheMiss:
    113 		if err := u.copyToLayer(name); err != nil {
    114 			return err
    115 		}
    116 		err = u.base.Chmod(name, mode)
    117 	}
    118 	if err != nil {
    119 		return err
    120 	}
    121 	return u.layer.Chmod(name, mode)
    122 }
    123 
    124 func (u *CacheOnReadFs) Chown(name string, uid, gid int) error {
    125 	st, _, err := u.cacheStatus(name)
    126 	if err != nil {
    127 		return err
    128 	}
    129 	switch st {
    130 	case cacheLocal:
    131 	case cacheHit:
    132 		err = u.base.Chown(name, uid, gid)
    133 	case cacheStale, cacheMiss:
    134 		if err := u.copyToLayer(name); err != nil {
    135 			return err
    136 		}
    137 		err = u.base.Chown(name, uid, gid)
    138 	}
    139 	if err != nil {
    140 		return err
    141 	}
    142 	return u.layer.Chown(name, uid, gid)
    143 }
    144 
    145 func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
    146 	st, fi, err := u.cacheStatus(name)
    147 	if err != nil {
    148 		return nil, err
    149 	}
    150 	switch st {
    151 	case cacheMiss:
    152 		return u.base.Stat(name)
    153 	default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
    154 		return fi, nil
    155 	}
    156 }
    157 
    158 func (u *CacheOnReadFs) Rename(oldname, newname string) error {
    159 	st, _, err := u.cacheStatus(oldname)
    160 	if err != nil {
    161 		return err
    162 	}
    163 	switch st {
    164 	case cacheLocal:
    165 	case cacheHit:
    166 		err = u.base.Rename(oldname, newname)
    167 	case cacheStale, cacheMiss:
    168 		if err := u.copyToLayer(oldname); err != nil {
    169 			return err
    170 		}
    171 		err = u.base.Rename(oldname, newname)
    172 	}
    173 	if err != nil {
    174 		return err
    175 	}
    176 	return u.layer.Rename(oldname, newname)
    177 }
    178 
    179 func (u *CacheOnReadFs) Remove(name string) error {
    180 	st, _, err := u.cacheStatus(name)
    181 	if err != nil {
    182 		return err
    183 	}
    184 	switch st {
    185 	case cacheLocal:
    186 	case cacheHit, cacheStale, cacheMiss:
    187 		err = u.base.Remove(name)
    188 	}
    189 	if err != nil {
    190 		return err
    191 	}
    192 	return u.layer.Remove(name)
    193 }
    194 
    195 func (u *CacheOnReadFs) RemoveAll(name string) error {
    196 	st, _, err := u.cacheStatus(name)
    197 	if err != nil {
    198 		return err
    199 	}
    200 	switch st {
    201 	case cacheLocal:
    202 	case cacheHit, cacheStale, cacheMiss:
    203 		err = u.base.RemoveAll(name)
    204 	}
    205 	if err != nil {
    206 		return err
    207 	}
    208 	return u.layer.RemoveAll(name)
    209 }
    210 
    211 func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
    212 	st, _, err := u.cacheStatus(name)
    213 	if err != nil {
    214 		return nil, err
    215 	}
    216 	switch st {
    217 	case cacheLocal, cacheHit:
    218 	default:
    219 		if err := u.copyFileToLayer(name, flag, perm); err != nil {
    220 			return nil, err
    221 		}
    222 	}
    223 	if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
    224 		bfi, err := u.base.OpenFile(name, flag, perm)
    225 		if err != nil {
    226 			return nil, err
    227 		}
    228 		lfi, err := u.layer.OpenFile(name, flag, perm)
    229 		if err != nil {
    230 			bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
    231 			return nil, err
    232 		}
    233 		return &UnionFile{Base: bfi, Layer: lfi}, nil
    234 	}
    235 	return u.layer.OpenFile(name, flag, perm)
    236 }
    237 
    238 func (u *CacheOnReadFs) Open(name string) (File, error) {
    239 	st, fi, err := u.cacheStatus(name)
    240 	if err != nil {
    241 		return nil, err
    242 	}
    243 
    244 	switch st {
    245 	case cacheLocal:
    246 		return u.layer.Open(name)
    247 
    248 	case cacheMiss:
    249 		bfi, err := u.base.Stat(name)
    250 		if err != nil {
    251 			return nil, err
    252 		}
    253 		if bfi.IsDir() {
    254 			return u.base.Open(name)
    255 		}
    256 		if err := u.copyToLayer(name); err != nil {
    257 			return nil, err
    258 		}
    259 		return u.layer.Open(name)
    260 
    261 	case cacheStale:
    262 		if !fi.IsDir() {
    263 			if err := u.copyToLayer(name); err != nil {
    264 				return nil, err
    265 			}
    266 			return u.layer.Open(name)
    267 		}
    268 	case cacheHit:
    269 		if !fi.IsDir() {
    270 			return u.layer.Open(name)
    271 		}
    272 	}
    273 	// the dirs from cacheHit, cacheStale fall down here:
    274 	bfile, _ := u.base.Open(name)
    275 	lfile, err := u.layer.Open(name)
    276 	if err != nil && bfile == nil {
    277 		return nil, err
    278 	}
    279 	return &UnionFile{Base: bfile, Layer: lfile}, nil
    280 }
    281 
    282 func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
    283 	err := u.base.Mkdir(name, perm)
    284 	if err != nil {
    285 		return err
    286 	}
    287 	return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
    288 }
    289 
    290 func (u *CacheOnReadFs) Name() string {
    291 	return "CacheOnReadFs"
    292 }
    293 
    294 func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
    295 	err := u.base.MkdirAll(name, perm)
    296 	if err != nil {
    297 		return err
    298 	}
    299 	return u.layer.MkdirAll(name, perm)
    300 }
    301 
    302 func (u *CacheOnReadFs) Create(name string) (File, error) {
    303 	bfh, err := u.base.Create(name)
    304 	if err != nil {
    305 		return nil, err
    306 	}
    307 	lfh, err := u.layer.Create(name)
    308 	if err != nil {
    309 		// oops, see comment about OS_TRUNC above, should we remove? then we have to
    310 		// remember if the file did not exist before
    311 		bfh.Close()
    312 		return nil, err
    313 	}
    314 	return &UnionFile{Base: bfh, Layer: lfh}, nil
    315 }