All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 34s
				
			Test / Sandbox (push) Successful in 2m8s
				
			Test / Hakurei (push) Successful in 3m15s
				
			Test / Hpkg (push) Successful in 3m33s
				
			Test / Sandbox (race detector) (push) Successful in 4m30s
				
			Test / Hakurei (race detector) (push) Successful in 5m19s
				
			Test / Flake checks (push) Successful in 1m35s
				
			This passes line information and handles strconv errors so it reads better. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			114 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			114 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package vfs
 | |
| 
 | |
| import (
 | |
| 	"iter"
 | |
| 	"path"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| 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) }
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 		// 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
 | |
| }
 |