Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a shorter symlink for Unix socket in mountoptions package #346

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions pkg/podmounter/mountoptions/mount_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"net"
"os"
"path/filepath"
"syscall"

"k8s.io/klog/v2"
Expand All @@ -25,7 +28,11 @@ type Options struct {

// Send sends given mount `options` to given `sockPath` to be received by `Recv` function on the other end.
func Send(ctx context.Context, sockPath string, options Options) error {
warnAboutLongUnixSocketPath(sockPath)
sockPath, cleanSockPath, err := ensureSocketPathLengthIsUnderLimit(sockPath)
if err != nil {
return err
}
defer cleanSockPath()

message, err := json.Marshal(&options)
if err != nil {
Expand Down Expand Up @@ -61,7 +68,11 @@ var (

// Recv receives passed mount options via `Send` function through given `sockPath`.
func Recv(ctx context.Context, sockPath string) (Options, error) {
warnAboutLongUnixSocketPath(sockPath)
sockPath, cleanSockPath, err := ensureSocketPathLengthIsUnderLimit(sockPath)
if err != nil {
return Options{}, err
}
defer cleanSockPath()

l, err := net.Listen("unix", sockPath)
if err != nil {
Expand Down Expand Up @@ -135,12 +146,37 @@ func parseUnixRights(buf []byte) ([]int, error) {
return fds, nil
}

// warnAboutLongUnixSocketPath emits a warning if `path` is longer than 108 characters.
// There is a limit on Unix domain socket path on some platforms and
// Go returns `bind: invalid argument` in this case which is hard to debug.
// ensureSocketPathLengthIsUnderLimit ensures `sockPath` is not longer than 108 characters,
// which Go returns `invalid argument` errors if you try to do `net.Listen()` or `net.Dial()`.
// See https://github.com/golang/go/issues/6895 for more details.
func warnAboutLongUnixSocketPath(path string) {
if len(path) > 108 {
klog.Warningf("Length of Unix domain socket is larger than 108 characters and it might not work in some platforms, see https://github.com/golang/go/issues/6895. Fullpath: %q", path)
//
// If `sockPath` is longer than 108 characters, this function creates a shorter symlink to it in the temp dir,
// and returns that symlink to use as `sockPath`.
// It also returns a clean up function to clean up this temporary symlink at the end.
func ensureSocketPathLengthIsUnderLimit(sockPath string) (string, func(), error) {
if len(sockPath) < 108 {
return sockPath, func() {}, nil
}

klog.Infof("Unix socket path %q is longer than 108 characters which known to be cause problems in some platforms. Creating a shorter symlink to it to use.", sockPath)

tempDir, err := os.MkdirTemp(os.TempDir(), "mountoptions")
if err != nil {
return "", nil, fmt.Errorf("failed to create a temp dir for a shorter symlink to %s: %w", sockPath, err)
}
shortSockPath := filepath.Join(tempDir, filepath.Base(sockPath))

err = os.Symlink(sockPath, shortSockPath)
if err != nil {
return "", nil, fmt.Errorf("failed to create a symlink from %s to %s: %w", sockPath, shortSockPath, err)
}

klog.Infof("Created %q as a symlink to %q and will be used to do Unix socket recv/send operations", shortSockPath, sockPath)

return shortSockPath, func() {
err := os.Remove(shortSockPath)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
klog.Infof("Failed to remove symlink %s: %v\n", shortSockPath, err)
}
}, nil
}
31 changes: 28 additions & 3 deletions pkg/podmounter/mountoptions/mount_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"os"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
Expand All @@ -13,7 +14,33 @@ import (
"github.com/awslabs/aws-s3-csi-driver/pkg/util/testutil/assert"
)

func TestSendingAndReceivingMountOptions(t *testing.T) {
func TestMountOptions(t *testing.T) {
// Go returns `invalid argument` errors if you try to do `net.Listen()` or `net.Dial()` with a Unix socket
// path thats longer than 108 characters by default. We're creating symlinks automatically and use those Unix
// socket paths in this case. Here we add test cases for both short and long Unix socket paths.
// See https://github.com/golang/go/issues/6895 for more details.

t.Run("Short Path", func(t *testing.T) {
mountSock := filepath.Join(t.TempDir(), "m")
if len(mountSock) >= 108 {
t.Fatalf("test Unix socket path %q must be shorter than 108 characters", mountSock)
}
testRoundtrip(t, mountSock)
})

t.Run("Long Path", func(t *testing.T) {
basepath := filepath.Join(t.TempDir(), "long"+strings.Repeat("g", 108))
assert.NoError(t, os.Mkdir(basepath, 0700))

mountSock := filepath.Join(basepath, "mount.sock")
if len(mountSock) <= 108 {
t.Fatalf("test Unix socket path %q must be longer than 108 characters", mountSock)
}
testRoundtrip(t, mountSock)
})
}

func testRoundtrip(t *testing.T, mountSock string) {
file, err := os.Open(os.DevNull)
assert.NoError(t, err)
defer file.Close()
Expand All @@ -22,8 +49,6 @@ func TestSendingAndReceivingMountOptions(t *testing.T) {
err = syscall.Fstat(int(file.Fd()), wantStat)
assert.NoError(t, err)

mountSock := filepath.Join(t.TempDir(), "m")

c := make(chan mountoptions.Options)
go func() {
mountOptions, err := mountoptions.Recv(defaultContext(t), mountSock)
Expand Down
Loading