Files
wazero/internal/sysfs/syscall_windows.go
Crypt Keeper 2a584a8937 fs: renames internal syscallfs package to sysfs and notes RATIONALE (#1056)
It will help for us to rename earlier vs later, and syscallfs will be
laborious, especially after we introduce an FSConfig type and need to
declare a method name that differentiates from normal fs.FS. e.g. WithFS
vs WithSysFS reads nicer than WithSyscallFS, and meanwhile sys is
already a public package.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2023-01-23 11:11:35 +08:00

127 lines
3.1 KiB
Go

package sysfs
import (
"errors"
"io"
"io/fs"
"os"
"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.EPERM
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_ALREADY_EXISTS is a Windows error returned by os.Mkdir
// instead of syscall.EEXIST
ERROR_ALREADY_EXISTS = syscall.Errno(183)
// ERROR_DIRECTORY is a Windows error returned by syscall.Rmdir
// instead of syscall.ENOTDIR
ERROR_DIRECTORY = syscall.Errno(267)
// ERROR_DIR_NOT_EMPTY is a Windows error returned by syscall.Rmdir
// instead of syscall.ENOTEMPTY
ERROR_DIR_NOT_EMPTY = syscall.Errno(145)
)
func adjustMkdirError(err error) error {
if err == ERROR_ALREADY_EXISTS {
return syscall.EEXIST
}
return err
}
func adjustRmdirError(err error) error {
switch err {
case ERROR_DIRECTORY:
return syscall.ENOTDIR
case ERROR_DIR_NOT_EMPTY:
return syscall.ENOTEMPTY
}
return err
}
func adjustUnlinkError(err error) error {
if err == ERROR_ACCESS_DENIED {
return syscall.EISDIR
}
return err
}
// rename uses os.Rename as `windows.Rename` is internal in Go's source tree.
func rename(old, new string) (err error) {
if err = os.Rename(old, new); err == nil {
return
}
err = errors.Unwrap(err) // unwrap the link error
if err == ERROR_ACCESS_DENIED {
var newIsDir bool
if stat, statErr := os.Stat(new); statErr == nil && stat.IsDir() {
newIsDir = true
}
var oldIsDir bool
if stat, statErr := os.Stat(old); statErr == nil && stat.IsDir() {
oldIsDir = true
}
if oldIsDir && newIsDir {
// Windows doesn't let you overwrite a directory. If we aim to
// allow this, we'll have to delete here and retry.
return syscall.EINVAL
} else if newIsDir {
err = syscall.EISDIR
} else { // use a mappable code
err = syscall.EPERM
}
}
return
}
// maybeWrapFile deals with errno portability issues in Windows. This code is
// likely to change as we complete syscall support needed for WASI and GOOS=js.
//
// If we don't map to syscall.Errno, wasm will crash in odd way attempting the
// same. This approach is an alternative to making our own fs.File public type.
// We aren't doing that yet, as mapping problems are generally contained to
// Windows. Hence, file is intentionally not exported.
func maybeWrapFile(f file) file {
return struct {
readFile
io.Writer
io.WriterAt // for pwrite
syncer
}{f, &windowsWriter{f}, f, f}
}
// windowsWriter translates error codes not mapped properly by Go.
type windowsWriter struct {
w io.Writer
}
// Write implements io.Writer
func (w windowsWriter) Write(p []byte) (n int, err error) {
n, err = w.w.Write(p)
if err == nil {
return
}
// os.File.Wrap wraps the syscall error in a path error
if pe, ok := err.(*fs.PathError); ok {
switch pe.Err {
case ERROR_INVALID_HANDLE:
pe.Err = syscall.EBADF
case ERROR_ACCESS_DENIED:
pe.Err = syscall.EPERM
}
}
return
}