diff --git a/internal/sys/syscall.go b/internal/sys/syscall.go index e37f4cf67..4fdb74c57 100644 --- a/internal/sys/syscall.go +++ b/internal/sys/syscall.go @@ -2,7 +2,6 @@ package sys import ( "runtime" - "syscall" "unsafe" "github.com/cilium/ebpf/internal/unix" @@ -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. // @@ -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 { @@ -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} } diff --git a/internal/unix/errno_linux.go b/internal/unix/errno_linux.go new file mode 100644 index 000000000..0c4886bd1 --- /dev/null +++ b/internal/unix/errno_linux.go @@ -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 +) diff --git a/internal/unix/errno_linux_test.go b/internal/unix/errno_linux_test.go new file mode 100644 index 000000000..32bf69e37 --- /dev/null +++ b/internal/unix/errno_linux_test.go @@ -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)) +} diff --git a/internal/unix/errno_other.go b/internal/unix/errno_other.go new file mode 100644 index 000000000..fc2b042b5 --- /dev/null +++ b/internal/unix/errno_other.go @@ -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 +) diff --git a/internal/unix/errno_string_windows.go b/internal/unix/errno_string_windows.go new file mode 100644 index 000000000..6077e983f --- /dev/null +++ b/internal/unix/errno_string_windows.go @@ -0,0 +1,59 @@ +// Code generated by "stringer -type=Errno -tags=windows -output=errno_string_windows.go"; DO NOT EDIT. + +package unix + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[EPERM-1] + _ = x[ENOENT-2] + _ = x[ESRCH-3] + _ = x[EINTR-4] + _ = x[E2BIG-7] + _ = x[EBADF-9] + _ = x[EAGAIN-11] + _ = x[EACCES-13] + _ = x[EFAULT-14] + _ = x[EEXIST-17] + _ = x[ENODEV-19] + _ = x[EINVAL-22] + _ = x[ENOSPC-28] + _ = x[EILSEQ-42] + _ = x[ENOTSUP-129] + _ = x[EOPNOTSUPP-130] + _ = x[ENOTSUPP-536870912] + _ = x[ESTALE-536870913] +} + +const _Errno_name = "EPERMENOENTESRCHEINTRE2BIGEBADFEAGAINEACCESEFAULTEEXISTENODEVEINVALENOSPCEILSEQENOTSUPEOPNOTSUPPENOTSUPPESTALE" + +var _Errno_map = map[Errno]string{ + 1: _Errno_name[0:5], + 2: _Errno_name[5:11], + 3: _Errno_name[11:16], + 4: _Errno_name[16:21], + 7: _Errno_name[21:26], + 9: _Errno_name[26:31], + 11: _Errno_name[31:37], + 13: _Errno_name[37:43], + 14: _Errno_name[43:49], + 17: _Errno_name[49:55], + 19: _Errno_name[55:61], + 22: _Errno_name[61:67], + 28: _Errno_name[67:73], + 42: _Errno_name[73:79], + 129: _Errno_name[79:86], + 130: _Errno_name[86:96], + 536870912: _Errno_name[96:104], + 536870913: _Errno_name[104:110], +} + +func (i Errno) String() string { + if str, ok := _Errno_map[i]; ok { + return str + } + return "Errno(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/internal/unix/errno_test.go b/internal/unix/errno_test.go new file mode 100644 index 000000000..84991f957 --- /dev/null +++ b/internal/unix/errno_test.go @@ -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)) +} diff --git a/internal/unix/errno_windows.go b/internal/unix/errno_windows.go new file mode 100644 index 000000000..7500cd6d4 --- /dev/null +++ b/internal/unix/errno_windows.go @@ -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 { + return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout() +} + +func (e Errno) Timeout() bool { + return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT +} diff --git a/internal/unix/types_linux.go b/internal/unix/types_linux.go index 144e608d1..1421addac 100644 --- a/internal/unix/types_linux.go +++ b/internal/unix/types_linux.go @@ -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 diff --git a/internal/unix/types_other.go b/internal/unix/types_other.go index 806c6e567..768737315 100644 --- a/internal/unix/types_other.go +++ b/internal/unix/types_other.go @@ -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 @@ -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 {