aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/path.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-07-03 02:59:43 +0900
committerOphestra <cat@gensokyo.uk>2025-07-03 02:59:43 +0900
commit1b5ecd9eaf3289d164d8ed1bce6013e0e4ef8e86 (patch)
tree047fe5fabdfb37d5c0088f7f572cebc8b13853bb /container/path.go
parent82561d62b66f17c05604e87f18187bb3a91f00d2 (diff)
container: move out of toplevel
This allows slightly easier use of the vanity url. This also provides some disambiguation between low level containers and hakurei app containers. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container/path.go')
-rw-r--r--container/path.go94
1 files changed, 94 insertions, 0 deletions
diff --git a/container/path.go b/container/path.go
new file mode 100644
index 00000000..a3938ff5
--- /dev/null
+++ b/container/path.go
@@ -0,0 +1,94 @@
+package container
+
+import (
+ "errors"
+ "fmt"
+ "io/fs"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "git.gensokyo.uk/security/hakurei/container/vfs"
+)
+
+const (
+ hostPath = "/" + hostDir
+ hostDir = "host"
+ sysrootPath = "/" + sysrootDir
+ sysrootDir = "sysroot"
+)
+
+func toSysroot(name string) string {
+ name = strings.TrimLeftFunc(name, func(r rune) bool { return r == '/' })
+ return path.Join(sysrootPath, name)
+}
+
+func toHost(name string) string {
+ name = strings.TrimLeftFunc(name, func(r rune) bool { return r == '/' })
+ return path.Join(hostPath, name)
+}
+
+func createFile(name string, perm, pperm os.FileMode, content []byte) error {
+ if err := os.MkdirAll(path.Dir(name), pperm); err != nil {
+ return wrapErrSelf(err)
+ }
+ f, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_EXCL|syscall.O_WRONLY, perm)
+ if err != nil {
+ return wrapErrSelf(err)
+ }
+ if content != nil {
+ _, err = f.Write(content)
+ if err != nil {
+ err = wrapErrSelf(err)
+ }
+ }
+ return errors.Join(f.Close(), err)
+}
+
+func ensureFile(name string, perm, pperm os.FileMode) error {
+ fi, err := os.Stat(name)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return err
+ }
+ return createFile(name, perm, pperm, nil)
+ }
+
+ if mode := fi.Mode(); mode&fs.ModeDir != 0 || mode&fs.ModeSymlink != 0 {
+ err = msg.WrapErr(syscall.EISDIR,
+ fmt.Sprintf("path %q is a directory", name))
+ }
+ return err
+}
+
+var hostProc = newProcPats(hostPath)
+
+func newProcPats(prefix string) *procPaths {
+ return &procPaths{prefix + "/proc", prefix + "/proc/self"}
+}
+
+type procPaths struct {
+ prefix string
+ self string
+}
+
+func (p *procPaths) stdout() string { return p.self + "/fd/1" }
+func (p *procPaths) fd(fd int) string { return p.self + "/fd/" + strconv.Itoa(fd) }
+func (p *procPaths) mountinfo(f func(d *vfs.MountInfoDecoder) error) error {
+ if r, err := os.Open(p.self + "/mountinfo"); err != nil {
+ return wrapErrSelf(err)
+ } else {
+ d := vfs.NewMountInfoDecoder(r)
+ err0 := f(d)
+ if err = r.Close(); err != nil {
+ return wrapErrSuffix(err,
+ "cannot close mountinfo:")
+ } else if err = d.Err(); err != nil {
+ return wrapErrSuffix(err,
+ "cannot parse mountinfo:")
+ }
+ return err0
+ }
+}