blob: a0b8e15da65daab1c85edf6d7c0794b492f0b695 [file] [edit]
// 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_test
import (
"internal/syscall/windows"
"os"
"path/filepath"
"syscall"
"testing"
"unsafe"
)
func TestOpen(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()
tests := []struct {
path string
flag int
err error
}{
{dir, syscall.O_RDONLY, nil},
{dir, syscall.O_CREAT, nil},
{dir, syscall.O_RDONLY | syscall.O_CREAT, nil},
{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE, nil},
{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE | os.O_TRUNC, nil},
{dir, syscall.O_RDONLY | syscall.O_TRUNC, syscall.ERROR_ACCESS_DENIED},
{dir, syscall.O_WRONLY | syscall.O_RDWR, nil}, // TODO: syscall.Open returns EISDIR here, we should reconcile this
{dir, syscall.O_WRONLY, syscall.EISDIR},
{dir, syscall.O_RDWR, syscall.EISDIR},
}
for i, tt := range tests {
dir := filepath.Dir(tt.path)
dirfd, err := syscall.Open(dir, syscall.O_RDONLY, 0)
if err != nil {
t.Error(err)
continue
}
base := filepath.Base(tt.path)
h, err := windows.Openat(dirfd, base, uint64(tt.flag), 0o660)
syscall.CloseHandle(dirfd)
if err == nil {
syscall.CloseHandle(h)
}
if err != tt.err {
t.Errorf("%d: Open got %q, want %q", i, err, tt.err)
}
}
}
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: (uintptr)(unsafe.Pointer(sid)),
},
}
}
entries := []windows.EXPLICIT_ACCESS{
entryForSid(creatorOwnerSID),
entryForSid(creatorGroupSID),
entryForSid(everyoneSID),
}
var oldAcl, newAcl *windows.ACL
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,
nil,
); err != nil {
t.Fatal(err)
}
}