internal/syscall/windows: add Openat, Mkdirat
Windows versions of openat and mkdirat, implemented using NtCreateFile. For #67002 Change-Id: If43b1c1069733e5c45f7d45a69699fec30187308 Reviewed-on: https://go-review.googlesource.com/c/go/+/619435 Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Quim Muntal <quimmuntal@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
1c6288f7e1
commit
86a1a994ff
168
src/internal/syscall/windows/at_windows.go
Normal file
168
src/internal/syscall/windows/at_windows.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Openat flags not supported by syscall.Open.
|
||||||
|
//
|
||||||
|
// These are invented values.
|
||||||
|
//
|
||||||
|
// When adding a new flag here, add an unexported version to
|
||||||
|
// the set of invented O_ values in syscall/types_windows.go
|
||||||
|
// to avoid overlap.
|
||||||
|
const (
|
||||||
|
O_DIRECTORY = 0x100000 // target must be a directory
|
||||||
|
O_NOFOLLOW_ANY = 0x20000000 // disallow symlinks anywhere in the path
|
||||||
|
)
|
||||||
|
|
||||||
|
func Openat(dirfd syscall.Handle, name string, flag int, perm uint32) (_ syscall.Handle, e1 error) {
|
||||||
|
if len(name) == 0 {
|
||||||
|
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
var access, options uint32
|
||||||
|
switch flag & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
|
||||||
|
case syscall.O_RDONLY:
|
||||||
|
// FILE_GENERIC_READ includes FILE_LIST_DIRECTORY.
|
||||||
|
access = FILE_GENERIC_READ
|
||||||
|
case syscall.O_WRONLY:
|
||||||
|
access = FILE_GENERIC_WRITE
|
||||||
|
options |= FILE_NON_DIRECTORY_FILE
|
||||||
|
case syscall.O_RDWR:
|
||||||
|
access = FILE_GENERIC_READ | FILE_GENERIC_WRITE
|
||||||
|
options |= FILE_NON_DIRECTORY_FILE
|
||||||
|
}
|
||||||
|
if flag&syscall.O_CREAT != 0 {
|
||||||
|
access |= FILE_GENERIC_WRITE
|
||||||
|
}
|
||||||
|
if flag&syscall.O_APPEND != 0 {
|
||||||
|
access &^= FILE_WRITE_DATA
|
||||||
|
access |= FILE_APPEND_DATA
|
||||||
|
}
|
||||||
|
if flag&O_DIRECTORY != 0 {
|
||||||
|
options |= FILE_DIRECTORY_FILE
|
||||||
|
access |= FILE_LIST_DIRECTORY
|
||||||
|
}
|
||||||
|
if flag&syscall.O_SYNC != 0 {
|
||||||
|
options |= FILE_WRITE_THROUGH
|
||||||
|
}
|
||||||
|
// Allow File.Stat.
|
||||||
|
access |= STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_READ_EA
|
||||||
|
|
||||||
|
objAttrs := &OBJECT_ATTRIBUTES{}
|
||||||
|
if flag&O_NOFOLLOW_ANY != 0 {
|
||||||
|
objAttrs.Attributes |= OBJ_DONT_REPARSE
|
||||||
|
}
|
||||||
|
if flag&syscall.O_CLOEXEC == 0 {
|
||||||
|
objAttrs.Attributes |= OBJ_INHERIT
|
||||||
|
}
|
||||||
|
if err := objAttrs.init(dirfd, name); err != nil {
|
||||||
|
return syscall.InvalidHandle, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't use FILE_OVERWRITE/FILE_OVERWRITE_IF, because when opening
|
||||||
|
// a file with FILE_ATTRIBUTE_READONLY these will replace an existing
|
||||||
|
// file with a new, read-only one.
|
||||||
|
//
|
||||||
|
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
||||||
|
var disposition uint32
|
||||||
|
switch {
|
||||||
|
case flag&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
|
||||||
|
disposition = FILE_CREATE
|
||||||
|
case flag&syscall.O_CREAT == syscall.O_CREAT:
|
||||||
|
disposition = FILE_OPEN_IF
|
||||||
|
default:
|
||||||
|
disposition = FILE_OPEN
|
||||||
|
}
|
||||||
|
|
||||||
|
fileAttrs := uint32(FILE_ATTRIBUTE_NORMAL)
|
||||||
|
if perm&syscall.S_IWRITE == 0 {
|
||||||
|
fileAttrs = FILE_ATTRIBUTE_READONLY
|
||||||
|
}
|
||||||
|
|
||||||
|
var h syscall.Handle
|
||||||
|
err := NtCreateFile(
|
||||||
|
&h,
|
||||||
|
SYNCHRONIZE|access,
|
||||||
|
objAttrs,
|
||||||
|
&IO_STATUS_BLOCK{},
|
||||||
|
nil,
|
||||||
|
fileAttrs,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
||||||
|
disposition,
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT|options,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return h, ntCreateFileError(err, flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag&syscall.O_TRUNC != 0 {
|
||||||
|
err = syscall.Ftruncate(h, 0)
|
||||||
|
if err != nil {
|
||||||
|
syscall.CloseHandle(h)
|
||||||
|
return syscall.InvalidHandle, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ntCreateFileError maps error returns from NTCreateFile to user-visible errors.
|
||||||
|
func ntCreateFileError(err error, flag int) error {
|
||||||
|
s, ok := err.(NTStatus)
|
||||||
|
if !ok {
|
||||||
|
// Shouldn't really be possible, NtCreateFile always returns NTStatus.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch s {
|
||||||
|
case STATUS_REPARSE_POINT_ENCOUNTERED:
|
||||||
|
return syscall.ELOOP
|
||||||
|
case STATUS_NOT_A_DIRECTORY:
|
||||||
|
// ENOTDIR is the errno returned by open when O_DIRECTORY is specified
|
||||||
|
// and the target is not a directory.
|
||||||
|
//
|
||||||
|
// NtCreateFile can return STATUS_NOT_A_DIRECTORY under other circumstances,
|
||||||
|
// such as when opening "file/" where "file" is not a directory.
|
||||||
|
// (This might be Windows version dependent.)
|
||||||
|
//
|
||||||
|
// Only map STATUS_NOT_A_DIRECTORY to ENOTDIR when O_DIRECTORY is specified.
|
||||||
|
if flag&O_DIRECTORY != 0 {
|
||||||
|
return syscall.ENOTDIR
|
||||||
|
}
|
||||||
|
case STATUS_FILE_IS_A_DIRECTORY:
|
||||||
|
return syscall.EISDIR
|
||||||
|
}
|
||||||
|
return s.Errno()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mkdirat(dirfd syscall.Handle, name string, mode uint32) error {
|
||||||
|
objAttrs := &OBJECT_ATTRIBUTES{}
|
||||||
|
if err := objAttrs.init(dirfd, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var h syscall.Handle
|
||||||
|
err := NtCreateFile(
|
||||||
|
&h,
|
||||||
|
FILE_GENERIC_READ,
|
||||||
|
objAttrs,
|
||||||
|
&IO_STATUS_BLOCK{},
|
||||||
|
nil,
|
||||||
|
syscall.FILE_ATTRIBUTE_NORMAL,
|
||||||
|
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||||
|
FILE_CREATE,
|
||||||
|
FILE_DIRECTORY_FILE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return ntCreateFileError(err, 0)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -517,6 +517,16 @@ func (s NTStatus) Error() string {
|
|||||||
return s.Errno().Error()
|
return s.Errno().Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// x/sys/windows/mkerrors.bash can generate a complete list of NTStatus codes.
|
||||||
|
//
|
||||||
|
// At the moment, we only need a couple, so just put them here manually.
|
||||||
|
// If this list starts getting long, we should consider generating the full set.
|
||||||
|
const (
|
||||||
|
STATUS_FILE_IS_A_DIRECTORY NTStatus = 0xC00000BA
|
||||||
|
STATUS_NOT_A_DIRECTORY NTStatus = 0xC0000103
|
||||||
|
STATUS_REPARSE_POINT_ENCOUNTERED NTStatus = 0xC000050B
|
||||||
|
)
|
||||||
|
|
||||||
// NT Native APIs
|
// NT Native APIs
|
||||||
//sys 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) (ntstatus error) = ntdll.NtCreateFile
|
//sys 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) (ntstatus error) = ntdll.NtCreateFile
|
||||||
//sys rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) = ntdll.RtlNtStatusToDosErrorNoTeb
|
//sys rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||||
|
@ -4,7 +4,10 @@
|
|||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
import "syscall"
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
// Socket related.
|
// Socket related.
|
||||||
const (
|
const (
|
||||||
@ -105,6 +108,23 @@ type OBJECT_ATTRIBUTES struct {
|
|||||||
SecurityQoS *SECURITY_QUALITY_OF_SERVICE
|
SecurityQoS *SECURITY_QUALITY_OF_SERVICE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init sets o's RootDirectory, ObjectName, and Length.
|
||||||
|
func (o *OBJECT_ATTRIBUTES) init(root syscall.Handle, name string) error {
|
||||||
|
if name == "." {
|
||||||
|
name = ""
|
||||||
|
}
|
||||||
|
objectName, err := NewNTUnicodeString(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.ObjectName = objectName
|
||||||
|
if root != syscall.InvalidHandle {
|
||||||
|
o.RootDirectory = root
|
||||||
|
}
|
||||||
|
o.Length = uint32(unsafe.Sizeof(*o))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Values for the Attributes member of OBJECT_ATTRIBUTES.
|
// Values for the Attributes member of OBJECT_ATTRIBUTES.
|
||||||
const (
|
const (
|
||||||
OBJ_INHERIT = 0x00000002
|
OBJ_INHERIT = 0x00000002
|
||||||
|
@ -34,18 +34,20 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Invented values to support what package os expects.
|
// Invented values to support what package os expects.
|
||||||
O_RDONLY = 0x00000
|
O_RDONLY = 0x00000
|
||||||
O_WRONLY = 0x00001
|
O_WRONLY = 0x00001
|
||||||
O_RDWR = 0x00002
|
O_RDWR = 0x00002
|
||||||
O_CREAT = 0x00040
|
O_CREAT = 0x00040
|
||||||
O_EXCL = 0x00080
|
O_EXCL = 0x00080
|
||||||
O_NOCTTY = 0x00100
|
O_NOCTTY = 0x00100
|
||||||
O_TRUNC = 0x00200
|
O_TRUNC = 0x00200
|
||||||
O_NONBLOCK = 0x00800
|
O_NONBLOCK = 0x00800
|
||||||
O_APPEND = 0x00400
|
O_APPEND = 0x00400
|
||||||
O_SYNC = 0x01000
|
O_SYNC = 0x01000
|
||||||
O_ASYNC = 0x02000
|
O_ASYNC = 0x02000
|
||||||
O_CLOEXEC = 0x80000
|
O_CLOEXEC = 0x80000
|
||||||
|
o_DIRECTORY = 0x100000 // used by internal/syscall/windows
|
||||||
|
o_NOFOLLOW_ANY = 0x20000000 // used by internal/syscall/windows
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user