Skip to content

Commit afda0fa

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 09f4792 commit afda0fa

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
@@ -79,8 +79,18 @@ func WithChmod(mask os.FileMode) SockOption {
7979

8080
// NewUnixSocketWithOpts creates a unix socket with the specified options
8181
func NewUnixSocketWithOpts(path string, opts ...SockOption) (net.Listener, error) {
82+
// Using syscall.Unlink(), not os.Remove() to prevent deleting the socket if it's in use
8283
if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
83-
return nil, err
84+
if err != syscall.EISDIR {
85+
// On Linux, attempting to remove a directory returns syscall.EISDIR,
86+
// in which case we try to remove the directory. MacOS does not return
87+
// this error, so we'll return immediately, see:
88+
// https://github.com/golang/go/blob/6b420169d798c7ebe733487b56ea5c3fa4aab5ce/src/os/file_unix.go#L300-L311
89+
return nil, err
90+
}
91+
if err := syscall.Rmdir(path); err != nil {
92+
return nil, err
93+
}
8494
}
8595
mask := syscall.Umask(0777)
8696
defer syscall.Umask(mask)

sockets/unix_socket_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package sockets
22

33
import (
44
"fmt"
5+
"io/ioutil"
56
"net"
67
"os"
8+
"path/filepath"
9+
"runtime"
710
"testing"
811
)
912

@@ -56,3 +59,48 @@ func TestUnixSocketWithOpts(t *testing.T) {
5659
defer l.Close()
5760
runTest(t, path, l, echoStr)
5861
}
62+
63+
func TestUnixSocketConflictDirectory(t *testing.T) {
64+
if runtime.GOOS == "darwin" {
65+
t.Skip("not supported on macOS")
66+
}
67+
tmpDir, err := ioutil.TempDir("", t.Name())
68+
if err != nil {
69+
t.Fatal(err)
70+
}
71+
defer os.RemoveAll(tmpDir)
72+
73+
t.Run("conflicting directory", func(t *testing.T){
74+
path := filepath.Join(tmpDir, "test.sock")
75+
76+
// Create a conflicting directory at the socket location
77+
err = os.MkdirAll(path, 0700)
78+
if err != nil {
79+
t.Fatal(err)
80+
}
81+
82+
l, err := NewUnixSocketWithOpts(path)
83+
if err != nil {
84+
t.Fatal(err)
85+
}
86+
defer l.Close()
87+
runTest(t, path, l, "hello")
88+
})
89+
90+
t.Run("conflicting file", func(t *testing.T){
91+
// Create a conflicting file at the socket location
92+
path := filepath.Join(tmpDir, "test2.sock")
93+
f, err := os.Create(path)
94+
if err != nil {
95+
t.Fatal(err)
96+
}
97+
f.Close()
98+
99+
l, err := NewUnixSocketWithOpts(path)
100+
if err != nil {
101+
t.Fatal(err)
102+
}
103+
defer l.Close()
104+
runTest(t, path, l, "hello")
105+
})
106+
}

0 commit comments

Comments
 (0)