<commit_before>// +build linux

package sys

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/EricLagergren/go-gnulib/ttyname"
	"github.com/stephane-martin/skewer/utils"
)

type mountPoint struct {
	Source string
	Target string
	Fs     string
	Flags  int
	Data   string
}

func StartInNamespaces(command *exec.Cmd, dumpable bool, storePath string, confDir string) error {
	myTtyName := ""
	if ttyname.IsAtty(1) {
		myTtyName, _ = ttyname.TtyName(1)
		command.Env = append(command.Env, fmt.Sprintf("SKEWER_TTYNAME=%s", myTtyName))
	}

	confDir = strings.TrimSpace(confDir)
	if len(confDir) > 0 {
		command.Env = append(command.Env, fmt.Sprintf("SKEWER_CONF_DIR=%s", confDir))
	}

	storePath = strings.TrimSpace(storePath)
	if len(storePath) > 0 {
		command.Env = append(command.Env, fmt.Sprintf("SKEWER_STORE_PATH=%s", storePath))
	}

	command.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWPID | syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS,

		UidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getuid(),
				Size:        1,
			},
		},
		GidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getgid(),
				Size:        1,
			},
		},
	}

	// TODO: DUMPABLE should only be set in the child, just to write uid_map
	if !dumpable {
		SetDumpable()
	}
	err := command.Start()
	if !dumpable {
		SetNonDumpable()
	}
	return err
}

func PivotRoot(root string) (err error) {
	oldroot := filepath.Join(root, "oldroot")
	err = utils.Chain(
		func() error { return os.Mkdir(oldroot, 0777) },
		func() error { return syscall.PivotRoot(root, oldroot) },
		func() error { return syscall.Chdir("/") },
		func() error { return syscall.Unmount("/oldroot", syscall.MNT_DETACH) },
		func() error { return os.Remove("/oldroot") },
		func() error { return syscall.Chroot("/newroot") },
		func() error { return os.Chdir("/") },
		func() error { return os.Symlink(filepath.Join("/dev", "pts", "ptmx"), filepath.Join("/dev", "ptmx")) },
	)
	if err != nil {
		err = fmt.Errorf("PivotRoot error: %s", err.Error())
	}
	return err
}

func SetJournalFs(targetExec string) error {
	var err error

	roRemounts := []mountPoint{
		{
			Target: "/bin",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/sbin",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/lib",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/lib64",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/usr",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/var",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC,
		},
		{
			Target: "/home",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/etc",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC,
		},
	}

	mounts := []mountPoint{
		{
			Source: "proc",
			Target: "/proc",
			Fs:     "proc",
			Flags:  syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV,
		},
		{
			Source: "tmpfs",
			Target: "/dev",
			Fs:     "tmpfs",
			Flags:  syscall.MS_NOSUID | syscall.MS_NOEXEC,
			Data:   "mode=755",
		},
		{
			Source: "devpts",
			Target: "/dev/pts",
			Fs:     "devpts",
			Flags:  syscall.MS_NOSUID | syscall.MS_NOEXEC,
			Data:   "newinstance,ptmxmode=0666,mode=620",
		},
		{
			Source: "tmpfs",
			Target: "/boot",
			Fs:     "tmpfs",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC,
			Data:   "mode=700",
		},
	}

	temp, err := ioutil.TempDir("", "skewer-confined")
	if err != nil {
		return fmt.Errorf("TempDir error: %s", err)
	}
	err = syscall.Mount("/dev", temp, "bind", syscall.MS_BIND|syscall.MS_REC, "")
	if err != nil {
		return fmt.Errorf("Error bind-mounting /dev: %s", err)
	}

	for _, m := range mounts {
		if _, err := os.Stat(m.Target); err != nil {
			if os.IsNotExist(err) {
				err = os.MkdirAll(m.Target, 0755)
				if err != nil {
					return fmt.Errorf("mkdirall %s error: %v", m.Target, err)
				}
			}
		}
		err = syscall.Mount(m.Source, m.Target, m.Fs, uintptr(m.Flags), "")
		if err != nil {
			return fmt.Errorf("failed to mount %s: %s", m.Target, err.Error())
		}
	}

	for _, m := range roRemounts {
		if _, err = os.Stat(m.Target); err == nil {
			err := syscall.Mount(m.Target, m.Target, "bind", syscall.MS_BIND, "")
			if err != nil {
				return fmt.Errorf("failed to bind-mount %s: %s", m.Target, err.Error())
			}
			err = syscall.Mount(m.Target, m.Target, "bind", uintptr(syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|m.Flags), "")
			if err != nil {
				return fmt.Errorf("failed to remount %s: %s", m.Target, err.Error())
			}
		}
	}
	devices := []string{"null", "zero", "full", "random", "urandom"}
	for _, device := range devices {
		f, err := os.Create(filepath.Join("/dev", device))<commit_msg>fix sandbox for debian jessie<commit_after>// +build linux

package sys

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/EricLagergren/go-gnulib/ttyname"
	"github.com/stephane-martin/skewer/utils"
)

type mountPoint struct {
	Source string
	Target string
	Fs     string
	Flags  int
	Data   string
}

func StartInNamespaces(command *exec.Cmd, dumpable bool, storePath string, confDir string) error {
	myTtyName := ""
	if ttyname.IsAtty(1) {
		myTtyName, _ = ttyname.TtyName(1)
		command.Env = append(command.Env, fmt.Sprintf("SKEWER_TTYNAME=%s", myTtyName))
	}

	confDir = strings.TrimSpace(confDir)
	if len(confDir) > 0 {
		command.Env = append(command.Env, fmt.Sprintf("SKEWER_CONF_DIR=%s", confDir))
	}

	storePath = strings.TrimSpace(storePath)
	if len(storePath) > 0 {
		command.Env = append(command.Env, fmt.Sprintf("SKEWER_STORE_PATH=%s", storePath))
	}

	command.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWPID | syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS,

		UidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getuid(),
				Size:        1,
			},
		},
		GidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getgid(),
				Size:        1,
			},
		},
	}

	// TODO: DUMPABLE should only be set in the child, just to write uid_map
	if !dumpable {
		SetDumpable()
	}
	err := command.Start()
	if !dumpable {
		SetNonDumpable()
	}
	return err
}

