Pre-open container root directory

A lot of filesystem-related stuff happens inside the container root
directory, and we have used its name before. It makes sense to pre-open
it and use a *os.File handle instead.

Function names in internal/pathrs are kept as is for simplicity (and it
is an internal package), but they now accept root as *os.File.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin
2026-03-18 17:21:55 -07:00
parent 78b80677f6
commit 28cb321887
5 changed files with 56 additions and 38 deletions
+2 -2
View File
@@ -31,12 +31,12 @@ import (
// Callers need to be very careful operating on the trailing path, as trivial
// mistakes like following symlinks can cause security bugs. Most people
// should probably just use [MkdirAllInRoot] or [CreateInRoot].
func MkdirAllParentInRoot(root, unsafePath string, mode os.FileMode) (*os.File, string, error) {
func MkdirAllParentInRoot(root *os.File, unsafePath string, mode os.FileMode) (*os.File, string, error) {
// MkdirAllInRoot also does hallucinateUnsafePath, but we need to do it
// here first because when we split unsafePath into (dir, file) components
// we want to be doing so with the hallucinated path (so that trailing
// dangling symlinks are treated correctly).
unsafePath, err := hallucinateUnsafePath(root, unsafePath)
unsafePath, err := hallucinateUnsafePath(root.Name(), unsafePath)
if err != nil {
return nil, "", fmt.Errorf("failed to construct hallucinated target path: %w", err)
}
+4 -11
View File
@@ -24,12 +24,11 @@ import (
"github.com/cyphar/filepath-securejoin/pathrs-lite"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
// MkdirAllInRoot attempts to make
//
// path, _ := securejoin.SecureJoin(root, unsafePath)
// path, _ := securejoin.SecureJoin(root.Name(), unsafePath)
// os.MkdirAll(path, mode)
// os.Open(path)
//
@@ -48,8 +47,8 @@ import (
// handling if unsafePath has already been scoped within the rootfs (this is
// needed for a lot of runc callers and fixing this would require reworking a
// lot of path logic).
func MkdirAllInRoot(root, unsafePath string, mode os.FileMode) (*os.File, error) {
unsafePath, err := hallucinateUnsafePath(root, unsafePath)
func MkdirAllInRoot(root *os.File, unsafePath string, mode os.FileMode) (*os.File, error) {
unsafePath, err := hallucinateUnsafePath(root.Name(), unsafePath)
if err != nil {
return nil, fmt.Errorf("failed to construct hallucinated target path: %w", err)
}
@@ -67,13 +66,7 @@ func MkdirAllInRoot(root, unsafePath string, mode os.FileMode) (*os.File, error)
mode &= 0o1777
}
rootDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
if err != nil {
return nil, fmt.Errorf("open root handle: %w", err)
}
defer rootDir.Close()
return retryEAGAIN(func() (*os.File, error) {
return pathrs.MkdirAllHandle(rootDir, unsafePath, mode)
return pathrs.MkdirAllHandle(root, unsafePath, mode)
})
}
+5 -5
View File
@@ -28,11 +28,11 @@ import (
)
// OpenInRoot opens the given path inside the root with the provided flags. It
// is effectively shorthand for [securejoin.OpenInRoot] followed by
// is effectively shorthand for [securejoin.OpenatInRoot] followed by
// [securejoin.Reopen].
func OpenInRoot(root, subpath string, flags int) (*os.File, error) {
func OpenInRoot(root *os.File, subpath string, flags int) (*os.File, error) {
handle, err := retryEAGAIN(func() (*os.File, error) {
return pathrs.OpenInRoot(root, subpath)
return pathrs.OpenatInRoot(root, subpath)
})
if err != nil {
return nil, err
@@ -47,7 +47,7 @@ func OpenInRoot(root, subpath string, flags int) (*os.File, error) {
// open(O_CREAT|O_NOFOLLOW) semantics. If you want the creation to use O_EXCL,
// include it in the passed flags. The fileMode argument uses unix.* mode bits,
// *not* os.FileMode.
func CreateInRoot(root, subpath string, flags int, fileMode uint32) (*os.File, error) {
func CreateInRoot(root *os.File, subpath string, flags int, fileMode uint32) (*os.File, error) {
dirFd, filename, err := MkdirAllParentInRoot(root, subpath, 0o755)
if err != nil {
return nil, err
@@ -63,5 +63,5 @@ func CreateInRoot(root, subpath string, flags int, fileMode uint32) (*os.File, e
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), root+"/"+subpath), nil
return os.NewFile(uintptr(fd), root.Name()+"/"+subpath), nil
}