Path: blob/main/vendor/github.com/spf13/afero/cacheOnReadFs.go
2875 views
package afero12import (3"os"4"syscall"5"time"6)78// If the cache duration is 0, cache time will be unlimited, i.e. once9// a file is in the layer, the base will never be read again for this file.10//11// For cache times greater than 0, the modification time of a file is12// checked. Note that a lot of file system implementations only allow a13// resolution of a second for timestamps... or as the godoc for os.Chtimes()14// states: "The underlying filesystem may truncate or round the values to a15// less precise time unit."16//17// This caching union will forward all write calls also to the base file18// system first. To prevent writing to the base Fs, wrap it in a read-only19// filter - Note: this will also make the overlay read-only, for writing files20// in the overlay, use the overlay Fs directly, not via the union Fs.21type CacheOnReadFs struct {22base Fs23layer Fs24cacheTime time.Duration25}2627func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {28return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}29}3031type cacheState int3233const (34// not present in the overlay, unknown if it exists in the base:35cacheMiss cacheState = iota36// present in the overlay and in base, base file is newer:37cacheStale38// present in the overlay - with cache time == 0 it may exist in the base,39// with cacheTime > 0 it exists in the base and is same age or newer in the40// overlay41cacheHit42// happens if someone writes directly to the overlay without43// going through this union44cacheLocal45)4647func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {48var lfi, bfi os.FileInfo49lfi, err = u.layer.Stat(name)50if err == nil {51if u.cacheTime == 0 {52return cacheHit, lfi, nil53}54if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {55bfi, err = u.base.Stat(name)56if err != nil {57return cacheLocal, lfi, nil58}59if bfi.ModTime().After(lfi.ModTime()) {60return cacheStale, bfi, nil61}62}63return cacheHit, lfi, nil64}6566if err == syscall.ENOENT || os.IsNotExist(err) {67return cacheMiss, nil, nil68}6970return cacheMiss, nil, err71}7273func (u *CacheOnReadFs) copyToLayer(name string) error {74return copyToLayer(u.base, u.layer, name)75}7677func (u *CacheOnReadFs) copyFileToLayer(name string, flag int, perm os.FileMode) error {78return copyFileToLayer(u.base, u.layer, name, flag, perm)79}8081func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {82st, _, err := u.cacheStatus(name)83if err != nil {84return err85}86switch st {87case cacheLocal:88case cacheHit:89err = u.base.Chtimes(name, atime, mtime)90case cacheStale, cacheMiss:91if err := u.copyToLayer(name); err != nil {92return err93}94err = u.base.Chtimes(name, atime, mtime)95}96if err != nil {97return err98}99return u.layer.Chtimes(name, atime, mtime)100}101102func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {103st, _, err := u.cacheStatus(name)104if err != nil {105return err106}107switch st {108case cacheLocal:109case cacheHit:110err = u.base.Chmod(name, mode)111case cacheStale, cacheMiss:112if err := u.copyToLayer(name); err != nil {113return err114}115err = u.base.Chmod(name, mode)116}117if err != nil {118return err119}120return u.layer.Chmod(name, mode)121}122123func (u *CacheOnReadFs) Chown(name string, uid, gid int) error {124st, _, err := u.cacheStatus(name)125if err != nil {126return err127}128switch st {129case cacheLocal:130case cacheHit:131err = u.base.Chown(name, uid, gid)132case cacheStale, cacheMiss:133if err := u.copyToLayer(name); err != nil {134return err135}136err = u.base.Chown(name, uid, gid)137}138if err != nil {139return err140}141return u.layer.Chown(name, uid, gid)142}143144func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {145st, fi, err := u.cacheStatus(name)146if err != nil {147return nil, err148}149switch st {150case cacheMiss:151return u.base.Stat(name)152default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo153return fi, nil154}155}156157func (u *CacheOnReadFs) Rename(oldname, newname string) error {158st, _, err := u.cacheStatus(oldname)159if err != nil {160return err161}162switch st {163case cacheLocal:164case cacheHit:165err = u.base.Rename(oldname, newname)166case cacheStale, cacheMiss:167if err := u.copyToLayer(oldname); err != nil {168return err169}170err = u.base.Rename(oldname, newname)171}172if err != nil {173return err174}175return u.layer.Rename(oldname, newname)176}177178func (u *CacheOnReadFs) Remove(name string) error {179st, _, err := u.cacheStatus(name)180if err != nil {181return err182}183switch st {184case cacheLocal:185case cacheHit, cacheStale, cacheMiss:186err = u.base.Remove(name)187}188if err != nil {189return err190}191return u.layer.Remove(name)192}193194func (u *CacheOnReadFs) RemoveAll(name string) error {195st, _, err := u.cacheStatus(name)196if err != nil {197return err198}199switch st {200case cacheLocal:201case cacheHit, cacheStale, cacheMiss:202err = u.base.RemoveAll(name)203}204if err != nil {205return err206}207return u.layer.RemoveAll(name)208}209210func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {211st, _, err := u.cacheStatus(name)212if err != nil {213return nil, err214}215switch st {216case cacheLocal, cacheHit:217default:218if err := u.copyFileToLayer(name, flag, perm); err != nil {219return nil, err220}221}222if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {223bfi, err := u.base.OpenFile(name, flag, perm)224if err != nil {225return nil, err226}227lfi, err := u.layer.OpenFile(name, flag, perm)228if err != nil {229bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?230return nil, err231}232return &UnionFile{Base: bfi, Layer: lfi}, nil233}234return u.layer.OpenFile(name, flag, perm)235}236237func (u *CacheOnReadFs) Open(name string) (File, error) {238st, fi, err := u.cacheStatus(name)239if err != nil {240return nil, err241}242243switch st {244case cacheLocal:245return u.layer.Open(name)246247case cacheMiss:248bfi, err := u.base.Stat(name)249if err != nil {250return nil, err251}252if bfi.IsDir() {253return u.base.Open(name)254}255if err := u.copyToLayer(name); err != nil {256return nil, err257}258return u.layer.Open(name)259260case cacheStale:261if !fi.IsDir() {262if err := u.copyToLayer(name); err != nil {263return nil, err264}265return u.layer.Open(name)266}267case cacheHit:268if !fi.IsDir() {269return u.layer.Open(name)270}271}272// the dirs from cacheHit, cacheStale fall down here:273bfile, _ := u.base.Open(name)274lfile, err := u.layer.Open(name)275if err != nil && bfile == nil {276return nil, err277}278return &UnionFile{Base: bfile, Layer: lfile}, nil279}280281func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {282err := u.base.Mkdir(name, perm)283if err != nil {284return err285}286return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache287}288289func (u *CacheOnReadFs) Name() string {290return "CacheOnReadFs"291}292293func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {294err := u.base.MkdirAll(name, perm)295if err != nil {296return err297}298return u.layer.MkdirAll(name, perm)299}300301func (u *CacheOnReadFs) Create(name string) (File, error) {302bfh, err := u.base.Create(name)303if err != nil {304return nil, err305}306lfh, err := u.layer.Create(name)307if err != nil {308// oops, see comment about OS_TRUNC above, should we remove? then we have to309// remember if the file did not exist before310bfh.Close()311return nil, err312}313return &UnionFile{Base: bfh, Layer: lfh}, nil314}315316317