fst: hide sockets exposed via Filesystem
This is mostly useful for permissive defaults. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
db03565614
commit
562f5ed797
11
fst/path.go
Normal file
11
fst/path.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package fst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func deepContainsH(basepath, targpath string) (bool, error) {
|
||||||
|
rel, err := filepath.Rel(basepath, targpath)
|
||||||
|
return err == nil && rel != ".." && !strings.HasPrefix(rel, string([]byte{'.', '.', filepath.Separator})), err
|
||||||
|
}
|
85
fst/path_test.go
Normal file
85
fst/path_test.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package fst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeepContainsH(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
basepath string
|
||||||
|
targpath string
|
||||||
|
want bool
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "equal abs",
|
||||||
|
basepath: "/run",
|
||||||
|
targpath: "/run",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "equal rel",
|
||||||
|
basepath: "./run",
|
||||||
|
targpath: "run",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "contains abs",
|
||||||
|
basepath: "/run",
|
||||||
|
targpath: "/run/dbus",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inverse contains abs",
|
||||||
|
basepath: "/run/dbus",
|
||||||
|
targpath: "/run",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "contains rel",
|
||||||
|
basepath: "../run",
|
||||||
|
targpath: "../run/dbus",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inverse contains rel",
|
||||||
|
basepath: "../run/dbus",
|
||||||
|
targpath: "../run",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "weird abs",
|
||||||
|
basepath: "/run/dbus",
|
||||||
|
targpath: "/run/dbus/../current-system",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "weird rel",
|
||||||
|
basepath: "../run/dbus",
|
||||||
|
targpath: "../run/dbus/../current-system",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "invalid mix",
|
||||||
|
basepath: "/run",
|
||||||
|
targpath: "./run",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if got, err := deepContainsH(tc.basepath, tc.targpath); (err != nil) != tc.wantErr {
|
||||||
|
t.Errorf("deepContainsH() error = %v, wantErr %v", err, tc.wantErr)
|
||||||
|
} else if got != tc.want {
|
||||||
|
t.Errorf("deepContainsH() = %v, want %v", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,13 @@ package fst
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"git.gensokyo.uk/security/fortify/dbus"
|
||||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||||
|
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||||
"git.gensokyo.uk/security/fortify/internal/linux"
|
"git.gensokyo.uk/security/fortify/internal/linux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,7 +73,8 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) {
|
|||||||
DieWithParent: true,
|
DieWithParent: true,
|
||||||
AsInit: true,
|
AsInit: true,
|
||||||
|
|
||||||
// initialise map
|
// initialise unconditionally as Once cannot be justified
|
||||||
|
// for saving such a miniscule amount of memory
|
||||||
Chmod: make(bwrap.ChmodConfig),
|
Chmod: make(bwrap.ChmodConfig),
|
||||||
}).
|
}).
|
||||||
SetUID(uid).SetGID(uid).
|
SetUID(uid).SetGID(uid).
|
||||||
@ -89,16 +95,85 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// retrieve paths and hide them if they're made available in the sandbox
|
||||||
|
var hidePaths []string
|
||||||
|
sc := os.Paths()
|
||||||
|
hidePaths = append(hidePaths, sc.RuntimePath, sc.SharePath)
|
||||||
|
_, systemBusAddr := dbus.Address()
|
||||||
|
if entries, err := dbus.Parse([]byte(systemBusAddr)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
// there is usually only one, do not preallocate
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.Method != "unix" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, pair := range entry.Values {
|
||||||
|
if pair[0] == "path" {
|
||||||
|
if path.IsAbs(pair[1]) {
|
||||||
|
// get parent dir of socket
|
||||||
|
dir := path.Dir(pair[1])
|
||||||
|
if dir == "." || dir == "/" {
|
||||||
|
fmsg.VPrintf("dbus socket %q is in an unusual location", pair[1])
|
||||||
|
}
|
||||||
|
hidePaths = append(hidePaths, dir)
|
||||||
|
} else {
|
||||||
|
fmsg.VPrintf("dbus socket %q is not absolute", pair[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hidePathMatch := make([]bool, len(hidePaths))
|
||||||
|
for i := range hidePaths {
|
||||||
|
if err := evalSymlinks(os, &hidePaths[i]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, c := range s.Filesystem {
|
for _, c := range s.Filesystem {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
src := c.Src
|
|
||||||
|
if !path.IsAbs(c.Src) {
|
||||||
|
return nil, fmt.Errorf("src path %q is not absolute", c.Src)
|
||||||
|
}
|
||||||
|
|
||||||
dest := c.Dst
|
dest := c.Dst
|
||||||
if c.Dst == "" {
|
if c.Dst == "" {
|
||||||
dest = c.Src
|
dest = c.Src
|
||||||
|
} else if !path.IsAbs(dest) {
|
||||||
|
return nil, fmt.Errorf("dst path %q is not absolute", dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcH := c.Src
|
||||||
|
if err := evalSymlinks(os, &srcH); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range hidePaths {
|
||||||
|
// skip matched entries
|
||||||
|
if hidePathMatch[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, err := deepContainsH(srcH, hidePaths[i]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if ok {
|
||||||
|
hidePathMatch[i] = true
|
||||||
|
fmsg.VPrintf("hiding paths from %q", c.Src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.Bind(c.Src, dest, !c.Must, c.Write, c.Device)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide marked paths before setting up shares
|
||||||
|
for i, ok := range hidePathMatch {
|
||||||
|
if ok {
|
||||||
|
conf.Tmpfs(hidePaths[i], 8192)
|
||||||
}
|
}
|
||||||
conf.Bind(src, dest, !c.Must, c.Write, c.Device)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, l := range s.Link {
|
for _, l := range s.Link {
|
||||||
@ -133,3 +208,15 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) {
|
|||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalSymlinks(os linux.System, v *string) error {
|
||||||
|
if p, err := os.EvalSymlinks(*v); err != nil {
|
||||||
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmsg.VPrintf("path %q does not yet exist", *v)
|
||||||
|
} else {
|
||||||
|
*v = p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -62,45 +62,14 @@ var testCasesPd = []sealTestCase{
|
|||||||
Bind("/lib64", "/lib64", false, true).
|
Bind("/lib64", "/lib64", false, true).
|
||||||
Bind("/nix", "/nix", false, true).
|
Bind("/nix", "/nix", false, true).
|
||||||
Bind("/root", "/root", false, true).
|
Bind("/root", "/root", false, true).
|
||||||
|
Bind("/run", "/run", false, true).
|
||||||
Bind("/srv", "/srv", false, true).
|
Bind("/srv", "/srv", false, true).
|
||||||
Bind("/sys", "/sys", false, true).
|
Bind("/sys", "/sys", false, true).
|
||||||
Bind("/usr", "/usr", false, true).
|
Bind("/usr", "/usr", false, true).
|
||||||
Bind("/var", "/var", false, true).
|
Bind("/var", "/var", false, true).
|
||||||
Bind("/run/agetty.reload", "/run/agetty.reload", false, true).
|
|
||||||
Bind("/run/binfmt", "/run/binfmt", false, true).
|
|
||||||
Bind("/run/booted-system", "/run/booted-system", false, true).
|
|
||||||
Bind("/run/credentials", "/run/credentials", false, true).
|
|
||||||
Bind("/run/cryptsetup", "/run/cryptsetup", false, true).
|
|
||||||
Bind("/run/current-system", "/run/current-system", false, true).
|
|
||||||
Bind("/run/host", "/run/host", false, true).
|
|
||||||
Bind("/run/keys", "/run/keys", false, true).
|
|
||||||
Bind("/run/libvirt", "/run/libvirt", false, true).
|
|
||||||
Bind("/run/libvirtd.pid", "/run/libvirtd.pid", false, true).
|
|
||||||
Bind("/run/lock", "/run/lock", false, true).
|
|
||||||
Bind("/run/log", "/run/log", false, true).
|
|
||||||
Bind("/run/lvm", "/run/lvm", false, true).
|
|
||||||
Bind("/run/mount", "/run/mount", false, true).
|
|
||||||
Bind("/run/NetworkManager", "/run/NetworkManager", false, true).
|
|
||||||
Bind("/run/nginx", "/run/nginx", false, true).
|
|
||||||
Bind("/run/nixos", "/run/nixos", false, true).
|
|
||||||
Bind("/run/nscd", "/run/nscd", false, true).
|
|
||||||
Bind("/run/opengl-driver", "/run/opengl-driver", false, true).
|
|
||||||
Bind("/run/pppd", "/run/pppd", false, true).
|
|
||||||
Bind("/run/resolvconf", "/run/resolvconf", false, true).
|
|
||||||
Bind("/run/sddm", "/run/sddm", false, true).
|
|
||||||
Bind("/run/store", "/run/store", false, true).
|
|
||||||
Bind("/run/syncoid", "/run/syncoid", false, true).
|
|
||||||
Bind("/run/system", "/run/system", false, true).
|
|
||||||
Bind("/run/systemd", "/run/systemd", false, true).
|
|
||||||
Bind("/run/tmpfiles.d", "/run/tmpfiles.d", false, true).
|
|
||||||
Bind("/run/udev", "/run/udev", false, true).
|
|
||||||
Bind("/run/udisks2", "/run/udisks2", false, true).
|
|
||||||
Bind("/run/utmp", "/run/utmp", false, true).
|
|
||||||
Bind("/run/virtlogd.pid", "/run/virtlogd.pid", false, true).
|
|
||||||
Bind("/run/wrappers", "/run/wrappers", false, true).
|
|
||||||
Bind("/run/zed.pid", "/run/zed.pid", false, true).
|
|
||||||
Bind("/run/zed.state", "/run/zed.state", false, true).
|
|
||||||
Bind("/dev/kvm", "/dev/kvm", true, true, true).
|
Bind("/dev/kvm", "/dev/kvm", true, true, true).
|
||||||
|
Tmpfs("/run/user/1971", 8192).
|
||||||
|
Tmpfs("/run/dbus", 8192).
|
||||||
Bind("/etc", fst.Tmp+"/etc").
|
Bind("/etc", fst.Tmp+"/etc").
|
||||||
Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||||
Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||||
@ -317,46 +286,15 @@ var testCasesPd = []sealTestCase{
|
|||||||
Bind("/lib64", "/lib64", false, true).
|
Bind("/lib64", "/lib64", false, true).
|
||||||
Bind("/nix", "/nix", false, true).
|
Bind("/nix", "/nix", false, true).
|
||||||
Bind("/root", "/root", false, true).
|
Bind("/root", "/root", false, true).
|
||||||
|
Bind("/run", "/run", false, true).
|
||||||
Bind("/srv", "/srv", false, true).
|
Bind("/srv", "/srv", false, true).
|
||||||
Bind("/sys", "/sys", false, true).
|
Bind("/sys", "/sys", false, true).
|
||||||
Bind("/usr", "/usr", false, true).
|
Bind("/usr", "/usr", false, true).
|
||||||
Bind("/var", "/var", false, true).
|
Bind("/var", "/var", false, true).
|
||||||
Bind("/run/agetty.reload", "/run/agetty.reload", false, true).
|
|
||||||
Bind("/run/binfmt", "/run/binfmt", false, true).
|
|
||||||
Bind("/run/booted-system", "/run/booted-system", false, true).
|
|
||||||
Bind("/run/credentials", "/run/credentials", false, true).
|
|
||||||
Bind("/run/cryptsetup", "/run/cryptsetup", false, true).
|
|
||||||
Bind("/run/current-system", "/run/current-system", false, true).
|
|
||||||
Bind("/run/host", "/run/host", false, true).
|
|
||||||
Bind("/run/keys", "/run/keys", false, true).
|
|
||||||
Bind("/run/libvirt", "/run/libvirt", false, true).
|
|
||||||
Bind("/run/libvirtd.pid", "/run/libvirtd.pid", false, true).
|
|
||||||
Bind("/run/lock", "/run/lock", false, true).
|
|
||||||
Bind("/run/log", "/run/log", false, true).
|
|
||||||
Bind("/run/lvm", "/run/lvm", false, true).
|
|
||||||
Bind("/run/mount", "/run/mount", false, true).
|
|
||||||
Bind("/run/NetworkManager", "/run/NetworkManager", false, true).
|
|
||||||
Bind("/run/nginx", "/run/nginx", false, true).
|
|
||||||
Bind("/run/nixos", "/run/nixos", false, true).
|
|
||||||
Bind("/run/nscd", "/run/nscd", false, true).
|
|
||||||
Bind("/run/opengl-driver", "/run/opengl-driver", false, true).
|
|
||||||
Bind("/run/pppd", "/run/pppd", false, true).
|
|
||||||
Bind("/run/resolvconf", "/run/resolvconf", false, true).
|
|
||||||
Bind("/run/sddm", "/run/sddm", false, true).
|
|
||||||
Bind("/run/store", "/run/store", false, true).
|
|
||||||
Bind("/run/syncoid", "/run/syncoid", false, true).
|
|
||||||
Bind("/run/system", "/run/system", false, true).
|
|
||||||
Bind("/run/systemd", "/run/systemd", false, true).
|
|
||||||
Bind("/run/tmpfiles.d", "/run/tmpfiles.d", false, true).
|
|
||||||
Bind("/run/udev", "/run/udev", false, true).
|
|
||||||
Bind("/run/udisks2", "/run/udisks2", false, true).
|
|
||||||
Bind("/run/utmp", "/run/utmp", false, true).
|
|
||||||
Bind("/run/virtlogd.pid", "/run/virtlogd.pid", false, true).
|
|
||||||
Bind("/run/wrappers", "/run/wrappers", false, true).
|
|
||||||
Bind("/run/zed.pid", "/run/zed.pid", false, true).
|
|
||||||
Bind("/run/zed.state", "/run/zed.state", false, true).
|
|
||||||
Bind("/dev/dri", "/dev/dri", true, true, true).
|
Bind("/dev/dri", "/dev/dri", true, true, true).
|
||||||
Bind("/dev/kvm", "/dev/kvm", true, true, true).
|
Bind("/dev/kvm", "/dev/kvm", true, true, true).
|
||||||
|
Tmpfs("/run/user/1971", 8192).
|
||||||
|
Tmpfs("/run/dbus", 8192).
|
||||||
Bind("/etc", fst.Tmp+"/etc").
|
Bind("/etc", fst.Tmp+"/etc").
|
||||||
Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||||
Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||||
|
@ -2,7 +2,6 @@ package app_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -128,12 +127,12 @@ func (s *stubNixOS) Open(name string) (fs.File, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubNixOS) Exit(code int) {
|
func (s *stubNixOS) EvalSymlinks(path string) (string, error) {
|
||||||
panic("called exit on stub with code " + strconv.Itoa(code))
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubNixOS) Stdout() io.Writer {
|
func (s *stubNixOS) Exit(code int) {
|
||||||
panic("requested stdout")
|
panic("called exit on stub with code " + strconv.Itoa(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubNixOS) Paths() linux.Paths {
|
func (s *stubNixOS) Paths() linux.Paths {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package app_test
|
package app_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
@ -48,14 +49,22 @@ func TestApp(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("compare bwrap", func(t *testing.T) {
|
t.Run("compare bwrap", func(t *testing.T) {
|
||||||
if !reflect.DeepEqual(gotBwrap, tc.wantBwrap) {
|
if !reflect.DeepEqual(gotBwrap, tc.wantBwrap) {
|
||||||
t.Errorf("seal: bwrap = %#v, want %#v",
|
t.Errorf("seal: bwrap =\n%s\n, want\n%s",
|
||||||
gotBwrap, tc.wantBwrap)
|
mustMarshal(gotBwrap), mustMarshal(tc.wantBwrap))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustMarshal(v any) string {
|
||||||
|
if b, err := json.Marshal(v); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func stubDirEntries(names ...string) (e []fs.DirEntry, err error) {
|
func stubDirEntries(names ...string) (e []fs.DirEntry, err error) {
|
||||||
e = make([]fs.DirEntry, len(names))
|
e = make([]fs.DirEntry, len(names))
|
||||||
for i, name := range names {
|
for i, name := range names {
|
||||||
|
@ -194,7 +194,6 @@ func (a *app) Seal(config *fst.Config) error {
|
|||||||
switch p {
|
switch p {
|
||||||
case "/proc":
|
case "/proc":
|
||||||
case "/dev":
|
case "/dev":
|
||||||
case "/run":
|
|
||||||
case "/tmp":
|
case "/tmp":
|
||||||
case "/mnt":
|
case "/mnt":
|
||||||
case "/etc":
|
case "/etc":
|
||||||
@ -205,23 +204,7 @@ func (a *app) Seal(config *fst.Config) error {
|
|||||||
}
|
}
|
||||||
conf.Filesystem = append(conf.Filesystem, b...)
|
conf.Filesystem = append(conf.Filesystem, b...)
|
||||||
}
|
}
|
||||||
// bind entries in /run
|
|
||||||
if d, err := a.os.ReadDir("/run"); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
b := make([]*fst.FilesystemConfig, 0, len(d))
|
|
||||||
for _, ent := range d {
|
|
||||||
name := ent.Name()
|
|
||||||
switch name {
|
|
||||||
case "user":
|
|
||||||
case "dbus":
|
|
||||||
default:
|
|
||||||
p := "/run/" + name
|
|
||||||
b = append(b, &fst.FilesystemConfig{Src: p, Write: true, Must: true})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conf.Filesystem = append(conf.Filesystem, b...)
|
|
||||||
}
|
|
||||||
// hide nscd from sandbox if present
|
// hide nscd from sandbox if present
|
||||||
nscd := "/var/run/nscd"
|
nscd := "/var/run/nscd"
|
||||||
if _, err := a.os.Stat(nscd); !errors.Is(err, fs.ErrNotExist) {
|
if _, err := a.os.Stat(nscd); !errors.Is(err, fs.ErrNotExist) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package linux
|
package linux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
@ -30,10 +29,10 @@ type System interface {
|
|||||||
Stat(name string) (fs.FileInfo, error)
|
Stat(name string) (fs.FileInfo, error)
|
||||||
// Open provides [os.Open]
|
// Open provides [os.Open]
|
||||||
Open(name string) (fs.File, error)
|
Open(name string) (fs.File, error)
|
||||||
|
// EvalSymlinks provides [filepath.EvalSymlinks]
|
||||||
|
EvalSymlinks(path string) (string, error)
|
||||||
// Exit provides [os.Exit].
|
// Exit provides [os.Exit].
|
||||||
Exit(code int)
|
Exit(code int)
|
||||||
// Stdout provides [os.Stdout].
|
|
||||||
Stdout() io.Writer
|
|
||||||
|
|
||||||
// Paths returns a populated [Paths] struct.
|
// Paths returns a populated [Paths] struct.
|
||||||
Paths() Paths
|
Paths() Paths
|
||||||
|
@ -2,11 +2,11 @@ package linux
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -37,8 +37,8 @@ func (s *Std) LookupGroup(name string) (*user.Group, error) { return user.Lookup
|
|||||||
func (s *Std) ReadDir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
func (s *Std) ReadDir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
||||||
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
||||||
func (s *Std) Open(name string) (fs.File, error) { return os.Open(name) }
|
func (s *Std) Open(name string) (fs.File, error) { return os.Open(name) }
|
||||||
|
func (s *Std) EvalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
|
||||||
func (s *Std) Exit(code int) { fmsg.Exit(code) }
|
func (s *Std) Exit(code int) { fmsg.Exit(code) }
|
||||||
func (s *Std) Stdout() io.Writer { return os.Stdout }
|
|
||||||
|
|
||||||
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user