summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go
new file mode 100644
index 000000000..717f8598a
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go
@@ -0,0 +1,161 @@
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
+ isDir := oflag&sys.O_DIRECTORY > 0
+ flag := toOsOpenFlag(oflag)
+
+ // TODO: document why we are opening twice
+ fd, err := open(path, flag|syscall.O_CLOEXEC, uint32(perm))
+ if err == nil {
+ return os.NewFile(uintptr(fd), path), 0
+ }
+
+ // TODO: Set FILE_SHARE_DELETE for directory as well.
+ f, err := os.OpenFile(path, flag, perm)
+ errno := sys.UnwrapOSError(err)
+ if errno == 0 {
+ return f, 0
+ }
+
+ switch errno {
+ case sys.EINVAL:
+ // WASI expects ENOTDIR for a file path with a trailing slash.
+ if strings.HasSuffix(path, "/") {
+ errno = sys.ENOTDIR
+ }
+ // To match expectations of WASI, e.g. TinyGo TestStatBadDir, return
+ // ENOENT, not ENOTDIR.
+ case sys.ENOTDIR:
+ errno = sys.ENOENT
+ case sys.ENOENT:
+ if isSymlink(path) {
+ // Either symlink or hard link not found. We change the returned
+ // errno depending on if it is symlink or not to have consistent
+ // behavior across OSes.
+ if isDir {
+ // Dangling symlink dir must raise ENOTDIR.
+ errno = sys.ENOTDIR
+ } else {
+ errno = sys.ELOOP
+ }
+ }
+ }
+ return f, errno
+}
+
+const supportedSyscallOflag = sys.O_NONBLOCK
+
+// Map to synthetic values here https://github.com/golang/go/blob/go1.20/src/syscall/types_windows.go#L34-L48
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ // O_DIRECTORY not defined in windows
+ // O_DSYNC not defined in windows
+ // O_NOFOLLOW not defined in windows
+ if oflag&sys.O_NONBLOCK != 0 {
+ flag |= syscall.O_NONBLOCK
+ }
+ // O_RSYNC not defined in windows
+ return flag
+}
+
+func isSymlink(path string) bool {
+ if st, e := os.Lstat(path); e == nil && st.Mode()&os.ModeSymlink != 0 {
+ return true
+ }
+ return false
+}
+
+// # Differences from syscall.Open
+//
+// This code is based on syscall.Open from the below link with some differences
+// https://github.com/golang/go/blame/go1.20/src/syscall/syscall_windows.go#L308-L379
+//
+// - syscall.O_CREAT doesn't imply syscall.GENERIC_WRITE as that breaks
+// flag expectations in wasi.
+// - add support for setting FILE_SHARE_DELETE.
+func open(path string, mode int, perm uint32) (fd syscall.Handle, err error) {
+ if len(path) == 0 {
+ return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
+ }
+ pathp, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return syscall.InvalidHandle, err
+ }
+ var access uint32
+ switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
+ case syscall.O_RDONLY:
+ access = syscall.GENERIC_READ
+ case syscall.O_WRONLY:
+ access = syscall.GENERIC_WRITE
+ case syscall.O_RDWR:
+ access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
+ }
+ if mode&syscall.O_APPEND != 0 {
+ access &^= syscall.GENERIC_WRITE
+ access |= syscall.FILE_APPEND_DATA
+ }
+ sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
+ var sa *syscall.SecurityAttributes
+ if mode&syscall.O_CLOEXEC == 0 {
+ var _sa syscall.SecurityAttributes
+ _sa.Length = uint32(unsafe.Sizeof(sa))
+ _sa.InheritHandle = 1
+ sa = &_sa
+ }
+ var createmode uint32
+ switch {
+ case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
+ createmode = syscall.CREATE_NEW
+ case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
+ createmode = syscall.CREATE_ALWAYS
+ case mode&syscall.O_CREAT == syscall.O_CREAT:
+ createmode = syscall.OPEN_ALWAYS
+ case mode&syscall.O_TRUNC == syscall.O_TRUNC:
+ createmode = syscall.TRUNCATE_EXISTING
+ default:
+ createmode = syscall.OPEN_EXISTING
+ }
+ var attrs uint32 = syscall.FILE_ATTRIBUTE_NORMAL
+ if perm&syscall.S_IWRITE == 0 {
+ attrs = syscall.FILE_ATTRIBUTE_READONLY
+ if createmode == syscall.CREATE_ALWAYS {
+ // We have been asked to create a read-only file.
+ // If the file already exists, the semantics of
+ // the Unix open system call is to preserve the
+ // existing permissions. If we pass CREATE_ALWAYS
+ // and FILE_ATTRIBUTE_READONLY to CreateFile,
+ // and the file already exists, CreateFile will
+ // change the file permissions.
+ // Avoid that to preserve the Unix semantics.
+ h, e := syscall.CreateFile(pathp, access, sharemode, sa, syscall.TRUNCATE_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
+ switch e {
+ case syscall.ERROR_FILE_NOT_FOUND, syscall.ERROR_PATH_NOT_FOUND:
+ // File does not exist. These are the same
+ // errors as Errno.Is checks for ErrNotExist.
+ // Carry on to create the file.
+ default:
+ // Success or some different error.
+ return h, e
+ }
+ }
+ }
+
+ // This shouldn't be included before 1.20 to have consistent behavior.
+ // https://github.com/golang/go/commit/0f0aa5d8a6a0253627d58b3aa083b24a1091933f
+ if createmode == syscall.OPEN_EXISTING && access == syscall.GENERIC_READ {
+ // Necessary for opening directory handles.
+ attrs |= syscall.FILE_FLAG_BACKUP_SEMANTICS
+ }
+
+ h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
+ return h, e
+}