Skip to content

[1/3] Proper path resolution: add openat functionality #2254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions internal/sysfs/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,40 @@ func OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (*os.Fi
return openFile(path, flag, perm)
}

func OpenFileAt(
dir *os.File,
path string,
flag experimentalsys.Oflag,
perm fs.FileMode,
) (*os.File, experimentalsys.Errno) {
return openFileAt(dir, path, flag, perm)
}

func OpenOSFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
f, errno := OpenFile(path, flag, perm)
if errno != 0 {
return nil, errno
}

return newOsFile(path, flag, perm, f), 0
}

func OpenOSFileAt(
dir experimentalsys.File,
path string,
flag experimentalsys.Oflag,
perm fs.FileMode,
) (experimentalsys.File, experimentalsys.Errno) {
dirOsFile, ok := dir.(*osFile)
if !ok {
return nil, experimentalsys.EBADF
}

f, errno := OpenFileAt(dirOsFile.file, path, flag, perm)
if errno != 0 {
return nil, errno
}

return newOsFile(path, flag, perm, f), 0
}

Expand Down
28 changes: 26 additions & 2 deletions internal/sysfs/file_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,47 @@ import (
"github.com/tetratelabs/wazero/experimental/sys"
)

// NTStatus corresponds with NTSTATUS, error values returned by ntdll.dll and
// other native functions.
type NTStatus uint32

func rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) syscall.Errno {
r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(ntstatus))

return syscall.Errno(r0)
}

func (s NTStatus) Errno() syscall.Errno {
return rtlNtStatusToDosErrorNoTeb(s)
}

const (
nonBlockingFileReadSupported = true
nonBlockingFileWriteSupported = false

_ERROR_IO_INCOMPLETE = syscall.Errno(996)
)

var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
ntdll = syscall.NewLazyDLL("ntdll.dll")
)

// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
var (
// procFormatMessageW is the syscall.LazyProc in kernel32 for FormatMessageW
procFormatMessageW = kernel32.NewProc("FormatMessageW")
// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe")
// procGetOverlappedResult is the syscall.LazyProc in kernel32 for GetOverlappedResult
procGetOverlappedResult = kernel32.NewProc("GetOverlappedResult")
// procCreateEventW is the syscall.LazyProc in kernel32 for CreateEventW
procCreateEventW = kernel32.NewProc("CreateEventW")
// procNtCreateFile is the syscall.LazyProc in ntdll for NtCreateFile
procNtCreateFile = ntdll.NewProc("NtCreateFile")
// procRtlInitUnicodeString is the syscall.LazyProc in ntdll for RtlInitUnicodeString
procRtlInitUnicodeString = ntdll.NewProc("RtlInitUnicodeString")
// procRtlNtStatusToDosErrorNoTeb is the syscall.LazyProc in ntdll for RtlNtStatusToDosErrorNoTeb
procRtlNtStatusToDosErrorNoTeb = ntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
)

// readFd returns ENOSYS on unsupported platforms.
Expand Down
197 changes: 197 additions & 0 deletions internal/sysfs/nt_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package sysfs

import (
"syscall"
"unsafe"
)

// The security identifier (SID) structure is a variable-length
// structure used to uniquely identify users or groups.
type SID struct{}

type ACL struct {
aclRevision byte
sbz1 byte
aclSize uint16
aceCount uint16
sbz2 uint16
}

type SECURITY_DESCRIPTOR_CONTROL uint16

type SECURITY_DESCRIPTOR struct {
revision byte
sbz1 byte
control SECURITY_DESCRIPTOR_CONTROL
owner *SID
group *SID
sacl *ACL
dacl *ACL
}

type SECURITY_QUALITY_OF_SERVICE struct {
Length uint32
ImpersonationLevel uint32
ContextTrackingMode byte
EffectiveOnly byte
}

