container/path: fhs path constants
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m6s
Test / Hakurei (push) Successful in 3m6s
Test / Sandbox (race detector) (push) Successful in 4m14s
Test / Hpkg (push) Successful in 4m11s
Test / Hakurei (race detector) (push) Successful in 4m40s
Test / Flake checks (push) Successful in 1m18s

This increases readability since this can help disambiguate absolute paths from similarly named path segments.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-08-03 20:46:41 +09:00
parent 38245559dc
commit c6be82bcf9
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
23 changed files with 164 additions and 123 deletions

View File

@ -14,6 +14,7 @@ import (
"time" "time"
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/internal/app" "hakurei.app/internal/app"
@ -94,7 +95,7 @@ func buildCommand(out io.Writer) command.Command {
Gid: us, Gid: us,
Username: "chronos", Username: "chronos",
Name: "Hakurei Permissive Default", Name: "Hakurei Permissive Default",
HomeDir: "/var/empty", HomeDir: container.FHSVarEmpty,
} }
} else { } else {
passwd = u passwd = u

View File

@ -43,12 +43,12 @@ func Test_printShowInstance(t *testing.T) {
Hostname: localhost Hostname: localhost
Flags: userns devel net device tty mapuid autoetc Flags: userns devel net device tty mapuid autoetc
Root: /var/lib/hakurei/base/org.debian (2) Root: /var/lib/hakurei/base/org.debian (2)
Etc: /etc Etc: /etc/
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
Filesystem Filesystem
w+tmpfs:/tmp w+tmpfs:/tmp/
+/nix/store +/nix/store
+/run/current-system +/run/current-system
+/run/opengl-driver +/run/opengl-driver
@ -123,12 +123,12 @@ App
Hostname: localhost Hostname: localhost
Flags: userns devel net device tty mapuid autoetc Flags: userns devel net device tty mapuid autoetc
Root: /var/lib/hakurei/base/org.debian (2) Root: /var/lib/hakurei/base/org.debian (2)
Etc: /etc Etc: /etc/
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
Filesystem Filesystem
w+tmpfs:/tmp w+tmpfs:/tmp/
+/nix/store +/nix/store
+/run/current-system +/run/current-system
+/run/opengl-driver +/run/opengl-driver
@ -276,7 +276,7 @@ App
"device": true, "device": true,
"filesystem": [ "filesystem": [
{ {
"dst": "/tmp", "dst": "/tmp/",
"src": "tmpfs", "src": "tmpfs",
"write": true "write": true
}, },
@ -311,7 +311,7 @@ App
], ],
"auto_root": "/var/lib/hakurei/base/org.debian", "auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2, "root_flags": 2,
"etc": "/etc", "etc": "/etc/",
"auto_etc": true "auto_etc": true
} }
}, },
@ -408,7 +408,7 @@ App
"device": true, "device": true,
"filesystem": [ "filesystem": [
{ {
"dst": "/tmp", "dst": "/tmp/",
"src": "tmpfs", "src": "tmpfs",
"write": true "write": true
}, },
@ -443,7 +443,7 @@ App
], ],
"auto_root": "/var/lib/hakurei/base/org.debian", "auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2, "root_flags": 2,
"etc": "/etc", "etc": "/etc/",
"auto_etc": true "auto_etc": true
} }
} }
@ -594,7 +594,7 @@ func Test_printPs(t *testing.T) {
"device": true, "device": true,
"filesystem": [ "filesystem": [
{ {
"dst": "/tmp", "dst": "/tmp/",
"src": "tmpfs", "src": "tmpfs",
"write": true "write": true
}, },
@ -629,7 +629,7 @@ func Test_printPs(t *testing.T) {
], ],
"auto_root": "/var/lib/hakurei/base/org.debian", "auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2, "root_flags": 2,
"etc": "/etc", "etc": "/etc/",
"auto_etc": true "auto_etc": true
} }
}, },

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"path" "path"
"hakurei.app/container"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system" "hakurei.app/system"
@ -94,17 +95,17 @@ func (app *appInfo) toFst(pathSet *appPathSet, argv []string, flagDropShell bool
Filesystem: []*hst.FilesystemConfig{ Filesystem: []*hst.FilesystemConfig{
{Src: path.Join(pathSet.nixPath, "store"), Dst: "/nix/store", Must: true}, {Src: path.Join(pathSet.nixPath, "store"), Dst: "/nix/store", Must: true},
{Src: pathSet.metaPath, Dst: path.Join(hst.Tmp, "app"), Must: true}, {Src: pathSet.metaPath, Dst: path.Join(hst.Tmp, "app"), Must: true},
{Src: "/etc/resolv.conf"}, {Src: container.FHSEtc + "resolv.conf"},
{Src: "/sys/block"}, {Src: container.FHSSys + "block"},
{Src: "/sys/bus"}, {Src: container.FHSSys + "bus"},
{Src: "/sys/class"}, {Src: container.FHSSys + "class"},
{Src: "/sys/dev"}, {Src: container.FHSSys + "dev"},
{Src: "/sys/devices"}, {Src: container.FHSSys + "devices"},
}, },
Link: [][2]string{ Link: [][2]string{
{app.CurrentSystem, "/run/current-system"}, {app.CurrentSystem, container.FHSRun + "current-system"},
{"/run/current-system/sw/bin", "/bin"}, {container.FHSRun + "current-system/sw/bin", "/bin"},
{"/run/current-system/sw/bin", "/usr/bin"}, {container.FHSRun + "current-system/sw/bin", container.FHSUsrBin},
}, },
Etc: path.Join(pathSet.cacheDir, "etc"), Etc: path.Join(pathSet.cacheDir, "etc"),
AutoEtc: true, AutoEtc: true,

View File

@ -11,6 +11,7 @@ import (
"syscall" "syscall"
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
@ -275,12 +276,12 @@ func main() {
"path:" + a.NixGL + "#nixVulkanNvidia", "path:" + a.NixGL + "#nixVulkanNvidia",
}, true, func(config *hst.Config) *hst.Config { }, true, func(config *hst.Config) *hst.Config {
config.Container.Filesystem = append(config.Container.Filesystem, []*hst.FilesystemConfig{ config.Container.Filesystem = append(config.Container.Filesystem, []*hst.FilesystemConfig{
{Src: "/etc/resolv.conf"}, {Src: container.FHSEtc + "resolv.conf"},
{Src: "/sys/block"}, {Src: container.FHSSys + "block"},
{Src: "/sys/bus"}, {Src: container.FHSSys + "bus"},
{Src: "/sys/class"}, {Src: container.FHSSys + "class"},
{Src: "/sys/dev"}, {Src: container.FHSSys + "dev"},
{Src: "/sys/devices"}, {Src: container.FHSSys + "devices"},
}...) }...)
appendGPUFilesystem(config) appendGPUFilesystem(config)
return config return config

View File

@ -8,6 +8,7 @@ import (
"strconv" "strconv"
"sync/atomic" "sync/atomic"
"hakurei.app/container"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
) )
@ -21,7 +22,7 @@ func init() {
if p, ok := os.LookupEnv("HAKUREI_DATA_HOME"); ok { if p, ok := os.LookupEnv("HAKUREI_DATA_HOME"); ok {
dataHome = p dataHome = p
} else { } else {
dataHome = "/var/lib/hakurei/" + strconv.Itoa(os.Getuid()) dataHome = container.FHSVarLib + "hakurei/" + strconv.Itoa(os.Getuid())
} }
} }

View File

@ -5,6 +5,7 @@ import (
"path" "path"
"strings" "strings"
"hakurei.app/container"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal" "hakurei.app/internal"
@ -52,9 +53,9 @@ func withNixDaemon(
{Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true}, {Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true},
}, },
Link: [][2]string{ Link: [][2]string{
{app.CurrentSystem, "/run/current-system"}, {app.CurrentSystem, container.FHSRun + "current-system"},
{"/run/current-system/sw/bin", "/bin"}, {container.FHSRun + "current-system/sw/bin", "/bin"},
{"/run/current-system/sw/bin", "/usr/bin"}, {container.FHSRun + "current-system/sw/bin", container.FHSUsrBin},
}, },
Etc: path.Join(pathSet.cacheDir, "etc"), Etc: path.Join(pathSet.cacheDir, "etc"),
AutoEtc: true, AutoEtc: true,
@ -93,11 +94,11 @@ func withCacheDir(
{Src: workDir, Dst: path.Join(hst.Tmp, "bundle"), Must: true}, {Src: workDir, Dst: path.Join(hst.Tmp, "bundle"), Must: true},
}, },
Link: [][2]string{ Link: [][2]string{
{app.CurrentSystem, "/run/current-system"}, {app.CurrentSystem, container.FHSRun + "current-system"},
{"/run/current-system/sw/bin", "/bin"}, {container.FHSRun + "current-system/sw/bin", "/bin"},
{"/run/current-system/sw/bin", "/usr/bin"}, {container.FHSRun + "current-system/sw/bin", container.FHSUsrBin},
}, },
Etc: path.Join(workDir, "etc"), Etc: path.Join(workDir, container.FHSEtc),
AutoEtc: true, AutoEtc: true,
}, },
}, dropShell, beforeFail) }, dropShell, beforeFail)

