diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-07-02 21:31:29 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-07-02 21:31:29 +0900 |
| commit | eec021cc4b4eb42bc9c8311755826828bfba1996 (patch) | |
| tree | 5dfe107c31ee525b13af275f8261982e916c331a /vfs/unfold.go | |
| parent | a1d98823f8d17b49b432508d071b62ccd426f1ce (diff) | |
hakurei: move container helpers toplevel
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'vfs/unfold.go')
| -rw-r--r-- | vfs/unfold.go | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/vfs/unfold.go b/vfs/unfold.go new file mode 100644 index 00000000..ca0e37b7 --- /dev/null +++ b/vfs/unfold.go @@ -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 +} |
