mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
pathrs: add MkdirAllParentInRoot helper
While CreateInRoot supports hallucinating the target path, we do not use it directly when constructing device inode targets because we need to have different handling for mknod and bind-mounts. The solution is to simply have a more generic MkdirAllParentInRoot helper that MkdirAll's the parent directory of the target path and then allows the caller to create the trailing component however they like. (This can be used by CreateInRoot internally as well!) Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
* Copyright (C) 2024-2025 SUSE LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package pathrs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// MkdirAllParentInRoot is like [MkdirAllInRoot] except that it only creates
|
||||
// the parent directory of the target path, returning the trailing component so
|
||||
// the caller has more flexibility around constructing the final inode.
|
||||
//
|
||||
// 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) {
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to construct hallucinated target path: %w", err)
|
||||
}
|
||||
|
||||
dirPath, filename := filepath.Split(unsafePath)
|
||||
if filepath.Join("/", filename) == "/" {
|
||||
return nil, "", fmt.Errorf("create parent dir in root subpath %q has bad trailing component %q", unsafePath, filename)
|
||||
}
|
||||
|
||||
dirFd, err := MkdirAllInRoot(root, dirPath, mode)
|
||||
return dirFd, filename, err
|
||||
}
|
||||
@@ -19,9 +19,7 @@
|
||||
package pathrs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite"
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -50,17 +48,7 @@ func OpenInRoot(root, subpath string, flags int) (*os.File, error) {
|
||||
// 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) {
|
||||
subpath, err := hallucinateUnsafePath(root, subpath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct hallucinated target path: %w", err)
|
||||
}
|
||||
|
||||
dir, filename := filepath.Split(subpath)
|
||||
if filepath.Join("/", filename) == "/" {
|
||||
return nil, fmt.Errorf("create in root subpath %q has bad trailing component %q", subpath, filename)
|
||||
}
|
||||
|
||||
dirFd, err := MkdirAllInRoot(root, dir, 0o755)
|
||||
dirFd, filename, err := MkdirAllParentInRoot(root, subpath, 0o755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/procfs"
|
||||
"github.com/moby/sys/mountinfo"
|
||||
"github.com/moby/sys/userns"
|
||||
@@ -506,8 +505,6 @@ func statfsToMountFlags(st unix.Statfs_t) int {
|
||||
return flags
|
||||
}
|
||||
|
||||
var errRootfsToFile = errors.New("config tries to change rootfs to file")
|
||||
|
||||
func (m *mountEntry) createOpenMountpoint(rootfs string) (Err error) {
|
||||
unsafePath := pathrs.LexicallyStripRoot(rootfs, m.Destination)
|
||||
dstFile, err := pathrs.OpenInRoot(rootfs, unsafePath, unix.O_PATH)
|
||||
@@ -974,15 +971,7 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
|
||||
// The node only exists for cgroup reasons, ignore it here.
|
||||
return nil
|
||||
}
|
||||
destPath, err := securejoin.SecureJoin(rootfs, node.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if destPath == rootfs {
|
||||
return fmt.Errorf("%w: mknod over rootfs", errRootfsToFile)
|
||||
}
|
||||
destDirPath, destName := filepath.Split(destPath)
|
||||
destDir, err := pathrs.MkdirAllInRoot(rootfs, destDirPath, 0o755)
|
||||
destDir, destName, err := pathrs.MkdirAllParentInRoot(rootfs, node.Path, 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mkdir parent of device inode %q: %w", node.Path, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user