Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 20 additions & 3 deletions src/internal/syscall/windows/at_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,14 +246,31 @@ func Deleteat(dirfd syscall.Handle, name string, options uint32) error {
var h syscall.Handle
err := NtOpenFile(
&h,
SYNCHRONIZE|FILE_READ_ATTRIBUTES|DELETE,
FILE_READ_ATTRIBUTES|DELETE,
objAttrs,
&IO_STATUS_BLOCK{},
FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|options,
FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|options,
)
if err != nil {
return ntCreateFileError(err, 0)
if ntStatus, ok := err.(NTStatus); !ok || ntStatus != STATUS_ACCESS_DENIED {
return ntCreateFileError(err, 0)
}

// Access denied, try opening with DELETE only.
// This may succeed if the file has restrictive permissions
// but the caller has delete child permission on the parent directory.
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|options,
)
if err != nil {
return ntCreateFileError(err, 0)
}
}
defer syscall.CloseHandle(h)

Expand Down
109 changes: 109 additions & 0 deletions src/internal/syscall/windows/at_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"path/filepath"
"syscall"
"testing"
"unsafe"
)

func TestOpen(t *testing.T) {
Expand Down Expand Up @@ -56,3 +57,111 @@ func TestOpen(t *testing.T) {
}
}
}

func TestDeleteAt(t *testing.T) {
testCases := []struct {
name string
modifier func(t *testing.T, path string)
}{
{"DeleteAt removes normal file", func(t *testing.T, name string) {}},
{"DeleteAt removes file with no read permission", makeFileNotReadable},
{"DeleteAt removes readonly file", makeFileReadonly},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

dir := t.TempDir()
file := filepath.Join(dir, "a")
f, err := os.Create(file)
if err != nil {
t.Fatal(err)
}
f.Close()

// Remove all permissions from the file.
// Do not use os.Chmod it sets only readonly attribute on Windows.
tc.modifier(t, file)

// delete file using DeleteAt
dirfd, err := syscall.Open(dir, syscall.O_RDONLY, 0)
if err != nil {
t.Fatal(err)
}
base := filepath.Base(file)
err = windows.Deleteat(dirfd, base, 0)
syscall.CloseHandle(dirfd)
if err != nil {
t.Fatalf("Deleteat failed: %v", err)
}

// Verify the file has been deleted.
if _, err := os.Stat(file); !os.IsNotExist(err) {
t.Fatalf("file still exists after DeleteAt")
}
})
}
}

func makeFileReadonly(t *testing.T, name string) {
if err := os.Chmod(name, 0); err != nil {
t.Fatal(err)
}
}

func makeFileNotReadable(t *testing.T, name string) {
creatorOwnerSID, err := syscall.StringToSid("S-1-3-0")
if err != nil {
t.Fatal(err)
}
creatorGroupSID, err := syscall.StringToSid("S-1-3-1")
if err != nil {
t.Fatal(err)
}
everyoneSID, err := syscall.StringToSid("S-1-1-0")
if err != nil {
t.Fatal(err)
}

entryForSid := func(sid *syscall.SID) windows.EXPLICIT_ACCESS {
return windows.EXPLICIT_ACCESS{
AccessPermissions: 0,
AccessMode: windows.GRANT_ACCESS,
Inheritance: windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
Trustee: windows.TRUSTEE{
TrusteeForm: windows.TRUSTEE_IS_SID,
Name: (*uint16)(unsafe.Pointer(sid)),
},
}
}

entries := []windows.EXPLICIT_ACCESS{
entryForSid(creatorOwnerSID),
entryForSid(creatorGroupSID),
entryForSid(everyoneSID),
}

var oldAcl, newAcl syscall.Handle
if err := windows.SetEntriesInAcl(
uint32(len(entries)),
&entries[0],
oldAcl,
&newAcl,
); err != nil {
t.Fatal(err)
}

defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newAcl)))
if err := windows.SetNamedSecurityInfo(
name,
windows.SE_FILE_OBJECT,
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
nil,
nil,
newAcl,
0,
); err != nil {
t.Fatal(err)
}
}
4 changes: 4 additions & 0 deletions src/internal/syscall/windows/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ const (
STATUS_NOT_SUPPORTED NTStatus = 0xC00000BB
STATUS_INVALID_PARAMETER NTStatus = 0xC000000D
STATUS_INVALID_INFO_CLASS NTStatus = 0xC0000003
STATUS_ACCESS_DENIED NTStatus = 0xC0000022
)

