Skip to content

Commit 5841ed4

Browse files
committed
internal/safepath: Adapt k8s openat2 fallback
Adapts the function source code to the Moby codebase. Signed-off-by: Paweł Gronowski <[email protected]>
1 parent 56bb143 commit 5841ed4

2 files changed

Lines changed: 29 additions & 41 deletions

File tree

internal/safepath/join_linux.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func safeOpenFd(path, subpath string) (int, error) {
8585
switch {
8686
case errors.Is(err, unix.ENOSYS):
8787
// Openat2 is not available, fallback to Openat loop.
88-
return softOpenat2(prevFd, subpath)
88+
return kubernetesSafeOpen(path, subpath)
8989
case errors.Is(err, unix.EXDEV):
9090
return -1, &ErrEscapesBase{Base: path, Subpath: subpath}
9191
case errors.Is(err, unix.ENOENT), errors.Is(err, unix.ELOOP):
@@ -98,10 +98,6 @@ func safeOpenFd(path, subpath string) (int, error) {
9898
return fd, nil
9999
}
100100

101-
func softOpenat2(baseFd int, subpath string) (int, error) {
102-
return -1, errors.New("temporary stub, will be removed in later commit")
103-
}
104-
105101
// tempMountPoint creates a temporary file/directory to act as mount
106102
// point for the file descriptor.
107103
func tempMountPoint(sourceFd int) (string, error) {

internal/safepath/k8s_safeopen_linux.go

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,57 +17,49 @@ limitations under the License.
1717
*/
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"path/filepath"
2223
"strings"
23-
"syscall"
2424

25+
"github.com/containerd/log"
26+
"github.com/docker/docker/internal/unix_noeintr"
2527
"golang.org/x/sys/unix"
26-
"k8s.io/klog/v2"
27-
"k8s.io/mount-utils"
2828
)
2929

30-
const (
30+
// kubernetesSafeOpen open path formed by concatenation of the base directory
31+
// and its subpath and return its fd.
32+
// Symlinks are disallowed (pathname must already resolve symlinks) and the path
33+
// path must be within the base directory.
34+
// This is minimally modified code from https://github.com/kubernetes/kubernetes/blob/55fb1805a1217b91b36fa8fe8f2bf3a28af2454d/pkg/volume/util/subpath/subpath_linux.go#L530
35+
func kubernetesSafeOpen(base, subpath string) (int, error) {
3136
// syscall.Openat flags used to traverse directories not following symlinks
32-
nofollowFlags = unix.O_RDONLY | unix.O_NOFOLLOW
37+
const nofollowFlags = unix.O_RDONLY | unix.O_NOFOLLOW
3338
// flags for getting file descriptor without following the symlink
34-
openFDFlags = unix.O_NOFOLLOW | unix.O_PATH
35-
)
36-
37-
// This implementation is shared between Linux and NsEnterMounter
38-
// Open path and return its fd.
39-
// Symlinks are disallowed (pathname must already resolve symlinks),
40-
// and the path must be within the base directory.
41-
func doSafeOpen(pathname string, base string) (int, error) {
42-
pathname = filepath.Clean(pathname)
43-
base = filepath.Clean(base)
39+
const openFDFlags = unix.O_NOFOLLOW | unix.O_PATH
4440

45-
// Calculate segments to follow
46-
subpath, err := filepath.Rel(base, pathname)
47-
if err != nil {
48-
return -1, err
49-
}
41+
pathname := filepath.Join(base, subpath)
5042
segments := strings.Split(subpath, string(filepath.Separator))
5143

5244
// Assumption: base is the only directory that we have under control.
5345
// Base dir is not allowed to be a symlink.
54-
parentFD, err := syscall.Open(base, nofollowFlags|unix.O_CLOEXEC, 0)
46+
parentFD, err := unix_noeintr.Open(base, nofollowFlags|unix.O_CLOEXEC, 0)
5547
if err != nil {
56-
return -1, fmt.Errorf("cannot open directory %s: %s", base, err)
48+
return -1, &ErrNotAccessible{Path: base, Cause: err}
5749
}
5850
defer func() {
5951
if parentFD != -1 {
60-
if err = syscall.Close(parentFD); err != nil {
61-
klog.V(4).Infof("Closing FD %v failed for safeopen(%v): %v", parentFD, pathname, err)
52+
if err = unix_noeintr.Close(parentFD); err != nil {
53+
log.G(context.TODO()).Errorf("Closing FD %v failed for safeopen(%v): %v", parentFD, pathname, err)
6254
}
6355
}
6456
}()
6557

6658
childFD := -1
6759
defer func() {
6860
if childFD != -1 {
69-
if err = syscall.Close(childFD); err != nil {
70-
klog.V(4).Infof("Closing FD %v failed for safeopen(%v): %v", childFD, pathname, err)
61+
if err = unix_noeintr.Close(childFD); err != nil {
62+
log.G(context.TODO()).Errorf("Closing FD %v failed for safeopen(%v): %v", childFD, pathname, err)
7163
}
7264
}
7365
}()
@@ -80,31 +72,31 @@ func doSafeOpen(pathname string, base string) (int, error) {
8072
var deviceStat unix.Stat_t
8173

8274
currentPath = filepath.Join(currentPath, seg)
83-
if !mount.PathWithinBase(currentPath, base) {
84-
return -1, fmt.Errorf("path %s is outside of allowed base %s", currentPath, base)
75+
if !isLocalTo(currentPath, base) {
76+
return -1, &ErrEscapesBase{Base: currentPath, Subpath: seg}
8577
}
8678

8779
// Trigger auto mount if it's an auto-mounted directory, ignore error if not a directory.
8880
// Notice the trailing slash is mandatory, see "automount" in openat(2) and open_by_handle_at(2).
89-
unix.Fstatat(parentFD, seg+"/", &deviceStat, unix.AT_SYMLINK_NOFOLLOW)
81+
unix_noeintr.Fstatat(parentFD, seg+"/", &deviceStat, unix.AT_SYMLINK_NOFOLLOW)
9082

91-
klog.V(5).Infof("Opening path %s", currentPath)
92-
childFD, err = syscall.Openat(parentFD, seg, openFDFlags|unix.O_CLOEXEC, 0)
83+
log.G(context.TODO()).Debugf("Opening path %s", currentPath)
84+
childFD, err = unix_noeintr.Openat(parentFD, seg, openFDFlags|unix.O_CLOEXEC, 0)
9385
if err != nil {
94-
return -1, fmt.Errorf("cannot open %s: %s", currentPath, err)
86+
return -1, &ErrNotAccessible{Path: currentPath, Cause: err}
9587
}
9688

97-
err := unix.Fstat(childFD, &deviceStat)
89+
err := unix_noeintr.Fstat(childFD, &deviceStat)
9890
if err != nil {
9991
return -1, fmt.Errorf("error running fstat on %s with %v", currentPath, err)
10092
}
101-
fileFmt := deviceStat.Mode & syscall.S_IFMT
102-
if fileFmt == syscall.S_IFLNK {
93+
fileFmt := deviceStat.Mode & unix.S_IFMT
94+
if fileFmt == unix.S_IFLNK {
10395
return -1, fmt.Errorf("unexpected symlink found %s", currentPath)
10496
}
10597

10698
// Close parentFD
107-
if err = syscall.Close(parentFD); err != nil {
99+
if err = unix_noeintr.Close(parentFD); err != nil {
108100
return -1, fmt.Errorf("closing fd for %q failed: %v", filepath.Dir(currentPath), err)
109101
}
110102
// Set child to new parent

0 commit comments

Comments
 (0)