All checks were successful
Test / Create distribution (push) Successful in 1m17s
Test / Sandbox (push) Successful in 3m5s
Test / Hakurei (push) Successful in 4m12s
Test / ShareFS (push) Successful in 4m25s
Test / Sandbox (race detector) (push) Successful in 5m39s
Test / Hakurei (race detector) (push) Successful in 6m44s
Test / Flake checks (push) Successful in 1m24s
This makes package check portable, and removes nonportable behaviour from package pkg, pipewire, and system. All other packages remain nonportable due to their nature. No latency increase was observed due to this change on amd64 and arm64 linux. Signed-off-by: Ophestra <cat@gensokyo.uk>
147 lines
3.8 KiB
Go
147 lines
3.8 KiB
Go
package container
|
|
|
|
import (
|
|
"encoding/gob"
|
|
"fmt"
|
|
"path/filepath"
|
|
. "syscall"
|
|
|
|
"hakurei.app/check"
|
|
"hakurei.app/fhs"
|
|
)
|
|
|
|
func init() { gob.Register(new(MountDevOp)) }
|
|
|
|
// Dev appends an [Op] that mounts a subset of host /dev.
|
|
func (f *Ops) Dev(target *check.Absolute, mqueue bool) *Ops {
|
|
*f = append(*f, &MountDevOp{target, mqueue, false})
|
|
return f
|
|
}
|
|
|
|
// DevWritable appends an [Op] that mounts a writable subset of host /dev.
|
|
//
|
|
// There is usually no good reason to write to /dev, so this should always be
|
|
// followed by a [RemountOp].
|
|
func (f *Ops) DevWritable(target *check.Absolute, mqueue bool) *Ops {
|
|
*f = append(*f, &MountDevOp{target, mqueue, true})
|
|
return f
|
|
}
|
|
|
|
// MountDevOp mounts a subset of host /dev on container path Target.
|
|
// If Mqueue is true, a private instance of [FstypeMqueue] is mounted.
|
|
// If Write is true, the resulting mount point is left writable.
|
|
type MountDevOp struct {
|
|
Target *check.Absolute
|
|
Mqueue bool
|
|
Write bool
|
|
}
|
|
|
|
func (d *MountDevOp) Valid() bool { return d != nil && d.Target != nil }
|
|
func (d *MountDevOp) early(*setupState, syscallDispatcher) error { return nil }
|
|
func (d *MountDevOp) apply(state *setupState, k syscallDispatcher) error {
|
|
target := toSysroot(d.Target.String())
|
|
|
|
if err := k.mountTmpfs(SourceTmpfsDevtmpfs, target, MS_NOSUID|MS_NODEV, 0, state.ParentPerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, name := range []string{"null", "zero", "full", "random", "urandom", "tty"} {
|
|
targetPath := filepath.Join(target, name)
|
|
if err := k.ensureFile(targetPath, 0444, state.ParentPerm); err != nil {
|
|
return err
|
|
}
|
|
if err := k.bindMount(
|
|
state,
|
|
toHost(fhs.Dev+name),
|
|
targetPath,
|
|
0,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for i, name := range []string{"stdin", "stdout", "stderr"} {
|
|
if err := k.symlink(
|
|
fhs.Proc+"self/fd/"+string(rune(i+'0')),
|
|
filepath.Join(target, name),
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, pair := range [][2]string{
|
|
{fhs.Proc + "self/fd", "fd"},
|
|
{fhs.Proc + "kcore", "core"},
|
|
{"pts/ptmx", "ptmx"},
|
|
} {
|
|
if err := k.symlink(pair[0], filepath.Join(target, pair[1])); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
devShmPath := filepath.Join(target, "shm")
|
|
devPtsPath := filepath.Join(target, "pts")
|
|
for _, name := range []string{devShmPath, devPtsPath} {
|
|
if err := k.mkdir(name, state.ParentPerm); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := k.mount(SourceDevpts, devPtsPath, FstypeDevpts, MS_NOSUID|MS_NOEXEC,
|
|
"newinstance,ptmxmode=0666,mode=620"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if state.RetainSession {
|
|
if k.isatty(Stdout) {
|
|
consolePath := filepath.Join(target, "console")
|
|
if err := k.ensureFile(consolePath, 0444, state.ParentPerm); err != nil {
|
|
return err
|
|
}
|
|
if name, err := k.readlink(hostProc.stdout()); err != nil {
|
|
return err
|
|
} else if err = k.bindMount(
|
|
state,
|
|
toHost(name),
|
|
consolePath,
|
|
0,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if d.Mqueue {
|
|
mqueueTarget := filepath.Join(target, "mqueue")
|
|
if err := k.mkdir(mqueueTarget, state.ParentPerm); err != nil {
|
|
return err
|
|
}
|
|
if err := k.mount(SourceMqueue, mqueueTarget, FstypeMqueue, MS_NOSUID|MS_NOEXEC|MS_NODEV, zeroString); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if d.Write {
|
|
return nil
|
|
}
|
|
|
|
if err := k.remount(state, target, MS_RDONLY); err != nil {
|
|
return err
|
|
}
|
|
return k.mountTmpfs(SourceTmpfs, devShmPath, MS_NOSUID|MS_NODEV, 0, 01777)
|
|
}
|
|
func (d *MountDevOp) late(*setupState, syscallDispatcher) error { return nil }
|
|
|
|
func (d *MountDevOp) Is(op Op) bool {
|
|
vd, ok := op.(*MountDevOp)
|
|
return ok && d.Valid() && vd.Valid() &&
|
|
d.Target.Is(vd.Target) &&
|
|
d.Mqueue == vd.Mqueue &&
|
|
d.Write == vd.Write
|
|
}
|
|
func (*MountDevOp) prefix() (string, bool) { return "mounting", true }
|
|
func (d *MountDevOp) String() string {
|
|
if d.Mqueue {
|
|
return fmt.Sprintf("dev on %q with mqueue", d.Target)
|
|
}
|
|
return fmt.Sprintf("dev on %q", d.Target)
|
|
}
|