aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/initdev.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-08-20 01:26:41 +0900
committerOphestra <cat@gensokyo.uk>2025-08-20 01:28:31 +0900
commitc81c9a9d75f179988cf858b85fe2de71bf9c2ff4 (patch)
tree157723a3f9a6f8a456fc939a1ef56e8e597d70f3 /container/initdev.go
parent339e4080dce73d372f6111773066b9c84308b6b7 (diff)
container/init: split setup ops into individual files
This significantly increases readability. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container/initdev.go')
-rw-r--r--container/initdev.go136
1 files changed, 136 insertions, 0 deletions
diff --git a/container/initdev.go b/container/initdev.go
new file mode 100644
index 00000000..827df9e9
--- /dev/null
+++ b/container/initdev.go
@@ -0,0 +1,136 @@
+package container
+
+import (
+ "encoding/gob"
+ "fmt"
+ "os"
+ "path"
+ . "syscall"
+ "unsafe"
+)
+
+func init() { gob.Register(new(MountDevOp)) }
+
+// Dev appends an [Op] that mounts a subset of host /dev.
+func (f *Ops) Dev(target *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 *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 *Absolute
+ Mqueue bool
+ Write bool
+}
+
+func (d *MountDevOp) early(*setupState) error { return nil }
+func (d *MountDevOp) apply(state *setupState) error {
+ if d.Target == nil {
+ return EBADE
+ }
+ target := toSysroot(d.Target.String())
+
+ if err := 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 := path.Join(target, name)
+ if err := ensureFile(targetPath, 0444, state.ParentPerm); err != nil {
+ return err
+ }
+ if err := hostProc.bindMount(
+ toHost(FHSDev+name),
+ targetPath,
+ 0,
+ true,
+ ); err != nil {
+ return err
+ }
+ }
+ for i, name := range []string{"stdin", "stdout", "stderr"} {
+ if err := os.Symlink(
+ FHSProc+"self/fd/"+string(rune(i+'0')),
+ path.Join(target, name),
+ ); err != nil {
+ return wrapErrSelf(err)
+ }
+ }
+ for _, pair := range [][2]string{
+ {FHSProc + "self/fd", "fd"},
+ {FHSProc + "kcore", "core"},
+ {"pts/ptmx", "ptmx"},
+ } {
+ if err := os.Symlink(pair[0], path.Join(target, pair[1])); err != nil {
+ return wrapErrSelf(err)
+ }
+ }
+
+ devPtsPath := path.Join(target, "pts")
+ for _, name := range []string{path.Join(target, "shm"), devPtsPath} {
+ if err := os.Mkdir(name, state.ParentPerm); err != nil {
+ return wrapErrSelf(err)
+ }
+ }
+
+ if err := Mount(SourceDevpts, devPtsPath, FstypeDevpts, MS_NOSUID|MS_NOEXEC,
+ "newinstance,ptmxmode=0666,mode=620"); err != nil {
+ return wrapErrSuffix(err,
+ fmt.Sprintf("cannot mount devpts on %q:", devPtsPath))
+ }
+
+ if state.RetainSession {
+ var buf [8]byte
+ if _, _, errno := Syscall(SYS_IOCTL, 1, TIOCGWINSZ, uintptr(unsafe.Pointer(&buf[0]))); errno == 0 {
+ consolePath := path.Join(target, "console")
+ if err := ensureFile(consolePath, 0444, state.ParentPerm); err != nil {
+ return err
+ }
+ if name, err := os.Readlink(hostProc.stdout()); err != nil {
+ return wrapErrSelf(err)
+ } else if err = hostProc.bindMount(
+ toHost(name),
+ consolePath,
+ 0,
+ false,
+ ); err != nil {
+ return err
+ }
+ }
+ }
+
+ if d.Mqueue {
+ mqueueTarget := path.Join(target, "mqueue")
+ if err := os.Mkdir(mqueueTarget, state.ParentPerm); err != nil {
+ return wrapErrSelf(err)
+ }
+ if err := Mount(SourceMqueue, mqueueTarget, FstypeMqueue, MS_NOSUID|MS_NOEXEC|MS_NODEV, zeroString); err != nil {
+ return wrapErrSuffix(err, "cannot mount mqueue:")
+ }
+ }
+
+ if d.Write {
+ return nil
+ }
+ return wrapErrSuffix(hostProc.remount(target, MS_RDONLY),
+ fmt.Sprintf("cannot remount %q:", target))
+}
+
+func (d *MountDevOp) Is(op Op) bool { vd, ok := op.(*MountDevOp); return ok && *d == *vd }
+func (*MountDevOp) prefix() string { return "mounting" }
+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)
+}