// NTUnicodeString is a UTF-16 string for NT native APIs, corresponding to UNICODE_STRING.
type NTUnicodeString struct {
Length uint16
MaximumLength uint16
Buffer *uint16
}

type OBJECT_ATTRIBUTES struct {
Length uint32
RootDirectory syscall.Handle
ObjectName *NTUnicodeString
Attributes uint32
SecurityDescriptor *SECURITY_DESCRIPTOR
SecurityQoS *SECURITY_QUALITY_OF_SERVICE
}

type IO_STATUS_BLOCK struct {
Status NTStatus
Information uintptr
}

// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask
type ACCESS_MASK uint32

// Constants for type ACCESS_MASK
const (
DELETE = 0x00010000
READ_CONTROL = 0x00020000
WRITE_DAC = 0x00040000
WRITE_OWNER = 0x00080000
SYNCHRONIZE = 0x00100000
STANDARD_RIGHTS_REQUIRED = 0x000F0000
STANDARD_RIGHTS_READ = READ_CONTROL
STANDARD_RIGHTS_WRITE = READ_CONTROL
STANDARD_RIGHTS_EXECUTE = READ_CONTROL
STANDARD_RIGHTS_ALL = 0x001F0000
SPECIFIC_RIGHTS_ALL = 0x0000FFFF
ACCESS_SYSTEM_SECURITY = 0x01000000
MAXIMUM_ALLOWED = 0x02000000
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000
)

// File access rights constants.
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
const (
FILE_READ_DATA = 0x00000001
FILE_READ_ATTRIBUTES = 0x00000080
FILE_READ_EA = 0x00000008
FILE_WRITE_DATA = 0x00000002
FILE_WRITE_ATTRIBUTES = 0x00000100
FILE_WRITE_EA = 0x00000010
FILE_APPEND_DATA = 0x00000004
FILE_EXECUTE = 0x00000020
)

const (
FILE_GENERIC_READ = STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE
FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE
FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE
)

// NtCreateFile CreateDisposition
const (
FILE_SUPERSEDE = 0x00000000
FILE_OPEN = 0x00000001
FILE_CREATE = 0x00000002
FILE_OPEN_IF = 0x00000003
FILE_OVERWRITE = 0x00000004
FILE_OVERWRITE_IF = 0x00000005
FILE_MAXIMUM_DISPOSITION = 0x00000005
)

// NtCreateFile CreateOptions
const (
FILE_DIRECTORY_FILE = 0x00000001
FILE_WRITE_THROUGH = 0x00000002
FILE_SEQUENTIAL_ONLY = 0x00000004
FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008
FILE_SYNCHRONOUS_IO_ALERT = 0x00000010
FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020
FILE_NON_DIRECTORY_FILE = 0x00000040
FILE_CREATE_TREE_CONNECTION = 0x00000080
FILE_COMPLETE_IF_OPLOCKED = 0x00000100
FILE_NO_EA_KNOWLEDGE = 0x00000200
FILE_OPEN_FOR_RECOVERY = 0x00000400
FILE_RANDOM_ACCESS = 0x00000800
FILE_DELETE_ON_CLOSE = 0x00001000
FILE_OPEN_BY_FILE_ID = 0x00002000
FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000
FILE_NO_COMPRESSION = 0x00008000
FILE_RESERVE_OPFILTER = 0x00100000
FILE_OPEN_REPARSE_POINT = 0x00200000
FILE_OPEN_NO_RECALL = 0x00400000
FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000
)

// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/nf-ntdef-initializeobjectattributes
const (
OBJ_CASE_INSENSITIVE = 0x00000040
)

