2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-10-23 07:19:11 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// These are not synchronized because we allow certain operations (file walk)
|
|
|
|
// to proceed without having to acquire a write lock. The lock in this
|
|
|
|
// structure exists to synchronize high-level, semantic operations, such as the
|
|
|
|
// simultaneous creation and deletion of a file.
|
|
|
|
//
|
|
|
|
// (+) below is the path component string.
|
|
|
|
type pathNode struct {
|
|
|
|
mu sync.RWMutex // See above.
|
|
|
|
fidRefs sync.Map // => map[*fidRef]string(+)
|
|
|
|
children sync.Map // => map[string(+)]*pathNode
|
|
|
|
count int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// pathNodeFor returns the path node for the given name, or a new one.
|
|
|
|
//
|
|
|
|
// Precondition: mu must be held in a readable fashion.
|
|
|
|
func (p *pathNode) pathNodeFor(name string) *pathNode {
|
|
|
|
// Load the existing path node.
|
|
|
|
if pn, ok := p.children.Load(name); ok {
|
|
|
|
return pn.(*pathNode)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new pathNode for shared use.
|
|
|
|
pn, _ := p.children.LoadOrStore(name, new(pathNode))
|
|
|
|
return pn.(*pathNode)
|
|
|
|
}
|
|
|
|
|
|
|
|
// nameFor returns the name for the given fidRef.
|
|
|
|
//
|
|
|
|
// Precondition: mu must be held in a readable fashion.
|
|
|
|
func (p *pathNode) nameFor(ref *fidRef) string {
|
|
|
|
if s, ok := p.fidRefs.Load(ref); ok {
|
|
|
|
return s.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
// This should not happen, don't proceed.
|
|
|
|
panic(fmt.Sprintf("expected name for %+v, none found", ref))
|
|
|
|
}
|
|
|
|
|
|
|
|
// addChild adds a child to the given pathNode.
|
|
|
|
//
|
|
|
|
// This applies only to an individual fidRef.
|
|
|
|
//
|
|
|
|
// Precondition: mu must be held in a writable fashion.
|
|
|
|
func (p *pathNode) addChild(ref *fidRef, name string) {
|
|
|
|
if s, ok := p.fidRefs.Load(ref); ok {
|
|
|
|
// This should not happen, don't proceed.
|
|
|
|
panic(fmt.Sprintf("unexpected fidRef %+v with path %q, wanted %q", ref, s, name))
|
|
|
|
}
|
|
|
|
|
|
|
|
p.fidRefs.Store(ref, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// removeChild removes the given child.
|
|
|
|
//
|
|
|
|
// This applies only to an individual fidRef.
|
|
|
|
//
|
|
|
|
// Precondition: mu must be held in a writable fashion.
|
|
|
|
func (p *pathNode) removeChild(ref *fidRef) {
|
|
|
|
p.fidRefs.Delete(ref)
|
|
|
|
}
|
|
|
|
|
|
|
|
// removeWithName removes all references with the given name.
|
|
|
|
//
|
|
|
|
// The original pathNode is returned by this function, and removed from this
|
|
|
|
// pathNode. Any operations on the removed tree must use this value.
|
|
|
|
//
|
|
|
|
// The provided function is executed after removal.
|
|
|
|
//
|
|
|
|
// Precondition: mu must be held in a writable fashion.
|
|
|
|
func (p *pathNode) removeWithName(name string, fn func(ref *fidRef)) *pathNode {
|
|
|
|
p.fidRefs.Range(func(key, value interface{}) bool {
|
|
|
|
if value.(string) == name {
|
|
|
|
p.fidRefs.Delete(key)
|
|
|
|
fn(key.(*fidRef))
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
// Return the original path node.
|
|
|
|
origPathNode := p.pathNodeFor(name)
|
|
|
|
p.children.Delete(name)
|
|
|
|
return origPathNode
|
|
|
|
}
|