package apply // What is never placed below an agent's home (novox/hq ADR 0266 ยง7). // // An account that never becomes root owns every directory in its home, so it can swap a link into the path // between the engine's check and the engine's write. The engine writes a whole file there through a // descriptor that followed no link (home_links.go), but it unpacks an archive, and writes into an existing // file, by path. So below the home of an account the declaration says never becomes root, neither is placed. // The catalogue's own check (checks/agenthome) refuses either under `${machine:agent-home}`, so no module // asks; this is the engine refusing it again where it applies. The machine's own trees, other accounts' // homes and a directory's owner below a home are placement_guard.go's (novox/hq issue 339). import ( "fmt" "path/filepath" "github.com/novox/mesh-host/internal/declaration" ) // AgentHomeRefusedError is an archive or a written-into file the engine will not place below an agent's home. type AgentHomeRefusedError struct { Path, What, Home string } func (e *AgentHomeRefusedError) Error() string { return fmt.Sprintf("%s is not placed: %s below %s, the home of an account that never becomes root, which "+ "owns every parent there and could swap a link in between the check and the write (novox/hq ADR 0266); "+ "nothing was touched", e.Path, e.What, e.Home) } // refuseUnderAgentHome says why a resource is not placed below one of agentHomes; nil when it may be. func refuseUnderAgentHome(r declaration.Resource, agentHomes []string) error { what := "" switch res := r.(type) { case *declaration.Archive: what = "an archive unpacked" case *declaration.File: if res.Into != "" { what = "a file written into (" + res.Into + ")" } } if what == "" { return nil } path := filepath.Clean(r.Target()) for _, p := range []string{path, resolved(path)} { for _, home := range agentHomes { if atOrBelow(p, home) { return &AgentHomeRefusedError{Path: path, What: what, Home: home} } } } return nil } // rootNeverHomes is the home of every account the declaration says never becomes root, from the user // database; an account not made yet has no home to protect. func rootNeverHomes(d *declaration.Declaration) []string { if d == nil { return nil } homes := accountsOfHomes() var out []string for _, r := range d.Resources { u, ok := r.(*declaration.User) if !ok || u.Root != declaration.RootNever { continue } for home, a := range homes { if a.Name == u.Name { out = append(out, home) } } } return out }