A walk run while a dataset was unmounted wrote onto root's empty mount point, or "dropped" entries from it and forgot the ones on the data; a folder kept while empty below an unnamed mount was then swept whole once the disk mounted, with no warrant. Every walk is now judged as an apply is, a changed mount judges the folder afresh, and systemd .mount units and mounts seen are read for the first-sight rule. Widening a mask caps the entries it limited, so the owning group gains nothing. The watch lets go of removed folders, re-arms on lost notice, reads users under its lock and ends with its context. A drop that cannot read an ACL is counted, a broken folder's words carry no path, a grant's group that could not be left is tried again, and a folder's stat is bounded.
67 lines
3.2 KiB
Go
67 lines
3.2 KiB
Go
package apply
|
|
|
|
import (
|
|
"errors"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
// A bounded walk under a kept folder (novox/hq ADR 0325 §8, §10, issue 567).
|
|
//
|
|
// **Walked by descriptor.** The folder is opened as a mesh directory is (meshWalk: no link another account could
|
|
// make, the folder 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 kept folder's is not entered, and an entry
|
|
// opened is checked to be the one that was looked at. An entry's ACL is read and written through its own
|
|
// descriptor, so a name swapped for a link between the look and the change changes nothing.
|
|
//
|
|
// **Bounded, and resumed.** A media 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 folder, kept in the engine's state and never
|
|
// said) and the next goes on from there, in the order of the names, so successive looks cover a folder whole.
|
|
|
|
// WalkLimits bound one walk under a folder: how many entries it reads, and for how long.
|
|
type WalkLimits struct {
|
|
Entries int
|
|
Time time.Duration
|
|
}
|
|
|
|
// ActLimits bound the take act and the walk that adds or drops a user's entries. One stopped goes on at the next
|
|
// apply, from the start: what already has its entries is passed quickly.
|
|
var ActLimits = WalkLimits{Entries: 5_000_000, Time: 30 * time.Minute}
|
|
|
|
// SweepLimits bound the sweep of one kept folder on one look (ADR 0325 §10): the look must come back within its
|
|
// own bound whatever the folder holds.
|
|
var SweepLimits = WalkLimits{Entries: 2000, Time: 2 * time.Second}
|
|
|
|
// WalkCounts is what one walk did or found. Counts only: never a path below the folder.
|
|
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.
|
|
Links, Other, Mounts int
|
|
// Read is every entry looked at.
|
|
Read int
|
|
// Narrowed is the entries that gave a user less than its right (judged by entry and mask together) and
|
|
// were put back; Unrepaired the ones that could not be — the owner's bits, which the mesh never changes.
|
|
Narrowed, Unrepaired 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")
|
|
|
|
// ErrNoACL is a filesystem that allows no ACLs here (ENOTSUP): a ZFS dataset whose acltype is off, say. The access
|
|
// waits for the take act, which sets it where it can (ADR 0325 §8).
|
|
var ErrNoACL = errors.New("the filesystem allows no access entries")
|
|
|
|
// keptPath refuses a folder 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 folder is not stated as the engine states one (absolute, no `..`, no doubled or " +
|
|
"trailing separator); nothing was changed")
|
|
}
|
|
return nil
|
|
}
|