func PivotRoot(root string) (err error) {
	oldroot := filepath.Join(root, "oldroot")
	err = utils.Chain(
		func() error { return os.Mkdir(oldroot, 0777) },
		func() error { return syscall.PivotRoot(root, oldroot) },
		func() error { return syscall.Chdir("/") },
		func() error { return syscall.Unmount("/oldroot", syscall.MNT_DETACH) },
		func() error { return os.Remove("/oldroot") },
		func() error { return syscall.Chroot("/newroot") },
		func() error { return os.Chdir("/") },
		func() error { return os.Symlink(filepath.Join("/dev", "pts", "ptmx"), filepath.Join("/dev", "ptmx")) },
	)
	if err != nil {
		err = fmt.Errorf("PivotRoot error: %s", err.Error())
	}
	return err
}

func SetJournalFs(targetExec string) error {
	var err error

	roRemounts := []mountPoint{
		{
			Target: "/bin",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/sbin",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/lib",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/lib64",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/usr",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/var",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC,
		},
		{
			Target: "/home",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV,
		},
		{
			Target: "/etc",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC,
		},
	}

	mounts := []mountPoint{
		{
			Source: "proc",
			Target: "/proc",
			Fs:     "proc",
			Flags:  syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV,
		},
		{
			Source: "tmpfs",
			Target: "/dev",
			Fs:     "tmpfs",
			Flags:  syscall.MS_NOSUID | syscall.MS_NOEXEC,
			Data:   "mode=755",
		},
		{
			Source: "tmpfs",
			Target: "/boot",
			Fs:     "tmpfs",
			Flags:  syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC,
			Data:   "mode=700",
		},
	}

	temp, err := ioutil.TempDir("", "skewer-confined")
	if err != nil {
		return fmt.Errorf("TempDir error: %s", err)
	}
	err = syscall.Mount("/dev", temp, "bind", syscall.MS_BIND|syscall.MS_REC, "")
	if err != nil {
		return fmt.Errorf("Error bind-mounting /dev: %s", err)
	}

	for _, m := range mounts {
		if _, err := os.Stat(m.Target); err != nil {
			if os.IsNotExist(err) {
				err = os.MkdirAll(m.Target, 0755)
				if err != nil {
					return fmt.Errorf("mkdirall %s error: %v", m.Target, err)
				}
			}
		}
		err = syscall.Mount(m.Source, m.Target, m.Fs, uintptr(m.Flags), "")
		if err != nil {
			return fmt.Errorf("failed to mount %s: %s", m.Target, err.Error())
		}
	}

	for _, m := range roRemounts {
		if _, err = os.Stat(m.Target); err == nil {
			err := syscall.Mount(m.Target, m.Target, "bind", syscall.MS_BIND|syscall.MS_REC, "")
			if err != nil {
				return fmt.Errorf("failed to bind-mount %s: %s", m.Target, err.Error())
			}
			err = syscall.Mount(m.Target, m.Target, "bind", uintptr(syscall.MS_BIND|syscall.MS_REC|syscall.MS_REMOUNT|syscall.MS_RDONLY|m.Flags), "")
			if err != nil {
				return fmt.Errorf("failed to remount %s: %s", m.Target, err.Error())
			}
		}
	}
	devices := []string{"null", "zero", "full", "random", "urandom"}
	for _, device := range devices {
		f, err := os.Create(filepath.Join("/dev", device))
{'fraction_non_alphanumeric': 0.16921317950317014, 'fraction_numerical': 0.005716661469701694, 'mean_word_length': 3.285968819599109, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4693967', 'n_tokens_mistral': 4394, 'n_tokens_neox': 3718, 'n_words': 768}