adds experimental sys.Errno to begin decoupling from the syscall package (#1582)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package platform
|
||||
|
||||
import "syscall"
|
||||
|
||||
func adjustErrno(err syscall.Errno) syscall.Errno {
|
||||
return err
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package platform
|
||||
|
||||
import "syscall"
|
||||
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
|
||||
const (
|
||||
// ERROR_ACCESS_DENIED is a Windows error returned by syscall.Unlink
|
||||
// instead of syscall.EACCES
|
||||
ERROR_ACCESS_DENIED = syscall.Errno(5)
|
||||
|
||||
// ERROR_INVALID_HANDLE is a Windows error returned by syscall.Write
|
||||
// instead of syscall.EBADF
|
||||
ERROR_INVALID_HANDLE = syscall.Errno(6)
|
||||
|
||||
// ERROR_FILE_EXISTS is a Windows error returned by os.OpenFile
|
||||
// instead of syscall.EEXIST
|
||||
ERROR_FILE_EXISTS = syscall.Errno(0x50)
|
||||
|
||||
// ERROR_INVALID_NAME is a Windows error returned by open when a file
|
||||
// path has a trailing slash
|
||||
ERROR_INVALID_NAME = syscall.Errno(0x7B)
|
||||
|
||||
// ERROR_NEGATIVE_SEEK is a Windows error returned by os.Truncate
|
||||
// instead of syscall.EINVAL
|
||||
ERROR_NEGATIVE_SEEK = syscall.Errno(0x83)
|
||||
|
||||
// ERROR_DIR_NOT_EMPTY is a Windows error returned by syscall.Rmdir
|
||||
// instead of syscall.ENOTEMPTY
|
||||
ERROR_DIR_NOT_EMPTY = syscall.Errno(0x91)
|
||||
|
||||
// ERROR_ALREADY_EXISTS is a Windows error returned by os.Mkdir
|
||||
// instead of syscall.EEXIST
|
||||
ERROR_ALREADY_EXISTS = syscall.Errno(0xB7)
|
||||
|
||||
// ERROR_DIRECTORY is a Windows error returned by syscall.Rmdir
|
||||
// instead of syscall.ENOTDIR
|
||||
ERROR_DIRECTORY = syscall.Errno(0x10B)
|
||||
)
|
||||
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--1300-1699-
|
||||
const (
|
||||
// ERROR_PRIVILEGE_NOT_HELD is a Windows error returned by os.Symlink
|
||||
// instead of syscall.EPERM.
|
||||
//
|
||||
// Note: This can happen when trying to create symlinks w/o admin perms.
|
||||
ERROR_PRIVILEGE_NOT_HELD = syscall.Errno(0x522)
|
||||
)
|
||||
|
||||
func adjustErrno(err syscall.Errno) syscall.Errno {
|
||||
// Note: In windows, ERROR_PATH_NOT_FOUND(0x3) maps to syscall.ENOTDIR
|
||||
switch err {
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
return syscall.EEXIST
|
||||
case ERROR_DIRECTORY:
|
||||
return syscall.ENOTDIR
|
||||
case ERROR_DIR_NOT_EMPTY:
|
||||
return syscall.ENOTEMPTY
|
||||
case ERROR_FILE_EXISTS:
|
||||
return syscall.EEXIST
|
||||
case ERROR_INVALID_HANDLE:
|
||||
return syscall.EBADF
|
||||
case ERROR_ACCESS_DENIED:
|
||||
// POSIX read and write functions expect EBADF, not EACCES when not
|
||||
// open for reading or writing.
|
||||
return syscall.EBADF
|
||||
case ERROR_PRIVILEGE_NOT_HELD:
|
||||
return syscall.EPERM
|
||||
case ERROR_NEGATIVE_SEEK, ERROR_INVALID_NAME:
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// UnwrapOSError returns a syscall.Errno or zero if the input is nil.
|
||||
func UnwrapOSError(err error) syscall.Errno {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
err = underlyingError(err)
|
||||
if se, ok := err.(syscall.Errno); ok {
|
||||
return adjustErrno(se)
|
||||
}
|
||||
// Below are all the fs.ErrXXX in fs.go.
|
||||
//
|
||||
// Note: Once we have our own file type, we should never see these.
|
||||
switch err {
|
||||
case nil, io.EOF:
|
||||
return 0 // EOF is not a syscall.Errno
|
||||
case fs.ErrInvalid:
|
||||
return syscall.EINVAL
|
||||
case fs.ErrPermission:
|
||||
return syscall.EPERM
|
||||
case fs.ErrExist:
|
||||
return syscall.EEXIST
|
||||
case fs.ErrNotExist:
|
||||
return syscall.ENOENT
|
||||
case fs.ErrClosed:
|
||||
return syscall.EBADF
|
||||
}
|
||||
return syscall.EIO
|
||||
}
|
||||
|
||||
// underlyingError returns the underlying error if a well-known OS error type.
|
||||
//
|
||||
// This impl is basically the same as os.underlyingError in os/error.go
|
||||
func underlyingError(err error) error {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return err.Err
|
||||
case *os.LinkError:
|
||||
return err.Err
|
||||
case *os.SyscallError:
|
||||
return err.Err
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
)
|
||||
|
||||
func TestUnwrapOSError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input error
|
||||
expected syscall.Errno
|
||||
}{
|
||||
{
|
||||
name: "io.EOF is not an error",
|
||||
input: io.EOF,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "LinkError ErrInvalid",
|
||||
input: &os.LinkError{Err: fs.ErrInvalid},
|
||||
expected: syscall.EINVAL,
|
||||
},
|
||||
{
|
||||
name: "PathError ErrInvalid",
|
||||
input: &os.PathError{Err: fs.ErrInvalid},
|
||||
expected: syscall.EINVAL,
|
||||
},
|
||||
{
|
||||
name: "SyscallError ErrInvalid",
|
||||
input: &os.SyscallError{Err: fs.ErrInvalid},
|
||||
expected: syscall.EINVAL,
|
||||
},
|
||||
{
|
||||
name: "PathError ErrPermission",
|
||||
input: &os.PathError{Err: os.ErrPermission},
|
||||
expected: syscall.EPERM,
|
||||
},
|
||||
{
|
||||
name: "PathError ErrExist",
|
||||
input: &os.PathError{Err: os.ErrExist},
|
||||
expected: syscall.EEXIST,
|
||||
},
|
||||
{
|
||||
name: "PathError syscall.ErrnotExist",
|
||||
input: &os.PathError{Err: os.ErrNotExist},
|
||||
expected: syscall.ENOENT,
|
||||
},
|
||||
{
|
||||
name: "PathError ErrClosed",
|
||||
input: &os.PathError{Err: os.ErrClosed},
|
||||
expected: syscall.EBADF,
|
||||
},
|
||||
{
|
||||
name: "PathError unknown == syscall.EIO",
|
||||
input: &os.PathError{Err: errors.New("ice cream")},
|
||||
expected: syscall.EIO,
|
||||
},
|
||||
{
|
||||
name: "unknown == syscall.EIO",
|
||||
input: errors.New("ice cream"),
|
||||
expected: syscall.EIO,
|
||||
},
|
||||
{
|
||||
name: "very wrapped unknown == syscall.EIO",
|
||||
input: fmt.Errorf("%w", fmt.Errorf("%w", fmt.Errorf("%w", errors.New("ice cream")))),
|
||||
expected: syscall.EIO,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
errno := UnwrapOSError(tc.input)
|
||||
require.EqualErrno(t, tc.expected, errno)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("nil -> zero", func(t *testing.T) {
|
||||
require.Zero(t, UnwrapOSError(nil))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user