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 }