Source file src/internal/syscall/windows/at_windows_test.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package windows_test
     6  
     7  import (
     8  	"internal/syscall/windows"
     9  	"os"
    10  	"path/filepath"
    11  	"syscall"
    12  	"testing"
    13  	"unsafe"
    14  )
    15  
    16  func TestOpen(t *testing.T) {
    17  	t.Parallel()
    18  
    19  	dir := t.TempDir()
    20  	file := filepath.Join(dir, "a")
    21  	f, err := os.Create(file)
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	f.Close()
    26  
    27  	tests := []struct {
    28  		path string
    29  		flag int
    30  		err  error
    31  	}{
    32  		{dir, syscall.O_RDONLY, nil},
    33  		{dir, syscall.O_CREAT, nil},
    34  		{dir, syscall.O_RDONLY | syscall.O_CREAT, nil},
    35  		{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE, nil},
    36  		{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE | os.O_TRUNC, nil},
    37  		{dir, syscall.O_RDONLY | syscall.O_TRUNC, syscall.ERROR_ACCESS_DENIED},
    38  		{dir, syscall.O_WRONLY | syscall.O_RDWR, nil}, // TODO: syscall.Open returns EISDIR here, we should reconcile this
    39  		{dir, syscall.O_WRONLY, syscall.EISDIR},
    40  		{dir, syscall.O_RDWR, syscall.EISDIR},
    41  	}
    42  	for i, tt := range tests {
    43  		dir := filepath.Dir(tt.path)
    44  		dirfd, err := syscall.Open(dir, syscall.O_RDONLY, 0)
    45  		if err != nil {
    46  			t.Error(err)
    47  			continue
    48  		}
    49  		base := filepath.Base(tt.path)
    50  		h, err := windows.Openat(dirfd, base, uint64(tt.flag), 0o660)
    51  		syscall.CloseHandle(dirfd)
    52  		if err == nil {
    53  			syscall.CloseHandle(h)
    54  		}
    55  		if err != tt.err {
    56  			t.Errorf("%d: Open got %q, want %q", i, err, tt.err)
    57  		}
    58  	}
    59  }
    60  
    61  func TestDeleteAt(t *testing.T) {
    62  	testCases := []struct {
    63  		name     string
    64  		modifier func(t *testing.T, path string)
    65  	}{
    66  		{"DeleteAt removes normal file", func(t *testing.T, name string) {}},
    67  		{"DeleteAt removes file with no read permission", makeFileNotReadable},
    68  		{"DeleteAt removes readonly file", makeFileReadonly},
    69  	}
    70  
    71  	for _, tc := range testCases {
    72  		t.Run(tc.name, func(t *testing.T) {
    73  			t.Parallel()
    74  
    75  			dir := t.TempDir()
    76  			file := filepath.Join(dir, "a")
    77  			f, err := os.Create(file)
    78  			if err != nil {
    79  				t.Fatal(err)
    80  			}
    81  			f.Close()
    82  
    83  			// Remove all permissions from the file.
    84  			// Do not use os.Chmod it sets only readonly attribute on Windows.
    85  			tc.modifier(t, file)
    86  
    87  			// delete file using DeleteAt
    88  			dirfd, err := syscall.Open(dir, syscall.O_RDONLY, 0)
    89  			if err != nil {
    90  				t.Fatal(err)
    91  			}
    92  			base := filepath.Base(file)
    93  			err = windows.Deleteat(dirfd, base, 0)
    94  			syscall.CloseHandle(dirfd)
    95  			if err != nil {
    96  				t.Fatalf("Deleteat failed: %v", err)
    97  			}
    98  
    99  			// Verify the file has been deleted.
   100  			if _, err := os.Stat(file); !os.IsNotExist(err) {
   101  				t.Fatalf("file still exists after DeleteAt")
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func makeFileReadonly(t *testing.T, name string) {
   108  	if err := os.Chmod(name, 0); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  }
   112  
   113  func makeFileNotReadable(t *testing.T, name string) {
   114  	creatorOwnerSID, err := syscall.StringToSid("S-1-3-0")
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	creatorGroupSID, err := syscall.StringToSid("S-1-3-1")
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	everyoneSID, err := syscall.StringToSid("S-1-1-0")
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  
   127  	entryForSid := func(sid *syscall.SID) windows.EXPLICIT_ACCESS {
   128  		return windows.EXPLICIT_ACCESS{
   129  			AccessPermissions: 0,
   130  			AccessMode:        windows.GRANT_ACCESS,
   131  			Inheritance:       windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
   132  			Trustee: windows.TRUSTEE{
   133  				TrusteeForm: windows.TRUSTEE_IS_SID,
   134  				Name:        (uintptr)(unsafe.Pointer(sid)),
   135  			},
   136  		}
   137  	}
   138  
   139  	entries := []windows.EXPLICIT_ACCESS{
   140  		entryForSid(creatorOwnerSID),
   141  		entryForSid(creatorGroupSID),
   142  		entryForSid(everyoneSID),
   143  	}
   144  
   145  	var oldAcl, newAcl *windows.ACL
   146  	if err := windows.SetEntriesInAcl(
   147  		uint32(len(entries)),
   148  		&entries[0],
   149  		oldAcl,
   150  		&newAcl,
   151  	); err != nil {
   152  		t.Fatal(err)
   153  	}
   154  
   155  	defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newAcl)))
   156  	if err := windows.SetNamedSecurityInfo(
   157  		name,
   158  		windows.SE_FILE_OBJECT,
   159  		windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
   160  		nil,
   161  		nil,
   162  		newAcl,
   163  		nil,
   164  	); err != nil {
   165  		t.Fatal(err)
   166  	}
   167  }
   168  

View as plain text