All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 34s
				
			Test / Sandbox (push) Successful in 2m6s
				
			Test / Hpkg (push) Successful in 4m1s
				
			Test / Sandbox (race detector) (push) Successful in 4m29s
				
			Test / Hakurei (race detector) (push) Successful in 3m5s
				
			Test / Hakurei (push) Successful in 2m10s
				
			Test / Flake checks (push) Successful in 1m21s
				
			This allows referencing FHS pathnames without importing container. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			144 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package container
 | |
| 
 | |
| import (
 | |
| 	"encoding/gob"
 | |
| 	"fmt"
 | |
| 	"path"
 | |
| 	. "syscall"
 | |
| 
 | |
| 	"hakurei.app/container/check"
 | |
| 	"hakurei.app/container/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 := path.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')),
 | |
| 			path.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], path.Join(target, pair[1])); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	devShmPath := path.Join(target, "shm")
 | |
| 	devPtsPath := path.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 := path.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 := path.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) 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)
 | |
| }
 |