const (
Expand All @@ -579,3 +580,6 @@ type FILE_MODE_INFORMATION struct {
//sys NtSetInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer unsafe.Pointer, inBufferLen uint32, class uint32) (ntstatus error) = ntdll.NtSetInformationFile
//sys RtlIsDosDeviceName_U(name *uint16) (ret uint32) = ntdll.RtlIsDosDeviceName_U
//sys NtQueryInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer unsafe.Pointer, inBufferLen uint32, class uint32) (ntstatus error) = ntdll.NtQueryInformationFile

//sys SetEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL syscall.Handle, newACL *syscall.Handle) (ret error) = advapi32.SetEntriesInAclW
//sys SetNamedSecurityInfo(objectName string, objectType int32, securityInformation uint32, owner *syscall.SID, group *syscall.SID, dacl syscall.Handle, sacl syscall.Handle) (ret error) = advapi32.SetNamedSecurityInfoW
82 changes: 82 additions & 0 deletions src/internal/syscall/windows/types_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,85 @@ const ValidFileFlagsMask = O_FILE_FLAG_OPEN_REPARSE_POINT |
O_FILE_FLAG_NO_BUFFERING |
O_FILE_FLAG_RANDOM_ACCESS |
O_FILE_FLAG_WRITE_THROUGH

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379636.aspx
type TRUSTEE struct {
MultipleTrustee *TRUSTEE
MultipleTrusteeOperation int32
TrusteeForm int32
TrusteeType int32
Name *uint16
}

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379638.aspx
const (
TRUSTEE_IS_SID = iota
TRUSTEE_IS_NAME
TRUSTEE_BAD_FORM
TRUSTEE_IS_OBJECTS_AND_SID
TRUSTEE_IS_OBJECTS_AND_NAME
)

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa446627.aspx
type EXPLICIT_ACCESS struct {
AccessPermissions uint32
AccessMode int32
Inheritance uint32
Trustee TRUSTEE
}

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa374899.aspx
const (
NOT_USED_ACCESS = iota
GRANT_ACCESS
SET_ACCESS
DENY_ACCESS
REVOKE_ACCESS
SET_AUDIT_SUCCESS
SET_AUDIT_FAILURE
)

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa446627.aspx
const (
NO_INHERITANCE = 0x0
SUB_OBJECTS_ONLY_INHERIT = 0x1
SUB_CONTAINERS_ONLY_INHERIT = 0x2
SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3
INHERIT_NO_PROPAGATE = 0x4
INHERIT_ONLY = 0x8
)

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379593.aspx
const (
SE_UNKNOWN_OBJECT_TYPE = iota
SE_FILE_OBJECT
SE_SERVICE
SE_PRINTER
SE_REGISTRY_KEY
SE_LMSHARE
SE_KERNEL_OBJECT
SE_WINDOW_OBJECT
SE_DS_OBJECT
SE_DS_OBJECT_ALL
SE_PROVIDER_DEFINED_OBJECT
SE_WMIGUID_OBJECT
SE_REGISTRY_WOW64_32KEY
SE_REGISTRY_WOW64_64KEY
)

// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343
const (
OWNER_SECURITY_INFORMATION = 0x00000001
GROUP_SECURITY_INFORMATION = 0x00000002
DACL_SECURITY_INFORMATION = 0x00000004
SACL_SECURITY_INFORMATION = 0x00000008
LABEL_SECURITY_INFORMATION = 0x00000010
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
ATTRIBUTE_SECURITY_INFORMATION = 0x00000020
SCOPE_SECURITY_INFORMATION = 0x00000040
PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080
BACKUP_SECURITY_INFORMATION = 0x00010000
)
27 changes: 27 additions & 0 deletions src/internal/syscall/windows/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.