github.com/hanwen/go-fuse@v1.0.0/unionfs/cachingfs.go (about)

     1  // Copyright 2016 the Go-FUSE Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package unionfs
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/hanwen/go-fuse/fuse"
    14  	"github.com/hanwen/go-fuse/fuse/nodefs"
    15  	"github.com/hanwen/go-fuse/fuse/pathfs"
    16  )
    17  
    18  const _XATTRSEP = "@XATTR@"
    19  
    20  type attrResponse struct {
    21  	*fuse.Attr
    22  	fuse.Status
    23  }
    24  
    25  type xattrResponse struct {
    26  	data []byte
    27  	fuse.Status
    28  }
    29  
    30  type dirResponse struct {
    31  	entries []fuse.DirEntry
    32  	fuse.Status
    33  }
    34  
    35  type linkResponse struct {
    36  	linkContent string
    37  	fuse.Status
    38  }
    39  
    40  // Caches filesystem metadata.
    41  type cachingFileSystem struct {
    42  	pathfs.FileSystem
    43  
    44  	attributes *TimedCache
    45  	dirs       *TimedCache
    46  	links      *TimedCache
    47  	xattr      *TimedCache
    48  }
    49  
    50  func readDir(fs pathfs.FileSystem, name string) *dirResponse {
    51  	origStream, code := fs.OpenDir(name, nil)
    52  
    53  	r := &dirResponse{nil, code}
    54  	if !code.Ok() {
    55  		return r
    56  	}
    57  	r.entries = origStream
    58  	return r
    59  }
    60  
    61  func getAttr(fs pathfs.FileSystem, name string) *attrResponse {
    62  	a, code := fs.GetAttr(name, nil)
    63  	return &attrResponse{
    64  		Attr:   a,
    65  		Status: code,
    66  	}
    67  }
    68  
    69  func getXAttr(fs pathfs.FileSystem, nameAttr string) *xattrResponse {
    70  	ns := strings.SplitN(nameAttr, _XATTRSEP, 2)
    71  	a, code := fs.GetXAttr(ns[0], ns[1], nil)
    72  	return &xattrResponse{
    73  		data:   a,
    74  		Status: code,
    75  	}
    76  }
    77  
    78  func readLink(fs pathfs.FileSystem, name string) *linkResponse {
    79  	a, code := fs.Readlink(name, nil)
    80  	return &linkResponse{
    81  		linkContent: a,
    82  		Status:      code,
    83  	}
    84  }
    85  
    86  func NewCachingFileSystem(fs pathfs.FileSystem, ttl time.Duration) pathfs.FileSystem {
    87  	c := new(cachingFileSystem)
    88  	c.FileSystem = fs
    89  	c.attributes = NewTimedCache(func(n string) (interface{}, bool) {
    90  		a := getAttr(fs, n)
    91  		return a, a.Ok()
    92  	}, ttl)
    93  	c.dirs = NewTimedCache(func(n string) (interface{}, bool) {
    94  		d := readDir(fs, n)
    95  		return d, d.Ok()
    96  	}, ttl)
    97  	c.links = NewTimedCache(func(n string) (interface{}, bool) {
    98  		l := readLink(fs, n)
    99  		return l, l.Ok()
   100  	}, ttl)
   101  	c.xattr = NewTimedCache(func(n string) (interface{}, bool) {
   102  		l := getXAttr(fs, n)
   103  		return l, l.Ok()
   104  	}, ttl)
   105  	return c
   106  }
   107  
   108  func (fs *cachingFileSystem) DropCache() {
   109  	for _, c := range []*TimedCache{fs.attributes, fs.dirs, fs.links, fs.xattr} {
   110  		c.DropAll(nil)
   111  	}
   112  }
   113  
   114  func (fs *cachingFileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
   115  	if name == _DROP_CACHE {
   116  		return &fuse.Attr{
   117  			Mode: fuse.S_IFREG | 0777,
   118  		}, fuse.OK
   119  	}
   120  
   121  	r := fs.attributes.Get(name).(*attrResponse)
   122  	return r.Attr, r.Status
   123  }
   124  
   125  func (fs *cachingFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
   126  	key := name + _XATTRSEP + attr
   127  	r := fs.xattr.Get(key).(*xattrResponse)
   128  	return r.data, r.Status
   129  }
   130  
   131  func (fs *cachingFileSystem) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
   132  	r := fs.links.Get(name).(*linkResponse)
   133  	return r.linkContent, r.Status
   134  }
   135  
   136  func (fs *cachingFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
   137  	r := fs.dirs.Get(name).(*dirResponse)
   138  	return r.entries, r.Status
   139  }
   140  
   141  func (fs *cachingFileSystem) String() string {
   142  	return fmt.Sprintf("cachingFileSystem(%v)", fs.FileSystem)
   143  }
   144  
   145  func (fs *cachingFileSystem) Open(name string, flags uint32, context *fuse.Context) (f nodefs.File, status fuse.Status) {
   146  	if flags&fuse.O_ANYWRITE != 0 && name == _DROP_CACHE {
   147  		log.Println("Dropping cache for", fs)
   148  		fs.DropCache()
   149  	}
   150  	return fs.FileSystem.Open(name, flags, context)
   151  }