//go:build linux package host import ( "errors" "io" "os" "path/filepath" "strings" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/instancefs" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/rootstate" "golang.org/x/sys/unix" ) type servingParkNamespace struct { Topology *servingParkTopology `json:",omitempty"` BootID string `json:",omitempty"` Version int Parent, Root servingArtifactDirectory } type servingParkFS struct { data string state rootstate.Store owner uint32 mount func(string) (bool, error) boot func() (string, error) volume func(uint64) string handle func(*os.File) (servingParkPersistentDirectory, error) rename func(int, string, int, string, uint) error sync func(int) error unlink func(int, string, int) error } // Production construction always requires root and a nosymfollow mount. Tests // use the private fields to exercise filesystems without mount privileges. func newServingParkFS(data string, state rootstate.Store) *servingParkFS { return &servingParkFS{data: data, state: state, owner: 0, mount: instancefs.MountNoSymfollow, boot: servingParkBootID, handle: servingParkPersistentDir, volume: servingParkVolume, rename: unix.Renameat2, sync: unix.Fsync, unlink: unix.Unlinkat} } func servingParkDirID(f *os.File) (servingArtifactDirectory, error) { var st unix.Statx_t if err := unix.Statx(int(f.Fd()), "", unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW, unix.STATX_BASIC_STATS|unix.STATX_MNT_ID, &st); err != nil { return servingArtifactDirectory{}, err } if st.Mask&unix.STATX_MNT_ID == 0 || st.Mode&unix.S_IFMT != unix.S_IFDIR || st.Ino == 0 || st.Mnt_id == 0 { return servingArtifactDirectory{}, hostbox.ErrChanged } return servingArtifactDirectory{Device: unix.Mkdev(st.Dev_major, st.Dev_minor), Inode: st.Ino, Mount: st.Mnt_id}, nil } func (f *servingParkFS) parent() (*os.File, servingArtifactDirectory, error) { var zero servingArtifactDirectory if f == nil || f.state == nil || f.mount == nil || f.rename == nil || f.sync == nil || f.unlink == nil || !filepath.IsAbs(f.data) || filepath.Clean(f.data) != f.data || uint32(os.Geteuid()) != f.owner { return nil, zero, hostbox.ErrUnsupported } path := filepath.Join(f.data, "instances") p, err := instancefs.OpenDirBeneath(path, ".") if err != nil { return nil, zero, err } id, err := servingParkDirID(p) if err != nil { p.Close() return nil, zero, err } ok, err := f.mount(path) if err != nil || !ok { p.Close() return nil, zero, hostbox.ErrChanged } // The pathname's effective mount must still name the pinned descriptor. again, err := instancefs.OpenDirBeneath(path, ".") if err != nil { p.Close() return nil, zero, err } next, err := servingParkDirID(again) again.Close() if err != nil || next != id { p.Close() return nil, zero, hostbox.ErrChanged } return p, id, nil } func servingParkOpenDir(parent *os.File, name string) (*os.File, error) { if name == "" || name == "." || name == ".." || strings.ContainsAny(name, "/\\\x00") { return nil, hostbox.ErrChanged } fd, err := unix.Openat(int(parent.Fd()), name, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) if err != nil { return nil, err } return os.NewFile(uintptr(fd), name), nil } func (f *servingParkFS) privateDir(parent *os.File, name string) (*os.File, servingArtifactDirectory, error) { var zero servingArtifactDirectory d, err := servingParkOpenDir(parent, name) if err != nil { return nil, zero, err } var st unix.Stat_t err = unix.Fstat(int(d.Fd()), &st) if err != nil || st.Uid != f.owner || st.Mode&0077 != 0 { d.Close() return nil, zero, hostbox.ErrChanged } id, err := servingParkDirID(d) if err != nil { d.Close() return nil, zero, err } return d, id, nil } // namespace refuses every unrecorded obstruction. Quarantine recovery must be // completed before a production caller can recover an unknown park root. func (f *servingParkFS) namespace() (*os.File, servingParkNamespace, error) { var n servingParkNamespace boot, err := f.currentBoot() if err != nil { return nil, n, err } parent, id, err := f.parent() if err != nil { return nil, n, err } defer parent.Close() n, err = f.readNamespace() if errors.Is(err, os.ErrNotExist) { p, e := f.namespaceParent() if e == nil && (p.Parent != id || f.checkBoot(p.BootID) != nil) { return nil, n, hostbox.ErrChanged } if e != nil && !errors.Is(e, os.ErrNotExist) { return nil, n, e } if err = f.checkNamespaceQuarantine(n); err != nil { return nil, n, err } // EEXIST is deliberately not adoption authority for a service-owned name. if err = unix.Mkdirat(int(parent.Fd()), ".serving-parked", 0700); err != nil { return nil, n, err } d, rid, e := f.privateDir(parent, ".serving-parked") if e != nil { return nil, n, e } if rid.Device != id.Device || rid.Mount != id.Mount { d.Close() return nil, n, hostbox.ErrChanged } n = servingParkNamespace{Version: 1, Parent: id, Root: rid, BootID: boot} n.Topology, e = f.captureTopology(parent, d, n) if e != nil { d.Close() return nil, n, e } if e = f.sync(int(parent.Fd())); e == nil { l := servingParkLedger{state: f.state} e = l.write("namespace.json", n) } if e != nil { d.Close() return nil, n, e } return d, n, nil } if err != nil { return nil, n, err } if n.Version != 1 || n.Parent != id || f.checkBoot(n.BootID) != nil { return nil, n, hostbox.ErrChanged } if err = f.checkNamespaceQuarantine(n); err != nil { return nil, n, err } d, rid, err := f.privateDir(parent, ".serving-parked") if err != nil { return nil, n, err } if rid != n.Root || rid.Device != id.Device || rid.Mount != id.Mount { d.Close() return nil, n, hostbox.ErrChanged } return d, n, nil } func (f *servingParkFS) source(a servingParkAuthorization) (*os.File, servingArtifactDirectory, error) { var zero servingArtifactDirectory host := a.Successor.Host if host == "" || host == "." || host == ".." || strings.ContainsAny(host, "/\\\x00") || len(host)+7 > 255 { return nil, zero, hostbox.ErrChanged } p, pid, err := f.parent() if err != nil { return nil, zero, err } defer p.Close() d, err := servingParkOpenDir(p, provision.InstanceID(host)) if err != nil { return nil, zero, err } id, err := servingParkDirID(d) if err != nil || id != a.Directory || id.Device != pid.Device || id.Mount != pid.Mount { d.Close() return nil, zero, hostbox.ErrChanged } return d, pid, nil } func servingParkStat(parent *os.File, name string) (servingParkFile, error) { out := servingParkFile{Name: name} var st unix.Statx_t err := unix.Statx(int(parent.Fd()), name, unix.AT_SYMLINK_NOFOLLOW, unix.STATX_BASIC_STATS|unix.STATX_MNT_ID, &st) if errors.Is(err, unix.ENOENT) { return out, nil } if err != nil { return out, err } if st.Mask&unix.STATX_MNT_ID == 0 || st.Mode&unix.S_IFMT != unix.S_IFREG || st.Nlink != 1 || st.Ino == 0 { return out, hostbox.ErrChanged } // Allocated bytes are the upper bound reclaimable by removing this link; // pressure callers must still remeasure f_bavail after deleting it. if st.Blocks > ^uint64(0)/512 { return out, hostbox.ErrChanged } out.Present, out.Device, out.Inode, out.Mount, out.Bytes, out.Mode, out.Links = true, unix.Mkdev(st.Dev_major, st.Dev_minor), st.Ino, st.Mnt_id, st.Blocks*512, uint32(st.Mode)&unix.S_IFMT, st.Nlink out.Persistent, err = servingParkPersistentArtifact(parent, out) return out, err } func sameServingParkFile(a, b servingParkFile) bool { return a.Name == b.Name && a.Present == b.Present && a.Device == b.Device && a.Inode == b.Inode && a.Mount == b.Mount && a.Mode == b.Mode && a.Links == b.Links && a.Bytes == b.Bytes && (a.Persistent == (servingParkPersistentFile{}) || a.Persistent == b.Persistent) } func (f *servingParkFS) Capture(a servingParkAuthorization) (servingParkSource, error) { var out servingParkSource source, parent, err := f.source(a) if err != nil { return out, err } defer source.Close() if f.handle == nil || f.volume == nil { return out, hostbox.ErrUnsupported } out.Directory, out.Parent = a.Directory, parent p, pid, err := f.parent() if err != nil { return out, err } defer p.Close() if pid != parent { return out, hostbox.ErrChanged } out.ParentPersistent, err = f.handle(p) if err != nil { return out, err } out.Volume = f.volume(parent.Device) out.BootID, err = f.currentBoot() if err != nil { return out, err } if f.handle == nil { return out, hostbox.ErrUnsupported } out.Persistent, err = f.handle(source) if err != nil { return out, err } if !out.Persistent.valid() { return out, hostbox.ErrChanged } for i, suffix := range []string{"", ".gen", "-wal", "-shm"} { file, err := servingParkStat(source, a.Successor.Host+".db"+suffix) if err != nil { return out, err } if file.Present && (file.Device != parent.Device || file.Mount != parent.Mount) { return out, hostbox.ErrChanged } out.Files[i] = file } marker := out.Files[1] if marker.Present { if a.Legacy { return out, hostbox.ErrChanged } fd, err := unix.Openat(int(source.Fd()), marker.Name, unix.O_RDONLY|unix.O_NONBLOCK|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) if err != nil { return out, err } mf := os.NewFile(uintptr(fd), marker.Name) var st unix.Stat_t if err = unix.Fstat(fd, &st); err != nil || uint64(st.Dev) != marker.Device || st.Ino != marker.Inode || st.Mode&unix.S_IFMT != unix.S_IFREG || st.Nlink != 1 { mf.Close() return out, hostbox.ErrChanged } body, err := io.ReadAll(io.LimitReader(mf, 4097)) mf.Close() if err != nil || len(body) > 4096 || strings.TrimSpace(string(body)) != a.Predecessor.Generation { return out, hostbox.ErrChanged } again, err := servingParkStat(source, marker.Name) if err != nil || !sameServingParkFile(marker, again) { return out, hostbox.ErrChanged } } if f.checkBoot(out.BootID) != nil { return out, hostbox.ErrChanged } again, pid, err := f.source(a) if err != nil { return out, err } defer again.Close() persistent, err := f.handle(again) if err != nil || pid != out.Parent || persistent != out.Persistent { return out, hostbox.ErrChanged } return out, nil } func (f *servingParkFS) event(r *servingParkRecord) (*os.File, *os.File, error) { root, n, err := f.eventRoot(r) if err != nil { return nil, nil, err } if n.Parent != r.Source.Parent { root.Close() return nil, nil, hostbox.ErrChanged } event, id, err := f.privateDir(root, r.Authorization.ID) // Older records did not pin their namespace. Their exact event must still // exist before they can be associated with the prior recorded root. if (err != nil || id != r.Event) && r.Prepared && r.Namespace == (servingArtifactDirectory{}) { if event != nil { event.Close() } root.Close() root, n, err = f.displacedRoot(r) if err != nil { return nil, nil, err } event, id, err = f.privateDir(root, r.Authorization.ID) } if err != nil { root.Close() return nil, nil, err } if !r.Prepared || id != r.Event || id.Device != n.Root.Device || id.Mount != n.Root.Mount { root.Close() event.Close() return nil, nil, hostbox.ErrChanged } return root, event, nil } func (f *servingParkFS) Prepare(r *servingParkRecord, save func() error) error { if err := f.checkRecordBoot(r); err != nil { return err } if r.Prepared { root, event, err := f.event(r) if err != nil { return err } defer root.Close() defer event.Close() if err := f.sync(int(root.Fd())); err != nil { return err } return f.recordEventRoot(r, root, save) } root, n, err := f.namespace() if err != nil { return err } defer root.Close() if n.Parent != r.Source.Parent { return hostbox.ErrChanged } if err = unix.Mkdirat(int(root.Fd()), r.Authorization.ID, 0700); err != nil && !errors.Is(err, unix.EEXIST) { return err } event, id, err := f.privateDir(root, r.Authorization.ID) if err != nil { return err } defer event.Close() if id.Device != n.Root.Device || id.Mount != n.Root.Mount { return hostbox.ErrChanged } // An event exists only under a recorded root-private namespace and a durable // intent. Before its identity is recorded it must be entirely empty. names, err := event.Readdirnames(1) if err != nil && !errors.Is(err, io.EOF) { return err } if len(names) != 0 { return hostbox.ErrChanged } if err = f.sync(int(root.Fd())); err != nil { return err } if r.Source.Persistent != (servingParkPersistentDirectory{}) { if f.checkBoot(r.Source.BootID) != nil || f.handle == nil { return hostbox.ErrChanged } persistent, e := f.handle(event) if e != nil { return e } if !persistent.valid() || persistent.Owner != f.owner || persistent.Mode&0077 != 0 || persistent.Filesystem != r.Source.Persistent.Filesystem { return hostbox.ErrChanged } r.EventPersistent = persistent } r.Event, r.Prepared, r.Namespace = id, true, n.Root return save() } func (f *servingParkFS) Move(r *servingParkRecord, save func() error) error { if err := f.checkRecordBoot(r); err != nil { return err } root, event, err := f.event(r) if err != nil { return err } defer root.Close() defer event.Close() source, parent, err := f.source(r.Authorization) if err != nil { return err } defer source.Close() if parent != r.Source.Parent { return hostbox.ErrChanged } // Check the complete set before moving any name. for _, recorded := range r.Source.Files { src, e := servingParkStat(source, recorded.Name) if e != nil { return e } dst, e := servingParkStat(event, recorded.Name) if e != nil { return e } if !recorded.Present { if src.Present || dst.Present { return hostbox.ErrChanged } } else if recorded.Moved { if src.Present || !sameServingParkFile(recorded, dst) { return hostbox.ErrChanged } } else if !((sameServingParkFile(recorded, src) && !dst.Present) || (!src.Present && sameServingParkFile(recorded, dst))) { return hostbox.ErrChanged } } for i := range r.Source.Files { item := &r.Source.Files[i] if !item.Present { continue } src, e := servingParkStat(source, item.Name) if e != nil { return e } if src.Present { if item.Moved || !sameServingParkFile(*item, src) { return hostbox.ErrChanged } if e = f.rename(int(source.Fd()), item.Name, int(event.Fd()), item.Name, unix.RENAME_NOREPLACE); e != nil { return e } } // A replacement raced into rename may be retained here, never deleted. dst, e := servingParkStat(event, item.Name) if e != nil || !sameServingParkFile(*item, dst) { return hostbox.ErrChanged } if e = f.sync(int(source.Fd())); e != nil { return e } if e = f.sync(int(event.Fd())); e != nil { return e } if !item.Moved { item.Moved = true if e = save(); e != nil { return e } } } return nil } func (f *servingParkFS) Delete(r *servingParkRecord, save func() error) error { if err := f.checkReapVolume(r); err != nil { return err } if err := f.checkRecordBoot(r); err != nil { return err } if r.EventRemoveIntent { return f.removeEvent(r, save) } root, event, err := f.event(r) if err != nil { return err } defer root.Close() defer event.Close() // Validate all children before unlinking any one of them. There is no // recursive deletion and no pathname derived from a registry row. names, err := event.Readdirnames(-1) if err != nil { return err } expected := make(map[string]servingParkFile) for _, item := range r.Source.Files { if item.Present { expected[item.Name] = item } } for _, name := range names { item, ok := expected[name] if !ok || item.Deleted { return hostbox.ErrChanged } actual, e := servingParkStat(event, name) if e != nil || !sameServingParkFile(item, actual) { return hostbox.ErrChanged } } for _, item := range r.Source.Files { if !item.Present { continue } actual, e := servingParkStat(event, item.Name) if e != nil { return e } if item.Deleted && actual.Present || !actual.Present && !item.DeleteIntent || actual.Present && !sameServingParkFile(item, actual) { return hostbox.ErrChanged } } if err := f.recordEventRoot(r, root, save); err != nil { return err } for i := range r.Source.Files { item := &r.Source.Files[i] if !item.Present { continue } actual, e := servingParkStat(event, item.Name) if e != nil { return e } if item.Deleted { if actual.Present { return hostbox.ErrChanged } continue } if !actual.Present && !item.DeleteIntent { return hostbox.ErrChanged } if actual.Present && !sameServingParkFile(*item, actual) { return hostbox.ErrChanged } if !item.DeleteIntent { item.DeleteIntent = true if e = save(); e != nil { return e } } if actual.Present { if e = f.unlink(int(event.Fd()), item.Name, 0); e != nil { return e } } if e = f.sync(int(event.Fd())); e != nil { return e } item.Deleted = true if e = save(); e != nil { return e } } return f.removeEvent(r, save) } func (f *servingParkFS) CheckParked(r *servingParkRecord) error { if err := f.checkRecordBoot(r); err != nil { return err } if r.EventRemoved { if err := f.removedEvent(r); err != nil { return err } source, parent, err := f.source(r.Authorization) if err != nil { return err } defer source.Close() if parent != r.Source.Parent { return hostbox.ErrChanged } for _, item := range r.Source.Files { actual, err := servingParkStat(source, item.Name) if err != nil || actual.Present { return hostbox.ErrChanged } } return nil } root, event, err := f.event(r) if err != nil { return err } defer root.Close() defer event.Close() source, parent, err := f.source(r.Authorization) if err != nil { return err } defer source.Close() if parent != r.Source.Parent { return hostbox.ErrChanged } expected := make(map[string]bool) for _, item := range r.Source.Files { src, e := servingParkStat(source, item.Name) if e != nil || src.Present { return hostbox.ErrChanged } dst, e := servingParkStat(event, item.Name) if e != nil { return e } if item.Present && !item.Deleted { if !sameServingParkFile(item, dst) { return hostbox.ErrChanged } expected[item.Name] = true } else if dst.Present { return hostbox.ErrChanged } } names, err := event.Readdirnames(-1) if err != nil { return err } for _, name := range names { if !expected[name] { return hostbox.ErrChanged } } return nil } // removeEvent removes only a recorded empty event. The durable intent makes // absence after a lost unlink reply replayable; it never licenses a replacement. func (f *servingParkFS) removeEvent(r *servingParkRecord, save func() error) error { if err := f.checkReapVolume(r); err != nil { return err } if !r.Deleting || !r.Prepared || !r.Parked { return hostbox.ErrChanged } for _, item := range r.Source.Files { if item.Present && !item.Deleted { return hostbox.ErrChanged } } root, n, err := f.eventRoot(r) if err != nil { return err } defer root.Close() if n.Parent != r.Source.Parent { return hostbox.ErrChanged } event, id, err := f.privateDir(root, r.Authorization.ID) if errors.Is(err, unix.ENOENT) { if !r.EventRemoveIntent { return hostbox.ErrChanged } } else if err != nil { return err } else { defer event.Close() if r.EventRemoved || id != r.Event || id.Device != n.Root.Device || id.Mount != n.Root.Mount { return hostbox.ErrChanged } names, e := event.Readdirnames(1) if e != nil && !errors.Is(e, io.EOF) { return e } if len(names) != 0 { return hostbox.ErrChanged } if !r.EventRemoveIntent { r.EventRemoveIntent = true if err = save(); err != nil { return err } } // Root owns the parent and namespace lock, including during recovery. if err = f.unlink(int(root.Fd()), r.Authorization.ID, unix.AT_REMOVEDIR); err != nil { return err } } if err = f.sync(int(root.Fd())); err != nil { return err } if !r.EventRemoved { r.EventRemoved = true return save() } return nil } func (f *servingParkFS) removedEvent(r *servingParkRecord) error { if !r.EventRemoveIntent || !r.EventRemoved { return hostbox.ErrChanged } root, n, err := f.eventRoot(r) if err != nil { return err } defer root.Close() if n.Parent != r.Source.Parent { return hostbox.ErrChanged } d, _, err := f.privateDir(root, r.Authorization.ID) if err == nil { d.Close() return hostbox.ErrChanged } if !errors.Is(err, unix.ENOENT) { return err } return nil }