@@ -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.
5267func 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 }
0 commit comments