// 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) } }