vendor/github.com/spf13/afero/cacheOnReadFs.go
changeset 242 2a9ec03fe5a1
child 256 6d9efbef00a9
equal deleted inserted replaced
241:e77dad242f4c 242:2a9ec03fe5a1
       
     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) Chtimes(name string, atime, mtime time.Time) error {
       
    79 	st, _, err := u.cacheStatus(name)
       
    80 	if err != nil {
       
    81 		return err
       
    82 	}
       
    83 	switch st {
       
    84 	case cacheLocal:
       
    85 	case cacheHit:
       
    86 		err = u.base.Chtimes(name, atime, mtime)
       
    87 	case cacheStale, cacheMiss:
       
    88 		if err := u.copyToLayer(name); err != nil {
       
    89 			return err
       
    90 		}
       
    91 		err = u.base.Chtimes(name, atime, mtime)
       
    92 	}
       
    93 	if err != nil {
       
    94 		return err
       
    95 	}
       
    96 	return u.layer.Chtimes(name, atime, mtime)
       
    97 }
       
    98 
       
    99 func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
       
   100 	st, _, err := u.cacheStatus(name)
       
   101 	if err != nil {
       
   102 		return err
       
   103 	}
       
   104 	switch st {
       
   105 	case cacheLocal:
       
   106 	case cacheHit:
       
   107 		err = u.base.Chmod(name, mode)
       
   108 	case cacheStale, cacheMiss:
       
   109 		if err := u.copyToLayer(name); err != nil {
       
   110 			return err
       
   111 		}
       
   112 		err = u.base.Chmod(name, mode)
       
   113 	}
       
   114 	if err != nil {
       
   115 		return err
       
   116 	}
       
   117 	return u.layer.Chmod(name, mode)
       
   118 }
       
   119 
       
   120 func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
       
   121 	st, fi, err := u.cacheStatus(name)
       
   122 	if err != nil {
       
   123 		return nil, err
       
   124 	}
       
   125 	switch st {
       
   126 	case cacheMiss:
       
   127 		return u.base.Stat(name)
       
   128 	default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
       
   129 		return fi, nil
       
   130 	}
       
   131 }
       
   132 
       
   133 func (u *CacheOnReadFs) Rename(oldname, newname string) error {
       
   134 	st, _, err := u.cacheStatus(oldname)
       
   135 	if err != nil {
       
   136 		return err
       
   137 	}
       
   138 	switch st {
       
   139 	case cacheLocal:
       
   140 	case cacheHit:
       
   141 		err = u.base.Rename(oldname, newname)
       
   142 	case cacheStale, cacheMiss:
       
   143 		if err := u.copyToLayer(oldname); err != nil {
       
   144 			return err
       
   145 		}
       
   146 		err = u.base.Rename(oldname, newname)
       
   147 	}
       
   148 	if err != nil {
       
   149 		return err
       
   150 	}
       
   151 	return u.layer.Rename(oldname, newname)
       
   152 }
       
   153 
       
   154 func (u *CacheOnReadFs) Remove(name string) error {
       
   155 	st, _, err := u.cacheStatus(name)
       
   156 	if err != nil {
       
   157 		return err
       
   158 	}
       
   159 	switch st {
       
   160 	case cacheLocal:
       
   161 	case cacheHit, cacheStale, cacheMiss:
       
   162 		err = u.base.Remove(name)
       
   163 	}
       
   164 	if err != nil {
       
   165 		return err
       
   166 	}
       
   167 	return u.layer.Remove(name)
       
   168 }
       
   169 
       
   170 func (u *CacheOnReadFs) RemoveAll(name string) error {
       
   171 	st, _, err := u.cacheStatus(name)
       
   172 	if err != nil {
       
   173 		return err
       
   174 	}
       
   175 	switch st {
       
   176 	case cacheLocal:
       
   177 	case cacheHit, cacheStale, cacheMiss:
       
   178 		err = u.base.RemoveAll(name)
       
   179 	}
       
   180 	if err != nil {
       
   181 		return err
       
   182 	}
       
   183 	return u.layer.RemoveAll(name)
       
   184 }
       
   185 
       
   186 func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
       
   187 	st, _, err := u.cacheStatus(name)
       
   188 	if err != nil {
       
   189 		return nil, err
       
   190 	}
       
   191 	switch st {
       
   192 	case cacheLocal, cacheHit:
       
   193 	default:
       
   194 		if err := u.copyToLayer(name); err != nil {
       
   195 			return nil, err
       
   196 		}
       
   197 	}
       
   198 	if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
       
   199 		bfi, err := u.base.OpenFile(name, flag, perm)
       
   200 		if err != nil {
       
   201 			return nil, err
       
   202 		}
       
   203 		lfi, err := u.layer.OpenFile(name, flag, perm)
       
   204 		if err != nil {
       
   205 			bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
       
   206 			return nil, err
       
   207 		}
       
   208 		return &UnionFile{Base: bfi, Layer: lfi}, nil
       
   209 	}
       
   210 	return u.layer.OpenFile(name, flag, perm)
       
   211 }
       
   212 
       
   213 func (u *CacheOnReadFs) Open(name string) (File, error) {
       
   214 	st, fi, err := u.cacheStatus(name)
       
   215 	if err != nil {
       
   216 		return nil, err
       
   217 	}
       
   218 
       
   219 	switch st {
       
   220 	case cacheLocal:
       
   221 		return u.layer.Open(name)
       
   222 
       
   223 	case cacheMiss:
       
   224 		bfi, err := u.base.Stat(name)
       
   225 		if err != nil {
       
   226 			return nil, err
       
   227 		}
       
   228 		if bfi.IsDir() {
       
   229 			return u.base.Open(name)
       
   230 		}
       
   231 		if err := u.copyToLayer(name); err != nil {
       
   232 			return nil, err
       
   233 		}
       
   234 		return u.layer.Open(name)
       
   235 
       
   236 	case cacheStale:
       
   237 		if !fi.IsDir() {
       
   238 			if err := u.copyToLayer(name); err != nil {
       
   239 				return nil, err
       
   240 			}
       
   241 			return u.layer.Open(name)
       
   242 		}
       
   243 	case cacheHit:
       
   244 		if !fi.IsDir() {
       
   245 			return u.layer.Open(name)
       
   246 		}
       
   247 	}
       
   248 	// the dirs from cacheHit, cacheStale fall down here:
       
   249 	bfile, _ := u.base.Open(name)
       
   250 	lfile, err := u.layer.Open(name)
       
   251 	if err != nil && bfile == nil {
       
   252 		return nil, err
       
   253 	}
       
   254 	return &UnionFile{Base: bfile, Layer: lfile}, nil
       
   255 }
       
   256 
       
   257 func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
       
   258 	err := u.base.Mkdir(name, perm)
       
   259 	if err != nil {
       
   260 		return err
       
   261 	}
       
   262 	return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
       
   263 }
       
   264 
       
   265 func (u *CacheOnReadFs) Name() string {
       
   266 	return "CacheOnReadFs"
       
   267 }
       
   268 
       
   269 func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
       
   270 	err := u.base.MkdirAll(name, perm)
       
   271 	if err != nil {
       
   272 		return err
       
   273 	}
       
   274 	return u.layer.MkdirAll(name, perm)
       
   275 }
       
   276 
       
   277 func (u *CacheOnReadFs) Create(name string) (File, error) {
       
   278 	bfh, err := u.base.Create(name)
       
   279 	if err != nil {
       
   280 		return nil, err
       
   281 	}
       
   282 	lfh, err := u.layer.Create(name)
       
   283 	if err != nil {
       
   284 		// oops, see comment about OS_TRUNC above, should we remove? then we have to
       
   285 		// remember if the file did not exist before
       
   286 		bfh.Close()
       
   287 		return nil, err
       
   288 	}
       
   289 	return &UnionFile{Base: bfh, Layer: lfh}, nil
       
   290 }