go-judge/env/mount_linux.go

143 lines
3.6 KiB
Go

package env
import (
"fmt"
"os"
"path"
"github.com/criyle/go-sandbox/container"
"github.com/criyle/go-sandbox/pkg/mount"
"gopkg.in/yaml.v2"
)
// Mount defines single mount point configuration.
// type could be bind / tmpfs
type Mount struct {
Type string `yaml:"type"`
Source string `yaml:"source"`
Target string `yaml:"target"`
Readonly bool `yaml:"readonly"`
Data string `yaml:"data"`
}
// Link defines symlinks to be created after mounts
type Link struct {
LinkPath string `yaml:"linkPath"`
Target string `yaml:"target"`
}
// Mounts defines mount points for the container.
type Mounts struct {
Mount []Mount `yaml:"mount"`
SymLinks []Link `yaml:"symLink"`
MaskPaths []string `yaml:"maskPath"`
InitCmd string `yaml:"initCmd"`
WorkDir string `yaml:"workDir"`
HostName string `yaml:"hostName"`
DomainName string `yaml:"domainName"`
UID int `yaml:"uid"`
GID int `yaml:"gid"`
Proc bool `yaml:"proc"`
ProcRW bool `yaml:"procrw"`
}
func readMountConfig(p string) (*Mounts, error) {
var m Mounts
d, err := os.ReadFile(p)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(d, &m); err != nil {
return nil, err
}
return &m, nil
}
func parseMountConfig(m *Mounts) (*mount.Builder, error) {
b := mount.NewBuilder()
wd, err := os.Getwd()
if err != nil {
return nil, err
}
for _, mt := range m.Mount {
target := mt.Target
if path.IsAbs(target) {
target = path.Clean(target[1:])
}
source := mt.Source
if !path.IsAbs(source) {
source = path.Join(wd, source)
}
switch mt.Type {
case "bind":
b.WithBind(source, target, mt.Readonly)
case "tmpfs":
b.WithTmpfs(target, mt.Data)
default:
return nil, fmt.Errorf("invalid_mount_type: %v", mt.Type)
}
}
if m.Proc {
b.WithProcRW(m.ProcRW)
}
return b, nil
}
func getDefaultMount(tmpFsConf string) *mount.Builder {
return mount.NewBuilder().
// basic exec and lib
WithBind("/bin", "bin", true).
WithBind("/lib", "lib", true).
WithBind("/lib64", "lib64", true).
WithBind("/usr", "usr", true).
WithBind("/etc/ld.so.cache", "etc/ld.so.cache", true).
// java wants /proc/self/exe as it need relative path for lib
// however, /proc gives interface like /proc/1/fd/3 ..
// it is fine since open that file will be a EPERM
// changing the fs uid and gid would be a good idea
WithProc().
// some compiler have multiple version
WithBind("/etc/alternatives", "etc/alternatives", true).
// fpc wants /etc/fpc.cfg
WithBind("/etc/fpc.cfg", "etc/fpc.cfg", true).
// mono wants /etc/mono
WithBind("/etc/mono", "etc/mono", true).
// go wants /dev/null
WithBind("/dev/null", "dev/null", false).
// ghc wants /var/lib/ghc
WithBind("/var/lib/ghc", "var/lib/ghc", true).
// javaScript wants /dev/urandom
WithBind("/dev/urandom", "dev/urandom", false).
// additional devices
WithBind("/dev/random", "dev/random", false).
WithBind("/dev/zero", "dev/zero", false).
WithBind("/dev/full", "dev/full", false).
// work dir
WithTmpfs("w", tmpFsConf).
// tmp dir
WithTmpfs("tmp", tmpFsConf)
}
var defaultSymLinks = []container.SymbolicLink{
{LinkPath: "/dev/fd", Target: "/proc/self/fd"},
{LinkPath: "/dev/stdin", Target: "/proc/self/fd/0"},
{LinkPath: "/dev/stdout", Target: "/proc/self/fd/1"},
{LinkPath: "/dev/stderr", Target: "/proc/self/fd/2"},
}
var defaultMaskPaths = []string{
"/sys/firmware",
"/sys/devices/virtual/powercap",
"/proc/acpi",
"/proc/asound",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/usr/lib/wsl/drivers",
"/usr/lib/wsl/lib",
}