github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/p9/path_tree.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package p9
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/atomicbitops"
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/sync"
    22  )
    23  
    24  // pathNode is a single node in a path traversal.
    25  //
    26  // These are shared by all fidRefs that point to the same path.
    27  //
    28  // Lock ordering:
    29  //
    30  //	opMu
    31  //	  childMu
    32  //
    33  // Two different pathNodes may only be locked if Server.renameMu is held for
    34  // write, in which case they can be acquired in any order.
    35  type pathNode struct {
    36  	// opMu synchronizes high-level, sematic operations, such as the
    37  	// simultaneous creation and deletion of a file.
    38  	opMu sync.RWMutex
    39  
    40  	// deleted indicates that the backing file has been deleted. We stop many
    41  	// operations at the API level if they are incompatible with a file that has
    42  	// already been unlinked. deleted is protected by opMu. However, it may be
    43  	// changed without opMu if this node is deleted as part of an entire subtree
    44  	// on unlink. So deleted must only be accessed/mutated using atomics.
    45  	deleted atomicbitops.Uint32
    46  
    47  	// childMu protects the fields below.
    48  	childMu sync.RWMutex
    49  
    50  	// childNodes maps child path component names to their pathNode.
    51  	childNodes map[string]*pathNode
    52  
    53  	// childRefs maps child path component names to all of the their
    54  	// references.
    55  	childRefs map[string]map[*fidRef]struct{}
    56  
    57  	// childRefNames maps child references back to their path component
    58  	// name.
    59  	childRefNames map[*fidRef]string
    60  }
    61  
    62  func newPathNode() *pathNode {
    63  	return &pathNode{
    64  		childNodes:    make(map[string]*pathNode),
    65  		childRefs:     make(map[string]map[*fidRef]struct{}),
    66  		childRefNames: make(map[*fidRef]string),
    67  	}
    68  }
    69  
    70  // forEachChildRef calls fn for each child reference.
    71  func (p *pathNode) forEachChildRef(fn func(ref *fidRef, name string)) {
    72  	p.childMu.RLock()
    73  	defer p.childMu.RUnlock()
    74  
    75  	for name, m := range p.childRefs {
    76  		for ref := range m {
    77  			fn(ref, name)
    78  		}
    79  	}
    80  }
    81  
    82  // forEachChildNode calls fn for each child pathNode.
    83  func (p *pathNode) forEachChildNode(fn func(pn *pathNode)) {
    84  	p.childMu.RLock()
    85  	defer p.childMu.RUnlock()
    86  
    87  	for _, pn := range p.childNodes {
    88  		fn(pn)
    89  	}
    90  }
    91  
    92  // pathNodeFor returns the path node for the given name, or a new one.
    93  func (p *pathNode) pathNodeFor(name string) *pathNode {
    94  	p.childMu.RLock()
    95  	// Fast path, node already exists.
    96  	if pn, ok := p.childNodes[name]; ok {
    97  		p.childMu.RUnlock()
    98  		return pn
    99  	}
   100  	p.childMu.RUnlock()
   101  
   102  	// Slow path, create a new pathNode for shared use.
   103  	p.childMu.Lock()
   104  
   105  	// Re-check after re-lock.
   106  	if pn, ok := p.childNodes[name]; ok {
   107  		p.childMu.Unlock()
   108  		return pn
   109  	}
   110  
   111  	pn := newPathNode()
   112  	p.childNodes[name] = pn
   113  	p.childMu.Unlock()
   114  	return pn
   115  }
   116  
   117  // nameFor returns the name for the given fidRef.
   118  //
   119  // Precondition: addChild is called for ref before nameFor.
   120  func (p *pathNode) nameFor(ref *fidRef) string {
   121  	p.childMu.RLock()
   122  	n, ok := p.childRefNames[ref]
   123  	p.childMu.RUnlock()
   124  
   125  	if !ok {
   126  		// This should not happen, don't proceed.
   127  		panic(fmt.Sprintf("expected name for %+v, none found", ref))
   128  	}
   129  
   130  	return n
   131  }
   132  
   133  // addChildLocked adds a child reference to p.
   134  //
   135  // Precondition: As addChild, plus childMu is locked for write.
   136  func (p *pathNode) addChildLocked(ref *fidRef, name string) {
   137  	if n, ok := p.childRefNames[ref]; ok {
   138  		// This should not happen, don't proceed.
   139  		panic(fmt.Sprintf("unexpected fidRef %+v with path %q, wanted %q", ref, n, name))
   140  	}
   141  
   142  	p.childRefNames[ref] = name
   143  
   144  	m, ok := p.childRefs[name]
   145  	if !ok {
   146  		m = make(map[*fidRef]struct{})
   147  		p.childRefs[name] = m
   148  	}
   149  
   150  	m[ref] = struct{}{}
   151  }
   152  
   153  // addChild adds a child reference to p.
   154  //
   155  // Precondition: ref may only be added once at a time.
   156  func (p *pathNode) addChild(ref *fidRef, name string) {
   157  	p.childMu.Lock()
   158  	p.addChildLocked(ref, name)
   159  	p.childMu.Unlock()
   160  }
   161  
   162  // removeChild removes the given child.
   163  //
   164  // This applies only to an individual fidRef, which is not required to exist.
   165  func (p *pathNode) removeChild(ref *fidRef) {
   166  	p.childMu.Lock()
   167  
   168  	// This ref may not exist anymore. This can occur, e.g., in unlink,
   169  	// where a removeWithName removes the ref, and then a DecRef on the ref
   170  	// attempts to remove again.
   171  	if name, ok := p.childRefNames[ref]; ok {
   172  		m, ok := p.childRefs[name]
   173  		if !ok {
   174  			// This should not happen, don't proceed.
   175  			p.childMu.Unlock()
   176  			panic(fmt.Sprintf("name %s missing from childfidRefs", name))
   177  		}
   178  
   179  		delete(m, ref)
   180  		if len(m) == 0 {
   181  			delete(p.childRefs, name)
   182  		}
   183  	}
   184  
   185  	delete(p.childRefNames, ref)
   186  
   187  	p.childMu.Unlock()
   188  }
   189  
   190  // addPathNodeFor adds an existing pathNode as the node for name.
   191  //
   192  // Preconditions: newName does not exist.
   193  func (p *pathNode) addPathNodeFor(name string, pn *pathNode) {
   194  	p.childMu.Lock()
   195  
   196  	if opn, ok := p.childNodes[name]; ok {
   197  		p.childMu.Unlock()
   198  		panic(fmt.Sprintf("unexpected pathNode %+v with path %q", opn, name))
   199  	}
   200  
   201  	p.childNodes[name] = pn
   202  	p.childMu.Unlock()
   203  }
   204  
   205  // removeWithName removes all references with the given name.
   206  //
   207  // The provided function is executed after reference removal. The only method
   208  // it may (transitively) call on this pathNode is addChildLocked.
   209  //
   210  // If a child pathNode for name exists, it is removed from this pathNode and
   211  // returned by this function. Any operations on the removed tree must use this
   212  // value.
   213  func (p *pathNode) removeWithName(name string, fn func(ref *fidRef)) *pathNode {
   214  	p.childMu.Lock()
   215  	defer p.childMu.Unlock()
   216  
   217  	if m, ok := p.childRefs[name]; ok {
   218  		for ref := range m {
   219  			delete(m, ref)
   220  			delete(p.childRefNames, ref)
   221  			if fn == nil {
   222  				// No callback provided.
   223  				continue
   224  			}
   225  			// Attempt to hold a reference while calling fn() to
   226  			// prevent concurrent destruction of the child, which
   227  			// can lead to data races. If the child has already
   228  			// been destroyed, then we can skip the callback.
   229  			if ref.TryIncRef() {
   230  				fn(ref)
   231  				ref.DecRef()
   232  			}
   233  		}
   234  	}
   235  
   236  	// Return the original path node, if it exists.
   237  	origPathNode := p.childNodes[name]
   238  	delete(p.childNodes, name)
   239  	return origPathNode
   240  }