diff options
| -rw-r--r-- | internal/syscallcompat/sys_common.go | 23 | ||||
| -rw-r--r-- | internal/syscallcompat/sys_common_test.go | 29 | 
2 files changed, 52 insertions, 0 deletions
| diff --git a/internal/syscallcompat/sys_common.go b/internal/syscallcompat/sys_common.go new file mode 100644 index 0000000..440b42f --- /dev/null +++ b/internal/syscallcompat/sys_common.go @@ -0,0 +1,23 @@ +package syscallcompat + +import ( +	"golang.org/x/sys/unix" +) + +// Readlinkat exists both in Linux and in MacOS 10.10+. We may add an +// emulated version for users on older MacOS versions if there is +// demand. +// Buffer allocation is handled internally, unlike the bare unix.Readlinkat. +func Readlinkat(dirfd int, path string) (string, error) { +	// Allocate the buffer exponentially like os.Readlink does. +	for bufsz := 128; ; bufsz *= 2 { +		buf := make([]byte, bufsz) +		n, err := unix.Readlinkat(dirfd, path, buf) +		if err != nil { +			return "", err +		} +		if n < bufsz { +			return string(buf[0:n]), nil +		} +	} +} diff --git a/internal/syscallcompat/sys_common_test.go b/internal/syscallcompat/sys_common_test.go new file mode 100644 index 0000000..bc694ba --- /dev/null +++ b/internal/syscallcompat/sys_common_test.go @@ -0,0 +1,29 @@ +package syscallcompat + +import ( +	"bytes" +	"os" +	"syscall" +	"testing" +) + +func TestReadlinkat(t *testing.T) { +	for _, targetLen := range []int{100, 500, 4000} { +		target := string(bytes.Repeat([]byte("x"), targetLen)) +		err := os.Symlink(target, tmpDir+"/readlinkat") +		if err != nil { +			t.Fatal(err) +		} +		target2, err := Readlinkat(tmpDirFd, "readlinkat") +		if err != nil { +			t.Fatal(err) +		} +		if target != target2 { +			t.Errorf("target=%q != target2=%q", target, target2) +		} +		err = syscall.Unlink(tmpDir + "/readlinkat") +		if err != nil { +			t.Fatal(err) +		} +	} +} | 
