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