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) }