Files
mesh-host/internal/apply/walk.go
T
jschoubben 2a2be687f3 Keep libraries under groups of their own, and give modules users in them (hq ADR 0328, issue 567)
ADR 0328 supersedes ADR 0325: shared data is a library with one home, kept usable by
its group rather than by ACL entries. The engine now makes each library's group and
each module's user in its libraries' groups; keeps a library's root and requested
sub-paths in its group with group write and setgid on its home, making missing
sub-paths as the root is; waits for the operator's word before touching a root that
holds data, and on the take act walks it by descriptor (no link, same filesystem,
bounded and resumed, files with other names left); holds the containers of a library
whose disk or mount is not there; and puts the tool runner's account in the group.
The ACL keeping, mask repair and watch are gone; an access is again as before ADR 0325.
2026-10-12 22:11:09 +02:00

55 lines
2.6 KiB
Go

package apply
import (
"errors"
"path/filepath"
"time"
)
// A bounded walk under a library's root (novox/hq ADR 0328 §7, issue 567).
//
// **Walked by descriptor.** The root is opened as a mesh directory is (meshWalk: no link another account could
// make, the root itself no link), and everything under it is reached from its parent's descriptor with O_NOFOLLOW:
// a link met is passed by, a folder whose device is not the root's is not entered, and an entry opened is checked
// to be the one that was looked at. An entry's group and mode are set through its own descriptor, so a name
// swapped for a link between the look and the change changes nothing.
//
// **Bounded, and resumed.** A library is millions of files, and a walk that never ends is a machine that cannot
// say what it did. Each walk reads at most so many entries for at most so long; one that meets its bound says how
// far it got (a cursor, the names of each level below the root, kept in the engine's state and never said) and the
// next goes on from there, in the order of the names, so successive walks cover the library whole.
// WalkLimits bound one walk under a root: how many entries it reads, and for how long.
type WalkLimits struct {
Entries int
Time time.Duration
}
// ActLimits bound one walk of the take act; one stopped at its bound goes on from its cursor at the next look.
var ActLimits = WalkLimits{Entries: 5_000_000, Time: 30 * time.Minute}
// WalkCounts is what one walk did or found. Counts only: never a path below the root.
type WalkCounts struct {
// Folders and Files are the entries changed; Kept the ones that already had what they need.
Folders, Files, Kept int
// Links are links passed by, Other entries neither a file nor a folder, Mounts folders on another filesystem
// not entered, Shared files with another name elsewhere left as they are.
Links, Other, Mounts, Shared int
// Read is every entry looked at; Failed the entries that could not be changed (an immutable file, say).
Read, Failed int
}
// ErrWalkBound is a walk that met its bound before it was through: what it did stays, and the cursor says where
// the next one goes on.
var ErrWalkBound = errors.New("the walk met its bound")
// keptPath refuses a root that is not stated exactly: absolute and clean, so `..` and a doubled separator never
// name it.
func keptPath(path string) error {
if !filepath.IsAbs(path) || filepath.Clean(path) != path {
return errors.New("the root is not stated as the engine states one (absolute, no `..`, no doubled or " +
"trailing separator); nothing was changed")
}
return nil
}