Skip to content

Commit d8bfe59

Browse files
committed
rootfs: add MkdirAllParentInRoot helper for hallucination
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 <[email protected]>
1 parent 6e66c35 commit d8bfe59

File tree

3 files changed

+49
-23
lines changed

3 files changed

+49
-23
lines changed

internal/pathrs/mkdirall.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
/*
3+
* Copyright (C) 2024-2025 Aleksa Sarai <[email protected]>
4+
* Copyright (C) 2024-2025 SUSE LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package pathrs
20+
21+
import (
22+
"fmt"
23+
"os"
24+
"path/filepath"
25+
)
26+
27+
// MkdirAllParentInRoot is like [MkdirAllInRoot] except that it only creates
28+
// the parent directory of the target path, returning the trailing component so
29+
// the caller has more flexibility around constructing the final inode.
30+
//
31+
// Callers need to be very careful operating on the trailing path, as trivial
32+
// mistakes like following symlinks can cause security bugs. Most people
33+
// should probably just use [MkdirAllInRoot] or [CreateInRoot].
34+
func MkdirAllParentInRoot(root, unsafePath string, mode os.FileMode) (*os.File, string, error) {
35+
unsafePath, err := hallucinateUnsafePath(root, unsafePath)
36+
if err != nil {
37+
return nil, "", fmt.Errorf("failed to construct hallucinated target path: %w", err)
38+
}
39+
40+
dirPath, filename := filepath.Split(unsafePath)
41+
if filepath.Join("/", filename) == "/" {
42+
return nil, "", fmt.Errorf("create parent dir in root subpath %q has bad trailing component %q", unsafePath, filename)
43+
}
44+
45+
dirFd, err := MkdirAllInRoot(root, dirPath, mode)
46+
return dirFd, filename, err
47+
}

internal/pathrs/root_pathrslite.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
package pathrs
2020

2121
import (
22-
"fmt"
2322
"os"
24-
"path/filepath"
2523

2624
"github.com/cyphar/filepath-securejoin/pathrs-lite"
2725
"golang.org/x/sys/unix"
@@ -50,17 +48,7 @@ func OpenInRoot(root, subpath string, flags int) (*os.File, error) {
5048
// include it in the passed flags. The fileMode argument uses unix.* mode bits,
5149
// *not* os.FileMode.
5250
func CreateInRoot(root, subpath string, flags int, fileMode uint32) (*os.File, error) {
53-
subpath, err := hallucinateUnsafePath(root, subpath)
54-
if err != nil {
55-
return nil, fmt.Errorf("failed to construct hallucinated target path: %w", err)
56-
}
57-
58-
dir, filename := filepath.Split(subpath)
59-
if filepath.Join("/", filename) == "/" {
60-
return nil, fmt.Errorf("create in root subpath %q has bad trailing component %q", subpath, filename)
61-
}
62-
63-
dirFd, err := MkdirAllInRoot(root, dir, 0o755)
51+
dirFd, filename, err := MkdirAllParentInRoot(root, subpath, 0o755)
6452
if err != nil {
6553
return nil, err
6654
}

libcontainer/rootfs_linux.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"syscall"
1313
"time"
1414

15-
securejoin "github.com/cyphar/filepath-securejoin"
1615
"github.com/cyphar/filepath-securejoin/pathrs-lite/procfs"
1716
"github.com/moby/sys/mountinfo"
1817
"github.com/moby/sys/userns"
@@ -975,15 +974,7 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
975974
// The node only exists for cgroup reasons, ignore it here.
976975
return nil
977976
}
978-
destPath, err := securejoin.SecureJoin(rootfs, node.Path)
979-
if err != nil {
980-
return err
981-
}
982-
if destPath == rootfs {
983-
return fmt.Errorf("%w: mknod over rootfs", errRootfsToFile)
984-
}
985-
destDirPath, destName := filepath.Split(destPath)
986-
destDir, err := pathrs.MkdirAllInRoot(rootfs, destDirPath, 0o755)
977+
destDir, destName, err := pathrs.MkdirAllParentInRoot(rootfs, node.Path, 0o755)
987978
if err != nil {
988979
return fmt.Errorf("mkdir parent of device inode %q: %w", node.Path, err)
989980
}

0 commit comments

Comments
 (0)