1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: GPLv2+ - See COPYING in the top level directory 4 * PURPOSE: Test for RtlGetFullPathName_UstrEx 5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org> 6 */ 7 8 #include "precomp.h" 9 10 /* 11 NTSTATUS 12 NTAPI 13 RtlGetFullPathName_UstrEx( 14 IN PUNICODE_STRING FileName, 15 IN PUNICODE_STRING StaticString, 16 IN PUNICODE_STRING DynamicString, 17 IN PUNICODE_STRING *StringUsed, 18 IN PSIZE_T FilePartSize OPTIONAL, 19 OUT PBOOLEAN NameInvalid, 20 OUT RTL_PATH_TYPE* PathType, 21 OUT PSIZE_T LengthNeeded OPTIONAL 22 ); 23 */ 24 25 NTSTATUS 26 (NTAPI 27 *pRtlGetFullPathName_UstrEx)( 28 IN PUNICODE_STRING FileName, 29 IN PUNICODE_STRING StaticString, 30 IN PUNICODE_STRING DynamicString, 31 IN PUNICODE_STRING *StringUsed, 32 IN PSIZE_T FilePartSize OPTIONAL, 33 OUT PBOOLEAN NameInvalid, 34 OUT RTL_PATH_TYPE* PathType, 35 OUT PSIZE_T LengthNeeded OPTIONAL 36 ); 37 38 #define ok_eq_ustr(str1, str2) do { \ 39 ok((str1)->Buffer == (str2)->Buffer, "Buffer modified\n"); \ 40 ok((str1)->Length == (str2)->Length, "Length modified\n"); \ 41 ok((str1)->MaximumLength == (str2)->MaximumLength, "MaximumLength modified\n"); \ 42 } while (0) 43 44 static 45 BOOLEAN 46 CheckStringBuffer( 47 PCUNICODE_STRING String, 48 PCWSTR Expected) 49 { 50 SIZE_T ExpectedLength = wcslen(Expected) * sizeof(WCHAR); 51 SIZE_T EqualLength; 52 BOOLEAN Result = TRUE; 53 SIZE_T i; 54 55 if (String->Length != ExpectedLength) 56 { 57 ok(0, "String length is %u, expected %lu\n", String->Length, (ULONG)ExpectedLength); 58 Result = FALSE; 59 } 60 61 EqualLength = RtlCompareMemory(String->Buffer, Expected, ExpectedLength); 62 if (EqualLength != ExpectedLength) 63 { 64 ok(0, "String is '%wZ', expected '%S'\n", String, Expected); 65 Result = FALSE; 66 } 67 68 if (String->Buffer[String->Length / sizeof(WCHAR)] != UNICODE_NULL) 69 { 70 ok(0, "Not null terminated\n"); 71 Result = FALSE; 72 } 73 74 /* the function nulls the rest of the buffer! */ 75 for (i = String->Length + sizeof(UNICODE_NULL); i < String->MaximumLength; i++) 76 { 77 UCHAR Char = ((PUCHAR)String->Buffer)[i]; 78 if (Char != 0) 79 { 80 ok(0, "Found 0x%x at offset %lu, expected 0x%x\n", Char, (ULONG)i, 0); 81 /* don't count this as a failure unless the string was actually wrong */ 82 //Result = FALSE; 83 /* don't flood the log */ 84 break; 85 } 86 } 87 88 return Result; 89 } 90 91 static 92 BOOLEAN 93 CheckBuffer( 94 PVOID Buffer, 95 SIZE_T Size, 96 UCHAR Value) 97 { 98 PUCHAR Array = Buffer; 99 SIZE_T i; 100 101 for (i = 0; i < Size; i++) 102 { 103 if (Array[i] != Value) 104 { 105 trace("Expected %x, found %x at offset %lu\n", Value, Array[i], (ULONG)i); 106 return FALSE; 107 } 108 } 109 return TRUE; 110 } 111 112 #define RtlPathTypeNotSet 123 113 114 /* winetest_platform is "windows" for us, so broken() doesn't do what it should :( */ 115 #undef broken 116 #define broken(x) 0 117 118 typedef enum 119 { 120 PrefixNone, 121 PrefixCurrentDrive, 122 PrefixCurrentPath, 123 PrefixCurrentPathWithoutLastPart 124 } PREFIX_TYPE; 125 126 static 127 VOID 128 RunTestCases(VOID) 129 { 130 /* TODO: don't duplicate this in the other tests */ 131 /* TODO: Drive Relative tests don't work yet if the current drive isn't C: */ 132 struct 133 { 134 ULONG Line; 135 PCWSTR FileName; 136 PREFIX_TYPE PrefixType; 137 PCWSTR FullPathName; 138 RTL_PATH_TYPE PathType; 139 PREFIX_TYPE FilePartPrefixType; 140 SIZE_T FilePartSize; 141 } TestCases[] = 142 { 143 // { __LINE__, L"C:", PrefixCurrentPath, L"", RtlPathTypeDriveRelative, PrefixCurrentPathWithoutLastPart }, // This is broken 144 { __LINE__, L"C:\\", PrefixNone, L"C:\\", RtlPathTypeDriveAbsolute }, 145 { __LINE__, L"C:\\test", PrefixNone, L"C:\\test", RtlPathTypeDriveAbsolute, PrefixCurrentDrive }, 146 { __LINE__, L"C:\\test\\", PrefixNone, L"C:\\test\\", RtlPathTypeDriveAbsolute }, 147 { __LINE__, L"C:/test/", PrefixNone, L"C:\\test\\", RtlPathTypeDriveAbsolute }, 148 149 { __LINE__, L"C:\\\\test", PrefixNone, L"C:\\test", RtlPathTypeDriveAbsolute, PrefixCurrentDrive }, 150 { __LINE__, L"test", PrefixCurrentPath, L"\\test", RtlPathTypeRelative, PrefixCurrentPath, sizeof(WCHAR) }, 151 { __LINE__, L"\\test", PrefixCurrentDrive, L"test", RtlPathTypeRooted, PrefixCurrentDrive }, 152 { __LINE__, L"/test", PrefixCurrentDrive, L"test", RtlPathTypeRooted, PrefixCurrentDrive }, 153 { __LINE__, L".\\test", PrefixCurrentPath, L"\\test", RtlPathTypeRelative, PrefixCurrentPath, sizeof(WCHAR) }, 154 155 { __LINE__, L"\\.", PrefixCurrentDrive, L"", RtlPathTypeRooted }, 156 { __LINE__, L"\\.\\", PrefixCurrentDrive, L"", RtlPathTypeRooted }, 157 { __LINE__, L"\\\\.", PrefixNone, L"\\\\.\\", RtlPathTypeRootLocalDevice }, 158 { __LINE__, L"\\\\.\\", PrefixNone, L"\\\\.\\", RtlPathTypeLocalDevice }, 159 { __LINE__, L"\\\\.\\Something\\", PrefixNone, L"\\\\.\\Something\\", RtlPathTypeLocalDevice }, 160 161 { __LINE__, L"\\??\\", PrefixCurrentDrive, L"??\\", RtlPathTypeRooted }, 162 { __LINE__, L"\\??\\C:", PrefixCurrentDrive, L"??\\C:", RtlPathTypeRooted, PrefixCurrentDrive, 3 * sizeof(WCHAR) }, 163 { __LINE__, L"\\??\\C:\\", PrefixCurrentDrive, L"??\\C:\\", RtlPathTypeRooted }, 164 { __LINE__, L"\\??\\C:\\test", PrefixCurrentDrive, L"??\\C:\\test", RtlPathTypeRooted, PrefixCurrentDrive, 6 * sizeof(WCHAR) }, 165 { __LINE__, L"\\??\\C:\\test\\", PrefixCurrentDrive, L"??\\C:\\test\\", RtlPathTypeRooted }, 166 167 { __LINE__, L"\\\\??\\", PrefixNone, L"\\\\??\\", RtlPathTypeUncAbsolute }, 168 { __LINE__, L"\\\\??\\C:", PrefixNone, L"\\\\??\\C:", RtlPathTypeUncAbsolute }, 169 { __LINE__, L"\\\\??\\C:\\", PrefixNone, L"\\\\??\\C:\\", RtlPathTypeUncAbsolute }, 170 { __LINE__, L"\\\\??\\C:\\test", PrefixNone, L"\\\\??\\C:\\test", RtlPathTypeUncAbsolute, PrefixNone, sizeof(L"\\\\??\\C:\\") }, 171 { __LINE__, L"\\\\??\\C:\\test\\", PrefixNone, L"\\\\??\\C:\\test\\", RtlPathTypeUncAbsolute }, 172 }; 173 NTSTATUS Status; 174 UNICODE_STRING FileName; 175 UNICODE_STRING FullPathName; 176 WCHAR FullPathNameBuffer[MAX_PATH]; 177 UNICODE_STRING TempString; 178 PUNICODE_STRING StringUsed; 179 SIZE_T FilePartSize; 180 BOOLEAN NameInvalid; 181 RTL_PATH_TYPE PathType; 182 SIZE_T LengthNeeded; 183 WCHAR ExpectedPathName[MAX_PATH]; 184 SIZE_T ExpectedFilePartSize; 185 const INT TestCount = sizeof(TestCases) / sizeof(TestCases[0]); 186 INT i; 187 BOOLEAN Okay; 188 189 for (i = 0; i < TestCount; i++) 190 { 191 switch (TestCases[i].PrefixType) 192 { 193 case PrefixNone: 194 ExpectedPathName[0] = UNICODE_NULL; 195 break; 196 case PrefixCurrentDrive: 197 GetCurrentDirectoryW(sizeof(ExpectedPathName) / sizeof(WCHAR), ExpectedPathName); 198 ExpectedPathName[3] = UNICODE_NULL; 199 break; 200 case PrefixCurrentPath: 201 { 202 ULONG Length; 203 Length = GetCurrentDirectoryW(sizeof(ExpectedPathName) / sizeof(WCHAR), ExpectedPathName); 204 if (Length == 3 && TestCases[i].FullPathName[0]) 205 ExpectedPathName[2] = UNICODE_NULL; 206 break; 207 } 208 default: 209 skip("Invalid test!\n"); 210 continue; 211 } 212 wcscat(ExpectedPathName, TestCases[i].FullPathName); 213 RtlInitUnicodeString(&FileName, TestCases[i].FileName); 214 RtlInitEmptyUnicodeString(&FullPathName, FullPathNameBuffer, sizeof(FullPathNameBuffer)); 215 RtlFillMemory(FullPathName.Buffer, FullPathName.MaximumLength, 0xAA); 216 TempString = FileName; 217 PathType = RtlPathTypeNotSet; 218 StringUsed = InvalidPointer; 219 FilePartSize = 1234; 220 NameInvalid = (BOOLEAN)-1; 221 LengthNeeded = 1234; 222 StartSeh() 223 Status = pRtlGetFullPathName_UstrEx(&FileName, 224 &FullPathName, 225 NULL, 226 &StringUsed, 227 &FilePartSize, 228 &NameInvalid, 229 &PathType, 230 &LengthNeeded); 231 ok(Status == STATUS_SUCCESS, "Line %lu: status = %lx\n", TestCases[i].Line, Status); 232 EndSeh(STATUS_SUCCESS); 233 ok_eq_ustr(&FileName, &TempString); 234 ok(FullPathName.Buffer == FullPathNameBuffer, "Line %lu: Buffer modified\n", TestCases[i].Line); 235 ok(FullPathName.MaximumLength == sizeof(FullPathNameBuffer), "Line %lu: MaximumLength modified\n", TestCases[i].Line); 236 Okay = CheckStringBuffer(&FullPathName, ExpectedPathName); 237 ok(Okay, "Line %lu: Wrong path name '%wZ', expected '%S'\n", TestCases[i].Line, &FullPathName, ExpectedPathName); 238 ok(StringUsed == &FullPathName, "Line %lu: StringUsed = %p, expected %p\n", TestCases[i].Line, StringUsed, &FullPathName); 239 switch (TestCases[i].FilePartPrefixType) 240 { 241 case PrefixNone: 242 ExpectedFilePartSize = 0; 243 break; 244 case PrefixCurrentDrive: 245 ExpectedFilePartSize = sizeof(L"C:\\"); 246 break; 247 case PrefixCurrentPath: 248 ExpectedFilePartSize = GetCurrentDirectoryW(0, NULL) * sizeof(WCHAR); 249 if (ExpectedFilePartSize == sizeof(L"C:\\")) 250 ExpectedFilePartSize -= sizeof(WCHAR); 251 break; 252 case PrefixCurrentPathWithoutLastPart: 253 { 254 WCHAR CurrentPath[MAX_PATH]; 255 PCWSTR BackSlash; 256 ExpectedFilePartSize = GetCurrentDirectoryW(sizeof(CurrentPath) / sizeof(WCHAR), CurrentPath) * sizeof(WCHAR) + sizeof(UNICODE_NULL); 257 if (ExpectedFilePartSize == sizeof(L"C:\\")) 258 ExpectedFilePartSize = 0; 259 else 260 { 261 BackSlash = wcsrchr(CurrentPath, L'\\'); 262 if (BackSlash) 263 ExpectedFilePartSize -= wcslen(BackSlash + 1) * sizeof(WCHAR); 264 else 265 ok(0, "Line %lu: GetCurrentDirectory returned %S\n", TestCases[i].Line, CurrentPath); 266 } 267 break; 268 } 269 default: 270 skip("Invalid test!\n"); 271 continue; 272 } 273 ExpectedFilePartSize += TestCases[i].FilePartSize; 274 if (ExpectedFilePartSize != 0) 275 ExpectedFilePartSize = (ExpectedFilePartSize - sizeof(UNICODE_NULL)) / sizeof(WCHAR); 276 ok(FilePartSize == ExpectedFilePartSize, 277 "Line %lu: FilePartSize = %lu, expected %lu\n", TestCases[i].Line, (ULONG)FilePartSize, (ULONG)ExpectedFilePartSize); 278 ok(NameInvalid == FALSE, "Line %lu: NameInvalid = %u\n", TestCases[i].Line, NameInvalid); 279 ok(PathType == TestCases[i].PathType, "Line %lu: PathType = %d, expected %d\n", TestCases[i].Line, PathType, TestCases[i].PathType); 280 ok(LengthNeeded == 0, "Line %lu: LengthNeeded = %lu\n", TestCases[i].Line, (ULONG)LengthNeeded); 281 } 282 } 283 284 START_TEST(RtlGetFullPathName_UstrEx) 285 { 286 NTSTATUS Status; 287 UNICODE_STRING FileName; 288 UNICODE_STRING TempString; 289 UNICODE_STRING StaticString; 290 PUNICODE_STRING StringUsed; 291 SIZE_T FilePartSize; 292 BOOLEAN NameInvalid; 293 BOOLEAN NameInvalidArray[sizeof(ULONGLONG)]; 294 RTL_PATH_TYPE PathType; 295 SIZE_T LengthNeeded; 296 BOOLEAN Okay; 297 298 pRtlGetFullPathName_UstrEx = (PVOID)GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlGetFullPathName_UstrEx"); 299 if (!pRtlGetFullPathName_UstrEx) 300 { 301 skip("RtlGetFullPathName_UstrEx unavailable\n"); 302 return; 303 } 304 305 /* NULL parameters */ 306 StartSeh() 307 pRtlGetFullPathName_UstrEx(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 308 EndSeh(STATUS_ACCESS_VIOLATION); 309 310 RtlInitUnicodeString(&FileName, NULL); 311 TempString = FileName; 312 StartSeh() 313 pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 314 EndSeh(STATUS_ACCESS_VIOLATION); 315 ok_eq_ustr(&FileName, &TempString); 316 317 RtlInitUnicodeString(&FileName, L""); 318 TempString = FileName; 319 StartSeh() 320 pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 321 EndSeh(STATUS_ACCESS_VIOLATION); 322 ok_eq_ustr(&FileName, &TempString); 323 324 PathType = RtlPathTypeNotSet; 325 StartSeh() 326 pRtlGetFullPathName_UstrEx(NULL, NULL, NULL, NULL, NULL, NULL, &PathType, NULL); 327 EndSeh(STATUS_ACCESS_VIOLATION); 328 ok(PathType == RtlPathTypeNotSet, "PathType = %d\n", PathType); 329 330 /* Check what else is initialized before it crashes */ 331 PathType = RtlPathTypeNotSet; 332 StringUsed = InvalidPointer; 333 FilePartSize = 1234; 334 NameInvalid = (BOOLEAN)-1; 335 LengthNeeded = 1234; 336 StartSeh() 337 pRtlGetFullPathName_UstrEx(NULL, NULL, NULL, &StringUsed, &FilePartSize, &NameInvalid, &PathType, &LengthNeeded); 338 EndSeh(STATUS_ACCESS_VIOLATION); 339 ok(StringUsed == NULL, "StringUsed = %p\n", StringUsed); 340 ok(FilePartSize == 0, "FilePartSize = %lu\n", (ULONG)FilePartSize); 341 ok(NameInvalid == FALSE, "NameInvalid = %u\n", NameInvalid); 342 ok(PathType == RtlPathTypeNotSet, "PathType = %d\n", PathType); 343 ok(LengthNeeded == 0, "LengthNeeded = %lu\n", (ULONG)LengthNeeded); 344 345 RtlInitUnicodeString(&FileName, L""); 346 TempString = FileName; 347 StringUsed = InvalidPointer; 348 FilePartSize = 1234; 349 NameInvalid = (BOOLEAN)-1; 350 LengthNeeded = 1234; 351 StartSeh() 352 pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, &StringUsed, &FilePartSize, &NameInvalid, NULL, &LengthNeeded); 353 EndSeh(STATUS_ACCESS_VIOLATION); 354 ok_eq_ustr(&FileName, &TempString); 355 ok(StringUsed == NULL, "StringUsed = %p\n", StringUsed); 356 ok(FilePartSize == 0, "FilePartSize = %lu\n", (ULONG)FilePartSize); 357 ok(NameInvalid == (BOOLEAN)-1, "NameInvalid = %u\n", NameInvalid); 358 ok(LengthNeeded == 0, "LengthNeeded = %lu\n", (ULONG)LengthNeeded); 359 360 /* This is the first one that doesn't crash. FileName and PathType cannot be NULL */ 361 RtlInitUnicodeString(&FileName, NULL); 362 TempString = FileName; 363 PathType = RtlPathTypeNotSet; 364 StartSeh() 365 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, &PathType, NULL); 366 ok(Status == STATUS_OBJECT_NAME_INVALID, "status = %lx\n", Status); 367 EndSeh(STATUS_SUCCESS); 368 ok_eq_ustr(&FileName, &TempString); 369 ok(PathType == RtlPathTypeUnknown, "PathType = %d\n", PathType); 370 371 RtlInitUnicodeString(&FileName, L""); 372 TempString = FileName; 373 PathType = RtlPathTypeNotSet; 374 StartSeh() 375 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, &PathType, NULL); 376 ok(Status == STATUS_OBJECT_NAME_INVALID, "status = %lx\n", Status); 377 EndSeh(STATUS_SUCCESS); 378 ok_eq_ustr(&FileName, &TempString); 379 ok(PathType == RtlPathTypeUnknown, "PathType = %d\n", PathType); 380 381 /* Show that NameInvalid is indeed BOOLEAN */ 382 RtlInitUnicodeString(&FileName, L""); 383 TempString = FileName; 384 PathType = RtlPathTypeNotSet; 385 RtlFillMemory(NameInvalidArray, sizeof(NameInvalidArray), 0x55); 386 StartSeh() 387 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NameInvalidArray, &PathType, NULL); 388 ok(Status == STATUS_OBJECT_NAME_INVALID, "status = %lx\n", Status); 389 EndSeh(STATUS_SUCCESS); 390 ok_eq_ustr(&FileName, &TempString); 391 ok(PathType == RtlPathTypeUnknown, "PathType = %d\n", PathType); 392 ok(NameInvalidArray[0] == FALSE, "NameInvalid = %u\n", NameInvalidArray[0]); 393 Okay = CheckBuffer(NameInvalidArray + 1, sizeof(NameInvalidArray) - sizeof(NameInvalidArray[0]), 0x55); 394 ok(Okay, "CheckBuffer failed\n"); 395 396 /* Give it a valid path */ 397 RtlInitUnicodeString(&FileName, L"C:\\test"); 398 TempString = FileName; 399 PathType = RtlPathTypeNotSet; 400 StartSeh() 401 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, &PathType, NULL); 402 ok(Status == STATUS_BUFFER_TOO_SMALL, "status = %lx\n", Status); 403 EndSeh(STATUS_SUCCESS); 404 ok_eq_ustr(&FileName, &TempString); 405 ok(PathType == RtlPathTypeDriveAbsolute, "PathType = %d\n", PathType); 406 407 /* Zero-length static string */ 408 RtlInitUnicodeString(&FileName, L"C:\\test"); 409 TempString = FileName; 410 RtlInitUnicodeString(&StaticString, NULL); 411 PathType = RtlPathTypeNotSet; 412 StartSeh() 413 Status = pRtlGetFullPathName_UstrEx(&FileName, &StaticString, NULL, NULL, NULL, NULL, &PathType, NULL); 414 ok(Status == STATUS_BUFFER_TOO_SMALL, "status = %lx\n", Status); 415 EndSeh(STATUS_SUCCESS); 416 ok_eq_ustr(&FileName, &TempString); 417 ok(PathType == RtlPathTypeDriveAbsolute, "PathType = %d\n", PathType); 418 419 /* TODO: play around with StaticString and DynamicString */ 420 421 /* Check the actual functionality with different paths */ 422 RunTestCases(); 423 } 424