Repository: tetratelabs/wazero
Pull request #1586 (2023-07-19 03:26:55)

fsapi: adds Oflag to decouple from syscall package
This adds `fsapi.Oflag` which includes oflag values needed to support `fsapi.FS`. Notably, this covers more area than Go does. Go defines portable abstractions for most open flags, but not all. Flags like `O_DIRECTORY` are needed by wasip1 and not defined in Go as `os.O_DIRECTORY`. Also, some operating systems don't define these either. Instead we have a new `fsapi.Oflag` type which includes all flags needed, even if notably in windows behavior is emulated.


Patch:
diff --git a/experimental/sys/errno.go b/experimental/sys/errno.go
index a07c21c2..81baadd0 100644
--- a/experimental/sys/errno.go
+++ b/experimental/sys/errno.go
@@ -14,7 +14,7 @@ type Errno uint16
 // the Errno it returns, and we export fs.FS. This is not in /internal/sys as
 // that would introduce a package cycle.
 
-// This is a subset of errors to reduce implementation burden. `wasip` defines
+// This is a subset of errors to reduce implementation burden. `wasip1` defines
 // almost all POSIX error numbers, but not all are used in practice. wazero
 // will add ones needed in POSIX order, as needed by functions that explicitly
 // document returning them.
diff --git a/imports/wasi_snapshot_preview1/fs.go b/imports/wasi_snapshot_preview1/fs.go
index bec2f037..f0985a79 100644
--- a/imports/wasi_snapshot_preview1/fs.go
+++ b/imports/wasi_snapshot_preview1/fs.go
@@ -1687,7 +1687,7 @@ func preopenPath(fsc *sys.FSContext, fd int32) (string, experimentalsys.Errno) {
 	}
 }
 
-func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags int) {
+func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags fsapi.Oflag) {
 	if dirflags&wasip1.LOOKUP_SYMLINK_FOLLOW == 0 {
 		openFlags |= fsapi.O_NOFOLLOW
 	}
@@ -1695,45 +1695,55 @@ func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags int)
 		openFlags |= fsapi.O_DIRECTORY
 		return // Early return for directories as the rest of flags doesn't make sense for it.
 	} else if oflags&wasip1.O_EXCL != 0 {
-		openFlags |= syscall.O_EXCL
+		openFlags |= fsapi.O_EXCL
 	}
-	// Because we don't implement rights, we paritally rely on the open flags
+	// Because we don't implement rights, we partially rely on the open flags
 	// to determine the mode in which the file will be opened. This will create
 	// divergent behavior compared to WASI runtimes which have a more strict
 	// interpretation of the WASI capabilities model; for example, a program
 	// which sets O_CREAT but does not give read or write permissions will
 	// successfully create a file when running with wazero, but might get a
 	// permission denied error on other runtimes.
-	defaultMode := syscall.O_RDONLY
+	defaultMode := fsapi.O_RDONLY
 	if oflags&wasip1.O_TRUNC != 0 {
-		openFlags |= syscall.O_TRUNC
-		defaultMode = syscall.O_RDWR
+		openFlags |= fsapi.O_TRUNC
+		defaultMode = fsapi.O_RDWR
 	}
 	if oflags&wasip1.O_CREAT != 0 {
-		openFlags |= syscall.O_CREAT
-		defaultMode = syscall.O_RDWR
+		openFlags |= fsapi.O_CREAT
+		defaultMode = fsapi.O_RDWR
 	}
 	if fdflags&wasip1.FD_NONBLOCK != 0 {
 		openFlags |= fsapi.O_NONBLOCK
 	}
 	if fdflags&wasip1.FD_APPEND != 0 {
-		openFlags |= syscall.O_APPEND
-		defaultMode = syscall.O_RDWR
+		openFlags |= fsapi.O_APPEND
+		defaultMode = fsapi.O_RDWR
 	}
+	if fdflags&wasip1.FD_DSYNC != 0 {
+		openFlags |= fsapi.O_DSYNC
+	}
+	if fdflags&wasip1.FD_RSYNC != 0 {
+		openFlags |= fsapi.O_RSYNC
+	}
+	if fdflags&wasip1.FD_SYNC != 0 {
+		openFlags |= fsapi.O_SYNC
+	}
+
 	// Since rights were discontinued in wasi, we only interpret RIGHT_FD_WRITE
 	// because it is the only way to know that we need to set write permissions
-	// on a file if the application did not pass any of O_CREATE, O_APPEND, nor
+	// on a file if the application did not pass any of O_CREAT, O_APPEND, nor
 	// O_TRUNC.
 	const r = wasip1.RIGHT_FD_READ
 	const w = wasip1.RIGHT_FD_WRITE
 	const rw = r | w
 	switch {
 	case (rights & rw) == rw:
-		openFlags |= syscall.O_RDWR
+		openFlags |= fsapi.O_RDWR
 	case (rights & w) == w:
-		openFlags |= syscall.O_WRONLY
+		openFlags |= fsapi.O_WRONLY
 	case (rights & r) == r:
-		openFlags |= syscall.O_RDONLY
+		openFlags |= fsapi.O_RDONLY
 	default:
 		openFlags |= defaultMode
 	}
