github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/node_cache.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"fmt"
     9  	"sync"
    10  
    11  	"github.com/keybase/client/go/kbfs/data"
    12  )
    13  
    14  type nodeCacheEntry struct {
    15  	core     *nodeCore
    16  	refCount int
    17  }
    18  
    19  // nodeCacheStandard implements the NodeCache interface by tracking
    20  // the reference counts of nodeStandard Nodes, and using their member
    21  // fields to construct paths.
    22  type nodeCacheStandard struct {
    23  	folderBranch data.FolderBranch
    24  
    25  	lock           sync.RWMutex
    26  	nodes          map[data.BlockRef]*nodeCacheEntry
    27  	rootWrappers   []func(Node) Node
    28  	makeObfuscator func() data.Obfuscator
    29  }
    30  
    31  var _ NodeCache = (*nodeCacheStandard)(nil)
    32  
    33  func newNodeCacheStandard(fb data.FolderBranch) *nodeCacheStandard {
    34  	return &nodeCacheStandard{
    35  		folderBranch: fb,
    36  		nodes:        make(map[data.BlockRef]*nodeCacheEntry),
    37  	}
    38  }
    39  
    40  // lock must be locked for writing by the caller
    41  func (ncs *nodeCacheStandard) forgetLocked(core *nodeCore) {
    42  	ref := core.pathNode.Ref()
    43  
    44  	entry, ok := ncs.nodes[ref]
    45  	if !ok {
    46  		return
    47  	}
    48  	if entry.core != core {
    49  		return
    50  	}
    51  
    52  	entry.refCount--
    53  	if entry.refCount <= 0 {
    54  		delete(ncs.nodes, ref)
    55  	}
    56  }
    57  
    58  // should be called only by nodeStandardFinalizer().
    59  func (ncs *nodeCacheStandard) forget(core *nodeCore) {
    60  	ncs.lock.Lock()
    61  	defer ncs.lock.Unlock()
    62  	ncs.forgetLocked(core)
    63  }
    64  
    65  // lock must be held for writing by the caller
    66  func (ncs *nodeCacheStandard) newChildForParentLocked(parent Node) (*nodeStandard, error) {
    67  	nodeStandard, ok := parent.Unwrap().(*nodeStandard)
    68  	if !ok {
    69  		return nil, ParentNodeNotFoundError{data.BlockRef{}}
    70  	}
    71  
    72  	ref := nodeStandard.core.pathNode.Ref()
    73  	entry, ok := ncs.nodes[ref]
    74  	if !ok {
    75  		return nil, ParentNodeNotFoundError{ref}
    76  	}
    77  	if nodeStandard.core != entry.core {
    78  		return nil, ParentNodeNotFoundError{ref}
    79  	}
    80  	return nodeStandard, nil
    81  }
    82  func (ncs *nodeCacheStandard) wrapNodeStandard(
    83  	n Node, rootWrappers []func(Node) Node, parent Node) Node {
    84  	if parent != nil {
    85  		return parent.WrapChild(n)
    86  	}
    87  	for _, f := range rootWrappers {
    88  		n = f(n)
    89  	}
    90  	return n
    91  }
    92  
    93  func (ncs *nodeCacheStandard) makeNodeStandardForEntryLocked(
    94  	entry *nodeCacheEntry) *nodeStandard {
    95  	entry.refCount++
    96  	return makeNodeStandard(entry.core)
    97  }
    98  
    99  // GetOrCreate implements the NodeCache interface for nodeCacheStandard.
   100  func (ncs *nodeCacheStandard) GetOrCreate(
   101  	ptr data.BlockPointer, name data.PathPartString, parent Node,
   102  	et data.EntryType) (n Node, err error) {
   103  	var rootWrappers []func(Node) Node
   104  	defer func() {
   105  		if n != nil {
   106  			n = ncs.wrapNodeStandard(n, rootWrappers, parent)
   107  		}
   108  	}()
   109  
   110  	if !ptr.IsValid() {
   111  		// Temporary code to track down bad block
   112  		// pointers. Remove when not needed anymore.
   113  		panic(InvalidBlockRefError{ptr.Ref()})
   114  	}
   115  
   116  	if name.Plaintext() == "" {
   117  		return nil, EmptyNameError{ptr.Ref()}
   118  	}
   119  
   120  	ncs.lock.Lock()
   121  	defer ncs.lock.Unlock()
   122  	rootWrappers = ncs.rootWrappers
   123  	entry, ok := ncs.nodes[ptr.Ref()]
   124  	if ok {
   125  		// If the entry happens to be unlinked, we may be in a
   126  		// situation where a node got unlinked and then recreated, but
   127  		// someone held onto a node the whole time and so it never got
   128  		// removed from the cache.  In that case, forcibly remove it
   129  		// from the cache to make room for the new node.
   130  		if parent != nil && entry.core.parent == nil {
   131  			delete(ncs.nodes, ptr.Ref())
   132  		} else {
   133  			return ncs.makeNodeStandardForEntryLocked(entry), nil
   134  		}
   135  	}
   136  
   137  	if parent != nil {
   138  		// Make sure a child can be made for this parent.
   139  		_, err := ncs.newChildForParentLocked(parent)
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  	}
   144  
   145  	entry = &nodeCacheEntry{}
   146  	if et == data.Dir && ncs.makeObfuscator != nil {
   147  		entry.core = newNodeCoreForDir(
   148  			ptr, name, parent, ncs, ncs.makeObfuscator())
   149  	} else {
   150  		entry.core = newNodeCore(ptr, name, parent, ncs, et)
   151  	}
   152  	ncs.nodes[ptr.Ref()] = entry
   153  	return ncs.makeNodeStandardForEntryLocked(entry), nil
   154  }
   155  
   156  // Get implements the NodeCache interface for nodeCacheStandard.
   157  func (ncs *nodeCacheStandard) Get(ref data.BlockRef) (n Node) {
   158  	if ref == (data.BlockRef{}) {
   159  		return nil
   160  	}
   161  
   162  	// Temporary code to track down bad block pointers. Remove (or
   163  	// return an error) when not needed anymore.
   164  	if !ref.IsValid() {
   165  		panic(InvalidBlockRefError{ref})
   166  	}
   167  
   168  	var rootWrappers []func(Node) Node
   169  	var parent Node
   170  	defer func() {
   171  		if n != nil {
   172  			n = ncs.wrapNodeStandard(n, rootWrappers, parent)
   173  		}
   174  	}()
   175  
   176  	ncs.lock.Lock()
   177  	defer ncs.lock.Unlock()
   178  	rootWrappers = ncs.rootWrappers
   179  	entry, ok := ncs.nodes[ref]
   180  	if !ok {
   181  		return nil
   182  	}
   183  	ns := ncs.makeNodeStandardForEntryLocked(entry)
   184  	parent = ns.core.parent // get while under lock
   185  	return ns
   186  }
   187  
   188  // UpdatePointer implements the NodeCache interface for nodeCacheStandard.
   189  func (ncs *nodeCacheStandard) UpdatePointer(
   190  	oldRef data.BlockRef, newPtr data.BlockPointer) (updatedNode NodeID) {
   191  	if oldRef == (data.BlockRef{}) && newPtr == (data.BlockPointer{}) {
   192  		return nil
   193  	}
   194  
   195  	if !oldRef.IsValid() {
   196  		panic(fmt.Sprintf("invalid oldRef %s with newPtr %s", oldRef, newPtr))
   197  	}
   198  
   199  	if !newPtr.IsValid() {
   200  		panic(fmt.Sprintf("invalid newPtr %s with oldRef %s", newPtr, oldRef))
   201  	}
   202  
   203  	ncs.lock.Lock()
   204  	defer ncs.lock.Unlock()
   205  	entry, ok := ncs.nodes[oldRef]
   206  	if !ok {
   207  		return nil
   208  	}
   209  
   210  	// Cannot update the pointer for an unlinked node.
   211  	if entry.core.cachedPath.IsValid() {
   212  		return nil
   213  	}
   214  
   215  	entry.core.pathNode.BlockPointer = newPtr
   216  	delete(ncs.nodes, oldRef)
   217  	ncs.nodes[newPtr.Ref()] = entry
   218  	return entry.core
   219  }
   220  
   221  // Move implements the NodeCache interface for nodeCacheStandard.
   222  func (ncs *nodeCacheStandard) Move(
   223  	ref data.BlockRef, newParent Node, newName data.PathPartString) (
   224  	undoFn func(), err error) {
   225  	if ref == (data.BlockRef{}) {
   226  		return nil, nil
   227  	}
   228  
   229  	// Temporary code to track down bad block pointers. Remove (or
   230  	// return an error) when not needed anymore.
   231  	if !ref.IsValid() {
   232  		panic(InvalidBlockRefError{ref})
   233  	}
   234  
   235  	if newName.Plaintext() == "" {
   236  		return nil, EmptyNameError{ref}
   237  	}
   238  
   239  	ncs.lock.Lock()
   240  	defer ncs.lock.Unlock()
   241  	entry, ok := ncs.nodes[ref]
   242  	if !ok {
   243  		return nil, nil
   244  	}
   245  
   246  	newParentNS, err := ncs.newChildForParentLocked(newParent)
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  
   251  	oldParent := entry.core.parent
   252  	oldName := entry.core.pathNode.Name
   253  
   254  	entry.core.parent = newParentNS
   255  	entry.core.pathNode.Name = newName
   256  
   257  	return func() {
   258  		entry.core.parent = oldParent
   259  		entry.core.pathNode.Name = oldName
   260  	}, nil
   261  }
   262  
   263  // Unlink implements the NodeCache interface for nodeCacheStandard.
   264  func (ncs *nodeCacheStandard) Unlink(
   265  	ref data.BlockRef, oldPath data.Path, oldDe data.DirEntry) (undoFn func()) {
   266  	if ref == (data.BlockRef{}) {
   267  		return nil
   268  	}
   269  
   270  	// Temporary code to track down bad block pointers. Remove (or
   271  	// return an error) when not needed anymore.
   272  	if !ref.IsValid() {
   273  		panic(InvalidBlockRefError{ref})
   274  	}
   275  
   276  	ncs.lock.Lock()
   277  	defer ncs.lock.Unlock()
   278  	entry, ok := ncs.nodes[ref]
   279  	if !ok {
   280  		return nil
   281  	}
   282  
   283  	if entry.core.cachedPath.IsValid() {
   284  		// Already unlinked!
   285  		return nil
   286  	}
   287  
   288  	oldParent := entry.core.parent
   289  	oldName := entry.core.pathNode.Name
   290  
   291  	entry.core.cachedPath = oldPath
   292  	entry.core.cachedDe = oldDe
   293  	entry.core.parent = nil
   294  	entry.core.pathNode.Name = data.PathPartString{}
   295  
   296  	return func() {
   297  		entry.core.cachedPath = data.Path{}
   298  		entry.core.cachedDe = data.DirEntry{}
   299  		entry.core.parent = oldParent
   300  		entry.core.pathNode.Name = oldName
   301  	}
   302  }
   303  
   304  // IsUnlinked implements the NodeCache interface for
   305  // nodeCacheStandard.
   306  func (ncs *nodeCacheStandard) IsUnlinked(node Node) bool {
   307  	ncs.lock.RLock()
   308  	defer ncs.lock.RUnlock()
   309  
   310  	ns, ok := node.Unwrap().(*nodeStandard)
   311  	if !ok {
   312  		return false
   313  	}
   314  
   315  	return ns.core.cachedPath.IsValid()
   316  }
   317  
   318  // UnlinkedDirEntry implements the NodeCache interface for
   319  // nodeCacheStandard.
   320  func (ncs *nodeCacheStandard) UnlinkedDirEntry(node Node) data.DirEntry {
   321  	ncs.lock.RLock()
   322  	defer ncs.lock.RUnlock()
   323  
   324  	ns, ok := node.Unwrap().(*nodeStandard)
   325  	if !ok {
   326  		return data.DirEntry{}
   327  	}
   328  
   329  	return ns.core.cachedDe
   330  }
   331  
   332  // UpdateUnlinkedDirEntry implements the NodeCache interface for
   333  // nodeCacheStandard.
   334  func (ncs *nodeCacheStandard) UpdateUnlinkedDirEntry(
   335  	node Node, newDe data.DirEntry) {
   336  	ncs.lock.Lock()
   337  	defer ncs.lock.Unlock()
   338  
   339  	ns, ok := node.Unwrap().(*nodeStandard)
   340  	if !ok {
   341  		return
   342  	}
   343  
   344  	ns.core.cachedDe = newDe
   345  }
   346  
   347  // PathFromNode implements the NodeCache interface for nodeCacheStandard.
   348  func (ncs *nodeCacheStandard) PathFromNode(node Node) (p data.Path) {
   349  	ncs.lock.RLock()
   350  	defer ncs.lock.RUnlock()
   351  
   352  	ns, ok := node.Unwrap().(*nodeStandard)
   353  	if !ok {
   354  		p.Path = nil
   355  		return
   356  	}
   357  
   358  	p.ChildObfuscator = ns.core.obfuscator
   359  
   360  	for ns != nil {
   361  		core := ns.core
   362  		if core.parent == nil && len(core.cachedPath.Path) > 0 {
   363  			// The node was unlinked, but is still in use, so use its
   364  			// cached path.  The path is already reversed, so append
   365  			// it backwards one-by-one to the existing path.  If this
   366  			// is the first node, we can just optimize by returning
   367  			// the complete cached path.
   368  			if len(p.Path) == 0 {
   369  				return core.cachedPath
   370  			}
   371  			for i := len(core.cachedPath.Path) - 1; i >= 0; i-- {
   372  				p.Path = append(p.Path, core.cachedPath.Path[i])
   373  			}
   374  			break
   375  		}
   376  
   377  		p.Path = append(p.Path, *core.pathNode)
   378  		if core.parent != nil {
   379  			ns = core.parent.Unwrap().(*nodeStandard)
   380  		} else {
   381  			break
   382  		}
   383  	}
   384  
   385  	// need to reverse the path nodes
   386  	for i := len(p.Path)/2 - 1; i >= 0; i-- {
   387  		opp := len(p.Path) - 1 - i
   388  		p.Path[i], p.Path[opp] = p.Path[opp], p.Path[i]
   389  	}
   390  
   391  	// TODO: would it make any sense to cache the constructed path?
   392  	p.FolderBranch = ncs.folderBranch
   393  	return
   394  }
   395  
   396  // AllNodes implements the NodeCache interface for nodeCacheStandard.
   397  func (ncs *nodeCacheStandard) AllNodes() (nodes []Node) {
   398  	ncs.lock.RLock()
   399  	defer ncs.lock.RUnlock()
   400  	nodes = make([]Node, 0, len(ncs.nodes))
   401  	for _, entry := range ncs.nodes {
   402  		nodes = append(nodes, ncs.makeNodeStandardForEntryLocked(entry))
   403  	}
   404  	return nodes
   405  }
   406  
   407  // AllNodeChildren implements the NodeCache interface for nodeCacheStandard.
   408  func (ncs *nodeCacheStandard) AllNodeChildren(n Node) (nodes []Node) {
   409  	ncs.lock.RLock()
   410  	defer ncs.lock.RUnlock()
   411  	nodes = make([]Node, 0, len(ncs.nodes))
   412  	entryIDs := make(map[NodeID]bool)
   413  	for _, entry := range ncs.nodes {
   414  		var pathIDs []NodeID
   415  		parent := entry.core.parent
   416  		for parent != nil {
   417  			// If the node's parent is what we're looking for (or on
   418  			// the path to what we're looking for), include it in the
   419  			// list.
   420  			parentID := parent.GetID()
   421  			if parentID == n.GetID() || entryIDs[parentID] {
   422  				nodes = append(nodes, ncs.makeNodeStandardForEntryLocked(entry))
   423  				for _, id := range pathIDs {
   424  					entryIDs[id] = true
   425  				}
   426  				entryIDs[entry.core] = true
   427  				break
   428  			}
   429  
   430  			// Otherwise, remember this parent and continue back
   431  			// toward the root.
   432  			pathIDs = append(pathIDs, parentID)
   433  			ns, ok := parent.Unwrap().(*nodeStandard)
   434  			if !ok {
   435  				break
   436  			}
   437  			parent = ns.core.parent
   438  		}
   439  	}
   440  	return nodes
   441  }
   442  
   443  func (ncs *nodeCacheStandard) AddRootWrapper(f func(Node) Node) {
   444  	ncs.lock.Lock()
   445  	defer ncs.lock.Unlock()
   446  	ncs.rootWrappers = append(ncs.rootWrappers, f)
   447  }
   448  
   449  func (ncs *nodeCacheStandard) SetObfuscatorMaker(
   450  	makeOb func() data.Obfuscator) {
   451  	ncs.lock.Lock()
   452  	defer ncs.lock.Unlock()
   453  	ncs.makeObfuscator = makeOb
   454  }
   455  
   456  func (ncs *nodeCacheStandard) ObfuscatorMaker() func() data.Obfuscator {
   457  	ncs.lock.RLock()
   458  	defer ncs.lock.RUnlock()
   459  	return ncs.makeObfuscator
   460  }