1// Copyright 2012 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
5package windows_test
6
7import (
8	"fmt"
9	"io/ioutil"
10	"os"
11	"path/filepath"
12	"runtime"
13	"strconv"
14	"strings"
15	"syscall"
16	"testing"
17	"unsafe"
18
19	"golang.org/x/sys/windows"
20)
21
22func TestWin32finddata(t *testing.T) {
23	dir, err := ioutil.TempDir("", "go-build")
24	if err != nil {
25		t.Fatalf("failed to create temp directory: %v", err)
26	}
27	defer os.RemoveAll(dir)
28
29	path := filepath.Join(dir, "long_name.and_extension")
30	f, err := os.Create(path)
31	if err != nil {
32		t.Fatalf("failed to create %v: %v", path, err)
33	}
34	f.Close()
35
36	type X struct {
37		fd  windows.Win32finddata
38		got byte
39		pad [10]byte // to protect ourselves
40
41	}
42	var want byte = 2 // it is unlikely to have this character in the filename
43	x := X{got: want}
44
45	pathp, _ := windows.UTF16PtrFromString(path)
46	h, err := windows.FindFirstFile(pathp, &(x.fd))
47	if err != nil {
48		t.Fatalf("FindFirstFile failed: %v", err)
49	}
50	err = windows.FindClose(h)
51	if err != nil {
52		t.Fatalf("FindClose failed: %v", err)
53	}
54
55	if x.got != want {
56		t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
57	}
58}
59
60func TestFormatMessage(t *testing.T) {
61	dll := windows.MustLoadDLL("netevent.dll")
62
63	const TITLE_SC_MESSAGE_BOX uint32 = 0xC0001B75
64	const flags uint32 = syscall.FORMAT_MESSAGE_FROM_HMODULE | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
65	buf := make([]uint16, 300)
66	_, err := windows.FormatMessage(flags, uintptr(dll.Handle), TITLE_SC_MESSAGE_BOX, 0, buf, nil)
67	if err != nil {
68		t.Fatalf("FormatMessage for handle=%x and errno=%x failed: %v", dll.Handle, TITLE_SC_MESSAGE_BOX, err)
69	}
70}
71
72func abort(funcname string, err error) {
73	panic(funcname + " failed: " + err.Error())
74}
75
76func ExampleLoadLibrary() {
77	h, err := windows.LoadLibrary("kernel32.dll")
78	if err != nil {
79		abort("LoadLibrary", err)
80	}
81	defer windows.FreeLibrary(h)
82	proc, err := windows.GetProcAddress(h, "GetVersion")
83	if err != nil {
84		abort("GetProcAddress", err)
85	}
86	r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
87	major := byte(r)
88	minor := uint8(r >> 8)
89	build := uint16(r >> 16)
90	print("windows version ", major, ".", minor, " (Build ", build, ")\n")
91}
92
93func TestTOKEN_ALL_ACCESS(t *testing.T) {
94	if windows.TOKEN_ALL_ACCESS != 0xF01FF {
95		t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", windows.TOKEN_ALL_ACCESS)
96	}
97}
98
99func TestCreateWellKnownSid(t *testing.T) {
100	sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
101	if err != nil {
102		t.Fatalf("Unable to create well known sid for administrators: %v", err)
103	}
104	if got, want := sid.String(), "S-1-5-32-544"; got != want {
105		t.Fatalf("Builtin Administrators SID = %s, want %s", got, want)
106	}
107}
108
109func TestPseudoTokens(t *testing.T) {
110	version, err := windows.GetVersion()
111	if err != nil {
112		t.Fatal(err)
113	}
114	if ((version&0xffff)>>8)|((version&0xff)<<8) < 0x0602 {
115		return
116	}
117
118	realProcessToken, err := windows.OpenCurrentProcessToken()
119	if err != nil {
120		t.Fatal(err)
121	}
122	defer realProcessToken.Close()
123	realProcessUser, err := realProcessToken.GetTokenUser()
124	if err != nil {
125		t.Fatal(err)
126	}
127
128	pseudoProcessToken := windows.GetCurrentProcessToken()
129	pseudoProcessUser, err := pseudoProcessToken.GetTokenUser()
130	if err != nil {
131		t.Fatal(err)
132	}
133	if !windows.EqualSid(realProcessUser.User.Sid, pseudoProcessUser.User.Sid) {
134		t.Fatal("The real process token does not have the same as the pseudo process token")
135	}
136
137	runtime.LockOSThread()
138	defer runtime.UnlockOSThread()
139
140	err = windows.RevertToSelf()
141	if err != nil {
142		t.Fatal(err)
143	}
144
145	pseudoThreadToken := windows.GetCurrentThreadToken()
146	_, err = pseudoThreadToken.GetTokenUser()
147	if err != windows.ERROR_NO_TOKEN {
148		t.Fatal("Expected an empty thread token")
149	}
150	pseudoThreadEffectiveToken := windows.GetCurrentThreadEffectiveToken()
151	pseudoThreadEffectiveUser, err := pseudoThreadEffectiveToken.GetTokenUser()
152	if err != nil {
153		t.Fatal(nil)
154	}
155	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadEffectiveUser.User.Sid) {
156		t.Fatal("The real process token does not have the same as the pseudo thread effective token, even though we aren't impersonating")
157	}
158
159	err = windows.ImpersonateSelf(windows.SecurityImpersonation)
160	if err != nil {
161		t.Fatal(err)
162	}
163	defer windows.RevertToSelf()
164	pseudoThreadUser, err := pseudoThreadToken.GetTokenUser()
165	if err != nil {
166		t.Fatal(err)
167	}
168	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadUser.User.Sid) {
169		t.Fatal("The real process token does not have the same as the pseudo thread token after impersonating self")
170	}
171}
172
173func TestGUID(t *testing.T) {
174	guid, err := windows.GenerateGUID()
175	if err != nil {
176		t.Fatal(err)
177	}
178	if guid.Data1 == 0 && guid.Data2 == 0 && guid.Data3 == 0 && guid.Data4 == [8]byte{} {
179		t.Fatal("Got an all zero GUID, which is overwhelmingly unlikely")
180	}
181	want := fmt.Sprintf("{%08X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
182	got := guid.String()
183	if got != want {
184		t.Fatalf("String = %q; want %q", got, want)
185	}
186	guid2, err := windows.GUIDFromString(got)
187	if err != nil {
188		t.Fatal(err)
189	}
190	if guid2 != guid {
191		t.Fatalf("Did not parse string back to original GUID = %q; want %q", guid2, guid)
192	}
193	_, err = windows.GUIDFromString("not-a-real-guid")
194	if err != syscall.Errno(windows.CO_E_CLASSSTRING) {
195		t.Fatalf("Bad GUID string error = %v; want CO_E_CLASSSTRING", err)
196	}
197}
198
199func TestKnownFolderPath(t *testing.T) {
200	token, err := windows.OpenCurrentProcessToken()
201	if err != nil {
202		t.Fatal(err)
203	}
204	defer token.Close()
205	profileDir, err := token.GetUserProfileDirectory()
206	if err != nil {
207		t.Fatal(err)
208	}
209	want := filepath.Join(profileDir, "Desktop")
210	got, err := windows.KnownFolderPath(windows.FOLDERID_Desktop, windows.KF_FLAG_DEFAULT)
211	if err != nil {
212		t.Fatal(err)
213	}
214	if want != got {
215		t.Fatalf("Path = %q; want %q", got, want)
216	}
217}
218
219func TestRtlGetVersion(t *testing.T) {
220	version := windows.RtlGetVersion()
221	major, minor, build := windows.RtlGetNtVersionNumbers()
222	// Go is not explictly added to the application compatibility database, so
223	// these two functions should return the same thing.
224	if version.MajorVersion != major || version.MinorVersion != minor || version.BuildNumber != build {
225		t.Fatalf("%d.%d.%d != %d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber, major, minor, build)
226	}
227}
228
229func TestGetNamedSecurityInfo(t *testing.T) {
230	path, err := windows.GetSystemDirectory()
231	if err != nil {
232		t.Fatal(err)
233	}
234	sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
235	if err != nil {
236		t.Fatal(err)
237	}
238	if !sd.IsValid() {
239		t.Fatal("Invalid security descriptor")
240	}
241	sdOwner, _, err := sd.Owner()
242	if err != nil {
243		t.Fatal(err)
244	}
245	if !sdOwner.IsValid() {
246		t.Fatal("Invalid security descriptor owner")
247	}
248}
249
250func TestGetSecurityInfo(t *testing.T) {
251	sd, err := windows.GetSecurityInfo(windows.CurrentProcess(), windows.SE_KERNEL_OBJECT, windows.DACL_SECURITY_INFORMATION)
252	if err != nil {
253		t.Fatal(err)
254	}
255	if !sd.IsValid() {
256		t.Fatal("Invalid security descriptor")
257	}
258	sdStr := sd.String()
259	if !strings.HasPrefix(sdStr, "D:(A;") {
260		t.Fatalf("DACL = %q; want D:(A;...", sdStr)
261	}
262}
263
264func TestSddlConversion(t *testing.T) {
265	sd, err := windows.SecurityDescriptorFromString("O:BA")
266	if err != nil {
267		t.Fatal(err)
268	}
269	if !sd.IsValid() {
270		t.Fatal("Invalid security descriptor")
271	}
272	sdOwner, _, err := sd.Owner()
273	if err != nil {
274		t.Fatal(err)
275	}
276	if !sdOwner.IsValid() {
277		t.Fatal("Invalid security descriptor owner")
278	}
279	if !sdOwner.IsWellKnown(windows.WinBuiltinAdministratorsSid) {
280		t.Fatalf("Owner = %q; want S-1-5-32-544", sdOwner)
281	}
282}
283
284func TestBuildSecurityDescriptor(t *testing.T) {
285	const want = "O:SYD:(A;;GA;;;BA)"
286
287	adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
288	if err != nil {
289		t.Fatal(err)
290	}
291	systemSid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
292	if err != nil {
293		t.Fatal(err)
294	}
295
296	access := []windows.EXPLICIT_ACCESS{{
297		AccessPermissions: windows.GENERIC_ALL,
298		AccessMode:        windows.GRANT_ACCESS,
299		Trustee: windows.TRUSTEE{
300			TrusteeForm:  windows.TRUSTEE_IS_SID,
301			TrusteeType:  windows.TRUSTEE_IS_GROUP,
302			TrusteeValue: windows.TrusteeValueFromSID(adminSid),
303		},
304	}}
305	owner := &windows.TRUSTEE{
306		TrusteeForm:  windows.TRUSTEE_IS_SID,
307		TrusteeType:  windows.TRUSTEE_IS_USER,
308		TrusteeValue: windows.TrusteeValueFromSID(systemSid),
309	}
310
311	sd, err := windows.BuildSecurityDescriptor(owner, nil, access, nil, nil)
312	if err != nil {
313		t.Fatal(err)
314	}
315	sd, err = sd.ToAbsolute()
316	if err != nil {
317		t.Fatal(err)
318	}
319	err = sd.SetSACL(nil, false, false)
320	if err != nil {
321		t.Fatal(err)
322	}
323	if got := sd.String(); got != want {
324		t.Fatalf("SD = %q; want %q", got, want)
325	}
326	sd, err = sd.ToSelfRelative()
327	if err != nil {
328		t.Fatal(err)
329	}
330	if got := sd.String(); got != want {
331		t.Fatalf("SD = %q; want %q", got, want)
332	}
333
334	sd, err = windows.NewSecurityDescriptor()
335	if err != nil {
336		t.Fatal(err)
337	}
338	acl, err := windows.ACLFromEntries(access, nil)
339	if err != nil {
340		t.Fatal(err)
341	}
342	err = sd.SetDACL(acl, true, false)
343	if err != nil {
344		t.Fatal(err)
345	}
346	err = sd.SetOwner(systemSid, false)
347	if err != nil {
348		t.Fatal(err)
349	}
350	if got := sd.String(); got != want {
351		t.Fatalf("SD = %q; want %q", got, want)
352	}
353	sd, err = sd.ToSelfRelative()
354	if err != nil {
355		t.Fatal(err)
356	}
357	if got := sd.String(); got != want {
358		t.Fatalf("SD = %q; want %q", got, want)
359	}
360}
361
362func TestGetDiskFreeSpaceEx(t *testing.T) {
363	cwd, err := windows.UTF16PtrFromString(".")
364	if err != nil {
365		t.Fatalf(`failed to call UTF16PtrFromString("."): %v`, err)
366	}
367	var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64
368	if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil {
369		t.Fatalf("failed to call GetDiskFreeSpaceEx: %v", err)
370	}
371
372	if freeBytesAvailableToCaller == 0 {
373		t.Errorf("freeBytesAvailableToCaller: got 0; want > 0")
374	}
375	if totalNumberOfBytes == 0 {
376		t.Errorf("totalNumberOfBytes: got 0; want > 0")
377	}
378	if totalNumberOfFreeBytes == 0 {
379		t.Errorf("totalNumberOfFreeBytes: got 0; want > 0")
380	}
381}
382
383func TestGetPreferredUILanguages(t *testing.T) {
384	tab := map[string]func(flags uint32) ([]string, error){
385		"GetProcessPreferredUILanguages": windows.GetProcessPreferredUILanguages,
386		"GetThreadPreferredUILanguages":  windows.GetThreadPreferredUILanguages,
387		"GetUserPreferredUILanguages":    windows.GetUserPreferredUILanguages,
388		"GetSystemPreferredUILanguages":  windows.GetSystemPreferredUILanguages,
389	}
390	for fName, f := range tab {
391		lang, err := f(windows.MUI_LANGUAGE_ID)
392		if err != nil {
393			t.Errorf(`failed to call %v(MUI_LANGUAGE_ID): %v`, fName, err)
394		}
395		for _, l := range lang {
396			_, err := strconv.ParseUint(l, 16, 16)
397			if err != nil {
398				t.Errorf(`%v(MUI_LANGUAGE_ID) returned unexpected LANGID: %v`, fName, l)
399			}
400		}
401
402		lang, err = f(windows.MUI_LANGUAGE_NAME)
403		if err != nil {
404			t.Errorf(`failed to call %v(MUI_LANGUAGE_NAME): %v`, fName, err)
405		}
406	}
407}
408
409func TestProcessWorkingSetSizeEx(t *testing.T) {
410	// Grab a handle to the current process
411	hProcess := windows.CurrentProcess()
412
413	// Allocate memory to store the result of the query
414	var minimumWorkingSetSize, maximumWorkingSetSize uintptr
415
416	// Make the system-call
417	var flag uint32
418	windows.GetProcessWorkingSetSizeEx(hProcess, &minimumWorkingSetSize, &maximumWorkingSetSize, &flag)
419
420	// Set the new limits to the current ones
421	if err := windows.SetProcessWorkingSetSizeEx(hProcess, minimumWorkingSetSize, maximumWorkingSetSize, flag); err != nil {
422		t.Error(err)
423	}
424}
425
426func TestJobObjectInfo(t *testing.T) {
427	jo, err := windows.CreateJobObject(nil, nil)
428	if err != nil {
429		t.Fatalf("CreateJobObject failed: %v", err)
430	}
431	defer windows.CloseHandle(jo)
432
433	var info windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
434
435	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
436		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
437	if err != nil {
438		t.Fatalf("QueryInformationJobObject failed: %v", err)
439	}
440
441	const wantMemLimit = 4 * 1024
442
443	info.BasicLimitInformation.LimitFlags |= windows.JOB_OBJECT_LIMIT_PROCESS_MEMORY
444	info.ProcessMemoryLimit = wantMemLimit
445	_, err = windows.SetInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
446		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)))
447	if err != nil {
448		t.Fatalf("SetInformationJobObject failed: %v", err)
449	}
450
451	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
452		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
453	if err != nil {
454		t.Fatalf("QueryInformationJobObject failed: %v", err)
455	}
456
457	if have := info.ProcessMemoryLimit; wantMemLimit != have {
458		t.Errorf("ProcessMemoryLimit is wrong: want %v have %v", wantMemLimit, have)
459	}
460}
461