Skip to content

Commit 210561a

Browse files
committed
Support named pipe mounts for Windows containers
Adds support to mount named pipes into Windows containers. This support already exists in hcsshim, so this change just passes them through correctly in cri. Named pipe mounts must start with "\\.\pipe\". Signed-off-by: Kevin Parsons <[email protected]>
1 parent 682d158 commit 210561a

2 files changed

Lines changed: 59 additions & 17 deletions

File tree

pkg/containerd/opts/spec_windows.go

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"context"
2323
"path/filepath"
2424
"sort"
25+
"strings"
2526

2627
"github.com/containerd/containerd/containers"
2728
"github.com/containerd/containerd/oci"
@@ -47,6 +48,20 @@ func WithWindowsNetworkNamespace(path string) oci.SpecOpts {
4748
}
4849
}
4950

51+
// namedPipePath returns true if the given path is to a named pipe.
52+
func namedPipePath(p string) bool {
53+
return strings.HasPrefix(p, `\\.\pipe\`)
54+
}
55+
56+
// cleanMount returns a cleaned version of the mount path. The input is returned
57+
// as-is if it is a named pipe path.
58+
func cleanMount(p string) string {
59+
if namedPipePath(p) {
60+
return p
61+
}
62+
return filepath.Clean(p)
63+
}
64+
5065
// WithWindowsMounts sorts and adds runtime and CRI mounts to the spec for
5166
// windows container.
5267
func WithWindowsMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount) oci.SpecOpts {
@@ -62,7 +77,7 @@ func WithWindowsMounts(osi osinterface.OS, config *runtime.ContainerConfig, extr
6277
for _, e := range extra {
6378
found := false
6479
for _, c := range criMounts {
65-
if filepath.Clean(e.ContainerPath) == filepath.Clean(c.ContainerPath) {
80+
if cleanMount(e.ContainerPath) == cleanMount(c.ContainerPath) {
6681
found = true
6782
break
6883
}
@@ -80,14 +95,14 @@ func WithWindowsMounts(osi osinterface.OS, config *runtime.ContainerConfig, extr
8095
// mounts overridden by supplied mount;
8196
mountSet := make(map[string]struct{})
8297
for _, m := range mounts {
83-
mountSet[filepath.Clean(m.ContainerPath)] = struct{}{}
98+
mountSet[cleanMount(m.ContainerPath)] = struct{}{}
8499
}
85100

86101
defaultMounts := s.Mounts
87102
s.Mounts = nil
88103

89104
for _, m := range defaultMounts {
90-
dst := filepath.Clean(m.Destination)
105+
dst := cleanMount(m.Destination)
91106
if _, ok := mountSet[dst]; ok {
92107
// filter out mount overridden by a supplied mount
93108
continue
@@ -100,17 +115,25 @@ func WithWindowsMounts(osi osinterface.OS, config *runtime.ContainerConfig, extr
100115
dst = mount.GetContainerPath()
101116
src = mount.GetHostPath()
102117
)
103-
// TODO(windows): Support special mount sources, e.g. named pipe.
104-
// Create the host path if it doesn't exist.
105-
if _, err := osi.Stat(src); err != nil {
106-
// If the source doesn't exist, return an error instead
107-
// of creating the source. This aligns with Docker's
108-
// behavior on windows.
109-
return errors.Wrapf(err, "failed to stat %q", src)
110-
}
111-
src, err := osi.ResolveSymbolicLink(src)
112-
if err != nil {
113-
return errors.Wrapf(err, "failed to resolve symlink %q", src)
118+
// In the case of a named pipe mount on Windows, don't stat the file
119+
// or do other operations that open it, as that could interfere with
120+
// the listening process. filepath.Clean also breaks named pipe
121+
// paths, so don't use it.
122+
if !namedPipePath(src) {
123+
if _, err := osi.Stat(src); err != nil {
124+
// If the source doesn't exist, return an error instead
125+
// of creating the source. This aligns with Docker's
126+
// behavior on windows.
127+
return errors.Wrapf(err, "failed to stat %q", src)
128+
}
129+
var err error
130+
src, err = osi.ResolveSymbolicLink(src)
131+
if err != nil {
132+
return errors.Wrapf(err, "failed to resolve symlink %q", src)
133+
}
134+
// hcsshim requires clean path, especially '/' -> '\'.
135+
src = filepath.Clean(src)
136+
dst = filepath.Clean(dst)
114137
}
115138

116139
var options []string
@@ -122,9 +145,8 @@ func WithWindowsMounts(osi osinterface.OS, config *runtime.ContainerConfig, extr
122145
options = append(options, "rw")
123146
}
124147
s.Mounts = append(s.Mounts, runtimespec.Mount{
125-
// hcsshim requires clean path, especially '/' -> '\'.
126-
Source: filepath.Clean(src),
127-
Destination: filepath.Clean(dst),
148+
Source: src,
149+
Destination: dst,
128150
Options: options,
129151
})
130152
}

pkg/server/container_create_windows_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,23 @@ func TestMountCleanPath(t *testing.T) {
167167
specCheck(t, testID, testSandboxID, testPid, spec)
168168
checkMount(t, spec.Mounts, "c:\\test\\host-path", "c:\\test\\container-path", "", []string{"rw"}, nil)
169169
}
170+
171+
func TestMountNamedPipe(t *testing.T) {
172+
testID := "test-id"
173+
testSandboxID := "sandbox-id"
174+
testContainerName := "container-name"
175+
testPid := uint32(1234)
176+
nsPath := "test-cni"
177+
c := newTestCRIService()
178+
179+
containerConfig, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
180+
containerConfig.Mounts = append(containerConfig.Mounts, &runtime.Mount{
181+
ContainerPath: `\\.\pipe\foo`,
182+
HostPath: `\\.\pipe\foo`,
183+
})
184+
spec, err := c.containerSpec(testID, testSandboxID, testPid, nsPath, testContainerName, containerConfig, sandboxConfig, imageConfig, nil, config.Runtime{})
185+
assert.NoError(t, err)
186+
assert.NotNil(t, spec)
187+
specCheck(t, testID, testSandboxID, testPid, spec)
188+
checkMount(t, spec.Mounts, `\\.\pipe\foo`, `\\.\pipe\foo`, "", []string{"rw"}, nil)
189+
}

0 commit comments

Comments
 (0)