View File

@ -12,7 +12,7 @@ func init() { gob.Register(new(AutoEtcOp)) }
// This is not a generic setup op. It is implemented here to reduce ipc overhead. // This is not a generic setup op. It is implemented here to reduce ipc overhead.
func (f *Ops) Etc(host, prefix string) *Ops { func (f *Ops) Etc(host, prefix string) *Ops {
e := &AutoEtcOp{prefix} e := &AutoEtcOp{prefix}
f.Mkdir("/etc", 0755) f.Mkdir(FHSEtc, 0755)
f.Bind(host, e.hostPath(), 0) f.Bind(host, e.hostPath(), 0)
*f = append(*f, e) *f = append(*f, e)
return f return f
@ -22,7 +22,7 @@ type AutoEtcOp struct{ Prefix string }
func (e *AutoEtcOp) early(*Params) error { return nil } func (e *AutoEtcOp) early(*Params) error { return nil }
func (e *AutoEtcOp) apply(*Params) error { func (e *AutoEtcOp) apply(*Params) error {
const target = sysrootPath + "/etc/" const target = sysrootPath + FHSEtc
rel := e.hostRel() + "/" rel := e.hostRel() + "/"
if err := os.MkdirAll(target, 0755); err != nil { if err := os.MkdirAll(target, 0755); err != nil {
@ -40,7 +40,7 @@ func (e *AutoEtcOp) apply(*Params) error {
case "group": case "group":
case "mtab": case "mtab":
if err = os.Symlink("/proc/mounts", target+n); err != nil { if err = os.Symlink(FHSProc+"mounts", target+n); err != nil {
return wrapErrSelf(err) return wrapErrSelf(err)
} }
@ -54,7 +54,7 @@ func (e *AutoEtcOp) apply(*Params) error {
return nil return nil
} }
func (e *AutoEtcOp) hostPath() string { return "/etc/" + e.hostRel() } func (e *AutoEtcOp) hostPath() string { return FHSEtc + e.hostRel() }
func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix } func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix }
func (e *AutoEtcOp) Is(op Op) bool { func (e *AutoEtcOp) Is(op Op) bool {

View File

@ -42,7 +42,7 @@ func (r *AutoRootOp) early(params *Params) error {
if IsAutoRootBindable(name) { if IsAutoRootBindable(name) {
op := &BindMountOp{ op := &BindMountOp{
Source: path.Join(r.Host, name), Source: path.Join(r.Host, name),
Target: "/" + name, Target: FHSRoot + name,
Flags: r.Flags, Flags: r.Flags,
} }
if err = op.early(params); err != nil { if err = op.early(params); err != nil {

View File

@ -18,10 +18,6 @@ import (
) )
const ( const (
// Nonexistent is a path that cannot exist.
// /proc is chosen because a system with covered /proc is unsupported by this package.
Nonexistent = "/proc/nonexistent"
// CancelSignal is the signal expected by container init on context cancel. // CancelSignal is the signal expected by container init on context cancel.
// A custom [Container.Cancel] function must eventually deliver this signal. // A custom [Container.Cancel] function must eventually deliver this signal.
CancelSignal = SIGTERM CancelSignal = SIGTERM
@ -142,7 +138,7 @@ func (p *Container) Start() error {
} else { } else {
p.cmd.Cancel = func() error { return p.cmd.Process.Signal(CancelSignal) } p.cmd.Cancel = func() error { return p.cmd.Process.Signal(CancelSignal) }
} }
p.cmd.Dir = "/" p.cmd.Dir = FHSRoot
p.cmd.SysProcAttr = &SysProcAttr{ p.cmd.SysProcAttr = &SysProcAttr{
Setsid: !p.RetainSession, Setsid: !p.RetainSession,
Pdeathsig: SIGKILL, Pdeathsig: SIGKILL,
@ -251,6 +247,6 @@ func (p *Container) ProcessState() *os.ProcessState {
func New(ctx context.Context, name string, args ...string) *Container { func New(ctx context.Context, name string, args ...string) *Container {
return &Container{name: name, ctx: ctx, return &Container{name: name, ctx: ctx,
Params: Params{Args: append([]string{name}, args...), Dir: "/", Ops: new(Ops)}, Params: Params{Args: append([]string{name}, args...), Dir: FHSRoot, Ops: new(Ops)},
} }
} }

View File

@ -31,7 +31,7 @@ const (
it should be noted that none of this should become relevant at any point since the resulting it should be noted that none of this should become relevant at any point since the resulting
intermediate root tmpfs should be effectively anonymous */ intermediate root tmpfs should be effectively anonymous */
intermediateHostPath = "/proc/self/fd" intermediateHostPath = FHSProc + "self/fd"
// setup params file descriptor // setup params file descriptor
setupEnv = "HAKUREI_SETUP" setupEnv = "HAKUREI_SETUP"
@ -88,17 +88,17 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
if err := SetDumpable(SUID_DUMP_USER); err != nil { if err := SetDumpable(SUID_DUMP_USER); err != nil {
log.Fatalf("cannot set SUID_DUMP_USER: %s", err) log.Fatalf("cannot set SUID_DUMP_USER: %s", err)
} }
if err := os.WriteFile("/proc/self/uid_map", if err := os.WriteFile(FHSProc+"self/uid_map",
append([]byte{}, strconv.Itoa(params.Uid)+" "+strconv.Itoa(params.HostUid)+" 1\n"...), append([]byte{}, strconv.Itoa(params.Uid)+" "+strconv.Itoa(params.HostUid)+" 1\n"...),
0); err != nil { 0); err != nil {
log.Fatalf("%v", err) log.Fatalf("%v", err)
} }
if err := os.WriteFile("/proc/self/setgroups", if err := os.WriteFile(FHSProc+"self/setgroups",
[]byte("deny\n"), []byte("deny\n"),
0); err != nil && !os.IsNotExist(err) { 0); err != nil && !os.IsNotExist(err) {
log.Fatalf("%v", err) log.Fatalf("%v", err)
} }
if err := os.WriteFile("/proc/self/gid_map", if err := os.WriteFile(FHSProc+"self/gid_map",
append([]byte{}, strconv.Itoa(params.Gid)+" "+strconv.Itoa(params.HostGid)+" 1\n"...), append([]byte{}, strconv.Itoa(params.Gid)+" "+strconv.Itoa(params.HostGid)+" 1\n"...),
0); err != nil { 0); err != nil {
log.Fatalf("%v", err) log.Fatalf("%v", err)
@ -117,7 +117,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
// cache sysctl before pivot_root // cache sysctl before pivot_root
LastCap() LastCap()
if err := Mount("", "/", "", MS_SILENT|MS_SLAVE|MS_REC, ""); err != nil { if err := Mount("", FHSRoot, "", MS_SILENT|MS_SLAVE|MS_REC, ""); err != nil {
log.Fatalf("cannot make / rslave: %v", err) log.Fatalf("cannot make / rslave: %v", err)
} }
@ -159,7 +159,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
if err := PivotRoot(intermediateHostPath, hostDir); err != nil { if err := PivotRoot(intermediateHostPath, hostDir); err != nil {
log.Fatalf("cannot pivot into intermediate root: %v", err) log.Fatalf("cannot pivot into intermediate root: %v", err)
} }
if err := os.Chdir("/"); err != nil { if err := os.Chdir(FHSRoot); err != nil {
log.Fatalf("%v", err) log.Fatalf("%v", err)
} }
@ -189,7 +189,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
{ {
var fd int var fd int
if err := IgnoringEINTR(func() (err error) { if err := IgnoringEINTR(func() (err error) {
fd, err = Open("/", O_DIRECTORY|O_RDONLY, 0) fd, err = Open(FHSRoot, O_DIRECTORY|O_RDONLY, 0)
return return
}); err != nil { }); err != nil {
log.Fatalf("cannot open intermediate root: %v", err) log.Fatalf("cannot open intermediate root: %v", err)
@ -207,7 +207,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
if err := Unmount(".", MNT_DETACH); err != nil { if err := Unmount(".", MNT_DETACH); err != nil {
log.Fatalf("cannot unmount intemediate root: %v", err) log.Fatalf("cannot unmount intemediate root: %v", err)
} }
if err := os.Chdir("/"); err != nil { if err := os.Chdir(FHSRoot); err != nil {
log.Fatalf("%v", err) log.Fatalf("%v", err)
} }

View File

@ -53,7 +53,7 @@ const (
FstypeNULL = zeroString FstypeNULL = zeroString
// FstypeProc represents the proc pseudo-filesystem. // FstypeProc represents the proc pseudo-filesystem.
// A fully visible instance of proc must be available in the mount namespace for proc to be mounted. // A fully visible instance of proc must be available in the mount namespace for proc to be mounted.
// This filesystem type is usually mounted on /proc. // This filesystem type is usually mounted on [FHSProc].
FstypeProc = "proc" FstypeProc = "proc"
// FstypeDevpts represents the devpts pseudo-filesystem. // FstypeDevpts represents the devpts pseudo-filesystem.
// This type of filesystem is usually mounted on /dev/pts. // This type of filesystem is usually mounted on /dev/pts.

View File

@ -215,7 +215,7 @@ func (d *MountDevOp) apply(params *Params) error {
return err return err
} }
if err := hostProc.bindMount( if err := hostProc.bindMount(
toHost("/dev/"+name), toHost(FHSDev+name),
targetPath, targetPath,
0, 0,
true, true,
@ -225,15 +225,15 @@ func (d *MountDevOp) apply(params *Params) error {
} }
for i, name := range []string{"stdin", "stdout", "stderr"} { for i, name := range []string{"stdin", "stdout", "stderr"} {
if err := os.Symlink( if err := os.Symlink(
"/proc/self/fd/"+string(rune(i+'0')), FHSProc+"self/fd/"+string(rune(i+'0')),
path.Join(target, name), path.Join(target, name),
); err != nil { ); err != nil {
return wrapErrSelf(err) return wrapErrSelf(err)
} }
} }
for _, pair := range [][2]string{ for _, pair := range [][2]string{
{"/proc/self/fd", "fd"}, {FHSProc + "self/fd", "fd"},
{"/proc/kcore", "core"}, {FHSProc + "kcore", "core"},
{"pts/ptmx", "ptmx"}, {"pts/ptmx", "ptmx"},
} { } {
if err := os.Symlink(pair[0], path.Join(target, pair[1])); err != nil { if err := os.Symlink(pair[0], path.Join(target, pair[1])); err != nil {
@ -436,7 +436,7 @@ func (t *TmpfileOp) apply(params *Params) error {
} }
var tmpPath string var tmpPath string
if f, err := os.CreateTemp("/", "tmp.*"); err != nil { if f, err := os.CreateTemp(FHSRoot, "tmp.*"); err != nil {
return wrapErrSelf(err) return wrapErrSelf(err)
} else if _, err = f.Write(t.Data); err != nil { } else if _, err = f.Write(t.Data); err != nil {
return wrapErrSuffix(err, return wrapErrSuffix(err,

View File

@ -14,9 +14,49 @@ import (
) )
const ( const (
hostPath = "/" + hostDir // FHSRoot points to the file system root.
FHSRoot = "/"
// FHSEtc points to the directory for system-specific configuration.
FHSEtc = "/etc/"
// FHSTmp points to the place for small temporary files.
FHSTmp = "/tmp/"
// FHSRun points to a "tmpfs" file system for system packages to place runtime data, socket files, and similar.
FHSRun = "/run/"
// FHSRunUser points to a directory containing per-user runtime directories,
// each usually individually mounted "tmpfs" instances.
FHSRunUser = FHSRun + "user/"
// FHSUsr points to vendor-supplied operating system resources.
FHSUsr = "/usr/"
// FHSUsrBin points to binaries and executables for user commands that shall appear in the $PATH search path.
FHSUsrBin = FHSUsr + "bin/"
// FHSVar points to persistent, variable system data. Writable during normal system operation.
FHSVar = "/var/"
// FHSVarLib points to persistent system data.
FHSVarLib = FHSVar + "lib/"
// FHSVarEmpty points to a nonstandard directory that is usually empty.
FHSVarEmpty = FHSVar + "empty/"
// FHSDev points to the root directory for device nodes.
FHSDev = "/dev/"
// FHSProc points to a virtual kernel file system exposing the process list and other functionality.
FHSProc = "/proc/"
// FHSProcSys points to a hierarchy below /proc/ that exposes a number of kernel tunables.
FHSProcSys = FHSProc + "sys/"
// FHSSys points to a virtual kernel file system exposing discovered devices and other functionality.
FHSSys = "/sys/"
)
const (
// Nonexistent is a path that cannot exist.
// /proc is chosen because a system with covered /proc is unsupported by this package.
Nonexistent = FHSProc + "nonexistent"
hostPath = FHSRoot + hostDir
hostDir = "host" hostDir = "host"
sysrootPath = "/" + sysrootDir sysrootPath = FHSRoot + sysrootDir
sysrootDir = "sysroot" sysrootDir = "sysroot"
) )

View File

@ -17,9 +17,9 @@ var (
) )
const ( const (
kernelOverflowuidPath = "/proc/sys/kernel/overflowuid" kernelOverflowuidPath = FHSProcSys + "kernel/overflowuid"
kernelOverflowgidPath = "/proc/sys/kernel/overflowgid" kernelOverflowgidPath = FHSProcSys + "kernel/overflowgid"
kernelCapLastCapPath = "/proc/sys/kernel/cap_last_cap" kernelCapLastCapPath = FHSProcSys + "kernel/cap_last_cap"
) )
func mustReadSysctl() { func mustReadSysctl() {

View File

@ -12,7 +12,7 @@ func Template() *Config {
return &Config{ return &Config{
ID: "org.chromium.Chromium", ID: "org.chromium.Chromium",
Path: "/run/current-system/sw/bin/chromium", Path: container.FHSRun + "current-system/sw/bin/chromium",
Args: []string{ Args: []string{
"chromium", "chromium",
"--ignore-gpu-blocklist", "--ignore-gpu-blocklist",
@ -46,12 +46,12 @@ func Template() *Config {
DirectWayland: false, DirectWayland: false,
Username: "chronos", Username: "chronos",
Shell: "/run/current-system/sw/bin/zsh", Shell: container.FHSRun + "current-system/sw/bin/zsh",
Data: "/var/lib/hakurei/u0/org.chromium.Chromium", Data: container.FHSVarLib + "hakurei/u0/org.chromium.Chromium",
Dir: "/data/data/org.chromium.Chromium", Dir: "/data/data/org.chromium.Chromium",
ExtraPerms: []*ExtraPermConfig{ ExtraPerms: []*ExtraPermConfig{
{Path: "/var/lib/hakurei/u0", Ensure: true, Execute: true}, {Path: container.FHSVarLib + "hakurei/u0", Ensure: true, Execute: true},
{Path: "/var/lib/hakurei/u0/org.chromium.Chromium", Read: true, Write: true, Execute: true}, {Path: container.FHSVarLib + "hakurei/u0/org.chromium.Chromium", Read: true, Write: true, Execute: true},
}, },
Identity: 9, Identity: 9,
@ -78,19 +78,19 @@ func Template() *Config {
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT", "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT",
}, },
Filesystem: []*FilesystemConfig{ Filesystem: []*FilesystemConfig{
{Dst: "/tmp", Src: SourceTmpfs, Write: true}, {Dst: container.FHSTmp, Src: SourceTmpfs, Write: true},
{Src: "/nix/store"}, {Src: "/nix/store"},
{Src: "/run/current-system"}, {Src: container.FHSRun + "current-system"},
{Src: "/run/opengl-driver"}, {Src: container.FHSRun + "opengl-driver"},
{Src: "/var/db/nix-channels"}, {Src: container.FHSVar + "db/nix-channels"},
{Src: "/var/lib/hakurei/u0/org.chromium.Chromium", {Src: container.FHSVarLib + "hakurei/u0/org.chromium.Chromium",
Dst: "/data/data/org.chromium.Chromium", Write: true, Must: true}, Dst: "/data/data/org.chromium.Chromium", Write: true, Must: true},
{Src: "/dev/dri", Device: true}, {Src: container.FHSDev + "dri", Device: true},
}, },
Link: [][2]string{{"/run/user/65534", "/run/user/150"}}, Link: [][2]string{{container.FHSRunUser + "65534", container.FHSRunUser + "150"}},
AutoRoot: "/var/lib/hakurei/base/org.debian", AutoRoot: container.FHSVarLib + "hakurei/base/org.debian",
RootFlags: container.BindWritable, RootFlags: container.BindWritable,
Etc: "/etc", Etc: container.FHSEtc,
AutoEtc: true, AutoEtc: true,
}, },
} }

View File

@ -98,7 +98,7 @@ func TestTemplate(t *testing.T) {
"device": true, "device": true,
"filesystem": [ "filesystem": [
{ {
"dst": "/tmp", "dst": "/tmp/",
"src": "tmpfs", "src": "tmpfs",
"write": true "write": true
}, },
@ -133,7 +133,7 @@ func TestTemplate(t *testing.T) {
], ],
"auto_root": "/var/lib/hakurei/base/org.debian", "auto_root": "/var/lib/hakurei/base/org.debian",
"root_flags": 2, "root_flags": 2,
"etc": "/etc", "etc": "/etc/",
"auto_etc": true "auto_etc": true
} }
}` }`

View File

@ -23,7 +23,7 @@ var testCasesNixos = []sealTestCase{
Container: &hst.ContainerConfig{ Container: &hst.ContainerConfig{
Userns: true, Net: true, MapRealUID: true, Env: nil, AutoEtc: true, Userns: true, Net: true, MapRealUID: true, Env: nil, AutoEtc: true,
Filesystem: []*hst.FilesystemConfig{ Filesystem: []*hst.FilesystemConfig{
{Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true}, {Src: "/bin", Must: true}, {Src: "/usr/bin/", Must: true},
{Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true}, {Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true},
{Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"}, {Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"},
{Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true}, {Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true},
@ -116,11 +116,11 @@ var testCasesNixos = []sealTestCase{
"XDG_SESSION_TYPE=tty", "XDG_SESSION_TYPE=tty",
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Proc("/proc"). Proc("/proc/").
Tmpfs(hst.Tmp, 4096, 0755). Tmpfs(hst.Tmp, 4096, 0755).
DevWritable("/dev", true). DevWritable("/dev/", true).
Bind("/bin", "/bin", 0). Bind("/bin", "/bin", 0).
Bind("/usr/bin", "/usr/bin", 0). Bind("/usr/bin/", "/usr/bin/", 0).
Bind("/nix/store", "/nix/store", 0). Bind("/nix/store", "/nix/store", 0).
Bind("/run/current-system", "/run/current-system", 0). Bind("/run/current-system", "/run/current-system", 0).
Bind("/sys/block", "/sys/block", container.BindOptional). Bind("/sys/block", "/sys/block", container.BindOptional).
@ -130,11 +130,11 @@ var testCasesNixos = []sealTestCase{
Bind("/sys/devices", "/sys/devices", container.BindOptional). Bind("/sys/devices", "/sys/devices", container.BindOptional).
Bind("/run/opengl-driver", "/run/opengl-driver", 0). Bind("/run/opengl-driver", "/run/opengl-driver", 0).
Bind("/dev/dri", "/dev/dri", container.BindDevice|container.BindWritable|container.BindOptional). Bind("/dev/dri", "/dev/dri", container.BindDevice|container.BindWritable|container.BindOptional).
Etc("/etc", "8e2c76b066dabe574cf073bdb46eb5c1"). Etc("/etc/", "8e2c76b066dabe574cf073bdb46eb5c1").
Remount("/dev", syscall.MS_RDONLY). Remount("/dev/", syscall.MS_RDONLY).
Tmpfs("/run/user", 4096, 0755). Tmpfs("/run/user/", 4096, 0755).
Bind("/tmp/hakurei.1971/runtime/1", "/run/user/1971", container.BindWritable). Bind("/tmp/hakurei.1971/runtime/1", "/run/user/1971", container.BindWritable).
Bind("/tmp/hakurei.1971/tmpdir/1", "/tmp", container.BindWritable). Bind("/tmp/hakurei.1971/tmpdir/1", "/tmp/", container.BindWritable).
Bind("/var/lib/persist/module/hakurei/0/1", "/var/lib/persist/module/hakurei/0/1", container.BindWritable). Bind("/var/lib/persist/module/hakurei/0/1", "/var/lib/persist/module/hakurei/0/1", container.BindWritable).
Place("/etc/passwd", []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")). Place("/etc/passwd", []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
Place("/etc/group", []byte("hakurei:x:100:\n")). Place("/etc/group", []byte("hakurei:x:100:\n")).

View File

@ -44,18 +44,18 @@ var testCasesPd = []sealTestCase{
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Root("/", "4a450b6596d7bc15bd01780eb9a607ac", container.BindWritable). Root("/", "4a450b6596d7bc15bd01780eb9a607ac", container.BindWritable).
Proc("/proc"). Proc("/proc/").
Tmpfs(hst.Tmp, 4096, 0755). Tmpfs(hst.Tmp, 4096, 0755).
DevWritable("/dev", true). DevWritable("/dev/", true).
Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional). Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional).
Readonly("/var/run/nscd", 0755). Readonly("/var/run/nscd", 0755).
Tmpfs("/run/user/1971", 8192, 0755). Tmpfs("/run/user/1971", 8192, 0755).
Tmpfs("/run/dbus", 8192, 0755). Tmpfs("/run/dbus", 8192, 0755).
Etc("/etc", "4a450b6596d7bc15bd01780eb9a607ac"). Etc("/etc/", "4a450b6596d7bc15bd01780eb9a607ac").
Remount("/dev", syscall.MS_RDONLY). Remount("/dev/", syscall.MS_RDONLY).
Tmpfs("/run/user", 4096, 0755). Tmpfs("/run/user/", 4096, 0755).
Bind("/tmp/hakurei.1971/runtime/0", "/run/user/65534", container.BindWritable). Bind("/tmp/hakurei.1971/runtime/0", "/run/user/65534", container.BindWritable).
Bind("/tmp/hakurei.1971/tmpdir/0", "/tmp", container.BindWritable). Bind("/tmp/hakurei.1971/tmpdir/0", "/tmp/", container.BindWritable).
Bind("/home/chronos", "/home/chronos", container.BindWritable). Bind("/home/chronos", "/home/chronos", container.BindWritable).
Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place("/etc/group", []byte("hakurei:x:65534:\n")). Place("/etc/group", []byte("hakurei:x:65534:\n")).
@ -179,19 +179,19 @@ var testCasesPd = []sealTestCase{
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Root("/", "ebf083d1b175911782d413369b64ce7c", container.BindWritable). Root("/", "ebf083d1b175911782d413369b64ce7c", container.BindWritable).
Proc("/proc"). Proc("/proc/").
Tmpfs(hst.Tmp, 4096, 0755). Tmpfs(hst.Tmp, 4096, 0755).
DevWritable("/dev", true). DevWritable("/dev/", true).
Bind("/dev/dri", "/dev/dri", container.BindWritable|container.BindDevice|container.BindOptional). Bind("/dev/dri", "/dev/dri", container.BindWritable|container.BindDevice|container.BindOptional).
Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional). Bind("/dev/kvm", "/dev/kvm", container.BindWritable|container.BindDevice|container.BindOptional).
Readonly("/var/run/nscd", 0755). Readonly("/var/run/nscd", 0755).
Tmpfs("/run/user/1971", 8192, 0755). Tmpfs("/run/user/1971", 8192, 0755).
Tmpfs("/run/dbus", 8192, 0755). Tmpfs("/run/dbus", 8192, 0755).
Etc("/etc", "ebf083d1b175911782d413369b64ce7c"). Etc("/etc/", "ebf083d1b175911782d413369b64ce7c").
Remount("/dev", syscall.MS_RDONLY). Remount("/dev/", syscall.MS_RDONLY).
Tmpfs("/run/user", 4096, 0755). Tmpfs("/run/user/", 4096, 0755).
Bind("/tmp/hakurei.1971/runtime/9", "/run/user/65534", container.BindWritable). Bind("/tmp/hakurei.1971/runtime/9", "/run/user/65534", container.BindWritable).
Bind("/tmp/hakurei.1971/tmpdir/9", "/tmp", container.BindWritable). Bind("/tmp/hakurei.1971/tmpdir/9", "/tmp/", container.BindWritable).
Bind("/home/chronos", "/home/chronos", container.BindWritable). Bind("/home/chronos", "/home/chronos", container.BindWritable).
Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). Place("/etc/passwd", []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place("/etc/group", []byte("hakurei:x:65534:\n")). Place("/etc/group", []byte("hakurei:x:65534:\n")).

View File

@ -81,13 +81,13 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
} }
params. params.
Proc("/proc"). Proc(container.FHSProc).
Tmpfs(hst.Tmp, 1<<12, 0755) Tmpfs(hst.Tmp, 1<<12, 0755)
if !s.Device { if !s.Device {
params.DevWritable("/dev", true) params.DevWritable(container.FHSDev, true)
} else { } else {
params.Bind("/dev", "/dev", container.BindWritable|container.BindDevice) params.Bind(container.FHSDev, container.FHSDev, container.BindWritable|container.BindDevice)
} }
/* retrieve paths and hide them if they're made available in the sandbox; /* retrieve paths and hide them if they're made available in the sandbox;
@ -111,7 +111,7 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
if path.IsAbs(pair[1]) { if path.IsAbs(pair[1]) {
// get parent dir of socket // get parent dir of socket
dir := path.Dir(pair[1]) dir := path.Dir(pair[1])
if dir == "." || dir == "/" { if dir == "." || dir == container.FHSRoot {
os.Printf("dbus socket %q is in an unusual location", pair[1]) os.Printf("dbus socket %q is in an unusual location", pair[1])
} }
hidePaths = append(hidePaths, dir) hidePaths = append(hidePaths, dir)
@ -229,19 +229,19 @@ func newContainer(s *hst.ContainerConfig, os sys.State, prefix string, uid, gid
if !s.AutoEtc { if !s.AutoEtc {
if s.Etc != "" { if s.Etc != "" {
params.Bind(s.Etc, "/etc", 0) params.Bind(s.Etc, container.FHSEtc, 0)
} }
} else { } else {
etcPath := s.Etc etcPath := s.Etc
if etcPath == "" { if etcPath == "" {
etcPath = "/etc" etcPath = container.FHSEtc
} }
params.Etc(etcPath, prefix) params.Etc(etcPath, prefix)
} }
// no more ContainerConfig paths beyond this point // no more ContainerConfig paths beyond this point
if !s.Device { if !s.Device {
params.Remount("/dev", syscall.MS_RDONLY) params.Remount(container.FHSDev, syscall.MS_RDONLY)
} }
return params, maps.Clone(s.Env), nil return params, maps.Clone(s.Env), nil

View File

@ -88,7 +88,7 @@ func (seal *outcome) Run(rs *RunState) error {
defer cancel() defer cancel()
cmd := exec.CommandContext(ctx, hsuPath) cmd := exec.CommandContext(ctx, hsuPath)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.Dir = "/" // container init enters final working directory cmd.Dir = container.FHSRoot // container init enters final working directory
// shim runs in the same session as monitor; see shim.go for behaviour // shim runs in the same session as monitor; see shim.go for behaviour
cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGCONT) } cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGCONT) }

View File

@ -242,19 +242,19 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
Tty: true, Tty: true,
AutoEtc: true, AutoEtc: true,
AutoRoot: "/", AutoRoot: container.FHSRoot,
RootFlags: container.BindWritable, RootFlags: container.BindWritable,
} }
// bind GPU stuff // bind GPU stuff
if config.Enablements&(system.EX11|system.EWayland) != 0 { if config.Enablements&(system.EX11|system.EWayland) != 0 {
conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: "/dev/dri", Device: true}) conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: container.FHSDev + "dri", Device: true})
} }
// opportunistically bind kvm // opportunistically bind kvm
conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: "/dev/kvm", Device: true}) conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Src: container.FHSDev + "kvm", Device: true})
// hide nscd from container if present // hide nscd from container if present
const nscd = "/var/run/nscd" const nscd = container.FHSVar + "run/nscd"
if _, err := sys.Stat(nscd); !errors.Is(err, fs.ErrNotExist) { if _, err := sys.Stat(nscd); !errors.Is(err, fs.ErrNotExist) {
conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Dst: nscd, Src: hst.SourceTmpfs}) conf.Filesystem = append(conf.Filesystem, &hst.FilesystemConfig{Dst: nscd, Src: hst.SourceTmpfs})
} }
@ -290,7 +290,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
} }
// inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as mapped uid // inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as mapped uid
innerRuntimeDir := path.Join("/run/user", mapuid.String()) innerRuntimeDir := path.Join(container.FHSRunUser, mapuid.String())
seal.env[xdgRuntimeDir] = innerRuntimeDir seal.env[xdgRuntimeDir] = innerRuntimeDir
seal.env[xdgSessionClass] = "user" seal.env[xdgSessionClass] = "user"
seal.env[xdgSessionType] = "tty" seal.env[xdgSessionType] = "tty"
@ -307,7 +307,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
runtimeDirInst := path.Join(runtimeDir, seal.user.aid.String()) runtimeDirInst := path.Join(runtimeDir, seal.user.aid.String())
seal.sys.Ensure(runtimeDirInst, 0700) seal.sys.Ensure(runtimeDirInst, 0700)
seal.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute) seal.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute)
seal.container.Tmpfs("/run/user", 1<<12, 0755) seal.container.Tmpfs(container.FHSRunUser, 1<<12, 0755)
seal.container.Bind(runtimeDirInst, innerRuntimeDir, container.BindWritable) seal.container.Bind(runtimeDirInst, innerRuntimeDir, container.BindWritable)
} }
@ -319,11 +319,11 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
seal.sys.Ensure(tmpdirInst, 01700) seal.sys.Ensure(tmpdirInst, 01700)
seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute) seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute)
// mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp // mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp
seal.container.Bind(tmpdirInst, "/tmp", container.BindWritable) seal.container.Bind(tmpdirInst, container.FHSTmp, container.BindWritable)
} }
{ {
homeDir := "/var/empty" homeDir := container.FHSVarEmpty
if seal.user.home != "" { if seal.user.home != "" {
homeDir = seal.user.home homeDir = seal.user.home
} }
@ -337,9 +337,9 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
seal.env["USER"] = username seal.env["USER"] = username
seal.env[shell] = config.Shell seal.env[shell] = config.Shell
seal.container.Place("/etc/passwd", seal.container.Place(container.FHSEtc+"passwd",
[]byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Hakurei:"+homeDir+":"+config.Shell+"\n")) []byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Hakurei:"+homeDir+":"+config.Shell+"\n"))
seal.container.Place("/etc/group", seal.container.Place(container.FHSEtc+"group",
[]byte("hakurei:x:"+mapgid.String()+":\n")) []byte("hakurei:x:"+mapgid.String()+":\n"))
} }
@ -388,7 +388,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
} else { } else {
seal.sys.ChangeHosts("#" + seal.user.uid.String()) seal.sys.ChangeHosts("#" + seal.user.uid.String())
seal.env[display] = d seal.env[display] = d
seal.container.Bind("/tmp/.X11-unix", "/tmp/.X11-unix", 0) seal.container.Bind(container.FHSTmp+".X11-unix", container.FHSTmp+".X11-unix", 0)
} }
} }
@ -467,7 +467,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
seal.container.Bind(sessionPath, sessionInner, 0) seal.container.Bind(sessionPath, sessionInner, 0)
seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write) seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write)
if config.SystemBus != nil { if config.SystemBus != nil {
systemInner := "/run/dbus/system_bus_socket" systemInner := container.FHSRun + "dbus/system_bus_socket"
seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner
seal.container.Bind(systemPath, systemInner, 0) seal.container.Bind(systemPath, systemInner, 0)
seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write) seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write)
@ -475,7 +475,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
} }
// mount root read-only as the final setup Op // mount root read-only as the final setup Op
seal.container.Remount("/", syscall.MS_RDONLY) seal.container.Remount(container.FHSRoot, syscall.MS_RDONLY)
// append ExtraPerms last // append ExtraPerms last
for _, p := range config.ExtraPerms { for _, p := range config.ExtraPerms {

View File

@ -86,7 +86,7 @@ func (s *Std) Uid(aid int) (int, error) {
cmd.Path = hsuPath cmd.Path = hsuPath
cmd.Stderr = os.Stderr // pass through fatal messages cmd.Stderr = os.Stderr // pass through fatal messages
cmd.Env = []string{"HAKUREI_APP_ID=" + strconv.Itoa(aid)} cmd.Env = []string{"HAKUREI_APP_ID=" + strconv.Itoa(aid)}
cmd.Dir = "/" cmd.Dir = container.FHSRoot
var ( var (
p []byte p []byte
exitError *exec.ExitError exitError *exec.ExitError

View File

@ -28,7 +28,7 @@ func Exec(ctx context.Context, p string) ([]*Entry, error) {
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
z.Stdout = stdout z.Stdout = stdout
z.Stderr = stderr z.Stderr = stderr
z.Bind("/", "/", 0).Proc("/proc").Dev("/dev", false) z.Bind(container.FHSRoot, container.FHSRoot, 0).Proc(container.FHSProc).Dev(container.FHSProc, false)
if err := z.Start(); err != nil { if err := z.Start(); err != nil {
return nil, err return nil, err