github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/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/sagernet/gvisor/pkg/atomicbitops" 21 "github.com/sagernet/gvisor/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, semantic 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 }