All checks were successful
Test / Create distribution (push) Successful in 1m17s
Test / Sandbox (push) Successful in 3m5s
Test / Hakurei (push) Successful in 4m12s
Test / ShareFS (push) Successful in 4m25s
Test / Sandbox (race detector) (push) Successful in 5m39s
Test / Hakurei (race detector) (push) Successful in 6m44s
Test / Flake checks (push) Successful in 1m24s
This makes package check portable, and removes nonportable behaviour from package pkg, pipewire, and system. All other packages remain nonportable due to their nature. No latency increase was observed due to this change on amd64 and arm64 linux. Signed-off-by: Ophestra <cat@gensokyo.uk>
116 lines
2.6 KiB
Go
116 lines
2.6 KiB
Go
package vfs
|
|
|
|
import (
|
|
"iter"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// UnfoldTargetError is a pathname that never appeared in a mount hierarchy.
|
|
type UnfoldTargetError string
|
|
|
|
func (e UnfoldTargetError) Error() string {
|
|
return "mount point " + string(e) + " never appeared in mountinfo"
|
|
}
|
|
|
|
// 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) }
|
|
}
|
|
|
|
// visit recursively visits all visible mountinfo nodes.
|
|
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 := filepath.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: filepath.Clean(ent.Target), MountInfoEntry: ent}
|
|
idIndex[ent.ID] = i
|
|
if mountinfo[i].Clean == targetClean {
|
|
targetIndex = i
|
|
}
|
|
|
|
i++
|
|
}
|
|
}
|
|
|
|
if targetIndex == -1 {
|
|
// target does not exist in parsed mountinfo
|
|
return nil, &DecoderError{Op: "unfold", Line: -1, Err: UnfoldTargetError(targetClean)}
|
|
}
|
|
|
|
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
|
|
}
|