Reactos
1/*
2 * PROJECT: ReactOS api tests
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Tests for GetFinalPathNameByHandleW
5 * COPYRIGHT: Copyright 2025 Timo Kreuzer <timo.kreuzer@reactos.org>
6 */
7
8#include "precomp.h"
9#include <lm.h>
10#include <ndk/obfuncs.h>
11#include <ndk/iofuncs.h>
12
13typedef
14DWORD
15WINAPI
16FN_GetFinalPathNameByHandleW(
17 _In_ HANDLE hFile,
18 _Out_writes_(cchFilePath) LPWSTR lpszFilePath,
19 _In_ DWORD cchFilePath,
20 _In_ DWORD dwFlags);
21FN_GetFinalPathNameByHandleW* pGetFinalPathNameByHandleW = NULL;
22
23#define VOLUME_NAME_DOS 0x0
24#define VOLUME_NAME_GUID 0x1
25#define VOLUME_NAME_NT 0x2
26#define VOLUME_NAME_NONE 0x4
27#define FILE_NAME_NORMALIZED 0x0
28#define FILE_NAME_OPENED 0x8
29
30static void Test_File(void)
31{
32 WCHAR FilePath[MAX_PATH];
33 WCHAR Buffer[MAX_PATH];
34 WCHAR VolumeBuffer[4];
35 PWSTR VolumeRelativePath;
36 WCHAR ExpectedString[MAX_PATH];
37 DWORD Result;
38 SIZE_T ExpectedStringLength;
39 HANDLE hFile;
40 BOOL Success;
41
42 /* Get the full path name of the current executable */
43 Result = GetModuleFileNameW(NULL, FilePath, ARRAYSIZE(FilePath));
44 ok(Result != 0, "GetModuleFileNameW failed: %ld\n", GetLastError());
45 if (Result == 0)
46 {
47 skip("GetModuleFileNameW failed\n");
48 return;
49 }
50
51 /* Open the executable file */
52 hFile = CreateFileW(FilePath,
53 GENERIC_READ,
54 FILE_SHARE_READ | FILE_SHARE_WRITE,
55 NULL,
56 OPEN_EXISTING,
57 FILE_ATTRIBUTE_NORMAL,
58 NULL);
59 ok(hFile != INVALID_HANDLE_VALUE, "File '%S': Opening failed\n", FilePath);
60 if (hFile == INVALID_HANDLE_VALUE)
61 {
62 skip("File '%S': Opening failed\n", FilePath);
63 return;
64 }
65
66 /* Expected string for VOLUME_NAME_DOS:
67 L"\\\\?\\C:\\ReactOS\\bin\\kernel32_apitest.exe" */
68 wcscpy(ExpectedString, L"\\\\?\\");
69 wcscat(ExpectedString, FilePath);
70 ExpectedStringLength = wcslen(ExpectedString);
71
72 SetLastError(0xdeadbeef);
73 StartSeh()
74 Result = pGetFinalPathNameByHandleW(hFile, NULL, ARRAYSIZE(Buffer), VOLUME_NAME_DOS);
75 EndSeh(STATUS_ACCESS_VIOLATION)
76
77 SetLastError(0xdeadbeef);
78 memset(Buffer, 0xCC, sizeof(Buffer));
79 Result = pGetFinalPathNameByHandleW(hFile, Buffer, 0, VOLUME_NAME_DOS);
80 ok_eq_ulong(Result, ExpectedStringLength + 1);
81 ok_eq_wchar(Buffer[0], 0xCCCC);
82 ok_err(ERROR_NOT_ENOUGH_MEMORY);
83
84 SetLastError(0xdeadbeef);
85 memset(Buffer, 0xCC, sizeof(Buffer));
86 Result = pGetFinalPathNameByHandleW(hFile, Buffer, 9, VOLUME_NAME_DOS);
87 ok_eq_ulong(Result, ExpectedStringLength + 1);
88 ok_eq_wchar(Buffer[0], 0xCCCC);
89 ok_err(ERROR_NOT_ENOUGH_MEMORY);
90
91 SetLastError(0xdeadbeef);
92 memset(Buffer, 0xCC, sizeof(Buffer));
93 Result = pGetFinalPathNameByHandleW(hFile, NULL, 0, VOLUME_NAME_DOS);
94 ok_eq_ulong(Result, ExpectedStringLength + 1);
95 ok_err(ERROR_NOT_ENOUGH_MEMORY);
96
97 SetLastError(0xdeadbeef);
98 memset(Buffer, 0xCC, sizeof(Buffer));
99 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), 0x1000);
100 ok_eq_ulong(Result, ExpectedStringLength);
101 ok_eq_wstr(Buffer, ExpectedString);
102 ok_err(0);
103
104 SetLastError(0xdeadbeef);
105 memset(Buffer, 0xCC, sizeof(Buffer));
106 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), 7);
107 ok_eq_ulong(Result, 0ul);
108 ok_eq_wchar(Buffer[0], 0xCCCC);
109 ok_err(ERROR_INVALID_PARAMETER);
110
111 SetLastError(0xdeadbeef);
112 memset(Buffer, 0xCC, sizeof(Buffer));
113 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_DOS);
114 ok_eq_ulong(Result, ExpectedStringLength);
115 ok_eq_wstr(Buffer, ExpectedString);
116 ok_err(0);
117
118 SetLastError(0xdeadbeef);
119 memset(Buffer, 0xCC, sizeof(Buffer));
120 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_DOS | FILE_NAME_OPENED);
121 ok_eq_ulong(Result, ExpectedStringLength);
122 ok_eq_wstr(Buffer, ExpectedString);
123 ok_err(0);
124
125 /* Query the GUID based volume name */
126 memcpy(VolumeBuffer, FilePath, 3 * sizeof(WCHAR));
127 VolumeBuffer[3] = UNICODE_NULL;
128 Success = GetVolumeNameForVolumeMountPointW(VolumeBuffer,
129 ExpectedString,
130 ARRAYSIZE(ExpectedString));
131 if (!Success)
132 {
133 skip("GetVolumeNameForVolumeMountPointW failed: %ld\n", GetLastError());
134 return;
135 }
136
137 /* Expected string for VOLUME_NAME_GUID:
138 L"\\\\?\\Volume{cd4317d4-A62f-53d7-b36c-73f935c37280}\\ReactOS\\bin\\kernel32_apitest.exe" */
139 VolumeRelativePath = &FilePath[wcslen(L"C:\\")];
140 wcscat(ExpectedString, VolumeRelativePath);
141 ExpectedStringLength = wcslen(ExpectedString);
142
143 SetLastError(0xdeadbeef);
144 memset(Buffer, 0xCC, sizeof(Buffer));
145 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_GUID);
146 ok_eq_ulong(Result, ExpectedStringLength);
147 ok_eq_wstr(Buffer, ExpectedString);
148 ok_err(0);
149
150 SetLastError(0xdeadbeef);
151 memset(Buffer, 0xCC, sizeof(Buffer));
152 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_GUID | FILE_NAME_OPENED);
153 ok_eq_ulong(Result, ExpectedStringLength);
154 ok_eq_wstr(Buffer, ExpectedString);
155 ok_err(0);
156
157 /* Expected string for VOLUME_NAME_NT (2):
158 L"\\Device\\HarddiskVolume1\\ReactOS\\bin\\kernel32_apitest.exe" */
159 ExpectedStringLength = wcslen(L"\\Device\\HarddiskVolume*\\") +
160 wcslen(VolumeRelativePath);
161
162 SetLastError(0xdeadbeef);
163 memset(Buffer, 0xCC, sizeof(Buffer));
164 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT);
165 ok_eq_ulong(Result, ExpectedStringLength);
166 ok_int(wcsncmp(Buffer, L"\\Device\\HarddiskVolume", 22), 0);
167 ok_int(wcscmp(Buffer + 24, VolumeRelativePath), 0);
168 ok_err(0xdeadbeef);
169
170 SetLastError(0xdeadbeef);
171 memset(Buffer, 0xCC, sizeof(Buffer));
172 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT | FILE_NAME_OPENED);
173 ok_eq_ulong(Result, ExpectedStringLength);
174 ok_int(wcsncmp(Buffer, L"\\Device\\HarddiskVolume", 22), 0);
175 ok_int(wcscmp(Buffer + 24, VolumeRelativePath), 0);
176 ok_err(0xdeadbeef);
177
178 /* Expected string for VOLUME_NAME_NONE:
179 L"\\ReactOS\\bin\\kernel32_apitest.exe" */
180 ExpectedStringLength = wcslen(VolumeRelativePath - 1);
181
182 SetLastError(0xdeadbeef);
183 memset(Buffer, 0xCC, sizeof(Buffer));
184 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NONE);
185 ok_eq_ulong(Result, ExpectedStringLength);
186 ok_eq_wstr(Buffer, VolumeRelativePath - 1);
187 ok_err(0xdeadbeef);
188
189 SetLastError(0xdeadbeef);
190 memset(Buffer, 0xCC, sizeof(Buffer));
191 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NONE | FILE_NAME_OPENED);
192 ok_eq_ulong(Result, ExpectedStringLength);
193 ok_eq_wstr(Buffer, VolumeRelativePath - 1);
194 ok_err(0xdeadbeef);
195
196 SetLastError(0xdeadbeef);
197 memset(Buffer, 0xCC, sizeof(Buffer));
198 Result = pGetFinalPathNameByHandleW(hFile, Buffer, 1, VOLUME_NAME_NONE | FILE_NAME_OPENED);
199 ok_eq_ulong(Result, ExpectedStringLength + 1);
200 ok_eq_wchar(Buffer[0], 0xCCCC);
201 ok_err(ERROR_NOT_ENOUGH_MEMORY);
202
203 CloseHandle(hFile);
204}
205
206static void Test_NetworkShare(void)
207{
208 WCHAR PathBuffer[MAX_PATH];
209 WCHAR RemotePathBuffer[MAX_PATH];
210 WCHAR Buffer[MAX_PATH];
211 WCHAR ExpectedString[MAX_PATH];
212 PWSTR FileName;
213 DWORD Result;
214 SIZE_T ExpectedStringLength;
215 DWORD dwError = 0;
216 HANDLE hFile;
217 NET_API_STATUS Status;
218
219 /* Get the full path name of the current executable */
220 Result = GetModuleFileNameW(NULL, PathBuffer, ARRAYSIZE(PathBuffer));
221 ok(Result != 0, "GetModuleFileNameW failed: %ld\n", GetLastError());
222 if (Result == 0)
223 {
224 skip("GetModuleFileNameW failed: %ld\n", GetLastError());
225 return;
226 }
227
228 /* Reduce to the containing folder */
229 FileName = wcsrchr(PathBuffer, L'\\');
230 *FileName = L'\0';
231 FileName++;
232
233 // Define the share parameters
234 static WCHAR ShareName[] = L"TestShare"; // Name of the share
235
236 NetShareDel(L"", ShareName, 0);
237
238 // Set up the SHARE_INFO_2 structure
239 SHARE_INFO_2 shareInfo = { 0 };
240 shareInfo.shi2_netname = ShareName;
241 shareInfo.shi2_type = STYPE_DISKTREE; // Disk directory share
242 shareInfo.shi2_remark = L"";
243 shareInfo.shi2_permissions = ACCESS_ALL; // Full access (adjust as needed)
244 shareInfo.shi2_max_uses = -1; // Unlimited connections
245 shareInfo.shi2_current_uses = 0; // 0 for new share
246 shareInfo.shi2_path = PathBuffer;
247 shareInfo.shi2_passwd = NULL; // No password
248
249 // Call NetShareAdd to create the share
250 Status = NetShareAdd(L"", // Empty string for local machine
251 2, // Level 2 for SHARE_INFO_2
252 (LPBYTE)&shareInfo, // Share info structure
253 &dwError); // Error code if applicable
254 if (Status != NERR_Success)
255 {
256 skip("Failed to create share. Error code: %ld\n", Status);
257 if (Status == ERROR_ACCESS_DENIED)
258 {
259 wprintf(L"Error: Access denied. Ensure you have administrative privileges.\n");
260 }
261
262 return;
263 }
264
265 swprintf(RemotePathBuffer, L"\\\\localhost\\%s\\%s", ShareName, FileName);
266 hFile = CreateFileW(RemotePathBuffer,
267 GENERIC_READ,
268 FILE_SHARE_READ | FILE_SHARE_WRITE,
269 NULL,
270 OPEN_EXISTING,
271 FILE_ATTRIBUTE_NORMAL,
272 NULL);
273 ok(hFile != INVALID_HANDLE_VALUE, "Failed to open file '%S'. Error: %ld\n",
274 RemotePathBuffer, GetLastError());
275 if (hFile == INVALID_HANDLE_VALUE)
276 {
277 skip("Failed to open file '%S'. Error: %ld\n", RemotePathBuffer, GetLastError());
278 /* Clean up by deleting the share */
279 NetShareDel(L"", ShareName, 0);
280 return;
281 }
282
283 /* Expected string for VOLUME_NAME_DOS:
284 L"\\\\?\\UNC\\localhost\\TestShare\\kernel32_apitest.exe" */
285 swprintf(ExpectedString, L"\\\\?\\UNC\\localhost\\%s\\%s", ShareName, FileName);
286 ExpectedStringLength = wcslen(ExpectedString);
287 SetLastError(0xdeadbeef);
288 memset(Buffer, 0xCC, sizeof(Buffer));
289 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_DOS);
290 ok_eq_ulong(Result, ExpectedStringLength);
291 ok_eq_wstr(Buffer, ExpectedString);
292 ok_err(0xdeadbeef);
293
294 SetLastError(0xdeadbeef);
295 memset(Buffer, 0xCC, sizeof(Buffer));
296 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_DOS | FILE_NAME_OPENED);
297 ok_eq_ulong(Result, ExpectedStringLength);
298 ok_eq_wstr(Buffer, ExpectedString);
299 ok_err(0xdeadbeef);
300
301 // VOLUME_NAME_GUID doesn't work for network shares
302 SetLastError(0xdeadbeef);
303 memset(Buffer, 0xCC, sizeof(Buffer));
304 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_GUID);
305 ok_eq_ulong(Result, 0ul);
306 ok_eq_wchar(Buffer[0], 0xCCCC);
307 ok_err(ERROR_PATH_NOT_FOUND);
308
309 SetLastError(0xdeadbeef);
310 memset(Buffer, 0xCC, sizeof(Buffer));
311 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_GUID | FILE_NAME_OPENED);
312 ok_eq_ulong(Result, 0ul);
313 ok_eq_wchar(Buffer[0], 0xCCCC);
314 ok_err(ERROR_PATH_NOT_FOUND);
315
316 /* Expected string for VOLUME_NAME_NT (2):
317 L"\\Device\\Mup\\localhost\\TestShare\\kernel32_apitest.exe" */
318 swprintf(ExpectedString, L"\\Device\\Mup\\localhost\\%s\\%s", ShareName, FileName);
319 ExpectedStringLength = wcslen(ExpectedString);
320 SetLastError(0xdeadbeef);
321 memset(Buffer, 0xCC, sizeof(Buffer));
322 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT);
323 ok_eq_ulong(Result, ExpectedStringLength);
324 ok_eq_wstr(Buffer, ExpectedString);
325 ok_err(0xdeadbeef);
326
327 SetLastError(0xdeadbeef);
328 memset(Buffer, 0xCC, sizeof(Buffer));
329 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT | FILE_NAME_OPENED);
330 ok_eq_ulong(Result, ExpectedStringLength);
331 ok_eq_wstr(Buffer, ExpectedString);
332 ok_err(0xdeadbeef);
333
334 /* Expected string for VOLUME_NAME_NONE:
335 L"\\localhost\\TestShare\\kernel32_apitest.exe" */
336 swprintf(ExpectedString, L"\\localhost\\%s\\%s", ShareName, FileName);
337 ExpectedStringLength = wcslen(ExpectedString);
338 SetLastError(0xdeadbeef);
339 memset(Buffer, 0xCC, sizeof(Buffer));
340 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NONE);
341 ok_eq_ulong(Result, ExpectedStringLength);
342 ok_eq_wstr(Buffer, ExpectedString);
343 ok_err(0xdeadbeef);
344
345 SetLastError(0xdeadbeef);
346 memset(Buffer, 0xCC, sizeof(Buffer));
347 Result = pGetFinalPathNameByHandleW(hFile, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NONE | FILE_NAME_OPENED);
348 ok_eq_ulong(Result, ExpectedStringLength);
349 ok_eq_wstr(Buffer, ExpectedString);
350 ok_err(0xdeadbeef);
351
352 /* Clean up */
353 CloseHandle(hFile);
354 Status = NetShareDel(L"", ShareName, 0);
355 ok_ntstatus(Status, NERR_Success);
356}
357
358static void Test_Other(void)
359{
360 WCHAR Buffer[MAX_PATH];
361 DWORD Result;
362 HANDLE Handle;
363 UNICODE_STRING DeviceName;
364 OBJECT_ATTRIBUTES ObjectAttributes;
365 IO_STATUS_BLOCK IoStatusBlock;
366 NTSTATUS Status;
367
368 /* Test NULL handle */
369 SetLastError(0xdeadbeef);
370 Result = pGetFinalPathNameByHandleW(NULL, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_DOS);
371 ok_eq_ulong(Result, 0ul);
372 ok_err(ERROR_INVALID_HANDLE);
373
374 /* Test NULL handle and NULL buffer */
375 SetLastError(0xdeadbeef);
376 Result = pGetFinalPathNameByHandleW(NULL, NULL, ARRAYSIZE(Buffer), VOLUME_NAME_DOS);
377 ok_eq_ulong(Result, 0ul);
378 ok_err(ERROR_INVALID_HANDLE);
379
380 /* Test NULL handle and invalid volume type */
381 SetLastError(0xdeadbeef);
382 Result = pGetFinalPathNameByHandleW(NULL, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT | VOLUME_NAME_GUID);
383 ok_eq_ulong(Result, 0ul);
384 ok_err(ERROR_INVALID_PARAMETER);
385
386 /* Test INVALID_HANDLE_VALUE */
387 SetLastError(0xdeadbeef);
388 memset(Buffer, 0xCC, sizeof(Buffer));
389 Result = pGetFinalPathNameByHandleW(INVALID_HANDLE_VALUE, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_DOS);
390 ok_eq_ulong(Result, 0ul);
391 ok_eq_wchar(Buffer[0], 0xCCCC);
392 ok_err(ERROR_INVALID_HANDLE);
393
394 /* Test NtCurrentProcess */
395 SetLastError(0xdeadbeef);
396 memset(Buffer, 0xCC, sizeof(Buffer));
397 Result = pGetFinalPathNameByHandleW(NtCurrentProcess(), Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT);
398 ok_eq_ulong(Result, 0ul);
399 ok_eq_wchar(Buffer[0], 0xCCCC);
400 ok_err(ERROR_INVALID_HANDLE);
401
402 /* Open a handle to the Beep device */
403 RtlInitUnicodeString(&DeviceName, L"\\Device\\Beep");
404 InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
405 Status = NtOpenFile(&Handle, FILE_READ_ATTRIBUTES, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, 0);
406 ok_ntstatus(Status, STATUS_SUCCESS);
407 if (!NT_SUCCESS(Status))
408 {
409 skip("Opening Beep device failed\n");
410 }
411 else
412 {
413 SetLastError(0xdeadbeef);
414 memset(Buffer, 0xCC, sizeof(Buffer));
415 Result = pGetFinalPathNameByHandleW(Handle, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT);
416 ok_eq_ulong(Result, 0ul);
417 ok_eq_wchar(Buffer[0], 0xCCCC);
418 ok_err(ERROR_INVALID_FUNCTION);
419 NtClose(Handle);
420 }
421
422 /* Open a handle to the Null device */
423 RtlInitUnicodeString(&DeviceName, L"\\Device\\Null");
424 InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);
425 Status = NtOpenFile(&Handle, FILE_READ_ATTRIBUTES, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, 0);
426 ok_ntstatus(Status, STATUS_SUCCESS);
427 if (!NT_SUCCESS(Status))
428 {
429 skip("Opening Null device failed\n");
430 }
431 else
432 {
433 SetLastError(0xdeadbeef);
434 memset(Buffer, 0xCC, sizeof(Buffer));
435 Result = pGetFinalPathNameByHandleW(Handle, Buffer, ARRAYSIZE(Buffer), VOLUME_NAME_NT);
436 ok_eq_ulong(Result, 0ul);
437 ok_eq_wchar(Buffer[0], 0xCCCC);
438 ok_err(ERROR_INVALID_PARAMETER);
439 NtClose(Handle);
440 }
441}
442
443
444START_TEST(GetFinalPathNameByHandle)
445{
446 HMODULE hmodKernel32 = GetModuleHandleW(L"kernel32.dll");
447 pGetFinalPathNameByHandleW = (FN_GetFinalPathNameByHandleW*)
448 GetProcAddress(hmodKernel32, "GetFinalPathNameByHandleW");
449 if (pGetFinalPathNameByHandleW == NULL)
450 {
451 hmodKernel32 = GetModuleHandleW(L"kernel32_vista.dll");
452 pGetFinalPathNameByHandleW = (FN_GetFinalPathNameByHandleW*)
453 GetProcAddress(hmodKernel32, "GetFinalPathNameByHandleW");
454 if (pGetFinalPathNameByHandleW == NULL)
455 {
456 skip("GetFinalPathNameByHandleW not available\n");
457 return;
458 }
459 }
460
461 Test_File();
462 Test_NetworkShare();
463 Test_Other();
464}