Skip to content

Commit

Permalink
Merge pull request #118 from willscott/fixup/renameinvalidate
Browse files Browse the repository at this point in the history
Fixup/renameinvalidate
  • Loading branch information
willscott authored Jan 2, 2024
2 parents 269dbac + 7bb08ad commit 9c599ee
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 7 deletions.
2 changes: 2 additions & 0 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type Handler interface {
// Can be safely implemented via helpers/cachinghandler.
ToHandle(fs billy.Filesystem, path []string) []byte
FromHandle(fh []byte) (billy.Filesystem, []string, error)
InvalidateHandle(billy.Filesystem, []byte) error

// How many handles can be safely maintained by the handler.
HandleLimit() int
}
Expand Down
79 changes: 72 additions & 7 deletions helpers/cachinghandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/sha256"
"encoding/binary"
"io/fs"
"reflect"

"github.com/willscott/go-nfs"

Expand All @@ -23,10 +24,12 @@ func NewCachingHandlerWithVerifierLimit(h nfs.Handler, limit int, verifierLimit
nfs.Log.Warnf("Caching handler created with insufficient cache to support directory listing", "size", limit, "verifiers", verifierLimit)
}
cache, _ := lru.New[uuid.UUID, entry](limit)
reverseCache := make(map[string][]uuid.UUID)
verifiers, _ := lru.New[uint64, verifier](verifierLimit)
return &CachingHandler{
Handler: h,
activeHandles: cache,
reverseHandles: reverseCache,
activeVerifiers: verifiers,
cacheLimit: limit,
}
Expand All @@ -36,6 +39,7 @@ func NewCachingHandlerWithVerifierLimit(h nfs.Handler, limit int, verifierLimit
type CachingHandler struct {
nfs.Handler
activeHandles *lru.Cache[uuid.UUID, entry]
reverseHandles map[string][]uuid.UUID
activeVerifiers *lru.Cache[uint64, verifier]
cacheLimit int
}
Expand All @@ -49,13 +53,29 @@ type entry struct {
// In stateless nfs (when it's serving a unix fs) this can be the device + inode
// but we can generalize with a stateful local cache of handed out IDs.
func (c *CachingHandler) ToHandle(f billy.Filesystem, path []string) []byte {
joinedPath := f.Join(path...)

if handle := c.searchReverseCache(f, joinedPath); handle != nil {
return handle
}

id := uuid.New()

newPath:=make([]string,len(path))

copy(newPath,path)
c.activeHandles.Add(id, entry{f, newPath})

newPath := make([]string, len(path))

copy(newPath, path)
evictedKey, evictedPath, ok := c.activeHandles.GetOldest()
if evicted := c.activeHandles.Add(id, entry{f, newPath}); evicted && ok {
rk := evictedPath.f.Join(evictedPath.p...)
c.evictReverseCache(rk, evictedKey)
}

if _, ok := c.reverseHandles[joinedPath]; !ok {
c.reverseHandles[joinedPath] = []uuid.UUID{}
}
c.reverseHandles[joinedPath] = append(c.reverseHandles[joinedPath], id)
b, _ := id.MarshalBinary()

return b
}

Expand All @@ -74,14 +94,59 @@ func (c *CachingHandler) FromHandle(fh []byte) (billy.Filesystem, []string, erro
}
}
if ok {
newP:=make([]string,len(f.p))
copy(newP,f.p)
newP := make([]string, len(f.p))
copy(newP, f.p)
return f.f, newP, nil
}
}
return nil, []string{}, &nfs.NFSStatusError{NFSStatus: nfs.NFSStatusStale}
}

func (c *CachingHandler) searchReverseCache(f billy.Filesystem, path string) []byte {
uuids, exists := c.reverseHandles[path]

if !exists {
return nil
}

for _, id := range uuids {
if candidate, ok := c.activeHandles.Get(id); ok {
if reflect.DeepEqual(candidate.f, f) {
return id[:]
}
}
}

return nil
}

func (c *CachingHandler) evictReverseCache(path string, handle uuid.UUID) {
uuids, exists := c.reverseHandles[path]

if !exists {
return
}
for i, u := range uuids {
if u == handle {
uuids = append(uuids[:i], uuids[i+1:]...)
c.reverseHandles[path] = uuids
return
}
}
}

func (c *CachingHandler) InvalidateHandle(fs billy.Filesystem, handle []byte) error {
//Remove from cache
id, _ := uuid.FromBytes(handle)
entry, ok := c.activeHandles.Get(id)
if ok {
rk := entry.f.Join(entry.p...)
c.evictReverseCache(rk, id)
}
c.activeHandles.Remove(id)
return nil
}

// HandleLimit exports how many file handles can be safely stored by this cache.
func (c *CachingHandler) HandleLimit() int {
return c.cacheLimit
Expand Down
4 changes: 4 additions & 0 deletions helpers/nullauthhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func (h *NullAuthHandler) FromHandle([]byte) (billy.Filesystem, []string, error)
return nil, []string{}, nil
}

func (c *NullAuthHandler) InvalidateHandle(billy.Filesystem, []byte) error {
return nil
}

// HandleLImit handled by cachingHandler
func (h *NullAuthHandler) HandleLimit() int {
return -1
Expand Down
4 changes: 4 additions & 0 deletions nfs_onrename.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ func onRename(ctx context.Context, w *response, userHandle Handler) error {
return &NFSStatusError{NFSStatusIO, err}
}

if err := userHandle.InvalidateHandle(fs, from.Handle); err != nil {
return &NFSStatusError{NFSStatusServerFault, err}
}

writer := bytes.NewBuffer([]byte{})
if err := xdr.Write(writer, uint32(NFSStatusOk)); err != nil {
return &NFSStatusError{NFSStatusServerFault, err}
Expand Down

0 comments on commit 9c599ee

Please sign in to comment.