sandbox/vfs: unfold mount hierarchy
This presents all visible mount points under path. This is useful for applying extra vfs options to bind mounts. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
107
sandbox/vfs/mount.go
Normal file
107
sandbox/vfs/mount.go
Normal file
@@ -0,0 +1,107 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user