108 lines
2.2 KiB
Go
108 lines
2.2 KiB
Go
package vfs
|
|
|
|
import (
|
|
"iter"
|
|
"path"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
// MountInfoNode positions a [MountInfoEntry] in its mount hierarchy.
|
|
type MountInfoNode struct {
|
|
*MountInfoEntry
|
|
FirstChild *MountInfoNode `json:"first_child"`
|
|
NextSibling *MountInfoNode `json:"next_sibling"`
|
|
|
|
Clean string `json:"clean"`
|
|
Covered bool `json:"covered"`
|
|
}
|
|
|
|
// Collective returns an iterator over visible mountinfo nodes.
|
|
func (n *MountInfoNode) Collective() iter.Seq[*MountInfoNode] {
|
|
return func(yield func(*MountInfoNode) bool) { n.visit(yield) }
|
|
}
|
|
|
|
func (n *MountInfoNode) visit(yield func(*MountInfoNode) bool) bool {
|
|
if !n.Covered && !yield(n) {
|
|
return false
|
|
}
|
|
for cur := n.FirstChild; cur != nil; cur = cur.NextSibling {
|
|
if !cur.visit(yield) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Unfold unfolds the mount hierarchy and resolves covered paths.
|
|
func (d *MountInfoDecoder) Unfold(target string) (*MountInfoNode, error) {
|
|
targetClean := path.Clean(target)
|
|
|
|
var mountinfoSize int
|
|
for range d.Entries() {
|
|
mountinfoSize++
|
|
}
|
|
if err := d.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mountinfo := make([]*MountInfoNode, mountinfoSize)
|
|
// mount ID to index lookup
|
|
idIndex := make(map[int]int, mountinfoSize)
|
|
// final entry to match target
|
|
targetIndex := -1
|
|
{
|
|
i := 0
|
|
for ent := range d.Entries() {
|
|
mountinfo[i] = &MountInfoNode{Clean: path.Clean(ent.Target), MountInfoEntry: ent}
|
|
idIndex[ent.ID] = i
|
|
if mountinfo[i].Clean == targetClean {
|
|
targetIndex = i
|
|
}
|
|
|
|
i++
|
|
}
|
|
}
|
|
|
|
if targetIndex == -1 {
|
|
return nil, syscall.ESTALE
|
|
}
|
|
|
|
for _, cur := range mountinfo {
|
|
var parent *MountInfoNode
|
|
if p, ok := idIndex[cur.Parent]; !ok {
|
|
continue
|
|
} else {
|
|
parent = mountinfo[p]
|
|
}
|
|
|
|
if !strings.HasPrefix(cur.Clean, targetClean) {
|
|
continue
|
|
}
|
|
if parent.Clean == cur.Clean {
|
|
parent.Covered = true
|
|
}
|
|
|
|
covered := false
|
|
nsp := &parent.FirstChild
|
|
for s := parent.FirstChild; s != nil; s = s.NextSibling {
|
|
if strings.HasPrefix(cur.Clean, s.Clean) {
|
|
covered = true
|
|
break
|
|
}
|
|
|
|
if strings.HasPrefix(s.Clean, cur.Clean) {
|
|
*nsp = s.NextSibling
|
|
} else {
|
|
nsp = &s.NextSibling
|
|
}
|
|
}
|
|
if covered {
|
|
continue
|
|
}
|
|
*nsp = cur
|
|
}
|
|
|
|
return mountinfo[targetIndex], nil
|
|
}
|