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

internal/unix: add Errno wrapper for Windows #1659

Merged
merged 1 commit into from
Feb 4, 2025
Merged
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
11 changes: 5 additions & 6 deletions internal/sys/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package sys

import (
"runtime"
"syscall"
"unsafe"

"github.com/cilium/ebpf/internal/unix"
Expand All @@ -11,7 +10,7 @@ import (
// ENOTSUPP is a Linux internal error code that has leaked into UAPI.
//
// It is not the same as ENOTSUP or EOPNOTSUPP.
const ENOTSUPP = syscall.Errno(524)
const ENOTSUPP = unix.Errno(524)

// BPF wraps SYS_BPF.
//
Expand Down Expand Up @@ -179,12 +178,12 @@ const (
const BPF_TAG_SIZE = 8
const BPF_OBJ_NAME_LEN = 16

// wrappedErrno wraps syscall.Errno to prevent direct comparisons with
// wrappedErrno wraps [unix.Errno] to prevent direct comparisons with
// syscall.E* or unix.E* constants.
//
// You should never export an error of this type.
type wrappedErrno struct {
syscall.Errno
unix.Errno
}

func (we wrappedErrno) Unwrap() error {
Expand All @@ -200,10 +199,10 @@ func (we wrappedErrno) Error() string {

type syscallError struct {
error
errno syscall.Errno
errno unix.Errno
}

func Error(err error, errno syscall.Errno) error {
func Error(err error, errno unix.Errno) error {
return &syscallError{err, errno}
}

Expand Down
29 changes: 29 additions & 0 deletions internal/unix/errno_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package unix

import (
"syscall"

linux "golang.org/x/sys/unix"
)

type Errno = syscall.Errno

const (
E2BIG = linux.E2BIG
EACCES = linux.EACCES
EAGAIN = linux.EAGAIN
EBADF = linux.EBADF
EEXIST = linux.EEXIST
EFAULT = linux.EFAULT
EILSEQ = linux.EILSEQ
EINTR = linux.EINTR
EINVAL = linux.EINVAL
ENODEV = linux.ENODEV
ENOENT = linux.ENOENT
ENOSPC = linux.ENOSPC
EOPNOTSUPP = linux.EOPNOTSUPP
EPERM = linux.EPERM
EPOLLIN = linux.EPOLLIN
ESRCH = linux.ESRCH
ESTALE = linux.ESTALE
)
13 changes: 13 additions & 0 deletions internal/unix/errno_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package unix

import (
"testing"

"github.com/go-quicktest/qt"
"golang.org/x/sys/unix"
)

func TestErrnoIsUnix(t *testing.T) {
qt.Assert(t, qt.ErrorIs(EPERM, unix.EPERM))
qt.Assert(t, qt.ErrorIs(ENOENT, unix.ENOENT))
}
29 changes: 29 additions & 0 deletions internal/unix/errno_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//go:build !linux && !windows

package unix

import "syscall"

type Errno = syscall.Errno

// Errnos are distinct and non-zero.
const (
E2BIG Errno = iota + 1
EACCES
EAGAIN
EBADF
EEXIST
EFAULT
EILSEQ
EINTR
EINVAL
ENODEV
ENOENT
ENOSPC
ENOTSUP
ENOTSUPP
EOPNOTSUPP
EPERM
ESRCH
ESTALE
)
59 changes: 59 additions & 0 deletions internal/unix/errno_string_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions internal/unix/errno_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package unix

import (
"os"
"testing"

"github.com/go-quicktest/qt"
)

func TestErrno(t *testing.T) {
qt.Assert(t, qt.ErrorIs(ENOENT, os.ErrNotExist))
}
78 changes: 78 additions & 0 deletions internal/unix/errno_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package unix

// The code in this file is derived from syscall_unix.go in the Go source code,
// licensed under the MIT license.

import (
"errors"
"os"
"syscall"
)

//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=Errno -tags=windows -output=errno_string_windows.go

// Windows specific constants for Unix errnos.
//
// The values do not always match Linux, for example EILSEQ and EOPNOTSUPP.
//
// See https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170
const (
EPERM Errno = 1
ENOENT Errno = 2
ESRCH Errno = 3
EINTR Errno = 4
E2BIG Errno = 7
EBADF Errno = 9
EAGAIN Errno = 11
EACCES Errno = 13
EFAULT Errno = 14
EEXIST Errno = 17
ENODEV Errno = 19
EINVAL Errno = 22
ENFILE Errno = 23
EMFILE Errno = 24
ENOSPC Errno = 28
ENOSYS Errno = 40
ENOTEMPTY Errno = 41
EILSEQ Errno = 42
ENOTSUP Errno = 129
EOPNOTSUPP Errno = 130
ETIMEDOUT Errno = 138
EWOULDBLOCK Errno = 140
)

// These constants do not exist on Windows and therefore have a non-zero
// dummy value.
const (
ENOTSUPP Errno = Errno(syscall.APPLICATION_ERROR) + iota
ESTALE
)

// Errno is a Windows compatibility shim for Unix errnos.
type Errno uintptr

func (e Errno) Error() string {
return e.String()
}

func (e Errno) Is(target error) bool {
switch target {
case os.ErrPermission:
return e == EACCES || e == EPERM
case os.ErrExist:
return e == EEXIST || e == ENOTEMPTY
case os.ErrNotExist:
return e == ENOENT
case errors.ErrUnsupported:
return e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP
}
return false
}

func (e Errno) Temporary() bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using these in other Windows-specific code? I don't think an external caller can get ahold of these methods since this is internal/unix.Errno. Unless we need them to satisfy some interface, I propose we remove them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We expose these indirectly because we wrap original errno from bpf() and friends. It'd be surprising / a footgun if those errors behaved different between platforms, I think.

return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout()
}

func (e Errno) Timeout() bool {
return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
}
20 changes: 0 additions & 20 deletions internal/unix/types_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,6 @@ import (
linux "golang.org/x/sys/unix"
)

const (
ENOENT = linux.ENOENT
EEXIST = linux.EEXIST
EAGAIN = linux.EAGAIN
ENOSPC = linux.ENOSPC
EINVAL = linux.EINVAL
EPOLLIN = linux.EPOLLIN
EINTR = linux.EINTR
EPERM = linux.EPERM
ESRCH = linux.ESRCH
ENODEV = linux.ENODEV
EBADF = linux.EBADF
E2BIG = linux.E2BIG
EFAULT = linux.EFAULT
EACCES = linux.EACCES
EILSEQ = linux.EILSEQ
EOPNOTSUPP = linux.EOPNOTSUPP
ESTALE = linux.ESTALE
)

const (
BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
Expand Down
24 changes: 2 additions & 22 deletions internal/unix/types_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,6 @@ import (
"syscall"
)

// Errnos are distinct and non-zero.
const (
ENOENT syscall.Errno = iota + 1
EEXIST
EAGAIN
ENOSPC
EINVAL
EINTR
EPERM
ESRCH
ENODEV
EBADF
E2BIG
EFAULT
EACCES
EILSEQ
EOPNOTSUPP
ESTALE
)

// Constants are distinct to avoid breaking switch statements.
const (
BPF_F_NO_PREALLOC = iota
Expand Down Expand Up @@ -133,8 +113,8 @@ type Sigset_t struct {
Val [4]uint64
}

func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return 0, 0, syscall.ENOTSUP
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
return 0, 0, ENOTSUP
}

func PthreadSigmask(how int, set, oldset *Sigset_t) error {
Expand Down
Loading