aboutsummaryrefslogtreecommitdiff
path: root/src/internal/syscall/windows
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/syscall/windows')
-rw-r--r--src/internal/syscall/windows/at_windows.go70
-rw-r--r--src/internal/syscall/windows/syscall_windows.go4
-rw-r--r--src/internal/syscall/windows/types_windows.go20
-rw-r--r--src/internal/syscall/windows/zsyscall_windows.go18
4 files changed, 112 insertions, 0 deletions
diff --git a/src/internal/syscall/windows/at_windows.go b/src/internal/syscall/windows/at_windows.go
index af8167dd06..72780139a0 100644
--- a/src/internal/syscall/windows/at_windows.go
+++ b/src/internal/syscall/windows/at_windows.go
@@ -6,6 +6,7 @@ package windows
import (
"syscall"
+ "unsafe"
)
// Openat flags not supported by syscall.Open.
@@ -171,3 +172,72 @@ func Mkdirat(dirfd syscall.Handle, name string, mode uint32) error {
syscall.CloseHandle(h)
return nil
}
+
+func Deleteat(dirfd syscall.Handle, name string) error {
+ objAttrs := &OBJECT_ATTRIBUTES{}
+ if err := objAttrs.init(dirfd, name); err != nil {
+ return err
+ }
+ var h syscall.Handle
+ err := NtOpenFile(
+ &h,
+ DELETE,
+ objAttrs,
+ &IO_STATUS_BLOCK{},
+ FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT,
+ )
+ if err != nil {
+ return ntCreateFileError(err, 0)
+ }
+ defer syscall.CloseHandle(h)
+
+ const (
+ FileDispositionInformation = 13
+ FileDispositionInformationEx = 64
+ )
+
+ // First, attempt to delete the file using POSIX semantics
+ // (which permit a file to be deleted while it is still open).
+ // This matches the behavior of DeleteFileW.
+ err = NtSetInformationFile(
+ h,
+ &IO_STATUS_BLOCK{},
+ uintptr(unsafe.Pointer(&FILE_DISPOSITION_INFORMATION_EX{
+ Flags: FILE_DISPOSITION_DELETE |
+ FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK |
+ FILE_DISPOSITION_POSIX_SEMANTICS |
+ // This differs from DeleteFileW, but matches os.Remove's
+ // behavior on Unix platforms of permitting deletion of
+ // read-only files.
+ FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
+ })),
+ uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION_EX{})),
+ FileDispositionInformationEx,
+ )
+ switch err {
+ case nil:
+ return nil
+ case STATUS_CANNOT_DELETE, STATUS_DIRECTORY_NOT_EMPTY:
+ return err.(NTStatus).Errno()
+ }
+
+ // If the prior deletion failed, the filesystem either doesn't support
+ // POSIX semantics (for example, FAT), or hasn't implemented
+ // FILE_DISPOSITION_INFORMATION_EX.
+ //
+ // Try again.
+ err = NtSetInformationFile(
+ h,
+ &IO_STATUS_BLOCK{},
+ uintptr(unsafe.Pointer(&FILE_DISPOSITION_INFORMATION{
+ DeleteFile: true,
+ })),
+ uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION{})),
+ FileDispositionInformation,
+ )
+ if st, ok := err.(NTStatus); ok {
+ return st.Errno()
+ }
+ return err
+}
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index 924f4951e7..f6fbf199bf 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -523,10 +523,14 @@ func (s NTStatus) Error() string {
// If this list starts getting long, we should consider generating the full set.
const (
STATUS_FILE_IS_A_DIRECTORY NTStatus = 0xC00000BA
+ STATUS_DIRECTORY_NOT_EMPTY NTStatus = 0xC0000101
STATUS_NOT_A_DIRECTORY NTStatus = 0xC0000103
+ STATUS_CANNOT_DELETE NTStatus = 0xC0000121
STATUS_REPARSE_POINT_ENCOUNTERED NTStatus = 0xC000050B
)
// 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 NtOpenFile(handle *syscall.Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO_STATUS_BLOCK, share uint32, options uint32) (ntstatus error) = ntdll.NtOpenFile
//sys rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) = ntdll.RtlNtStatusToDosErrorNoTeb
+//sys NtSetInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer uintptr, inBufferLen uint32, class uint32) (ntstatus error) = ntdll.NtSetInformationFile
diff --git a/src/internal/syscall/windows/types_windows.go b/src/internal/syscall/windows/types_windows.go
index 514feafae4..6ae37afff8 100644
--- a/src/internal/syscall/windows/types_windows.go
+++ b/src/internal/syscall/windows/types_windows.go
@@ -196,3 +196,23 @@ const (
FILE_OPEN_NO_RECALL = 0x00400000
FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000
)
+
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information
+type FILE_DISPOSITION_INFORMATION struct {
+ DeleteFile bool
+}
+
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information_ex
+type FILE_DISPOSITION_INFORMATION_EX struct {
+ Flags uint32
+}
+
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information_ex
+const (
+ FILE_DISPOSITION_DO_NOT_DELETE = 0x00000000
+ FILE_DISPOSITION_DELETE = 0x00000001
+ FILE_DISPOSITION_POSIX_SEMANTICS = 0x00000002
+ FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK = 0x00000004
+ FILE_DISPOSITION_ON_CLOSE = 0x00000008
+ FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE = 0x00000010
+)
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
index c81bc399ff..6a6ea7bdc0 100644
--- a/src/internal/syscall/windows/zsyscall_windows.go
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -93,6 +93,8 @@ var (
procNetUserDel = modnetapi32.NewProc("NetUserDel")
procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups")
procNtCreateFile = modntdll.NewProc("NtCreateFile")
+ procNtOpenFile = modntdll.NewProc("NtOpenFile")
+ procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
procRtlGetVersion = modntdll.NewProc("RtlGetVersion")
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
@@ -477,6 +479,22 @@ func NtCreateFile(handle *syscall.Handle, access uint32, oa *OBJECT_ATTRIBUTES,
return
}
+func NtOpenFile(handle *syscall.Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO_STATUS_BLOCK, share uint32, options uint32) (ntstatus error) {
+ r0, _, _ := syscall.Syscall6(procNtOpenFile.Addr(), 6, uintptr(unsafe.Pointer(handle)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(options))
+ if r0 != 0 {
+ ntstatus = NTStatus(r0)
+ }
+ return
+}
+
+func NtSetInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer uintptr, inBufferLen uint32, class uint32) (ntstatus error) {
+ r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(inBuffer), uintptr(inBufferLen), uintptr(class), 0)
+ if r0 != 0 {
+ ntstatus = NTStatus(r0)
+ }
+ return
+}
+
func rtlGetVersion(info *_OSVERSIONINFOW) {
syscall.Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0)
return