Paused: ADR 0325 replaces runs-as and the group grant. Kept for reuse: the bounded descriptor walks, the warrant-backed access act (share, create), the access wait and the missing-path waiting state.
87 lines
3.8 KiB
Go
87 lines
3.8 KiB
Go
package apply
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os/user"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// The share act on a read-write access's path (novox/hq ADR 0322 §6, issue 567).
|
|
//
|
|
// **The one change the mesh makes on an accessed path, and only on a warrant.** ADR 0312 §2 changes nothing on
|
|
// an accessed path. When the identities that write one path are more than one account, they share it only
|
|
// through its group: the group may write it, and every folder carries setgid, so a folder one of them makes keeps
|
|
// the group and the next can write in it too (§3). The operator lets that happen by approving a warrant for it
|
|
// (ADR 0259); the controller then asks this engine, signed, to run the act. The act adds group write to the path
|
|
// and every file and folder under it, and setgid to every folder: it changes no owner, no group and no other bit,
|
|
// follows no link, enters no other filesystem, and runs only for the path's own group, never a root group's.
|
|
//
|
|
// **Walked by descriptor.** The path is opened as a mesh directory is (meshWalk: no link another account could
|
|
// make, the path 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 path's is not entered, and an entry
|
|
// opened is checked to be the one that was looked at. A file is changed through its own descriptor, so a name
|
|
// swapped for a link between the look and the change changes nothing.
|
|
|
|
// ShareLimits bound one walk under an access's path: how many entries it reads, and for how long.
|
|
type ShareLimits struct {
|
|
Entries int
|
|
Time time.Duration
|
|
}
|
|
|
|
// ShareActLimits bound the act: a media library is millions of files, and an act that never ends is a machine
|
|
// that cannot say what it did. One stopped is asked again and goes on: what has the bits already is kept.
|
|
var ShareActLimits = ShareLimits{Entries: 5_000_000, Time: 30 * time.Minute}
|
|
|
|
// ShareCounts is what one walk did or found. Counts only: never a path below the access.
|
|
type ShareCounts struct {
|
|
// Folders and Files are the entries changed; Kept the ones that had the bits already.
|
|
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
|
|
}
|
|
|
|
// ErrShareBound is a walk that met its bound before it was through: what it did stays, what it did not reach is
|
|
// not known.
|
|
var ErrShareBound = errors.New("the walk met its bound")
|
|
|
|
// SharedVerdict is the read-only judgement of a shared path (ADR 0322 §3).
|
|
type SharedVerdict struct {
|
|
// Shared is that the path may be written by its group and it and every folder under it carry setgid.
|
|
Shared bool
|
|
// Why is what keeps it from being shared, in words with no path.
|
|
Why string
|
|
Counts ShareCounts
|
|
}
|
|
|
|
// ShareRefusal is why the act is never run for gid on a path whose group is pathGID, or nil.
|
|
func ShareRefusal(pathGID, gid int) error {
|
|
if gid != pathGID {
|
|
return fmt.Errorf("the share act runs for the path's own group, gid %d, and was asked for gid %d; nothing "+
|
|
"was changed", pathGID, gid)
|
|
}
|
|
g := PathGroup{GID: pathGID}
|
|
if found, err := user.LookupGroupId(strconv.Itoa(pathGID)); err == nil {
|
|
g.Name = found.Name
|
|
}
|
|
if err := GrantRefusal(g); err != nil {
|
|
return fmt.Errorf("the share act never runs for this group: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// shareablePath refuses a path that is not stated exactly: absolute and clean, so `..` and a doubled separator
|
|
// never name it.
|
|
func shareablePath(path string) error {
|
|
if !filepath.IsAbs(path) || filepath.Clean(path) != path {
|
|
return fmt.Errorf("%q is not an access's path as the engine states it (absolute, no `..`, no doubled or "+
|
|
"trailing separator); nothing was changed", path)
|
|
}
|
|
return nil
|
|
}
|