diff --git a/internal/fsapi/constants.go b/internal/fsapi/constants.go
deleted file mode 100644
index 868b9c16..00000000
--- a/internal/fsapi/constants.go
+++ /dev/null
@@ -1,13 +0,0 @@
-//go:build !windows && !js && !illumos && !solaris
-
-package fsapi
-
-import "syscall"
-
-// Simple aliases to constants in the syscall package for portability with
-// platforms which do not have them (e.g. windows)
-const (
-	O_DIRECTORY = syscall.O_DIRECTORY
-	O_NOFOLLOW  = syscall.O_NOFOLLOW
-	O_NONBLOCK  = syscall.O_NONBLOCK
-)
diff --git a/internal/fsapi/constants_js.go b/internal/fsapi/constants_js.go
deleted file mode 100644
index af73ddb6..00000000
--- a/internal/fsapi/constants_js.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package fsapi
-
-// See the comments on the same constants in constants_windows.go
-const (
-	O_DIRECTORY = 1 << 29
-	O_NOFOLLOW  = 1 << 30
-	O_NONBLOCK  = 1 << 31
-)
diff --git a/internal/fsapi/constants_sun.go b/internal/fsapi/constants_sun.go
deleted file mode 100644
index a0de49b7..00000000
--- a/internal/fsapi/constants_sun.go
+++ /dev/null
@@ -1,12 +0,0 @@
-//go:build illumos || solaris
-
-package fsapi
-
-import "syscall"
-
-// See https://github.com/illumos/illumos-gate/blob/edd580643f2cf1434e252cd7779e83182ea84945/usr/src/uts/common/sys/fcntl.h#L90
-const (
-	O_DIRECTORY = 0x1000000
-	O_NOFOLLOW  = syscall.O_NOFOLLOW
-	O_NONBLOCK  = syscall.O_NONBLOCK
-)
diff --git a/internal/fsapi/constants_windows.go b/internal/fsapi/constants_windows.go
deleted file mode 100644
index 33aed870..00000000
--- a/internal/fsapi/constants_windows.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package fsapi
-
-import "syscall"
-
-// Windows does not have these constants, we declare placeholders which should
-// not conflict with other open flags. These placeholders are not declared as
-// value zero so code written in a way which expects them to be bit flags still
-// works as expected.
-//
-// Since those placeholder are not interpreted by the open function, the unix
-// features they represent are also not implemented on windows:
-//
-//   - O_DIRECTORY allows programs to ensure that the opened file is a directory.
-//     This could be emulated by doing a stat call on the file after opening it
-//     to verify that it is in fact a directory, then closing it and returning an
-//     error if it is not.
-//
-//   - O_NOFOLLOW allows programs to ensure that if the opened file is a symbolic
-//     link, the link itself is opened instead of its target.
-const (
-	O_DIRECTORY = 1 << 29
-	O_NOFOLLOW  = 1 << 30
-	O_NONBLOCK  = syscall.O_NONBLOCK
-)
diff --git a/internal/fsapi/file.go b/internal/fsapi/file.go
index b4ee85b8..f40d4155 100644
--- a/internal/fsapi/file.go
+++ b/internal/fsapi/file.go
@@ -96,7 +96,7 @@ type File interface {
 	//     POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html
 	SetNonblock(enable bool) experimentalsys.Errno
 
-	// IsAppend returns true if the file was opened with syscall.O_APPEND, or
+	// IsAppend returns true if the file was opened with fsapi.O_APPEND, or
 	// SetAppend was successfully enabled on this file.
 	//
 	// # Notes
@@ -105,7 +105,7 @@ type File interface {
 	//     the file was not opened via OpenFile.
 	IsAppend() bool
 
-	// SetAppend toggles the append mode (syscall.O_APPEND) of this file.
+	// SetAppend toggles the append mode (fsapi.O_APPEND) of this file.
 	//
 	// # Errors
 	//
@@ -351,7 +351,7 @@ type File interface {
 	//
 	//   - This is like syscall.UtimesNano and `futimens` in POSIX. See
 	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html
-	//   - Windows requires files to be open with syscall.O_RDWR, which means you
+	//   - Windows requires files to be open with fsapi.O_RDWR, which means you
 	//     cannot use this to update timestamps on a directory (EPERM).
 	Utimens(times *[2]syscall.Timespec) experimentalsys.Errno
 
diff --git a/internal/fsapi/fs.go b/internal/fsapi/fs.go
index 50dfd1e6..25cc0302 100644
--- a/internal/fsapi/fs.go
+++ b/internal/fsapi/fs.go
@@ -34,18 +34,17 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `path` or `flag` is invalid.
-	//   - EISDIR: the path was a directory, but flag included
-	//     syscall.O_RDWR or syscall.O_WRONLY
-	//   - ENOENT: `path` doesn't exist and `flag` doesn't contain
-	//     os.O_CREATE.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `path` or `flag` is invalid.
+	//   - sys.EISDIR: the path was a directory, but flag included O_RDWR or
+	//     O_WRONLY
+	//   - sys.ENOENT: `path` doesn't exist and `flag` doesn't contain O_CREAT.
 	//
 	// # Constraints on the returned file
 	//
 	// Implementations that can read flags should enforce them regardless of
 	// the type returned. For example, while os.File implements io.Writer,
-	// attempts to write to a directory or a file opened with os.O_RDONLY fail
+	// attempts to write to a directory or a file opened with O_RDONLY fail
 	// with a EBADF.
 	//
 	// Some implementations choose whether to enforce read-only opens, namely
@@ -57,21 +56,18 @@ type FS interface {
 	//
 	//   - This is like os.OpenFile, except the path is relative to this file
 	//     system, and Errno is returned instead of os.PathError.
-	//   - flag are the same as os.OpenFile, for example, os.O_CREATE.
-	//   - Implications of permissions when os.O_CREATE are described in Chmod
-	//     notes.
+	//   - Implications of permissions when O_CREAT are described in Chmod notes.
 	//   - This is like `open` in POSIX. See
 	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
-	OpenFile(path string, flag int, perm fs.FileMode) (File, experimentalsys.Errno)
-	// TODO: make sure all flags are not in the syscall package
+	OpenFile(path string, flag Oflag, perm fs.FileMode) (File, experimentalsys.Errno)
 
 	// Lstat gets file status without following symbolic links.
 	//
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - ENOENT: `path` doesn't exist.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.ENOENT: `path` doesn't exist.
 	//
 	// # Notes
 	//
@@ -90,8 +86,8 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - ENOENT: `path` doesn't exist.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.ENOENT: `path` doesn't exist.
 	//
 	// # Notes
 	//
@@ -110,10 +106,10 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `path` is invalid.
-	//   - EEXIST: `path` exists and is a directory.
-	//   - ENOTDIR: `path` exists and is a file.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `path` is invalid.
+	//   - sys.EEXIST: `path` exists and is a directory.
+	//   - sys.ENOTDIR: `path` exists and is a file.
 	//
 	// # Notes
 	//
@@ -129,9 +125,9 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `path` is invalid.
-	//   - ENOENT: `path` does not exist.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `path` is invalid.
+	//   - sys.ENOENT: `path` does not exist.
 	//
 	// # Notes
 	//
@@ -149,12 +145,12 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `from` or `to` is invalid.
-	//   - ENOENT: `from` or `to` don't exist.
-	//   - ENOTDIR: `from` is a directory and `to` exists as a file.
-	//   - EISDIR: `from` is a file and `to` exists as a directory.
-	//   - ENOTEMPTY: `both from` and `to` are existing directory, but
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `from` or `to` is invalid.
+	//   - sys.ENOENT: `from` or `to` don't exist.
+	//   - sys.ENOTDIR: `from` is a directory and `to` exists as a file.
+	//   - sys.EISDIR: `from` is a file and `to` exists as a directory.
+	//   - sys.ENOTEMPTY: `both from` and `to` are existing directory, but
 	//    `to` is not empty.
 	//
 	// # Notes
@@ -171,11 +167,11 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `path` is invalid.
-	//   - ENOENT: `path` doesn't exist.
-	//   - ENOTDIR: `path` exists, but isn't a directory.
-	//   - ENOTEMPTY: `path` exists, but isn't empty.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `path` is invalid.
+	//   - sys.ENOENT: `path` doesn't exist.
+	//   - sys.ENOTDIR: `path` exists, but isn't a directory.
+	//   - sys.ENOTEMPTY: `path` exists, but isn't empty.
 	//
 	// # Notes
 	//
@@ -183,7 +179,7 @@ type FS interface {
 	//     file system.
 	//   - This is like `rmdir` in POSIX. See
 	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html
-	//   - As of Go 1.19, Windows maps ENOTDIR to ENOENT.
+	//   - As of Go 1.19, Windows maps sys.ENOTDIR to sys.ENOENT.
 	Rmdir(path string) experimentalsys.Errno
 
 	// Unlink removes a directory entry.
@@ -191,10 +187,10 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `path` is invalid.
-	//   - ENOENT: `path` doesn't exist.
-	//   - EISDIR: `path` exists, but is a directory.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `path` is invalid.
+	//   - sys.ENOENT: `path` doesn't exist.
+	//   - sys.EISDIR: `path` exists, but is a directory.
 	//
 	// # Notes
 	//
@@ -213,10 +209,10 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EPERM: `oldPath` is invalid.
-	//   - ENOENT: `oldPath` doesn't exist.
-	//   - EISDIR: `newPath` exists, but is a directory.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EPERM: `oldPath` is invalid.
+	//   - sys.ENOENT: `oldPath` doesn't exist.
+	//   - sys.EISDIR: `newPath` exists, but is a directory.
 	//
 	// # Notes
 	//
@@ -232,9 +228,9 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EPERM: `oldPath` or `newPath` is invalid.
-	//   - EEXIST: `newPath` exists.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EPERM: `oldPath` or `newPath` is invalid.
+	//   - sys.EEXIST: `newPath` exists.
 	//
 	// # Notes
 	//
@@ -248,7 +244,7 @@ type FS interface {
 	//     See https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409
 	//     for how others implement this.
 	//   - Symlinks in Windows requires `SeCreateSymbolicLinkPrivilege`.
-	//     Otherwise, EPERM results.
+	//     Otherwise, sys.EPERM results.
 	//     See https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
 	Symlink(oldPath, linkName string) experimentalsys.Errno
 
@@ -257,8 +253,8 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `path` is invalid.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `path` is invalid.
 	//
 	// # Notes
 	//
@@ -287,10 +283,10 @@ type FS interface {
 	// # Errors
 	//
 	// A zero Errno is success. The below are expected otherwise:
-	//   - ENOSYS: the implementation does not support this function.
-	//   - EINVAL: `path` is invalid.
-	//   - EEXIST: `path` exists and is a directory.
-	//   - ENOTDIR: `path` exists and is a file.
+	//   - sys.ENOSYS: the implementation does not support this function.
+	//   - sys.EINVAL: `path` is invalid.
+	//   - sys.EEXIST: `path` exists and is a directory.
+	//   - sys.ENOTDIR: `path` exists and is a file.
 	//
 	// # Notes
 	//
diff --git a/internal/fsapi/oflag.go b/internal/fsapi/oflag.go
new file mode 100644
index 00000000..eac4fc98
--- /dev/null
+++ b/internal/fsapi/oflag.go
@@ -0,0 +1,70 @@
+package fsapi
+
+// Oflag are flags used for FS.OpenFile. Values, including zero, should not be
+// interpreted numerically. Instead, use by constants prefixed with 'O_' with
+// special casing noted below.
+//
+// # Notes
+//
+//   - O_RDONLY, O_RDWR and O_WRONLY are mutually exclusive, while the other
+//     flags can coexist bitwise.
+//   - This is like `flag` in os.OpenFile and `oflag` in POSIX. See
+//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
+type Oflag uint32
+
+// This is a subset of oflags to reduce implementation burden. `wasip1` splits
+// these across `oflags` and `fdflags`. We can't rely on the Go `os` package,
+// as it is missing some values. Any flags added will be defined in POSIX
+// order, as needed by functions that explicitly document accepting them.
+//
+// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-oflags-flagsu16
+// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fdflags-flagsu16
+const (
+	// O_RDONLY is like os.O_RDONLY
+	O_RDONLY Oflag = iota
+
+	// O_RDWR is like os.O_RDWR
+	O_RDWR
+
+	// O_WRONLY is like os.O_WRONLY
+	O_WRONLY
+
+	// Define bitflags as they are in POSIX `open`: alphabetically
+
+	// O_APPEND is like os.O_APPEND
+	O_APPEND Oflag = 1 << iota
+
+	// O_CREAT is link os.O_CREATE
+	O_CREAT
+
+	// O_DIRECTORY is defined on some platforms as syscall.O_DIRECTORY.
+	//
+	// Note: This ensures that the opened file is a directory. Those emulating
+	// on platforms that don't support the O_DIRECTORY, can double-check the
+	// result with File.IsDir (or stat) and err if not a directory.
+	O_DIRECTORY
+
+	// O_DSYNC is defined on some platforms as syscall.O_DSYNC.
+	O_DSYNC
+
+	// O_EXCL is defined on some platforms as syscall.O_EXCL.
+	O_EXCL
+
+	// O_NOFOLLOW is defined on some platforms as syscall.O_NOFOLLOW.
+	//
+	// Note: This allows programs to ensure that if the opened file is a
+	// symbolic link, the link itself is opened instead of its target.
+	O_NOFOLLOW
+
+	// O_NONBLOCK is defined on some platforms as syscall.O_NONBLOCK.
+	O_NONBLOCK
+
+	// O_RSYNC is defined on some platforms as syscall.O_RSYNC.
+	O_RSYNC
+
+	// O_SYNC is defined on some platforms as syscall.O_SYNC.
+	O_SYNC
+
+	// O_TRUNC is defined on some platforms as syscall.O_TRUNC.
+	O_TRUNC
+)
diff --git a/internal/fsapi/unimplemented.go b/internal/fsapi/unimplemented.go
index 81a636f3..6f2ab1f1 100644
--- a/internal/fsapi/unimplemented.go
+++ b/internal/fsapi/unimplemented.go
@@ -24,7 +24,7 @@ func (UnimplementedFS) Open(name string) (fs.File, error) {
 }
 
 // OpenFile implements FS.OpenFile
-func (UnimplementedFS) OpenFile(path string, flag int, perm fs.FileMode) (File, experimentalsys.Errno) {
+func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, experimentalsys.Errno) {
 	return nil, experimentalsys.ENOSYS
 }
 
diff --git a/internal/gojs/fs.go b/internal/gojs/fs.go
index eeabecfd..14226653 100644
--- a/internal/gojs/fs.go
+++ b/internal/gojs/fs.go
@@ -3,11 +3,11 @@ package gojs
 import (
 	"context"
 	"fmt"
-	"os"
 	"syscall"
 
 	"github.com/tetratelabs/wazero/api"
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/internal/fsapi"
 	"github.com/tetratelabs/wazero/internal/gojs/custom"
 	"github.com/tetratelabs/wazero/internal/gojs/goos"
 	"github.com/tetratelabs/wazero/internal/gojs/util"
@@ -29,22 +29,22 @@ var (
 		})
 
 	// oWRONLY = jsfsConstants Get("O_WRONLY").Int() // fs_js.go init
-	oWRONLY = float64(os.O_WRONLY)
+	oWRONLY = float64(fsapi.O_WRONLY)
 
 	// oRDWR = jsfsConstants Get("O_RDWR").Int() // fs_js.go init
-	oRDWR = float64(os.O_RDWR)
+	oRDWR = float64(fsapi.O_RDWR)
 
 	// o CREAT = jsfsConstants Get("O_CREAT").Int() // fs_js.go init
-	oCREAT = float64(os.O_CREATE)
+	oCREAT = float64(fsapi.O_CREAT)
 
 	// oTRUNC = jsfsConstants Get("O_TRUNC").Int() // fs_js.go init
-	oTRUNC = float64(os.O_TRUNC)
+	oTRUNC = float64(fsapi.O_TRUNC)
 
 	// oAPPEND = jsfsConstants Get("O_APPEND").Int() // fs_js.go init
-	oAPPEND = float64(os.O_APPEND)
+	oAPPEND = float64(fsapi.O_APPEND)
 
 	// oEXCL = jsfsConstants Get("O_EXCL").Int() // fs_js.go init
-	oEXCL = float64(os.O_EXCL)
+	oEXCL = float64(fsapi.O_EXCL)
 )
 
 // jsfs = js.Global().Get("fs") // fs_js.go init
@@ -92,13 +92,15 @@ type jsfsOpen struct {
 
 func (o *jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
 	path := util.ResolvePath(o.proc.cwd, args[0].(string))
-	flags := toUint64(args[1]) // flags are derived from constants like oWRONLY
+	// Note: these are already fsapi.Flag because Go uses constants we define:
+	// https://github.com/golang/go/blob/go1.20/src/syscall/fs_js.go#L24-L31
+	flags := fsapi.Oflag(toUint64(args[1]))
 	perm := custom.FromJsMode(goos.ValueToUint32(args[2]), o.proc.umask)
 	callback := args[3].(funcWrapper)
 
 	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
 
-	fd, errno := fsc.OpenFile(fsc.RootFS(), path, int(flags), perm)
+	fd, errno := fsc.OpenFile(fsc.RootFS(), path, flags, perm)
 
 	return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), fd) // note: error first
 }
@@ -318,7 +320,7 @@ func syscallReaddir(_ context.Context, mod api.Module, name string) (*objectArra
 	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
 
 	// don't allocate a file descriptor
-	f, errno := fsc.RootFS().OpenFile(name, os.O_RDONLY, 0)
+	f, errno := fsc.RootFS().OpenFile(name, fsapi.O_RDONLY, 0)
 	if errno != 0 {
 		return nil, errno
 	}
@@ -357,7 +359,7 @@ func (m *jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interfac
 		perm = 0o0500
 	}
 	if errno = root.Mkdir(path, perm); errno == 0 {
-		fd, errno = fsc.OpenFile(root, path, os.O_RDONLY, 0)
+		fd, errno = fsc.OpenFile(root, path, fsapi.O_RDONLY, 0)
 	}
 
 	return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), fd) // note: error first
diff --git a/internal/sys/fs.go b/internal/sys/fs.go
index 32e2e7b4..f85e2ff2 100644
--- a/internal/sys/fs.go
+++ b/internal/sys/fs.go
@@ -284,7 +284,7 @@ func (c *FSContext) LookupFile(fd int32) (*FileEntry, bool) {
 
 // OpenFile opens the file into the table and returns its file descriptor.
 // The result must be closed by CloseFile or Close.
-func (c *FSContext) OpenFile(fs fsapi.FS, path string, flag int, perm fs.FileMode) (int32, sys.Errno) {
+func (c *FSContext) OpenFile(fs fsapi.FS, path string, flag fsapi.Oflag, perm fs.FileMode) (int32, sys.Errno) {
 	if f, errno := fs.OpenFile(path, flag, perm); errno != 0 {
 		return 0, errno
 	} else {
diff --git a/internal/sys/lazy.go b/internal/sys/lazy.go
index 718398b9..011a5a4d 100644
--- a/internal/sys/lazy.go
+++ b/internal/sys/lazy.go
@@ -1,7 +1,6 @@
 package sys
 
 import (
-	"os"
 	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
@@ -118,7 +117,7 @@ func (r *lazyDir) file() (fsapi.File, bool) {
 		return f, true
 	}
 	var errno experimentalsys.Errno
-	r.f, errno = r.fs.OpenFile(".", os.O_RDONLY, 0)
+	r.f, errno = r.fs.OpenFile(".", fsapi.O_RDONLY, 0)
 	switch errno {
 	case 0:
 		return r.f, true
diff --git a/internal/sys/stdio.go b/internal/sys/stdio.go
index 59ffd084..b153e32c 100644
--- a/internal/sys/stdio.go
+++ b/internal/sys/stdio.go
@@ -3,7 +3,6 @@ package sys
 import (
 	"io"
 	"os"
-	"syscall"
 	"time"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
@@ -45,11 +44,6 @@ type noopStdinFile struct {
 	noopStdioFile
 }
 
-// AccessMode implements the same method as documented on fsapi.File
-func (noopStdinFile) AccessMode() int {
-	return syscall.O_RDONLY
-}
-
 // Read implements the same method as documented on fsapi.File
 func (noopStdinFile) Read([]byte) (int, experimentalsys.Errno) {
 	return 0, 0 // Always EOF
@@ -66,11 +60,6 @@ type noopStdoutFile struct {
 	noopStdioFile
 }
 
-// AccessMode implements the same method as documented on fsapi.File
-func (noopStdoutFile) AccessMode() int {
-	return syscall.O_WRONLY
-}
-
 // Write implements the same method as documented on fsapi.File
 func (noopStdoutFile) Write(buf []byte) (int, experimentalsys.Errno) {
 	return len(buf), 0 // same as io.Discard
diff --git a/internal/sysfs/adapter.go b/internal/sysfs/adapter.go
index 06b5369d..75d70888 100644
--- a/internal/sysfs/adapter.go
+++ b/internal/sysfs/adapter.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 	"io/fs"
 	"path"
-	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/internal/fsapi"
@@ -39,13 +38,13 @@ func (a *adapter) String() string {
 }
 
 // OpenFile implements the same method as documented on fsapi.FS
-func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
+func (a *adapter) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
 	return OpenFSFile(a.fs, cleanPath(path), flag, perm)
 }
 
 // Stat implements the same method as documented on fsapi.FS
 func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
-	f, errno := a.OpenFile(path, syscall.O_RDONLY, 0)
+	f, errno := a.OpenFile(path, fsapi.O_RDONLY, 0)
 	if errno != 0 {
 		return sys.Stat_t{}, errno
 	}
diff --git a/internal/sysfs/dirfs.go b/internal/sysfs/dirfs.go
index 28138127..5f3f01eb 100644
--- a/internal/sysfs/dirfs.go
+++ b/internal/sysfs/dirfs.go
@@ -39,7 +39,7 @@ func (d *dirFS) String() string {
 }
 
 // OpenFile implements the same method as documented on fsapi.FS
-func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
+func (d *dirFS) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
 	return OpenOSFile(d.join(path), flag, perm)
 }
 
diff --git a/internal/sysfs/file.go b/internal/sysfs/file.go
index 72d08868..b3285cab 100644
--- a/internal/sysfs/file.go
+++ b/internal/sysfs/file.go
@@ -4,7 +4,6 @@ import (
 	"io"
 	"io/fs"
 	"os"
-	"syscall"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/internal/fsapi"
@@ -21,11 +20,11 @@ func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) {
 	} else {
 		mode = st.Mode()
 	}
-	var flag int
+	var flag fsapi.Oflag
 	if stdin {
-		flag = syscall.O_RDONLY
+		flag = fsapi.O_RDONLY
 	} else {
-		flag = syscall.O_WRONLY
+		flag = fsapi.O_WRONLY
 	}
 	var file fsapi.File
 	if of, ok := f.(*os.File); ok {
@@ -37,14 +36,14 @@ func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) {
 	return &stdioFile{File: file, st: sys.Stat_t{Mode: mode, Nlink: 1}}, nil
 }
 
-func OpenFile(path string, flag int, perm fs.FileMode) (*os.File, experimentalsys.Errno) {
-	if flag&fsapi.O_DIRECTORY != 0 && flag&(syscall.O_WRONLY|syscall.O_RDWR) != 0 {
+func OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (*os.File, experimentalsys.Errno) {
+	if flag&fsapi.O_DIRECTORY != 0 && flag&(fsapi.O_WRONLY|fsapi.O_RDWR) != 0 {
 		return nil, experimentalsys.EISDIR // invalid to open a directory writeable
 	}
 	return openFile(path, flag, perm)
 }
 
-func OpenOSFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
+func OpenOSFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
 	f, errno := OpenFile(path, flag, perm)
 	if errno != 0 {
 		return nil, errno
@@ -52,8 +51,8 @@ func OpenOSFile(path string, flag int, perm fs.FileMode) (fsapi.File, experiment
 	return newOsFile(path, flag, perm, f), 0
 }
 
-func OpenFSFile(fs fs.FS, path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
-	if flag&fsapi.O_DIRECTORY != 0 && flag&(syscall.O_WRONLY|syscall.O_RDWR) != 0 {
+func OpenFSFile(fs fs.FS, path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
+	if flag&fsapi.O_DIRECTORY != 0 && flag&(fsapi.O_WRONLY|fsapi.O_RDWR) != 0 {
 		return nil, experimentalsys.EISDIR // invalid to open a directory writeable
 	}
 	f, err := fs.Open(path)
diff --git a/internal/sysfs/oflag.go b/internal/sysfs/oflag.go
new file mode 100644
index 00000000..e6c3b06c
--- /dev/null
+++ b/internal/sysfs/oflag.go
@@ -0,0 +1,38 @@
+package sysfs
+
+import (
+	"os"
+
+	"github.com/tetratelabs/wazero/internal/fsapi"
+)
+
+// toOsOpenFlag converts the input to the flag parameter of os.OpenFile
+func toOsOpenFlag(oflag fsapi.Oflag) (flag int) {
+	// First flags are exclusive
+	switch oflag & (fsapi.O_RDONLY | fsapi.O_RDWR | fsapi.O_WRONLY) {
+	case fsapi.O_RDONLY:
+		flag |= os.O_RDONLY
+	case fsapi.O_RDWR:
+		flag |= os.O_RDWR
+	case fsapi.O_WRONLY:
+		flag |= os.O_WRONLY
+	}
+
+	// Run down the flags defined in the os package
+	if oflag&fsapi.O_APPEND != 0 {
+		flag |= os.O_APPEND
+	}
+	if oflag&fsapi.O_CREAT != 0 {
+		flag |= os.O_CREATE
+	}
+	if oflag&fsapi.O_EXCL != 0 {
+		flag |= os.O_EXCL
+	}
+	if oflag&fsapi.O_SYNC != 0 {
+		flag |= os.O_SYNC
+	}
+	if oflag&fsapi.O_TRUNC != 0 {
+		flag |= os.O_TRUNC
+	}
+	return withSyscallOflag(oflag, flag)
+}
diff --git a/internal/sysfs/open_file.go b/internal/sysfs/open_file.go
deleted file mode 100644
index 61ced344..00000000
--- a/internal/sysfs/open_file.go
+++ /dev/null
@@ -1,20 +0,0 @@
-//go:build !windows && !js && !illumos && !solaris
-
-package sysfs
-
-import (
-	"io/fs"
-	"os"
-
-	"github.com/tetratelabs/wazero/experimental/sys"
-)
-
-// OpenFile is like os.OpenFile except it returns sys.Errno. A zero
-// sys.Errno is success.
-func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) {
-	f, err := os.OpenFile(path, flag, perm)
-	// Note: This does not return a fsapi.File because fsapi.FS that returns
-	// one may want to hide the real OS path. For example, this is needed for
-	// pre-opens.
-	return f, sys.UnwrapOSError(err)
-}
diff --git a/internal/sysfs/open_file_darwin.go b/internal/sysfs/open_file_darwin.go
new file mode 100644
index 00000000..82275393
--- /dev/null
+++ b/internal/sysfs/open_file_darwin.go
@@ -0,0 +1,26 @@
+package sysfs
+
+import (
+	"syscall"
+
+	"github.com/tetratelabs/wazero/internal/fsapi"
+)
+
+const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_DSYNC | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK
+
+func withSyscallOflag(oflag fsapi.Oflag, flag int) int {
+	if oflag&fsapi.O_DIRECTORY != 0 {
+		flag |= syscall.O_DIRECTORY
+	}
+	if oflag&fsapi.O_DSYNC != 0 {
+		flag |= syscall.O_DSYNC
+	}
+	if oflag&fsapi.O_NOFOLLOW != 0 {
+		flag |= syscall.O_NOFOLLOW
+	}
+	if oflag&fsapi.O_NONBLOCK != 0 {
+		flag |= syscall.O_NONBLOCK
+	}
+	// syscall.O_RSYNC not defined on darwin
+	return flag
+}
diff --git a/internal/sysfs/open_file_freebsd.go b/internal/sysfs/open_file_freebsd.go
new file mode 100644
index 00000000..e91da95d
--- /dev/null
+++ b/internal/sysfs/open_file_freebsd.go
@@ -0,0 +1,24 @@
+package sysfs
+
+import (
+	"syscall"
+
+	"github.com/tetratelabs/wazero/internal/fsapi"
+)
+
+const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK
+
+func withSyscallOflag(oflag fsapi.Oflag, flag int) int {
+	if oflag&fsapi.O_DIRECTORY != 0 {
+		flag |= syscall.O_DIRECTORY
+	}
+	// syscall.O_DSYNC not defined on darwin
+	if oflag&fsapi.O_NOFOLLOW != 0 {
+		flag |= syscall.O_NOFOLLOW
+	}
+	if oflag&fsapi.O_NONBLOCK != 0 {
+		flag |= syscall.O_NONBLOCK
+	}
+	// syscall.O_RSYNC not defined on darwin
+	return flag
+}
diff --git a/internal/sysfs/open_file_js.go b/internal/sysfs/open_file_js.go
deleted file mode 100644
index 613b06f8..00000000
--- a/internal/sysfs/open_file_js.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package sysfs
-
-import (
-	"io/fs"
-	"os"
-
-	"github.com/tetratelabs/wazero/experimental/sys"
-)
-
-func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) {
-	flag &= ^(O_DIRECTORY | O_NOFOLLOW) // erase placeholders
-	f, err := os.OpenFile(path, flag, perm)
-	return f, sys.UnwrapOSError(err)
-}
diff --git a/internal/sysfs/open_file_linux.go b/internal/sysfs/open_file_linux.go
new file mode 100644
index 00000000..bfa9a23e
--- /dev/null
+++ b/internal/sysfs/open_file_linux.go
@@ -0,0 +1,28 @@
+package sysfs
+
+import (
+	"syscall"
+
+	"github.com/tetratelabs/wazero/internal/fsapi"
+)
+
+const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_DSYNC | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK | fsapi.O_RSYNC
+
+func withSyscallOflag(oflag fsapi.Oflag, flag int) int {
+	if oflag&fsapi.O_DIRECTORY != 0 {
+		flag |= syscall.O_DIRECTORY
+	}
+	if oflag&fsapi.O_DSYNC != 0 {
+		flag |= syscall.O_DSYNC
+	}
+	if oflag&fsapi.O_NOFOLLOW != 0 {
+		flag |= syscall.O_NOFOLLOW
+	}
+	if oflag&fsapi.O_NONBLOCK != 0 {
+		flag |= syscall.O_NONBLOCK
+	}
+	if oflag&fsapi.O_RSYNC != 0 {
+		flag |= syscall.O_RSYNC
+	}
+	return flag
+}
diff --git a/internal/sysfs/open_file_notwindows.go b/internal/sysfs/open_file_notwindows.go
new file mode 100644
index 00000000..4e886fb5
--- /dev/null
+++ b/internal/sysfs/open_file_notwindows.go
@@ -0,0 +1,21 @@
+//go:build !windows
+
+package sysfs
+
+import (
+	"io/fs"
+	"os"
+
+	"github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/internal/fsapi"
+)
+
+// openFile is like os.OpenFile except it accepts a fsapi.Oflag and returns
+// sys.Errno. A zero sys.Errno is success.
+func openFile(path string, oflag fsapi.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
+	f, err := os.OpenFile(path, toOsOpenFlag(oflag), perm)
+	// Note: This does not return a fsapi.File because fsapi.FS that returns
+	// one may want to hide the real OS path. For example, this is needed for
+	// pre-opens.
+	return f, sys.UnwrapOSError(err)
+}
diff --git a/internal/sysfs/open_file_sun.go b/internal/sysfs/open_file_sun.go
index a75ab393..6589ddac 100644
--- a/internal/sysfs/open_file_sun.go
+++ b/internal/sysfs/open_file_sun.go
@@ -3,13 +3,29 @@
 package sysfs
 
 import (
-	"io/fs"
-	"os"
+	"syscall"
 
-	"github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/internal/fsapi"
 )
 
-func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) {
-	f, err := os.OpenFile(path, flag, perm)
-	return f, sys.UnwrapOSError(err)
+const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_DSYNC | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK | fsapi.O_RSYNC
+
+func withSyscallOflag(oflag fsapi.Oflag, flag int) int {
+	if oflag&fsapi.O_DIRECTORY != 0 {
+		// See https://github.com/illumos/illumos-gate/blob/edd580643f2cf1434e252cd7779e83182ea84945/usr/src/uts/common/sys/fcntl.h#L90
+		flag |= 0x1000000
+	}
+	if oflag&fsapi.O_DSYNC != 0 {
+		flag |= syscall.O_DSYNC
+	}
+	if oflag&fsapi.O_NOFOLLOW != 0 {
+		flag |= syscall.O_NOFOLLOW
+	}
+	if oflag&fsapi.O_NONBLOCK != 0 {
+		flag |= syscall.O_NONBLOCK
+	}
+	if oflag&fsapi.O_RSYNC != 0 {
+		flag |= syscall.O_RSYNC
+	}
+	return flag
 }
diff --git a/internal/sysfs/open_file_unsupported.go b/internal/sysfs/open_file_unsupported.go
new file mode 100644
index 00000000..9b925949
--- /dev/null
+++ b/internal/sysfs/open_file_unsupported.go
@@ -0,0 +1,16 @@
+//go:build !darwin && !linux && !windows && !illumos && !solaris && !freebsd
+
+package sysfs
+
+import "github.com/tetratelabs/wazero/internal/fsapi"
+
+const supportedSyscallOflag = fsapi.Oflag(0)
+
+func withSyscallOflag(oflag fsapi.Oflag, flag int) int {
+	// O_DIRECTORY not defined
+	// O_DSYNC not defined
+	// O_NOFOLLOW not defined
+	// O_NONBLOCK not defined
+	// O_RSYNC not defined
+	return flag
+}
diff --git a/internal/sysfs/open_file_windows.go b/internal/sysfs/open_file_windows.go
index 9190fefa..bc7a7f7d 100644
--- a/internal/sysfs/open_file_windows.go
+++ b/internal/sysfs/open_file_windows.go
@@ -12,9 +12,9 @@ import (
 	"github.com/tetratelabs/wazero/internal/platform"
 )
 
-func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) {
-	isDir := flag&fsapi.O_DIRECTORY > 0
-	flag &= ^(fsapi.O_DIRECTORY | fsapi.O_NOFOLLOW) // erase placeholders
+func openFile(path string, oflag fsapi.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
+	isDir := oflag&fsapi.O_DIRECTORY > 0
+	flag := toOsOpenFlag(oflag)
 
 	// TODO: document why we are opening twice
 	fd, err := open(path, flag|syscall.O_CLOEXEC, uint32(perm))
@@ -55,6 +55,20 @@ func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) {
 	return f, errno
 }
 
+const supportedSyscallOflag = fsapi.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 fsapi.Oflag, flag int) int {
+	// O_DIRECTORY not defined in windows
+	// O_DSYNC not defined in windows
+	// O_NOFOLLOW not defined in windows
+	if oflag&fsapi.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
diff --git a/internal/sysfs/osfile.go b/internal/sysfs/osfile.go
index b64f030a..e39c9256 100644
--- a/internal/sysfs/osfile.go
+++ b/internal/sysfs/osfile.go
@@ -14,19 +14,19 @@ import (
 	"github.com/tetratelabs/wazero/sys"
 )
 
-func newOsFile(openPath string, openFlag int, openPerm fs.FileMode, f *os.File) fsapi.File {
+func newOsFile(path string, flag fsapi.Oflag, perm fs.FileMode, f *os.File) fsapi.File {
 	// Windows cannot read files written to a directory after it was opened.
 	// This was noticed in #1087 in zig tests. Use a flag instead of a
 	// different type.
 	reopenDir := runtime.GOOS == "windows"
-	return &osFile{path: openPath, flag: openFlag, perm: openPerm, reopenDir: reopenDir, file: f, fd: f.Fd()}
+	return &osFile{path: path, flag: flag, perm: perm, reopenDir: reopenDir, file: f, fd: f.Fd()}
 }
 
 // osFile is a file opened with this package, and uses os.File or syscalls to
 // implement api.File.
 type osFile struct {
 	path string
-	flag int
+	flag fsapi.Oflag
 	perm fs.FileMode
 	file *os.File
 	fd   uintptr
@@ -75,19 +75,19 @@ func (f *osFile) IsDir() (bool, experimentalsys.Errno) {
 
 // IsAppend implements File.IsAppend
 func (f *osFile) IsAppend() bool {
-	return f.flag&syscall.O_APPEND == syscall.O_APPEND
+	return f.flag&fsapi.O_APPEND == fsapi.O_APPEND
 }
 
 // SetAppend implements the same method as documented on fsapi.File
 func (f *osFile) SetAppend(enable bool) (errno experimentalsys.Errno) {
 	if enable {
-		f.flag |= syscall.O_APPEND
+		f.flag |= fsapi.O_APPEND
 	} else {
-		f.flag &= ^syscall.O_APPEND
+		f.flag &= ^fsapi.O_APPEND
 	}
 
 	// Clear any create flag, as we are re-opening, not re-creating.
-	f.flag &= ^syscall.O_CREAT
+	f.flag &= ^fsapi.O_CREAT
 
 	// appendMode (bool) cannot be changed later, so we have to re-open the
 	// file. https://github.com/golang/go/blob/go1.20/src/os/file_unix.go#L60
@@ -99,7 +99,7 @@ var _ reopenFile = (*fsFile)(nil).reopen
 
 func (f *osFile) reopen() (errno experimentalsys.Errno) {
 	// Clear any create flag, as we are re-opening, not re-creating.
-	f.flag &= ^syscall.O_CREAT
+	f.flag &= ^fsapi.O_CREAT
 
 	_ = f.close()
 	f.file, errno = OpenFile(f.path, f.flag, f.perm)
diff --git a/internal/sysfs/readfs.go b/internal/sysfs/readfs.go
index 6150a096..76d182c1 100644
--- a/internal/sysfs/readfs.go
+++ b/internal/sysfs/readfs.go
@@ -2,13 +2,10 @@ package sysfs
 
 import (
 	"io/fs"
-	"os"
 	"syscall"
-	"time"
 
 	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
 	"github.com/tetratelabs/wazero/internal/fsapi"
-	"github.com/tetratelabs/wazero/sys"
 )
 
 // NewReadFS is used to mask an existing fsapi.FS for reads. Notably, this allows
@@ -21,15 +18,15 @@ func NewReadFS(fs fsapi.FS) fsapi.FS {
 	} else if _, ok = fs.(fsapi.UnimplementedFS); ok {
 		return fs // unimplemented is read-only
 	}
-	return &readFS{fs: fs}
+	return &readFS{fs}
 }
 
 type readFS struct {
-	fs fsapi.FS
+	fsapi.FS
 }
 
 // OpenFile implements the same method as documented on fsapi.FS
-func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
+func (r *readFS) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
 	// TODO: Once the real implementation is complete, move the below to
 	// /RATIONALE.md. Doing this while the type is unstable creates
 	// documentation drift as we expect a lot of reshaping meanwhile.
@@ -49,87 +46,67 @@ func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File,
 	// there isn't a current flag to OR in with that, there may be in the
 	// future. What we do instead is mask the flags about read/write mode and
 	// check if they are the opposite of read or not.
-	switch flag & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) {
-	case os.O_WRONLY, os.O_RDWR:
+	switch flag & (fsapi.O_RDONLY | fsapi.O_WRONLY | fsapi.O_RDWR) {
+	case fsapi.O_WRONLY, fsapi.O_RDWR:
 		if flag&fsapi.O_DIRECTORY != 0 {
 			return nil, experimentalsys.EISDIR
 		}
 		return nil, experimentalsys.ENOSYS
-	default: // os.O_RDONLY (or no flag) so we are ok!
+	default: // fsapi.O_RDONLY (or no flag) so we are ok!
 	}
 
-	f, errno := r.fs.OpenFile(path, flag, perm)
+	f, errno := r.FS.OpenFile(path, flag, perm)
 	if errno != 0 {
 		return nil, errno
 	}
-	return &readFile{f: f}, 0
+	return &readFile{f}, 0
 }
 
-// compile-time check to ensure readFile implements api.File.
-var _ fsapi.File = (*readFile)(nil)
-
-type readFile struct {
-	f fsapi.File
-}
-
-// Dev implements the same method as documented on fsapi.File.
-func (r *readFile) Dev() (uint64, experimentalsys.Errno) {
-	return r.f.Dev()
-}
-
-// Ino implements the same method as documented on fsapi.File.
-func (r *readFile) Ino() (sys.Inode, experimentalsys.Errno) {
-	return r.f.Ino()
-}
-
-// IsDir implements the same method as documented on fsapi.File.
-func (r *readFile) IsDir() (bool, experimentalsys.Errno) {
-	return r.f.IsDir()
+// Mkdir implements the same method as documented on fsapi.FS
+func (r *readFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// IsNonblock implements the same method as documented on fsapi.File.
-func (r *readFile) IsNonblock() bool {
-	return r.f.IsNonblock()
+// Chmod implements the same method as documented on fsapi.FS
+func (r *readFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// SetNonblock implements the same method as documented on fsapi.File.
-func (r *readFile) SetNonblock(enabled bool) experimentalsys.Errno {
-	return r.f.SetNonblock(enabled)
+// Rename implements the same method as documented on fsapi.FS
+func (r *readFS) Rename(from, to string) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// IsAppend implements the same method as documented on fsapi.File.
-func (r *readFile) IsAppend() bool {
-	return r.f.IsAppend()
+// Rmdir implements the same method as documented on fsapi.FS
+func (r *readFS) Rmdir(path string) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// SetAppend implements the same method as documented on fsapi.File.
-func (r *readFile) SetAppend(enabled bool) experimentalsys.Errno {
-	return r.f.SetAppend(enabled)
+// Link implements the same method as documented on fsapi.FS
+func (r *readFS) Link(_, _ string) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// Stat implements the same method as documented on fsapi.File.
-func (r *readFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
-	return r.f.Stat()
+// Symlink implements the same method as documented on fsapi.FS
+func (r *readFS) Symlink(_, _ string) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// Read implements the same method as documented on fsapi.File.
-func (r *readFile) Read(buf []byte) (int, experimentalsys.Errno) {
-	return r.f.Read(buf)
+// Unlink implements the same method as documented on fsapi.FS
+func (r *readFS) Unlink(path string) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// Pread implements the same method as documented on fsapi.File.
-func (r *readFile) Pread(buf []byte, offset int64) (int, experimentalsys.Errno) {
-	return r.f.Pread(buf, offset)
+// Utimens implements the same method as documented on fsapi.FS
+func (r *readFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) experimentalsys.Errno {
+	return experimentalsys.EROFS
 }
 
-// Seek implements the same method as documented on fsapi.File.
-func (r *readFile) Seek(offset int64, whence int) (int64, experimentalsys.Errno) {
-	return r.f.Seek(offset, whence)
-}
+// compile-time check to ensure readFile implements api.File.
+var _ fsapi.File = (*readFile)(nil)
 
-// Readdir implements the same method as documented on fsapi.File.
-func (r *readFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) {
-	return r.f.Readdir(n)
+type readFile struct {
+	fsapi.File
 }
 
 // Write implements the same method as documented on fsapi.File.
@@ -170,68 +147,3 @@ func (r *readFile) writeErr() experimentalsys.Errno {
 	}
 	return experimentalsys.EBADF
 }
-
-// Close implements the same method as documented on fsapi.File.
-func (r *readFile) Close() experimentalsys.Errno {
-	return r.f.Close()
-}
-
-// PollRead implements File.PollRead
-func (r *readFile) PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) {
-	return r.f.PollRead(timeout)
-}
-
-// Lstat implements the same method as documented on fsapi.FS
-func (r *readFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
-	return r.fs.Lstat(path)
-}
-
-// Stat implements the same method as documented on fsapi.FS
-func (r *readFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
-	return r.fs.Stat(path)
-}
-
-// Readlink implements the same method as documented on fsapi.FS
-func (r *readFS) Readlink(path string) (dst string, err experimentalsys.Errno) {
-	return r.fs.Readlink(path)
-}
-
-// Mkdir implements the same method as documented on fsapi.FS
-func (r *readFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
-
-// Chmod implements the same method as documented on fsapi.FS
-func (r *readFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
-
-// Rename implements the same method as documented on fsapi.FS
-func (r *readFS) Rename(from, to string) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
-
-// Rmdir implements the same method as documented on fsapi.FS
-func (r *readFS) Rmdir(path string) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
-
-// Link implements the same method as documented on fsapi.FS
-func (r *readFS) Link(_, _ string) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
-
-// Symlink implements the same method as documented on fsapi.FS
-func (r *readFS) Symlink(_, _ string) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
-
-// Unlink implements the same method as documented on fsapi.FS
-func (r *readFS) Unlink(path string) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
-
-// Utimens implements the same method as documented on fsapi.FS
-func (r *readFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) experimentalsys.Errno {
-	return experimentalsys.EROFS
-}
diff --git a/internal/wasip1/rights.go b/internal/wasip1/rights.go
index 3b6919cf..2ab56c60 100644
--- a/internal/wasip1/rights.go
+++ b/internal/wasip1/rights.go
@@ -41,7 +41,7 @@ const (
 	RIGHT_PATH_CREATE_DIRECTORY
 
 	// RIGHT_PATH_CREATE_FILE when RIGHT_PATH_OPEN is set, the right to invoke
-	// path_open with O_CREATE.
+	// path_open with O_CREAT.
 	RIGHT_PATH_CREATE_FILE
 
 	// RIGHT_PATH_LINK_SOURCE is the right to invoke path_link with the file


Test patch:
diff --git a/imports/wasi_snapshot_preview1/fs_test.go b/imports/wasi_snapshot_preview1/fs_test.go
index a86e5639..80d375df 100644
--- a/imports/wasi_snapshot_preview1/fs_test.go
+++ b/imports/wasi_snapshot_preview1/fs_test.go
@@ -11,7 +11,6 @@ import (
 	"path"
 	"runtime"
 	"strings"
-	"syscall"
 	"testing"
 	gofstest "testing/fstest"
 	"time"
@@ -60,7 +59,7 @@ func Test_fdAllocate(t *testing.T) {
 	preopen := fsc.RootFS()
 	defer r.Close(testCtx)
 
-	fd, errno := fsc.OpenFile(preopen, fileName, os.O_RDWR, 0)
+	fd, errno := fsc.OpenFile(preopen, fileName, fsapi.O_RDWR, 0)
 	require.EqualErrno(t, 0, errno)
 
 	f, ok := fsc.LookupFile(fd)
@@ -138,10 +137,10 @@ func Test_fdClose(t *testing.T) {
 	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
 	preopen := fsc.RootFS()
 
-	fdToClose, errno := fsc.OpenFile(preopen, path1, os.O_RDONLY, 0)
+	fdToClose, errno := fsc.OpenFile(preopen, path1, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
-	fdToKeep, errno := fsc.OpenFile(preopen, path2, os.O_RDONLY, 0)
+	fdToKeep, errno := fsc.OpenFile(preopen, path2, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	// Close
@@ -253,10 +252,10 @@ func Test_fdFdstatGet(t *testing.T) {
 	stdin.File = stdinFile
 
 	// Make this file writeable, to ensure flags read-back correctly.
-	fileFD, errno := fsc.OpenFile(preopen, file, os.O_RDWR, 0)
+	fileFD, errno := fsc.OpenFile(preopen, file, fsapi.O_RDWR, 0)
 	require.EqualErrno(t, 0, errno)
 
-	dirFD, errno := fsc.OpenFile(preopen, dir, os.O_RDONLY, 0)
+	dirFD, errno := fsc.OpenFile(preopen, dir, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	tests := []struct {
@@ -503,12 +502,12 @@ func Test_fdFdstatSetFlags(t *testing.T) {
 	preopen := fsc.RootFS()
 	defer r.Close(testCtx)
 
-	// First, O_CREATE the file with O_APPEND. We use O_EXCL because that
-	// triggers an EEXIST error if called a second time with O_CREATE. Our
-	// logic should clear O_CREATE preventing this.
+	// First, O_CREAT the file with O_APPEND. We use O_EXCL because that
+	// triggers an EEXIST error if called a second time with O_CREAT. Our
+	// logic should clear O_CREAT preventing this.
 	const fileName = "file.txt"
 	// Create the target file.
-	fd, errno := fsc.OpenFile(preopen, fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE|syscall.O_EXCL, 0o600)
+	fd, errno := fsc.OpenFile(preopen, fileName, fsapi.O_RDWR|fsapi.O_APPEND|fsapi.O_CREAT|fsapi.O_EXCL, 0o600)
 	require.EqualErrno(t, 0, errno)
 
 	// Write the initial text to the file.
@@ -615,10 +614,10 @@ func Test_fdFilestatGet(t *testing.T) {
 	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
 	preopen := fsc.RootFS()
 
-	fileFD, errno := fsc.OpenFile(preopen, file, os.O_RDONLY, 0)
+	fileFD, errno := fsc.OpenFile(preopen, file, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
-	dirFD, errno := fsc.OpenFile(preopen, dir, os.O_RDONLY, 0)
+	dirFD, errno := fsc.OpenFile(preopen, dir, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	tests := []struct {
@@ -2157,7 +2156,7 @@ func Test_fdReaddir(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			defer log.Reset()
 
-			fd, errno := fsc.OpenFile(preopen, tc.initialDir, os.O_RDONLY, 0)
+			fd, errno := fsc.OpenFile(preopen, tc.initialDir, fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer fsc.CloseFile(fd) // nolint
 
@@ -2199,7 +2198,7 @@ func Test_fdReaddir_Rewind(t *testing.T) {
 
 	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
 
-	fd, errno := fsc.OpenFile(fsc.RootFS(), ".", os.O_RDONLY, 0)
+	fd, errno := fsc.OpenFile(fsc.RootFS(), ".", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	mem := mod.Memory()
@@ -2248,7 +2247,7 @@ func Test_fdReaddir_Errors(t *testing.T) {
 	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
 	preopen := fsc.RootFS()
 
-	fileFD, errno := fsc.OpenFile(preopen, "animals.txt", os.O_RDONLY, 0)
+	fileFD, errno := fsc.OpenFile(preopen, "animals.txt", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	// Directories are stateful, so we open them during the test.
@@ -2352,7 +2351,7 @@ func Test_fdReaddir_Errors(t *testing.T) {
 
 			// Reset the directory so that tests don't taint each other.
 			if tc.fd == dirFD {
-				dirFD, errno = fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0)
+				dirFD, errno = fsc.OpenFile(preopen, "dir", fsapi.O_RDONLY, 0)
 				require.EqualErrno(t, 0, errno)
 				defer fsc.CloseFile(dirFD) // nolint
 			}
@@ -2465,11 +2464,11 @@ func Test_fdRenumber(t *testing.T) {
 			preopen := fsc.RootFS()
 
 			// Sanity check of the file descriptor assignment.
-			fileFDAssigned, errno := fsc.OpenFile(preopen, "animals.txt", os.O_RDONLY, 0)
+			fileFDAssigned, errno := fsc.OpenFile(preopen, "animals.txt", fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			require.Equal(t, int32(fileFD), fileFDAssigned)
 
-			dirFDAssigned, errno := fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0)
+			dirFDAssigned, errno := fsc.OpenFile(preopen, "dir", fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			require.Equal(t, int32(dirFD), dirFDAssigned)
 
@@ -3605,7 +3604,7 @@ func Test_pathLink(t *testing.T) {
 			uint64(newFd), uint64(destination), uint64(len(destinationName)))
 		require.Contains(t, log.String(), wasip1.ErrnoName(wasip1.ErrnoSuccess))
 
-		f := openFile(t, destinationRealPath, os.O_RDONLY, 0)
+		f := openFile(t, destinationRealPath, fsapi.O_RDONLY, 0)
 		defer f.Close()
 
 		st, errno := f.Stat()
@@ -3927,7 +3926,7 @@ func requireOpenFD(t *testing.T, mod api.Module, path string) int32 {
 	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
 	preopen := fsc.RootFS()
 
-	fd, errno := fsc.OpenFile(preopen, path, os.O_RDONLY, 0)
+	fd, errno := fsc.OpenFile(preopen, path, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	return fd
 }
@@ -4856,14 +4855,14 @@ func Test_pathUnlinkFile_Errors(t *testing.T) {
 }
 
 func requireOpenFile(t *testing.T, tmpDir string, pathName string, data []byte, readOnly bool) (api.Module, int32, *bytes.Buffer, api.Closer) {
-	oflags := os.O_RDWR
+	oflags := fsapi.O_RDWR
 	if readOnly {
-		oflags = os.O_RDONLY
+		oflags = fsapi.O_RDONLY
 	}
 
 	realPath := joinPath(tmpDir, pathName)
 	if data == nil {
-		oflags = os.O_RDONLY
+		oflags = fsapi.O_RDONLY
 		require.NoError(t, os.Mkdir(realPath, 0o700))
 	} else {
 		require.NoError(t, os.WriteFile(realPath, data, 0o600))
@@ -4910,7 +4909,7 @@ func Test_fdReaddir_dotEntryHasARealInode(t *testing.T) {
 		uint64(sys.FdPreopen), uint64(0), uint64(len(readDirTarget)))
 
 	// Open the directory, before writing files!
-	fd, errno := fsc.OpenFile(preopen, readDirTarget, os.O_RDONLY, 0)
+	fd, errno := fsc.OpenFile(preopen, readDirTarget, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	// get the real inode of the current directory
@@ -4968,11 +4967,11 @@ func Test_fdReaddir_opened_file_written(t *testing.T) {
 		uint64(sys.FdPreopen), uint64(0), uint64(len(dirName)))
 
 	// Open the directory, before writing files!
-	dirFD, errno := fsc.OpenFile(preopen, dirName, os.O_RDONLY, 0)
+	dirFD, errno := fsc.OpenFile(preopen, dirName, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	// Then write a file to the directory.
-	f := openFile(t, joinPath(dirPath, "file"), os.O_CREATE, 0)
+	f := openFile(t, joinPath(dirPath, "file"), fsapi.O_CREAT, 0)
 	defer f.Close()
 
 	// get the real inode of the current directory
@@ -5021,7 +5020,7 @@ func joinPath(dirName, baseName string) string {
 	return path.Join(dirName, baseName)
 }
 
-func openFile(t *testing.T, path string, flag int, perm fs.FileMode) fsapi.File {
+func openFile(t *testing.T, path string, flag fsapi.Oflag, perm fs.FileMode) fsapi.File {
 	f, errno := sysfs.OpenOSFile(path, flag, perm)
 	require.EqualErrno(t, 0, errno)
 	return f
diff --git a/imports/wasi_snapshot_preview1/fs_unit_test.go b/imports/wasi_snapshot_preview1/fs_unit_test.go
index c5a9c8e4..a6550608 100644
--- a/imports/wasi_snapshot_preview1/fs_unit_test.go
+++ b/imports/wasi_snapshot_preview1/fs_unit_test.go
@@ -2,7 +2,6 @@ package wasi_snapshot_preview1
 
 import (
 	"os"
-	"syscall"
 	"testing"
 
 	"github.com/tetratelabs/wazero/internal/fsapi"
@@ -110,7 +109,7 @@ func Test_maxDirents(t *testing.T) {
 var (
 	testDirents = func() []fsapi.Dirent {
 		dPath := "dir"
-		d, errno := sysfs.OpenFSFile(fstest.FS, dPath, syscall.O_RDONLY, 0)
+		d, errno := sysfs.OpenFSFile(fstest.FS, dPath, fsapi.O_RDONLY, 0)
 		if errno != 0 {
 			panic(errno)
 		}
@@ -208,16 +207,16 @@ func Test_openFlags(t *testing.T) {
 		name                      string
 		dirflags, oflags, fdflags uint16
 		rights                    uint32
-		expectedOpenFlags         int
+		expectedOpenFlags         fsapi.Oflag
 	}{
 		{
 			name:              "oflags=0",
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDONLY,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDONLY,
 		},
 		{
 			name:              "oflags=O_CREAT",
 			oflags:            wasip1.O_CREAT,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDWR | syscall.O_CREAT,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDWR | fsapi.O_CREAT,
 		},
 		{
 			name:              "oflags=O_DIRECTORY",
@@ -227,42 +226,42 @@ func Test_openFlags(t *testing.T) {
 		{
 			name:              "oflags=O_EXCL",
 			oflags:            wasip1.O_EXCL,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDONLY | syscall.O_EXCL,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDONLY | fsapi.O_EXCL,
 		},
 		{
 			name:              "oflags=O_TRUNC",
 			oflags:            wasip1.O_TRUNC,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDWR | syscall.O_TRUNC,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDWR | fsapi.O_TRUNC,
 		},
 		{
 			name:              "fdflags=FD_APPEND",
 			fdflags:           wasip1.FD_APPEND,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDWR | syscall.O_APPEND,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDWR | fsapi.O_APPEND,
 		},
 		{
 			name:              "oflags=O_TRUNC|O_CREAT",
 			oflags:            wasip1.O_TRUNC | wasip1.O_CREAT,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDWR | syscall.O_TRUNC | syscall.O_CREAT,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDWR | fsapi.O_TRUNC | fsapi.O_CREAT,
 		},
 		{
 			name:              "dirflags=LOOKUP_SYMLINK_FOLLOW",
 			dirflags:          wasip1.LOOKUP_SYMLINK_FOLLOW,
-			expectedOpenFlags: syscall.O_RDONLY,
+			expectedOpenFlags: fsapi.O_RDONLY,
 		},
 		{
 			name:              "rights=FD_READ",
 			rights:            wasip1.RIGHT_FD_READ,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDONLY,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDONLY,
 		},
 		{
 			name:              "rights=FD_WRITE",
 			rights:            wasip1.RIGHT_FD_WRITE,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_WRONLY,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_WRONLY,
 		},
 		{
 			name:              "rights=FD_READ|FD_WRITE",
 			rights:            wasip1.RIGHT_FD_READ | wasip1.RIGHT_FD_WRITE,
-			expectedOpenFlags: fsapi.O_NOFOLLOW | syscall.O_RDWR,
+			expectedOpenFlags: fsapi.O_NOFOLLOW | fsapi.O_RDWR,
 		},
 	}
 
diff --git a/imports/wasi_snapshot_preview1/wasi_bench_test.go b/imports/wasi_snapshot_preview1/wasi_bench_test.go
index b03f9944..41dd3967 100644
--- a/imports/wasi_snapshot_preview1/wasi_bench_test.go
+++ b/imports/wasi_snapshot_preview1/wasi_bench_test.go
@@ -9,6 +9,7 @@ import (
 	"github.com/tetratelabs/wazero"
 	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
+	"github.com/tetratelabs/wazero/internal/fsapi"
 	"github.com/tetratelabs/wazero/internal/sys"
 	"github.com/tetratelabs/wazero/internal/testing/proxy"
 	"github.com/tetratelabs/wazero/internal/wasip1"
@@ -198,7 +199,7 @@ func Benchmark_fdReaddir(b *testing.B) {
 
 				// Open the root directory as a file-descriptor.
 				fsc := mod.(*wasm.ModuleInstance).Sys.FS()
-				fd, errno := fsc.OpenFile(fsc.RootFS(), ".", os.O_RDONLY, 0)
+				fd, errno := fsc.OpenFile(fsc.RootFS(), ".", fsapi.O_RDONLY, 0)
 				if errno != 0 {
 					b.Fatal(errno)
 				}
@@ -307,7 +308,7 @@ func Benchmark_pathFilestat(b *testing.B) {
 			fd := sys.FdPreopen
 			if bc.fd != sys.FdPreopen {
 				fsc := mod.(*wasm.ModuleInstance).Sys.FS()
-				fd, errno := fsc.OpenFile(fsc.RootFS(), "zig", os.O_RDONLY, 0)
+				fd, errno := fsc.OpenFile(fsc.RootFS(), "zig", fsapi.O_RDONLY, 0)
 				if errno != 0 {
 					b.Fatal(errno)
 				}
diff --git a/internal/gojs/testdata/writefs/main.go b/internal/gojs/testdata/writefs/main.go
index 1766044e..d06b95cc 100644
--- a/internal/gojs/testdata/writefs/main.go
+++ b/internal/gojs/testdata/writefs/main.go
@@ -24,6 +24,7 @@ func Main() {
 	// Create a test file in that directory
 	file := path.Join(dir, "file")
 	file1 := path.Join(os.TempDir(), "file1")
+
 	if err := os.WriteFile(file, []byte{}, 0o600); err != nil {
 		log.Panicln(err)
 		return
diff --git a/internal/sys/fs_test.go b/internal/sys/fs_test.go
index b8b7a1b7..b3e58140 100644
--- a/internal/sys/fs_test.go
+++ b/internal/sys/fs_test.go
@@ -105,10 +105,10 @@ func TestFSContext_CloseFile(t *testing.T) {
 	fsc := c.fsc
 	defer fsc.Close()
 
-	fdToClose, errno := fsc.OpenFile(testFS, "empty.txt", os.O_RDONLY, 0)
+	fdToClose, errno := fsc.OpenFile(testFS, "empty.txt", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
-	fdToKeep, errno := fsc.OpenFile(testFS, "test.txt", os.O_RDONLY, 0)
+	fdToKeep, errno := fsc.OpenFile(testFS, "test.txt", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	// Close
@@ -165,7 +165,7 @@ func TestContext_Close(t *testing.T) {
 	// Verify base case
 	require.Equal(t, 1+FdPreopen, int32(fsc.openedFiles.Len()))
 
-	_, errno := fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0)
+	_, errno := fsc.OpenFile(testFS, "foo", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	require.Equal(t, 2+FdPreopen, int32(fsc.openedFiles.Len()))
 
@@ -190,7 +190,7 @@ func TestContext_Close_Error(t *testing.T) {
 	fsc := c.fsc
 
 	// open another file
-	_, errno := fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0)
+	_, errno := fsc.OpenFile(testFS, "foo", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 
 	// arbitrary errors coerce to EIO
@@ -216,7 +216,7 @@ func TestFSContext_Renumber(t *testing.T) {
 	defer fsc.Close()
 
 	for _, toFd := range []int32{10, 100, 100} {
-		fromFd, errno := fsc.OpenFile(dirFS, dirName, os.O_RDONLY, 0)
+		fromFd, errno := fsc.OpenFile(dirFS, dirName, fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 
 		prevDirFile, ok := fsc.LookupFile(fromFd)
@@ -405,7 +405,7 @@ func TestDirentCache_Read(t *testing.T) {
 	for _, tt := range tests {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
-			fd, errno := fsc.OpenFile(fsc.RootFS(), tc.initialDir, os.O_RDONLY, 0)
+			fd, errno := fsc.OpenFile(fsc.RootFS(), tc.initialDir, fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer fsc.CloseFile(fd) // nolint
 			f, _ := fsc.LookupFile(fd)
@@ -436,7 +436,7 @@ func TestDirentCache_ReadNewFile(t *testing.T) {
 	fsc := c.fsc
 	defer fsc.Close()
 
-	fd, errno := fsc.OpenFile(fsc.RootFS(), ".", os.O_RDONLY, 0)
+	fd, errno := fsc.OpenFile(fsc.RootFS(), ".", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	defer fsc.CloseFile(fd) // nolint
 	f, _ := fsc.LookupFile(fd)
diff --git a/internal/sysfs/adapter_test.go b/internal/sysfs/adapter_test.go
index 6c6ce917..9cd77660 100644
--- a/internal/sysfs/adapter_test.go
+++ b/internal/sysfs/adapter_test.go
@@ -107,7 +107,7 @@ func TestAdapt_Open_Read(t *testing.T) {
 	testOpen_Read(t, testFS, statSetsIno(), runtime.GOOS != "windows")
 
 	t.Run("path outside root invalid", func(t *testing.T) {
-		_, err := testFS.OpenFile("../foo", os.O_RDONLY, 0)
+		_, err := testFS.OpenFile("../foo", fsapi.O_RDONLY, 0)
 
 		// fsapi.FS doesn't allow relative path lookups
 		require.EqualErrno(t, experimentalsys.EINVAL, err)
@@ -137,7 +137,7 @@ func TestAdapt_Stat(t *testing.T) {
 	testStat(t, testFS)
 }
 
-// hackFS cheats the api.FS contract by opening for write (os.O_RDWR).
+// hackFS cheats the api.FS contract by opening for write (fsapi.O_RDWR).
 //
 // Until we have an alternate public interface for filesystems, some users will
 // rely on this. Via testing, we ensure we don't accidentally break them.
diff --git a/internal/sysfs/bench_test.go b/internal/sysfs/bench_test.go
index fd7e1a28..b5c34fd1 100644
--- a/internal/sysfs/bench_test.go
+++ b/internal/sysfs/bench_test.go
@@ -9,10 +9,11 @@ import (
 	"testing"
 
 	"github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/internal/fsapi"
 )
 
 func BenchmarkFsFileUtimesNs(b *testing.B) {
-	f, errno := OpenOSFile(path.Join(b.TempDir(), "file"), syscall.O_CREAT, 0)
+	f, errno := OpenOSFile(path.Join(b.TempDir(), "file"), fsapi.O_CREAT, 0)
 	if errno != 0 {
 		b.Fatal(errno)
 	}
@@ -56,7 +57,7 @@ func BenchmarkFsFileRead(b *testing.B) {
 
 		b.Run(bc.name, func(b *testing.B) {
 			name := "wazero.txt"
-			f, errno := OpenFSFile(bc.fs, name, syscall.O_RDONLY, 0)
+			f, errno := OpenFSFile(bc.fs, name, fsapi.O_RDONLY, 0)
 			if errno != 0 {
 				b.Fatal(errno)
 			}
diff --git a/internal/sysfs/dir_test.go b/internal/sysfs/dir_test.go
index 5aafb89d..ed0cd20f 100644
--- a/internal/sysfs/dir_test.go
+++ b/internal/sysfs/dir_test.go
@@ -6,7 +6,6 @@ import (
 	"os"
 	"runtime"
 	"sort"
-	"syscall"
 	"testing"
 
 	"github.com/tetratelabs/wazero/experimental/sys"
@@ -42,7 +41,7 @@ func TestFSFileReaddir(t *testing.T) {
 		tc := tc
 
 		t.Run(tc.name, func(t *testing.T) {
-			dotF, errno := sysfs.OpenFSFile(tc.fs, ".", syscall.O_RDONLY, 0)
+			dotF, errno := sysfs.OpenFSFile(tc.fs, ".", fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer dotF.Close()
 
@@ -76,7 +75,7 @@ func TestFSFileReaddir(t *testing.T) {
 				require.EqualErrno(t, sys.EBADF, errno)
 			})
 
-			fileF, errno := sysfs.OpenFSFile(tc.fs, "empty.txt", syscall.O_RDONLY, 0)
+			fileF, errno := sysfs.OpenFSFile(tc.fs, "empty.txt", fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer fileF.Close()
 
@@ -85,7 +84,7 @@ func TestFSFileReaddir(t *testing.T) {
 				require.EqualErrno(t, sys.EBADF, errno)
 			})
 
-			dirF, errno := sysfs.OpenFSFile(tc.fs, "dir", syscall.O_RDONLY, 0)
+			dirF, errno := sysfs.OpenFSFile(tc.fs, "dir", fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer dirF.Close()
 
@@ -124,7 +123,7 @@ func TestFSFileReaddir(t *testing.T) {
 				require.EqualErrno(t, 0, errno)
 			})
 
-			subdirF, errno := sysfs.OpenFSFile(tc.fs, "sub", syscall.O_RDONLY, 0)
+			subdirF, errno := sysfs.OpenFSFile(tc.fs, "sub", fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer subdirF.Close()
 
diff --git a/internal/sysfs/dirfs_test.go b/internal/sysfs/dirfs_test.go
index 632efcd6..7f3de3ba 100644
--- a/internal/sysfs/dirfs_test.go
+++ b/internal/sysfs/dirfs_test.go
@@ -22,20 +22,20 @@ func TestNewDirFS(t *testing.T) {
 	testFS := NewDirFS(".")
 
 	// Guest can look up /
-	f, errno := testFS.OpenFile("/", os.O_RDONLY, 0)
+	f, errno := testFS.OpenFile("/", fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	require.EqualErrno(t, 0, f.Close())
 
 	t.Run("host path not found", func(t *testing.T) {
 		testFS := NewDirFS("a")
-		_, errno = testFS.OpenFile(".", os.O_RDONLY, 0)
+		_, errno = testFS.OpenFile(".", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, sys.ENOENT, errno)
 	})
 	t.Run("host path not a directory", func(t *testing.T) {
 		arg0 := os.Args[0] // should be safe in scratch tests which don't have the source mounted.
 
 		testFS := NewDirFS(arg0)
-		d, errno := testFS.OpenFile(".", os.O_RDONLY, 0)
+		d, errno := testFS.OpenFile(".", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		_, errno = d.Readdir(-1)
 		require.EqualErrno(t, sys.EBADF, errno)
@@ -696,7 +696,7 @@ func TestDirFS_OpenFile(t *testing.T) {
 	testOpen_O_RDWR(t, tmpDir, testFS)
 
 	t.Run("path outside root valid", func(t *testing.T) {
-		_, err := testFS.OpenFile("../foo", os.O_RDONLY, 0)
+		_, err := testFS.OpenFile("../foo", fsapi.O_RDONLY, 0)
 
 		// fsapi.FS allows relative path lookups
 		require.True(t, errors.Is(err, fs.ErrNotExist))
@@ -731,7 +731,7 @@ func TestDirFS_Readdir(t *testing.T) {
 	require.EqualErrno(t, 0, errno)
 
 	// Open the empty directory
-	dirFile, errno := testFS.OpenFile(readDirTarget, os.O_RDONLY, 0)
+	dirFile, errno := testFS.OpenFile(readDirTarget, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	defer dirFile.Close()
 
diff --git a/internal/sysfs/file_test.go b/internal/sysfs/file_test.go
index 16c5f415..6023fa9b 100644
--- a/internal/sysfs/file_test.go
+++ b/internal/sysfs/file_test.go
@@ -7,7 +7,6 @@ import (
 	"os"
 	"path"
 	"runtime"
-	"syscall"
 	"testing"
 	gofstest "testing/fstest"
 	"time"
@@ -55,7 +54,7 @@ func TestRegularFileSetNonblock(t *testing.T) {
 	defer r.Close()
 	defer w.Close()
 
-	rF := newOsFile("", syscall.O_RDONLY, 0, r)
+	rF := newOsFile("", fsapi.O_RDONLY, 0, r)
 
 	errno := rF.SetNonblock(true)
 	require.EqualErrno(t, 0, errno)
@@ -126,7 +125,7 @@ func TestFileSetAppend(t *testing.T) {
 	require.NoError(t, os.WriteFile(fPath, []byte("0123456789"), 0o600))
 
 	// Open without APPEND.
-	f, errno := OpenOSFile(fPath, os.O_RDWR, 0o600)
+	f, errno := OpenOSFile(fPath, fsapi.O_RDWR, 0o600)
 	require.EqualErrno(t, 0, errno)
 	require.False(t, f.IsAppend())
 
@@ -187,7 +186,7 @@ func TestFileIno(t *testing.T) {
 		tc := tc
 
 		t.Run(tc.name, func(t *testing.T) {
-			d, errno := OpenFSFile(tc.fs, ".", syscall.O_RDONLY, 0)
+			d, errno := OpenFSFile(tc.fs, ".", fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer d.Close()
 
@@ -201,7 +200,7 @@ func TestFileIno(t *testing.T) {
 	}
 
 	t.Run("OS", func(t *testing.T) {
-		d, errno := OpenOSFile(tmpDir, syscall.O_RDONLY, 0)
+		d, errno := OpenOSFile(tmpDir, fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer d.Close()
 
@@ -245,7 +244,7 @@ func TestFileIsDir(t *testing.T) {
 
 		t.Run(tc.name, func(t *testing.T) {
 			t.Run("file", func(t *testing.T) {
-				f, errno := OpenFSFile(tc.fs, wazeroFile, syscall.O_RDONLY, 0)
+				f, errno := OpenFSFile(tc.fs, wazeroFile, fsapi.O_RDONLY, 0)
 				require.EqualErrno(t, 0, errno)
 				defer f.Close()
 
@@ -255,7 +254,7 @@ func TestFileIsDir(t *testing.T) {
 			})
 
 			t.Run("dir", func(t *testing.T) {
-				d, errno := OpenFSFile(tc.fs, ".", syscall.O_RDONLY, 0)
+				d, errno := OpenFSFile(tc.fs, ".", fsapi.O_RDONLY, 0)
 				require.EqualErrno(t, 0, errno)
 				defer d.Close()
 
@@ -267,7 +266,7 @@ func TestFileIsDir(t *testing.T) {
 	}
 
 	t.Run("OS dir", func(t *testing.T) {
-		d, errno := OpenOSFile(t.TempDir(), syscall.O_RDONLY, 0)
+		d, errno := OpenOSFile(t.TempDir(), fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer d.Close()
 
@@ -295,7 +294,7 @@ func TestFileReadAndPread(t *testing.T) {
 		tc := tc
 
 		t.Run(tc.name, func(t *testing.T) {
-			f, errno := OpenFSFile(tc.fs, wazeroFile, syscall.O_RDONLY, 0)
+			f, errno := OpenFSFile(tc.fs, wazeroFile, fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer f.Close()
 
@@ -385,7 +384,7 @@ func TestFileRead_empty(t *testing.T) {
 		tc := tc
 
 		t.Run(tc.name, func(t *testing.T) {
-			f, errno := OpenFSFile(tc.fs, emptyFile, syscall.O_RDONLY, 0)
+			f, errno := OpenFSFile(tc.fs, emptyFile, fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer f.Close()
 
@@ -418,7 +417,7 @@ func TestFilePread_Unsupported(t *testing.T) {
 	embedFS, err := fs.Sub(testdata, "testdata")
 	require.NoError(t, err)
 
-	f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, syscall.O_RDONLY, 0)
+	f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	defer f.Close()
 
@@ -432,7 +431,7 @@ func TestFileRead_Errors(t *testing.T) {
 	path := path.Join(t.TempDir(), emptyFile)
 
 	// Open the file write-only
-	flag := syscall.O_WRONLY | syscall.O_CREAT
+	flag := fsapi.O_WRONLY | fsapi.O_CREAT
 	f := requireOpenFile(t, path, flag, 0o600)
 	defer f.Close()
 	buf := make([]byte, 5)
@@ -474,16 +473,16 @@ func TestFileSeek(t *testing.T) {
 		openFile func(string) (fsapi.File, experimentalsys.Errno)
 	}{
 		{name: "fsFile os.DirFS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) {
-			return OpenFSFile(dirFS, name, syscall.O_RDONLY, 0)
+			return OpenFSFile(dirFS, name, fsapi.O_RDONLY, 0)
 		}},
 		{name: "fsFile embed.api.FS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) {
-			return OpenFSFile(embedFS, name, syscall.O_RDONLY, 0)
+			return OpenFSFile(embedFS, name, fsapi.O_RDONLY, 0)
 		}},
 		{name: "fsFile fstest.MapFS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) {
-			return OpenFSFile(mapFS, name, syscall.O_RDONLY, 0)
+			return OpenFSFile(mapFS, name, fsapi.O_RDONLY, 0)
 		}},
 		{name: "osFile", openFile: func(name string) (fsapi.File, experimentalsys.Errno) {
-			return OpenOSFile(path.Join(tmpDir, name), syscall.O_RDONLY, 0o666)
+			return OpenOSFile(path.Join(tmpDir, name), fsapi.O_RDONLY, 0o666)
 		}},
 	}
 
@@ -592,7 +591,7 @@ func TestFileSeek_empty(t *testing.T) {
 		tc := tc
 
 		t.Run(tc.name, func(t *testing.T) {
-			f, errno := OpenFSFile(tc.fs, emptyFile, syscall.O_RDONLY, 0)
+			f, errno := OpenFSFile(tc.fs, emptyFile, fsapi.O_RDONLY, 0)
 			require.EqualErrno(t, 0, errno)
 			defer f.Close()
 
@@ -615,7 +614,7 @@ func TestFileSeek_Unsupported(t *testing.T) {
 	embedFS, err := fs.Sub(testdata, "testdata")
 	require.NoError(t, err)
 
-	f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, syscall.O_RDONLY, 0)
+	f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	defer f.Close()
 
@@ -627,7 +626,7 @@ func TestFileWriteAndPwrite(t *testing.T) {
 	// fsapi.FS doesn't support writes, and there is no other built-in
 	// implementation except os.File.
 	path := path.Join(t.TempDir(), wazeroFile)
-	f := requireOpenFile(t, path, syscall.O_RDWR|os.O_CREATE, 0o600)
+	f := requireOpenFile(t, path, fsapi.O_RDWR|fsapi.O_CREAT, 0o600)
 	defer f.Close()
 
 	text := "wazero"
@@ -680,7 +679,7 @@ func TestFileWrite_empty(t *testing.T) {
 	// fsapi.FS doesn't support writes, and there is no other built-in
 	// implementation except os.File.
 	path := path.Join(t.TempDir(), emptyFile)
-	f := requireOpenFile(t, path, syscall.O_RDWR|os.O_CREATE, 0o600)
+	f := requireOpenFile(t, path, fsapi.O_RDWR|fsapi.O_CREAT, 0o600)
 	defer f.Close()
 
 	tests := []struct {
@@ -720,8 +719,8 @@ func TestFileWrite_Unsupported(t *testing.T) {
 	embedFS, err := fs.Sub(testdata, "testdata")
 	require.NoError(t, err)
 
-	// Use syscall.O_RDWR so that it fails due to type not flags
-	f, errno := OpenFSFile(&maskFS{embedFS}, wazeroFile, syscall.O_RDWR, 0)
+	// Use fsapi.O_RDWR so that it fails due to type not flags
+	f, errno := OpenFSFile(&maskFS{embedFS}, wazeroFile, fsapi.O_RDWR, 0)
 	require.EqualErrno(t, 0, errno)
 	defer f.Close()
 
@@ -757,7 +756,7 @@ func TestFileWrite_Errors(t *testing.T) {
 	require.NoError(t, of.Close())
 
 	// Open the file read-only
-	flag := syscall.O_RDONLY
+	flag := fsapi.O_RDONLY
 	f := requireOpenFile(t, path, flag, 0o600)
 	defer f.Close()
 	buf := []byte("wazero")
@@ -800,12 +799,12 @@ func TestFileDatasync_NoError(t *testing.T) {
 
 func testSync_NoError(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) {
 	roPath := "file_test.go"
-	ro, errno := OpenFSFile(embedFS, roPath, syscall.O_RDONLY, 0)
+	ro, errno := OpenFSFile(embedFS, roPath, fsapi.O_RDONLY, 0)
 	require.EqualErrno(t, 0, errno)
 	defer ro.Close()
 
 	rwPath := path.Join(t.TempDir(), "datasync")
-	rw, errno := OpenOSFile(rwPath, syscall.O_CREAT|syscall.O_RDWR, 0o600)
+	rw, errno := OpenOSFile(rwPath, fsapi.O_CREAT|fsapi.O_RDWR, 0o600)
 	require.EqualErrno(t, 0, errno)
 	defer rw.Close()
 
@@ -841,7 +840,7 @@ func TestFileDatasync(t *testing.T) {
 func testSync(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) {
 	// Even though it is invalid, try to sync a directory
 	dPath := t.TempDir()
-	d := requireOpenFile(t, dPath, syscall.O_RDONLY, 0)
+	d := requireOpenFile(t, dPath, fsapi.O_RDONLY, 0)
 	defer d.Close()
 
 	errno := sync(d)
@@ -849,7 +848,7 @@ func testSync(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) {
 
 	fPath := path.Join(dPath, t.Name())
 
-	f := requireOpenFile(t, fPath, syscall.O_RDWR|os.O_CREATE, 0o600)
+	f := requireOpenFile(t, fPath, fsapi.O_RDWR|fsapi.O_CREAT, 0o600)
 	defer f.Close()
 
 	expected := "hello world!"
@@ -1045,7 +1044,7 @@ func TestNewStdioFile(t *testing.T) {
 
 func testEBADFIfDirClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool {
 	return t.Run("EBADF if dir closed", func(t *testing.T) {
-		d := requireOpenFile(t, t.TempDir(), syscall.O_RDONLY, 0o755)
+		d := requireOpenFile(t, t.TempDir(), fsapi.O_RDONLY, 0o755)
 
 		// close the directory underneath
 		require.EqualErrno(t, 0, d.Close())
@@ -1069,7 +1068,7 @@ func testEBADFIfFileClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Err
 
 func testEISDIR(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool {
 	return t.Run("EISDIR if directory", func(t *testing.T) {
-		f := requireOpenFile(t, os.TempDir(), syscall.O_RDONLY|fsapi.O_DIRECTORY, 0o666)
+		f := requireOpenFile(t, os.TempDir(), fsapi.O_RDONLY|fsapi.O_DIRECTORY, 0o666)
 		defer f.Close()
 
 		require.EqualErrno(t, experimentalsys.EISDIR, fn(f))
@@ -1078,13 +1077,13 @@ func testEISDIR(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool {
 
 func openForWrite(t *testing.T, path string, content []byte) fsapi.File {
 	require.NoError(t, os.WriteFile(path, content, 0o0666))
-	f := requireOpenFile(t, path, syscall.O_RDWR, 0o666)
+	f := requireOpenFile(t, path, fsapi.O_RDWR, 0o666)
 	_, errno := f.Write(content)
 	require.EqualErrno(t, 0, errno)
 	return f
 }
 
-func requireOpenFile(t *testing.T, path string, flag int, perm fs.FileMode) fsapi.File {
+func requireOpenFile(t *testing.T, path string, flag fsapi.Oflag, perm fs.FileMode) fsapi.File {
 	f, errno := OpenOSFile(path, flag, perm)
 	require.EqualErrno(t, 0, errno)
 	return f
diff --git a/internal/sysfs/futimens_test.go b/internal/sysfs/futimens_test.go
index 8292147e..3cd22108 100644
--- a/internal/sysfs/futimens_test.go
+++ b/internal/sysfs/futimens_test.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/internal/fsapi"
 	"github.com/tetratelabs/wazero/internal/platform"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
@@ -160,9 +161,9 @@ func testUtimens(t *testing.T, futimes bool) {
 					}
 					require.EqualErrno(t, 0, errno)
 				} else {
-					flag := syscall.O_RDWR
+					flag := fsapi.O_RDWR
 					if path == dir {
-						flag = syscall.O_RDONLY
+						flag = fsapi.O_RDONLY
 						if runtime.GOOS == "windows" {
 							// windows requires O_RDWR, which is invalid for directories
 							t.Skip("windows cannot update timestamps on a dir")
diff --git a/internal/sysfs/oflag_test.go b/internal/sysfs/oflag_test.go
new file mode 100644
index 00000000..e1c9beb3
--- /dev/null
+++ b/internal/sysfs/oflag_test.go
@@ -0,0 +1,55 @@
+package sysfs
+
+import (
+	"os"
+	"testing"
+
+	"github.com/tetratelabs/wazero/internal/fsapi"
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+// Test_toOsOpenFlag doesn't use subtests to reduce volume of verbose output,
+// and in recognition we have tens of thousands of tests, which can hit IDE
+// limits.
+func Test_toOsOpenFlag(t *testing.T) {
+	tests := []struct {
+		name     string
+		flag     fsapi.Oflag
+		expected int
+	}{
+		{name: "O_RDONLY", flag: fsapi.O_RDONLY, expected: os.O_RDONLY},
+		{name: "O_RDWR", flag: fsapi.O_RDWR, expected: os.O_RDWR},
+		{name: "O_WRONLY", flag: fsapi.O_WRONLY, expected: os.O_WRONLY},
+		{name: "O_CREAT", flag: fsapi.O_CREAT, expected: os.O_RDONLY | os.O_CREATE},
+		{name: "O_APPEND", flag: fsapi.O_APPEND, expected: os.O_RDONLY | os.O_APPEND},
+		{
+			name:     "all portable",
+			flag:     fsapi.O_RDWR | fsapi.O_APPEND | fsapi.O_CREAT | fsapi.O_EXCL | fsapi.O_SYNC | fsapi.O_TRUNC,
+			expected: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC,
+		},
+		{name: "undefined", flag: 1 << 15, expected: os.O_RDONLY},
+	}
+
+	for _, tc := range tests {
+		require.Equal(t, tc.expected, toOsOpenFlag(tc.flag), tc.name)
+	}
+
+	// Tests any supported syscall flags
+	for n, f := range map[string]fsapi.Oflag{
+		"O_DIRECTORY": fsapi.O_DIRECTORY,
+		"O_DSYNC":     fsapi.O_DSYNC,
+		"O_NOFOLLOW":  fsapi.O_NOFOLLOW,
+		"O_NONBLOCK":  fsapi.O_NONBLOCK,
+		"O_RSYNC":     fsapi.O_RSYNC,
+	} {
+		if supportedSyscallOflag&f == 0 {
+			continue
+		}
+		require.NotEqual(t, 0, toOsOpenFlag(f), n)
+	}
+
+	// Example of a flag that can be or'd into O_RDONLY even if not
+	// currently supported in WASI or GOOS=js
+	const O_NOATIME = fsapi.Oflag(0x40000)
+	require.Zero(t, 0, toOsOpenFlag(O_NOATIME))
+}
diff --git a/internal/sysfs/open_file_test.go b/internal/sysfs/open_file_test.go
index ab1d3a66..3381e6ba 100644
--- a/internal/sysfs/open_file_test.go
+++ b/internal/sysfs/open_file_test.go
@@ -4,7 +4,6 @@ import (
 	"os"
 	path "path/filepath"
 	"runtime"
-	"syscall"
 	"testing"
 
 	"github.com/tetratelabs/wazero/experimental/sys"
@@ -20,7 +19,7 @@ func TestOpenFile(t *testing.T) {
 		err := os.MkdirAll(path, 0o700)
 		require.NoError(t, err)
 
-		f, errno := OpenFile(path+"/", os.O_RDONLY, 0)
+		f, errno := OpenFile(path+"/", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		require.NoError(t, f.Close())
 	})
@@ -28,7 +27,7 @@ func TestOpenFile(t *testing.T) {
 	// from os.TestDirFSPathsValid
 	if runtime.GOOS != "windows" {
 		t.Run("strange name", func(t *testing.T) {
-			f, errno := OpenFile(path.Join(tmpDir, `e:xperi\ment.txt`), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
+			f, errno := OpenFile(path.Join(tmpDir, `e:xperi\ment.txt`), fsapi.O_WRONLY|fsapi.O_CREAT|fsapi.O_TRUNC, 0o600)
 			require.EqualErrno(t, 0, errno)
 			require.NoError(t, f.Close())
 		})
@@ -47,23 +46,23 @@ func TestOpenFile_Errors(t *testing.T) {
 		err = os.WriteFile(nestedFile, nil, 0o700)
 		require.NoError(t, err)
 
-		_, errno := OpenFile(nestedFile+"/", os.O_RDONLY, 0)
+		_, errno := OpenFile(nestedFile+"/", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, sys.ENOTDIR, errno)
 	})
 
 	t.Run("not found must be ENOENT", func(t *testing.T) {
-		_, errno := OpenFile(path.Join(tmpDir, "not-really-exist.txt"), os.O_RDONLY, 0o600)
+		_, errno := OpenFile(path.Join(tmpDir, "not-really-exist.txt"), fsapi.O_RDONLY, 0o600)
 		require.EqualErrno(t, sys.ENOENT, errno)
 	})
 
 	// This is the same as https://github.com/ziglang/zig/blob/d24ebf1d12cf66665b52136a2807f97ff021d78d/lib/std/os/test.zig#L105-L112
 	t.Run("try creating on existing file must be EEXIST", func(t *testing.T) {
 		filepath := path.Join(tmpDir, "file.txt")
-		f, errno := OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666)
+		f, errno := OpenFile(filepath, fsapi.O_RDWR|fsapi.O_CREAT|fsapi.O_EXCL, 0o666)
 		require.EqualErrno(t, 0, errno)
 		defer f.Close()
 
-		_, err := OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666)
+		_, err := OpenFile(filepath, fsapi.O_RDWR|fsapi.O_CREAT|fsapi.O_EXCL, 0o666)
 		require.EqualErrno(t, sys.EEXIST, err)
 	})
 
@@ -71,7 +70,7 @@ func TestOpenFile_Errors(t *testing.T) {
 		path := path.Join(tmpDir, "file")
 		require.NoError(t, os.WriteFile(path, nil, 0o600))
 
-		f := requireOpenFile(t, path, os.O_RDONLY, 0)
+		f := requireOpenFile(t, path, fsapi.O_RDONLY, 0)
 		defer f.Close()
 
 		_, errno := f.Write([]byte{1, 2, 3, 4})
@@ -82,7 +81,7 @@ func TestOpenFile_Errors(t *testing.T) {
 		path := path.Join(tmpDir, "diragain")
 		require.NoError(t, os.Mkdir(path, 0o755))
 
-		f := requireOpenFile(t, path, os.O_RDONLY, 0)
+		f := requireOpenFile(t, path, fsapi.O_RDONLY, 0)
 		defer f.Close()
 
 		_, errno := f.Write([]byte{1, 2, 3, 4})
@@ -105,10 +104,10 @@ func TestOpenFile_Errors(t *testing.T) {
 	})
 
 	t.Run("opening a directory writeable is EISDIR", func(t *testing.T) {
-		_, errno := OpenFile(tmpDir, fsapi.O_DIRECTORY|syscall.O_WRONLY, 0o0666)
+		_, errno := OpenFile(tmpDir, fsapi.O_DIRECTORY|fsapi.O_WRONLY, 0o0666)
 		require.EqualErrno(t, sys.EISDIR, errno)
 
-		_, errno = OpenFile(tmpDir, fsapi.O_DIRECTORY|syscall.O_WRONLY, 0o0666)
+		_, errno = OpenFile(tmpDir, fsapi.O_DIRECTORY|fsapi.O_WRONLY, 0o0666)
 		require.EqualErrno(t, sys.EISDIR, errno)
 	})
 }
diff --git a/internal/sysfs/readfs_test.go b/internal/sysfs/readfs_test.go
index dd6d6092..f1667fd4 100644
--- a/internal/sysfs/readfs_test.go
+++ b/internal/sysfs/readfs_test.go
@@ -119,46 +119,51 @@ func TestReadFS_UtimesNano(t *testing.T) {
 }
 
 func TestReadFS_Open_Read(t *testing.T) {
-	t.Parallel()
-
-	tmpDir := t.TempDir()
-	require.NoError(t, fstest.WriteTestFiles(tmpDir))
-
 	type test struct {
 		name          string
-		fs            fsapi.FS
+		fs            func(tmpDir string) fsapi.FS
 		expectFileIno bool
 		expectDirIno  bool
 	}
 
 	tests := []test{
 		{
-			name:          "DirFS",
-			fs:            NewReadFS(NewDirFS(tmpDir)),
+			name: "DirFS",
+			fs: func(tmpDir string) fsapi.FS {
+				return NewDirFS(tmpDir)
+			},
 			expectFileIno: true,
 			expectDirIno:  true,
 		},
 		{
-			name:          "fstest.MapFS",
-			fs:            NewReadFS(Adapt(fstest.FS)),
+			name: "fstest.MapFS",
+			fs: func(tmpDir string) fsapi.FS {
+				return Adapt(fstest.FS)
+			},
 			expectFileIno: false,
 			expectDirIno:  false,
 		},
 		{
-			name:          "os.DirFS",
-			fs:            NewReadFS(Adapt(os.DirFS(tmpDir))),
+			name: "os.DirFS",
+			fs: func(tmpDir string) fsapi.FS {
+				return Adapt(os.DirFS(tmpDir))
+			},
 			expectFileIno: statSetsIno(),
 			expectDirIno:  runtime.GOOS != "windows",
 		},
 		{
-			name:          "mask(os.DirFS)",
-			fs:            NewReadFS(Adapt(&MaskOsFS{os.DirFS(tmpDir), false})),
+			name: "mask(os.DirFS)",
+			fs: func(tmpDir string) fsapi.FS {
+				return Adapt(&MaskOsFS{Fs: os.DirFS(tmpDir)})
+			},
 			expectFileIno: statSetsIno(),
 			expectDirIno:  runtime.GOOS != "windows",
 		},
 		{
-			name:          "mask(os.DirFS) ZeroIno",
-			fs:            NewReadFS(Adapt(&MaskOsFS{os.DirFS(tmpDir), true})),
+			name: "mask(os.DirFS) ZeroIno",
+			fs: func(tmpDir string) fsapi.FS {
+				return Adapt(&MaskOsFS{Fs: os.DirFS(tmpDir), ZeroIno: true})
+			},
 			expectFileIno: false,
 			expectDirIno:  false,
 		},
@@ -168,7 +173,13 @@ func TestReadFS_Open_Read(t *testing.T) {
 		tc := tc
 
 		t.Run(tc.name, func(t *testing.T) {
-			testOpen_Read(t, tc.fs, tc.expectFileIno, tc.expectDirIno)
+			t.Parallel()
+
+			// Ensure tests don't conflict on the same directory
+			tmpDir := t.TempDir()
+			require.NoError(t, fstest.WriteTestFiles(tmpDir))
+
+			testOpen_Read(t, NewReadFS(tc.fs(tmpDir)), tc.expectFileIno, tc.expectDirIno)
 		})
 	}
 }
diff --git a/internal/sysfs/stat_test.go b/internal/sysfs/stat_test.go
index 36d42564..81df9b1e 100644
--- a/internal/sysfs/stat_test.go
+++ b/internal/sysfs/stat_test.go
@@ -4,7 +4,6 @@ import (
 	"os"
 	"path"
 	"runtime"
-	"syscall"
 	"testing"
 	"time"
 
@@ -105,7 +104,7 @@ func TestStat(t *testing.T) {
 func TestStatFile(t *testing.T) {
 	tmpDir := t.TempDir()
 
-	tmpDirF := requireOpenFile(t, tmpDir, syscall.O_RDONLY, 0)
+	tmpDirF := requireOpenFile(t, tmpDir, fsapi.O_RDONLY, 0)
 	defer tmpDirF.Close()
 
 	t.Run("dir", func(t *testing.T) {
@@ -127,7 +126,7 @@ func TestStatFile(t *testing.T) {
 
 	file := path.Join(tmpDir, "file")
 	require.NoError(t, os.WriteFile(file, nil, 0o400))
-	fileF := requireOpenFile(t, file, syscall.O_RDONLY, 0)
+	fileF := requireOpenFile(t, file, fsapi.O_RDONLY, 0)
 	defer fileF.Close()
 
 	t.Run("file", func(t *testing.T) {
@@ -146,7 +145,7 @@ func TestStatFile(t *testing.T) {
 
 	subdir := path.Join(tmpDir, "sub")
 	require.NoError(t, os.Mkdir(subdir, 0o500))
-	subdirF := requireOpenFile(t, subdir, syscall.O_RDONLY, 0)
+	subdirF := requireOpenFile(t, subdir, fsapi.O_RDONLY, 0)
 	defer subdirF.Close()
 
 	t.Run("subdir", func(t *testing.T) {
@@ -204,7 +203,7 @@ func Test_StatFile_times(t *testing.T) {
 			err := os.Chtimes(file, time.UnixMicro(tc.atimeNsec/1e3), time.UnixMicro(tc.mtimeNsec/1e3))
 			require.NoError(t, err)
 
-			f := requireOpenFile(t, file, syscall.O_RDONLY, 0)
+			f := requireOpenFile(t, file, fsapi.O_RDONLY, 0)
 			defer f.Close()
 
 			st, errno := f.Stat()
@@ -218,21 +217,21 @@ func Test_StatFile_times(t *testing.T) {
 
 func TestStatFile_dev_inode(t *testing.T) {
 	tmpDir := t.TempDir()
-	d := requireOpenFile(t, tmpDir, os.O_RDONLY, 0)
+	d := requireOpenFile(t, tmpDir, fsapi.O_RDONLY, 0)
 	defer d.Close()
 
 	path1 := path.Join(tmpDir, "1")
-	f1 := requireOpenFile(t, path1, os.O_CREATE, 0o666)
+	f1 := requireOpenFile(t, path1, fsapi.O_CREAT, 0o666)
 	defer f1.Close()
 
 	path2 := path.Join(tmpDir, "2")
-	f2 := requireOpenFile(t, path2, os.O_CREATE, 0o666)
+	f2 := requireOpenFile(t, path2, fsapi.O_CREAT, 0o666)
 	defer f2.Close()
 
 	pathLink2 := path.Join(tmpDir, "link2")
 	err := os.Symlink(path2, pathLink2)
 	require.NoError(t, err)
-	l2 := requireOpenFile(t, pathLink2, os.O_RDONLY, 0)
+	l2 := requireOpenFile(t, pathLink2, fsapi.O_RDONLY, 0)
 	defer l2.Close()
 
 	// First, stat the directory
@@ -275,7 +274,7 @@ func TestStatFile_dev_inode(t *testing.T) {
 
 	// Renaming a file shouldn't change its inodes.
 	require.EqualErrno(t, 0, rename(path1, path2))
-	f1 = requireOpenFile(t, path2, os.O_RDONLY, 0)
+	f1 = requireOpenFile(t, path2, fsapi.O_RDONLY, 0)
 	defer f1.Close()
 
 	st1Again, errno = f1.Stat()
diff --git a/internal/sysfs/sysfs_test.go b/internal/sysfs/sysfs_test.go
index 667a12de..0b8149d3 100644
--- a/internal/sysfs/sysfs_test.go
+++ b/internal/sysfs/sysfs_test.go
@@ -23,7 +23,7 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS fsapi.FS) {
 	err := os.WriteFile(realPath, []byte{}, 0o600)
 	require.NoError(t, err)
 
-	f, errno := testFS.OpenFile(file, os.O_RDWR, 0)
+	f, errno := testFS.OpenFile(file, fsapi.O_RDWR, 0)
 	require.EqualErrno(t, 0, errno)
 	defer f.Close()
 
@@ -42,7 +42,7 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS fsapi.FS) {
 
 	// re-create as read-only, using 0444 to allow read-back on windows.
 	require.NoError(t, os.Remove(realPath))
-	f, errno = testFS.OpenFile(file, os.O_RDONLY|os.O_CREATE, 0o444)
+	f, errno = testFS.OpenFile(file, fsapi.O_RDONLY|fsapi.O_CREAT, 0o444)
 	require.EqualErrno(t, 0, errno)
 	defer f.Close()
 
@@ -60,7 +60,7 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS fsapi.FS) {
 	// from os.TestDirFSPathsValid
 	if runtime.GOOS != "windows" {
 		t.Run("strange name", func(t *testing.T) {
-			f, errno = testFS.OpenFile(`e:xperi\ment.txt`, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
+			f, errno = testFS.OpenFile(`e:xperi\ment.txt`, fsapi.O_WRONLY|fsapi.O_CREAT|fsapi.O_TRUNC, 0o600)
 			require.EqualErrno(t, 0, errno)
 			defer f.Close()
 
@@ -77,7 +77,7 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS fsapi.FS) {
 		realPath := path.Join(tmpDir, name)
 		require.NoError(t, os.WriteFile(realPath, []byte("123456"), 0o0666))
 
-		f, errno = testFS.OpenFile(name, os.O_RDWR|os.O_TRUNC, 0o444)
+		f, errno = testFS.OpenFile(name, fsapi.O_RDWR|fsapi.O_TRUNC, 0o444)
 		require.EqualErrno(t, 0, errno)
 		require.EqualErrno(t, 0, f.Close())
 
@@ -91,14 +91,14 @@ func testOpen_Read(t *testing.T, testFS fsapi.FS, requireFileIno, expectDirIno b
 	t.Helper()
 
 	t.Run("doesn't exist", func(t *testing.T) {
-		_, errno := testFS.OpenFile("nope", os.O_RDONLY, 0)
+		_, errno := testFS.OpenFile("nope", fsapi.O_RDONLY, 0)
 
 		// We currently follow os.Open not syscall.Open, so the error is wrapped.
 		require.EqualErrno(t, experimentalsys.ENOENT, errno)
 	})
 
 	t.Run("readdir . opens root", func(t *testing.T) {
-		f, errno := testFS.OpenFile(".", os.O_RDONLY, 0)
+		f, errno := testFS.OpenFile(".", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer f.Close()
 
@@ -119,7 +119,7 @@ func testOpen_Read(t *testing.T, testFS fsapi.FS, requireFileIno, expectDirIno b
 	})
 
 	t.Run("readdir empty", func(t *testing.T) {
-		f, errno := testFS.OpenFile("emptydir", os.O_RDONLY, 0)
+		f, errno := testFS.OpenFile("emptydir", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer f.Close()
 
@@ -128,7 +128,7 @@ func testOpen_Read(t *testing.T, testFS fsapi.FS, requireFileIno, expectDirIno b
 	})
 
 	t.Run("readdir partial", func(t *testing.T) {
-		dirF, errno := testFS.OpenFile("dir", os.O_RDONLY, 0)
+		dirF, errno := testFS.OpenFile("dir", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer dirF.Close()
 
@@ -167,7 +167,7 @@ func testOpen_Read(t *testing.T, testFS fsapi.FS, requireFileIno, expectDirIno b
 	})
 
 	t.Run("file exists", func(t *testing.T) {
-		f, errno := testFS.OpenFile("animals.txt", os.O_RDONLY, 0)
+		f, errno := testFS.OpenFile("animals.txt", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer f.Close()
 
@@ -198,7 +198,7 @@ human
 	})
 
 	t.Run("file stat includes inode", func(t *testing.T) {
-		f, errno := testFS.OpenFile("empty.txt", os.O_RDONLY, 0)
+		f, errno := testFS.OpenFile("empty.txt", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer f.Close()
 
@@ -215,15 +215,15 @@ human
 	t.Run("or'd flag", func(t *testing.T) {
 		// Example of a flag that can be or'd into O_RDONLY even if not
 		// currently supported in WASI or GOOS=js
-		const O_NOATIME = 0x40000
+		const O_NOATIME = fsapi.Oflag(0x40000)
 
-		f, errno := testFS.OpenFile("animals.txt", os.O_RDONLY|O_NOATIME, 0)
+		f, errno := testFS.OpenFile("animals.txt", fsapi.O_RDONLY|O_NOATIME, 0)
 		require.EqualErrno(t, 0, errno)
 		defer f.Close()
 	})
 
 	t.Run("writing to a read-only file is EBADF", func(t *testing.T) {
-		f, errno := testFS.OpenFile("animals.txt", os.O_RDONLY, 0)
+		f, errno := testFS.OpenFile("animals.txt", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 		defer f.Close()
 
@@ -232,7 +232,7 @@ human
 	})
 
 	t.Run("opening a directory with O_RDWR is EISDIR", func(t *testing.T) {
-		_, errno := testFS.OpenFile("sub", fsapi.O_DIRECTORY|os.O_RDWR, 0)
+		_, errno := testFS.OpenFile("sub", fsapi.O_DIRECTORY|fsapi.O_RDWR, 0)
 		require.EqualErrno(t, experimentalsys.EISDIR, errno)
 	})
 }
diff --git a/internal/wasm/module_instance_test.go b/internal/wasm/module_instance_test.go
index 761bdf42..1c33588a 100644
--- a/internal/wasm/module_instance_test.go
+++ b/internal/wasm/module_instance_test.go
@@ -4,13 +4,13 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"os"
 	"sync"
 	"sync/atomic"
 	"testing"
 	"time"
 
 	"github.com/tetratelabs/wazero/experimental/sys"
+	"github.com/tetratelabs/wazero/internal/fsapi"
 	internalsys "github.com/tetratelabs/wazero/internal/sys"
 	"github.com/tetratelabs/wazero/internal/sysfs"
 	testfs "github.com/tetratelabs/wazero/internal/testing/fs"
@@ -117,7 +117,7 @@ func TestModuleInstance_Close(t *testing.T) {
 		sysCtx := internalsys.DefaultContext(testFS)
 		fsCtx := sysCtx.FS()
 
-		_, errno := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0)
+		_, errno := fsCtx.OpenFile(testFS, "/foo", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 
 		m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil)
@@ -152,7 +152,7 @@ func TestModuleInstance_Close(t *testing.T) {
 		sysCtx := internalsys.DefaultContext(testFS)
 		fsCtx := sysCtx.FS()
 
-		_, errno := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0)
+		_, errno := fsCtx.OpenFile(testFS, "/foo", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 
 		m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil)
@@ -222,7 +222,7 @@ func TestModuleInstance_CallDynamic(t *testing.T) {
 		sysCtx := internalsys.DefaultContext(testFS)
 		fsCtx := sysCtx.FS()
 
-		_, errno := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0)
+		_, errno := fsCtx.OpenFile(testFS, "/foo", fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 
 		m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil)
@@ -251,7 +251,7 @@ func TestModuleInstance_CallDynamic(t *testing.T) {
 		fsCtx := sysCtx.FS()
 
 		path := "/foo"
-		_, errno := fsCtx.OpenFile(testFS, path, os.O_RDONLY, 0)
+		_, errno := fsCtx.OpenFile(testFS, path, fsapi.O_RDONLY, 0)
 		require.EqualErrno(t, 0, errno)
 
 		m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil)