Skip to content

Commit b93e89d

Browse files
committed
socket: try removing if socket-location is a directory on Linux
Due to race-conditions between containers starting and the Docker remote API being up, containers bind-mounting the docker-socket may cause the socket-path to be created as a directory. This patch will attempt to remove the directory in such situations. Removing will fail if the directory is not empty. MacOS does not allow us to detect that the path is a directory, and we'll return immediately instead of retrying. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent 0b8c1f4 commit b93e89d

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

sockets/unix_socket.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,18 @@ func WithChmod(mask os.FileMode) SockOption {
8686
// this should only be for a short duration, it may affect other processes that
8787
// create files/directories during that period.
8888
func NewUnixSocketWithOpts(path string, opts ...SockOption) (net.Listener, error) {
89+
// Using syscall.Unlink(), not os.Remove() to prevent deleting the socket if it's in use
8990
if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
90-
return nil, err
91+
if err != syscall.EISDIR {
92+
// On Linux, attempting to remove a directory returns syscall.EISDIR,
93+
// in which case we try to remove the directory. MacOS does not return
94+
// this error, so we'll return immediately, see:
95+
// https://github.com/golang/go/blob/6b420169d798c7ebe733487b56ea5c3fa4aab5ce/src/os/file_unix.go#L300-L311
96+
return nil, err
97+
}
98+
if err := syscall.Rmdir(path); err != nil {
99+
return nil, err
100+
}
91101
}
92102

93103
// net.Listen does not allow for permissions to be set. As a result, when

sockets/unix_socket_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ package sockets
44

55
import (
66
"fmt"
7+
"io/ioutil"
78
"net"
89
"os"
10+
"path"
11+
"runtime"
912
"syscall"
1013
"testing"
1114
)
@@ -75,3 +78,48 @@ func TestUnixSocketWithOpts(t *testing.T) {
7578
}
7679
runTest(t, path, l, echoStr)
7780
}
81+
82+
func TestUnixSocketConflictDirectory(t *testing.T) {
83+
tmpDir, err := ioutil.TempDir("", t.Name())
84+
if err != nil {
85+
t.Fatal(err)
86+
}
87+
defer os.RemoveAll(tmpDir)
88+
89+
t.Run("conflicting directory", func(t *testing.T) {
90+
if runtime.GOOS == "darwin" {
91+
t.Skip("not supported on macOS")
92+
}
93+
path := path.Join(tmpDir, "test.sock")
94+
95+
// Create a conflicting directory at the socket location
96+
err = os.MkdirAll(path, 0700)
97+
if err != nil {
98+
t.Fatal(err)
99+
}
100+
101+
l, err := NewUnixSocketWithOpts(path)
102+
if err != nil {
103+
t.Fatal(err)
104+
}
105+
defer l.Close()
106+
runTest(t, path, l, "hello")
107+
})
108+
109+
t.Run("conflicting file", func(t *testing.T) {
110+
// Create a conflicting file at the socket location
111+
path := path.Join(tmpDir, "test2.sock")
112+
f, err := os.Create(path)
113+
if err != nil {
114+
t.Fatal(err)
115+
}
116+
f.Close()
117+
118+
l, err := NewUnixSocketWithOpts(path)
119+
if err != nil {
120+
t.Fatal(err)
121+
}
122+
defer l.Close()
123+
runTest(t, path, l, "hello")
124+
})
125+
}

0 commit comments

Comments
 (0)