func NtCreateFile(
handle *syscall.Handle,
access uint32,
oa *OBJECT_ATTRIBUTES,
iosb *IO_STATUS_BLOCK,
allocationSize *int64,
attributes uint32,
share uint32,
disposition uint32,
options uint32,
eabuffer uintptr,
ealength uint32,
) syscall.Errno {
r0, _, _ := syscall.SyscallN(
procNtCreateFile.Addr(),
uintptr(unsafe.Pointer(handle)),
uintptr(access),
uintptr(unsafe.Pointer(oa)),
uintptr(unsafe.Pointer(iosb)),
uintptr(unsafe.Pointer(allocationSize)),
uintptr(attributes),
uintptr(share),
uintptr(disposition),
uintptr(options),
uintptr(eabuffer),
uintptr(ealength),
)

return NTStatus(r0).Errno()
}

// NewNTUnicodeString returns a new NTUnicodeString structure for use with native
// NT APIs that work over the NTUnicodeString type. Note that most Windows APIs
// do not use NTUnicodeString, and instead UTF16PtrFromString should be used for
// the more common *uint16 string type.
func NewNTUnicodeString(s string) (*NTUnicodeString, error) {
var u NTUnicodeString

s16, err := syscall.UTF16PtrFromString(s)
if err != nil {
return nil, err
}

RtlInitUnicodeString(&u, s16)

return &u, nil
}

func RtlInitUnicodeString(destinationString *NTUnicodeString, sourceString *uint16) {
syscall.SyscallN(
procRtlInitUnicodeString.Addr(),
uintptr(unsafe.Pointer(destinationString)),
uintptr(unsafe.Pointer(sourceString)),
)
}
43 changes: 43 additions & 0 deletions internal/sysfs/open_file_at_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//go:build !tinygo

package sysfs

import (
"io/fs"
"os"
"path"
"syscall"
"unsafe"

"github.com/tetratelabs/wazero/experimental/sys"
)

func openFileAt(dir *os.File, filePath string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
fd, err := openat(int(dir.Fd()), filePath, toOsOpenFlag(oflag), uint32(perm))
if err != nil {
return nil, sys.UnwrapOSError(err)
}

return os.NewFile(uintptr(fd), path.Base(filePath)), 0
}

func openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) {
var p0 *byte

p0, err = syscall.BytePtrFromString(path)
if err != nil {
return
}

r0, _, e1 := syscall_syscall6(libc_openat_trampoline_addr, uintptr(dirfd), uintptr(unsafe.Pointer(p0)), uintptr(mode), uintptr(perm), 0, 0)
fd = int(r0)
if e1 != 0 {
err = sys.UnwrapOSError(e1)
}

return
}

//go:cgo_import_dynamic libc_openat openat "/usr/lib/libSystem.B.dylib"

var libc_openat_trampoline_addr uintptr
7 changes: 7 additions & 0 deletions internal/sysfs/open_file_at_darwin_amd64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "textflag.h"

TEXT libc_openat_trampoline<>(SB), NOSPLIT, $0-0
JMP libc_openat(SB)

GLOBL ·libc_openat_trampoline_addr(SB), RODATA, $8
DATA ·libc_openat_trampoline_addr(SB)/8, $libc_openat_trampoline<>(SB)
7 changes: 7 additions & 0 deletions internal/sysfs/open_file_at_darwin_arm64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "textflag.h"

TEXT libc_openat_trampoline<>(SB), NOSPLIT, $0-0
JMP libc_openat(SB)

GLOBL ·libc_openat_trampoline_addr(SB), RODATA, $8
DATA ·libc_openat_trampoline_addr(SB)/8, $libc_openat_trampoline<>(SB)
21 changes: 21 additions & 0 deletions internal/sysfs/open_file_at_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//go:build !tinygo

package sysfs

import (
"io/fs"
"os"
"path"
"syscall"

"github.com/tetratelabs/wazero/experimental/sys"
)

func openFileAt(dir *os.File, filePath string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
fd, err := syscall.Openat(int(dir.Fd()), filePath, toOsOpenFlag(oflag), uint32(perm))
if err != nil {
return nil, sys.UnwrapOSError(err)
}

return os.NewFile(uintptr(fd), path.Base(filePath)), 0
}
Loading
Loading