1 /* 2 * PROJECT: ReactOS Win32 Base API 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/win32/kernel32/client/path.c 5 * PURPOSE: Handles path APIs 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include <k32.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* GLOBALS ********************************************************************/ 17 18 UNICODE_STRING NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath"); 19 20 UNICODE_STRING BaseWindowsSystemDirectory, BaseWindowsDirectory; 21 UNICODE_STRING BaseDefaultPathAppend, BaseDefaultPath, BaseDllDirectory; 22 23 PVOID gpTermsrvGetWindowsDirectoryA; 24 PVOID gpTermsrvGetWindowsDirectoryW; 25 26 /* This is bitmask for each illegal filename character */ 27 /* If someone has time, please feel free to use 0b notation */ 28 DWORD IllegalMask[4] = 29 { 30 0xFFFFFFFF, // None allowed (00 to 1F) 31 0xFC009C05, // 20, 22, 2A, 2B, 2C, 2F, 3A, 3B, 3C, 3D, 3E, 3F not allowed 32 0x38000000, // 5B, 5C, 5D not allowed 33 0x10000000 // 7C not allowed 34 }; 35 36 BASE_SEARCH_PATH_TYPE BaseDllOrderCurrent[BaseCurrentDirPlacementMax][BaseSearchPathMax] = 37 { 38 { 39 BaseSearchPathApp, 40 BaseSearchPathCurrent, 41 BaseSearchPathDefault, 42 BaseSearchPathEnv, 43 BaseSearchPathInvalid 44 }, 45 { 46 BaseSearchPathApp, 47 BaseSearchPathDefault, 48 BaseSearchPathCurrent, 49 BaseSearchPathEnv, 50 BaseSearchPathInvalid 51 } 52 }; 53 54 BASE_SEARCH_PATH_TYPE BaseProcessOrderNoCurrent[BaseSearchPathMax] = 55 { 56 BaseSearchPathApp, 57 BaseSearchPathDefault, 58 BaseSearchPathEnv, 59 BaseSearchPathInvalid, 60 BaseSearchPathInvalid 61 }; 62 63 BASE_SEARCH_PATH_TYPE BaseDllOrderNoCurrent[BaseSearchPathMax] = 64 { 65 BaseSearchPathApp, 66 BaseSearchPathDll, 67 BaseSearchPathDefault, 68 BaseSearchPathEnv, 69 BaseSearchPathInvalid 70 }; 71 72 BASE_SEARCH_PATH_TYPE BaseProcessOrder[BaseSearchPathMax] = 73 { 74 BaseSearchPathApp, 75 BaseSearchPathCurrent, 76 BaseSearchPathDefault, 77 BaseSearchPathEnv, 78 BaseSearchPathInvalid 79 }; 80 81 BASE_CURRENT_DIR_PLACEMENT BasepDllCurrentDirPlacement = BaseCurrentDirPlacementInvalid; 82 83 extern UNICODE_STRING BasePathVariableName; 84 85 /* PRIVATE FUNCTIONS **********************************************************/ 86 87 PWCHAR 88 WINAPI 89 BasepEndOfDirName(IN PWCHAR FileName) 90 { 91 PWCHAR FileNameEnd, FileNameSeparator; 92 93 /* Find the first slash */ 94 FileNameSeparator = wcschr(FileName, OBJ_NAME_PATH_SEPARATOR); 95 if (FileNameSeparator) 96 { 97 /* Find the last one */ 98 FileNameEnd = wcsrchr(FileNameSeparator, OBJ_NAME_PATH_SEPARATOR); 99 ASSERT(FileNameEnd); 100 101 /* Handle the case where they are one and the same */ 102 if (FileNameEnd == FileNameSeparator) FileNameEnd++; 103 } 104 else 105 { 106 /* No directory was specified */ 107 FileNameEnd = NULL; 108 } 109 110 /* Return where the directory ends and the filename starts */ 111 return FileNameEnd; 112 } 113 114 LPWSTR 115 WINAPI 116 BasepComputeProcessPath(IN PBASE_SEARCH_PATH_TYPE PathOrder, 117 IN LPWSTR AppName, 118 IN LPVOID Environment) 119 { 120 PWCHAR PathBuffer, Buffer, AppNameEnd, PathCurrent; 121 SIZE_T PathLengthInBytes; 122 NTSTATUS Status; 123 UNICODE_STRING EnvPath; 124 PBASE_SEARCH_PATH_TYPE Order; 125 126 /* Initialize state */ 127 AppNameEnd = Buffer = PathBuffer = NULL; 128 Status = STATUS_SUCCESS; 129 PathLengthInBytes = 0; 130 131 /* Loop the ordering array */ 132 for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) { 133 switch (*Order) 134 { 135 /* Compute the size of the DLL path */ 136 case BaseSearchPathDll: 137 138 /* This path only gets called if SetDllDirectory was called */ 139 ASSERT(BaseDllDirectory.Buffer != NULL); 140 141 /* Make sure there's a DLL directory size */ 142 if (BaseDllDirectory.Length) 143 { 144 /* Add it, plus the separator */ 145 PathLengthInBytes += BaseDllDirectory.Length + sizeof(L';'); 146 } 147 break; 148 149 /* Compute the size of the current path */ 150 case BaseSearchPathCurrent: 151 152 /* Add ".;" */ 153 PathLengthInBytes += (2 * sizeof(WCHAR)); 154 break; 155 156 /* Compute the size of the "PATH" environment variable */ 157 case BaseSearchPathEnv: 158 159 /* Grab PEB lock if one wasn't passed in */ 160 if (!Environment) RtlAcquirePebLock(); 161 162 /* Query the size first */ 163 EnvPath.MaximumLength = 0; 164 Status = RtlQueryEnvironmentVariable_U(Environment, 165 &BasePathVariableName, 166 &EnvPath); 167 if (Status == STATUS_BUFFER_TOO_SMALL) 168 { 169 /* Compute the size we'll need for the environment */ 170 EnvPath.MaximumLength = EnvPath.Length + sizeof(WCHAR); 171 if ((EnvPath.Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) 172 { 173 /* Don't let it overflow */ 174 EnvPath.MaximumLength = EnvPath.Length; 175 } 176 177 /* Allocate the environment buffer */ 178 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 179 0, 180 EnvPath.MaximumLength); 181 if (Buffer) 182 { 183 /* Now query the PATH environment variable */ 184 EnvPath.Buffer = Buffer; 185 Status = RtlQueryEnvironmentVariable_U(Environment, 186 &BasePathVariableName, 187 &EnvPath); 188 } 189 else 190 { 191 /* Failure case */ 192 Status = STATUS_NO_MEMORY; 193 } 194 } 195 196 /* Release the PEB lock from above */ 197 if (!Environment) RtlReleasePebLock(); 198 199 /* There might not be a PATH */ 200 if (Status == STATUS_VARIABLE_NOT_FOUND) 201 { 202 /* In this case, skip this PathOrder */ 203 EnvPath.Length = EnvPath.MaximumLength = 0; 204 Status = STATUS_SUCCESS; 205 } 206 else if (!NT_SUCCESS(Status)) 207 { 208 /* An early failure, go to exit code */ 209 goto Quickie; 210 } 211 else 212 { 213 /* Add the length of the PATH variable unless it's empty */ 214 ASSERT(!(EnvPath.Length & 1)); 215 if (EnvPath.Length) 216 { 217 /* Reserve space for the variable and a semicolon */ 218 PathLengthInBytes += (EnvPath.Length + sizeof(L';')); 219 } 220 } 221 break; 222 223 /* Compute the size of the default search path */ 224 case BaseSearchPathDefault: 225 226 /* Just add it... it already has a ';' at the end */ 227 ASSERT(!(BaseDefaultPath.Length & 1)); 228 PathLengthInBytes += BaseDefaultPath.Length; 229 break; 230 231 /* Compute the size of the current app directory */ 232 case BaseSearchPathApp: 233 /* Find out where the app name ends, to get only the directory */ 234 if (AppName) AppNameEnd = BasepEndOfDirName(AppName); 235 236 /* Check if there was no application name passed in */ 237 if (!(AppName) || !(AppNameEnd)) 238 { 239 /* Do we have a per-thread CURDIR to use? */ 240 if (NtCurrentTeb()->NtTib.SubSystemTib) 241 { 242 /* This means someone added RTL_PERTHREAD_CURDIR */ 243 UNIMPLEMENTED_DBGBREAK(); 244 } 245 246 /* We do not. Do we have the LDR_ENTRY for the executable? */ 247 if (!BasepExeLdrEntry) 248 { 249 /* We do not. Grab it */ 250 LdrEnumerateLoadedModules(0, 251 BasepLocateExeLdrEntry, 252 NtCurrentPeb()->ImageBaseAddress); 253 } 254 255 /* Now do we have it? */ 256 if (BasepExeLdrEntry) 257 { 258 /* Yes, so read the name out of it */ 259 AppName = BasepExeLdrEntry->FullDllName.Buffer; 260 } 261 262 /* Find out where the app name ends, to get only the directory */ 263 if (AppName) AppNameEnd = BasepEndOfDirName(AppName); 264 } 265 266 /* So, do we have an application name and its directory? */ 267 if ((AppName) && (AppNameEnd)) 268 { 269 /* Add the size of the app's directory, plus the separator */ 270 PathLengthInBytes += ((AppNameEnd - AppName) * sizeof(WCHAR)) + sizeof(L';'); 271 } 272 break; 273 274 default: 275 break; 276 } 277 } 278 279 /* Bam, all done, we now have the final path size */ 280 ASSERT(PathLengthInBytes > 0); 281 ASSERT(!(PathLengthInBytes & 1)); 282 283 /* Allocate the buffer to hold it */ 284 PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes); 285 if (!PathBuffer) 286 { 287 /* Failure path */ 288 Status = STATUS_NO_MEMORY; 289 goto Quickie; 290 } 291 292 /* Now we loop again, this time to copy the data */ 293 PathCurrent = PathBuffer; 294 for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) { 295 switch (*Order) 296 { 297 /* Add the DLL path */ 298 case BaseSearchPathDll: 299 if (BaseDllDirectory.Length) 300 { 301 /* Copy it in the buffer, ASSERT there's enough space */ 302 ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + BaseDllDirectory.Length) <= PathLengthInBytes); 303 RtlCopyMemory(PathCurrent, 304 BaseDllDirectory.Buffer, 305 BaseDllDirectory.Length); 306 307 /* Update the current pointer, add a separator */ 308 PathCurrent += (BaseDllDirectory.Length / sizeof(WCHAR)); 309 *PathCurrent++ = ';'; 310 } 311 break; 312 313 /* Add the current application path */ 314 case BaseSearchPathApp: 315 if ((AppName) && (AppNameEnd)) 316 { 317 /* Copy it in the buffer, ASSERT there's enough space */ 318 ASSERT(((PathCurrent - PathBuffer + 1 + (AppNameEnd - AppName)) * sizeof(WCHAR)) <= PathLengthInBytes); 319 RtlCopyMemory(PathCurrent, 320 AppName, 321 (AppNameEnd - AppName) * sizeof(WCHAR)); 322 323 /* Update the current pointer, add a separator */ 324 PathCurrent += AppNameEnd - AppName; 325 *PathCurrent++ = ';'; 326 } 327 break; 328 329 /* Add the default search path */ 330 case BaseSearchPathDefault: 331 /* Copy it in the buffer, ASSERT there's enough space */ 332 ASSERT((((PathCurrent - PathBuffer) * sizeof(WCHAR)) + BaseDefaultPath.Length) <= PathLengthInBytes); 333 RtlCopyMemory(PathCurrent, BaseDefaultPath.Buffer, BaseDefaultPath.Length); 334 335 /* Update the current pointer. The default path already has a ";" */ 336 PathCurrent += (BaseDefaultPath.Length / sizeof(WCHAR)); 337 break; 338 339 /* Add the path in the PATH environment variable */ 340 case BaseSearchPathEnv: 341 if (EnvPath.Length) 342 { 343 /* Copy it in the buffer, ASSERT there's enough space */ 344 ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + EnvPath.Length) <= PathLengthInBytes); 345 RtlCopyMemory(PathCurrent, EnvPath.Buffer, EnvPath.Length); 346 347 /* Update the current pointer, add a separator */ 348 PathCurrent += (EnvPath.Length / sizeof(WCHAR)); 349 *PathCurrent++ = ';'; 350 } 351 break; 352 353 /* Add the current directory */ 354 case BaseSearchPathCurrent: 355 356 /* Copy it in the buffer, ASSERT there's enough space */ 357 ASSERT(((PathCurrent - PathBuffer + 2) * sizeof(WCHAR)) <= PathLengthInBytes); 358 *PathCurrent++ = '.'; 359 360 /* Add the path separator */ 361 *PathCurrent++ = ';'; 362 break; 363 364 default: 365 break; 366 } 367 } 368 369 /* Everything should've perfectly fit in there */ 370 ASSERT((PathCurrent - PathBuffer) * sizeof(WCHAR) == PathLengthInBytes); 371 ASSERT(PathCurrent > PathBuffer); 372 373 /* Terminate the whole thing */ 374 PathCurrent[-1] = UNICODE_NULL; 375 376 Quickie: 377 /* Exit path: free our buffers */ 378 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 379 if (PathBuffer) 380 { 381 /* This only gets freed in the failure path, since caller wants it */ 382 if (!NT_SUCCESS(Status)) 383 { 384 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer); 385 PathBuffer = NULL; 386 } 387 } 388 389 /* Return the path! */ 390 return PathBuffer; 391 } 392 393 LPWSTR 394 WINAPI 395 BaseComputeProcessSearchPath(VOID) 396 { 397 DPRINT("Computing Process Search path\n"); 398 399 /* Compute the path using default process order */ 400 return BasepComputeProcessPath(BaseProcessOrder, NULL, NULL); 401 } 402 403 LPWSTR 404 WINAPI 405 BaseComputeProcessExePath(IN LPWSTR FullPath) 406 { 407 PBASE_SEARCH_PATH_TYPE PathOrder; 408 DPRINT("Computing EXE path: %S\n", FullPath); 409 410 /* Check if we should use the current directory */ 411 PathOrder = NeedCurrentDirectoryForExePathW(FullPath) ? 412 BaseProcessOrder : BaseProcessOrderNoCurrent; 413 414 /* And now compute the path */ 415 return BasepComputeProcessPath(PathOrder, NULL, NULL); 416 } 417 418 LPWSTR 419 WINAPI 420 BaseComputeProcessDllPath(IN LPWSTR FullPath, 421 IN PVOID Environment) 422 { 423 LPWSTR DllPath = NULL; 424 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager"); 425 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"SafeDllSearchMode"); 426 OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE); 427 KEY_VALUE_PARTIAL_INFORMATION PartialInfo; 428 HANDLE KeyHandle; 429 NTSTATUS Status; 430 ULONG ResultLength; 431 BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement, OldCurrentDirPlacement; 432 433 /* Acquire DLL directory lock */ 434 RtlEnterCriticalSection(&BaseDllDirectoryLock); 435 436 /* Check if we have a base dll directory */ 437 if (BaseDllDirectory.Buffer) 438 { 439 /* Then compute the process path using DLL order (without curdir) */ 440 DllPath = BasepComputeProcessPath(BaseDllOrderNoCurrent, FullPath, Environment); 441 442 /* Release DLL directory lock */ 443 RtlLeaveCriticalSection(&BaseDllDirectoryLock); 444 445 /* Return dll path */ 446 return DllPath; 447 } 448 449 /* Release DLL directory lock */ 450 RtlLeaveCriticalSection(&BaseDllDirectoryLock); 451 452 /* Read the current placement */ 453 CurrentDirPlacement = BasepDllCurrentDirPlacement; 454 if (CurrentDirPlacement == BaseCurrentDirPlacementInvalid) 455 { 456 /* Open the configuration key */ 457 Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes); 458 if (NT_SUCCESS(Status)) 459 { 460 /* Query if safe search is enabled */ 461 Status = NtQueryValueKey(KeyHandle, 462 &ValueName, 463 KeyValuePartialInformation, 464 &PartialInfo, 465 sizeof(PartialInfo), 466 &ResultLength); 467 if (NT_SUCCESS(Status)) 468 { 469 /* Read the value if the size is OK */ 470 if (ResultLength == sizeof(PartialInfo)) 471 { 472 CurrentDirPlacement = *(PULONG)PartialInfo.Data; 473 } 474 } 475 476 /* Close the handle */ 477 NtClose(KeyHandle); 478 479 /* Validate the registry value */ 480 if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) || 481 (CurrentDirPlacement >= BaseCurrentDirPlacementMax)) 482 { 483 /* Default to safe search */ 484 CurrentDirPlacement = BaseCurrentDirPlacementSafe; 485 } 486 } 487 488 /* Update the placement and read the old one */ 489 OldCurrentDirPlacement = InterlockedCompareExchange((PLONG)&BasepDllCurrentDirPlacement, 490 CurrentDirPlacement, 491 BaseCurrentDirPlacementInvalid); 492 if (OldCurrentDirPlacement != BaseCurrentDirPlacementInvalid) 493 { 494 /* If there already was a placement, use it */ 495 CurrentDirPlacement = OldCurrentDirPlacement; 496 } 497 } 498 499 /* Check if the placement is invalid or not set */ 500 if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) || 501 (CurrentDirPlacement >= BaseCurrentDirPlacementMax)) 502 { 503 /* Default to safe search */ 504 CurrentDirPlacement = BaseCurrentDirPlacementSafe; 505 } 506 507 /* Compute the process path using either normal or safe search */ 508 DllPath = BasepComputeProcessPath(BaseDllOrderCurrent[CurrentDirPlacement], 509 FullPath, 510 Environment); 511 512 /* Return dll path */ 513 return DllPath; 514 } 515 516 BOOLEAN 517 WINAPI 518 CheckForSameCurdir(IN PUNICODE_STRING DirName) 519 { 520 PUNICODE_STRING CurDir; 521 USHORT CurLength; 522 BOOLEAN Result; 523 UNICODE_STRING CurDirCopy; 524 525 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath; 526 527 CurLength = CurDir->Length; 528 if (CurDir->Length <= 6) 529 { 530 if (CurLength != DirName->Length) return FALSE; 531 } 532 else 533 { 534 if ((CurLength - 2) != DirName->Length) return FALSE; 535 } 536 537 RtlAcquirePebLock(); 538 539 CurDirCopy = *CurDir; 540 if (CurDirCopy.Length > 6) CurDirCopy.Length -= 2; 541 542 Result = 0; 543 544 if (RtlEqualUnicodeString(&CurDirCopy, DirName, TRUE)) Result = TRUE; 545 546 RtlReleasePebLock(); 547 548 return Result; 549 } 550 551 /* 552 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is 553 * identical (other than the Rtl can optionally check for spaces), however the 554 * Rtl will always convert to OEM, while kernel32 has two possible file modes 555 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get 556 * the correct compatible results 557 */ 558 BOOL 559 WINAPI 560 IsShortName_U(IN PWCHAR Name, 561 IN ULONG Length) 562 { 563 BOOLEAN HasExtension; 564 UCHAR c; 565 NTSTATUS Status; 566 UNICODE_STRING UnicodeName; 567 ANSI_STRING AnsiName; 568 ULONG i, Dots; 569 CHAR AnsiBuffer[MAX_PATH]; 570 ASSERT(Name); 571 572 /* What do you think 8.3 means? */ 573 if (Length > 12) return FALSE; 574 575 /* Sure, any empty name is a short name */ 576 if (!Length) return TRUE; 577 578 /* This could be . or .. or something else */ 579 if (*Name == L'.') 580 { 581 /* Which one is it */ 582 if ((Length == 1) || ((Length == 2) && *(Name + 1) == L'.')) 583 { 584 /* . or .., this is good */ 585 return TRUE; 586 } 587 588 /* Some other bizare dot-based name, not good */ 589 return FALSE; 590 } 591 592 /* Initialize our two strings */ 593 RtlInitEmptyAnsiString(&AnsiName, AnsiBuffer, MAX_PATH); 594 RtlInitEmptyUnicodeString(&UnicodeName, Name, (USHORT)Length * sizeof(WCHAR)); 595 UnicodeName.Length = UnicodeName.MaximumLength; 596 597 /* Now do the conversion */ 598 Status = BasepUnicodeStringTo8BitString(&AnsiName, &UnicodeName, FALSE); 599 if (!NT_SUCCESS(Status)) return FALSE; 600 601 /* Now we loop the name */ 602 HasExtension = FALSE; 603 for (i = 0, Dots = Length - 1; i < AnsiName.Length; i++, Dots--) 604 { 605 /* Read the current byte */ 606 c = AnsiName.Buffer[i]; 607 608 /* Is it DBCS? */ 609 if (IsDBCSLeadByte(c)) 610 { 611 /* If we're near the end of the string, we can't allow a DBCS */ 612 if ((!(HasExtension) && (i >= 7)) || (i == AnsiName.Length - 1)) 613 { 614 return FALSE; 615 } 616 617 /* Otherwise we skip over it */ 618 continue; 619 } 620 621 /* Check for illegal characters */ 622 if ((c > 0x7F) || (IllegalMask[c / 32] & (1 << (c % 32)))) 623 { 624 return FALSE; 625 } 626 627 /* Check if this is perhaps an extension? */ 628 if (c == '.') 629 { 630 /* Unless the extension is too large or there's more than one */ 631 if ((HasExtension) || (Dots > 3)) return FALSE; 632 633 /* This looks like an extension */ 634 HasExtension = TRUE; 635 } 636 637 /* 8.3 length was validated, but now we must guard against 9.2 or similar */ 638 if ((i >= 8) && !(HasExtension)) return FALSE; 639 } 640 641 /* You survived the loop, this is a good short name */ 642 return TRUE; 643 } 644 645 BOOL 646 WINAPI 647 IsLongName_U(IN PWCHAR FileName, 648 IN ULONG Length) 649 { 650 BOOLEAN HasExtension; 651 ULONG i, Dots; 652 653 /* More than 8.3, any combination of dots, and NULL names are all long */ 654 if (!(Length) || (Length > 12) || (*FileName == L'.')) return TRUE; 655 656 /* Otherwise, initialize our scanning loop */ 657 HasExtension = FALSE; 658 for (i = 0, Dots = Length - 1; i < Length; i++, Dots--) 659 { 660 /* Check if this could be an extension */ 661 if (FileName[i] == L'.') 662 { 663 /* Unlike the short case, we WANT more than one extension, or a long one */ 664 if ((HasExtension) || (Dots > 3)) 665 { 666 return TRUE; 667 } 668 HasExtension = TRUE; 669 } 670 671 /* Check if this would violate the "8" in 8.3, ie. 9.2 */ 672 if ((i >= 8) && (!HasExtension)) return TRUE; 673 } 674 675 /* The name *seems* to conform to 8.3 */ 676 return FALSE; 677 } 678 679 BOOL 680 WINAPI 681 FindLFNorSFN_U(IN PWCHAR Path, 682 OUT PWCHAR *First, 683 OUT PWCHAR *Last, 684 IN BOOL UseShort) 685 { 686 PWCHAR p; 687 ULONG Length; 688 BOOL Found = 0; 689 ASSERT(Path); 690 691 /* Loop while there is something in the path */ 692 while (TRUE) 693 { 694 /* Loop within the path skipping slashes */ 695 while ((*Path == L'\\') || (*Path == L'/')) Path++; 696 697 /* Make sure there's something after the slashes too! */ 698 if (*Path == UNICODE_NULL) break; 699 700 /* Now skip past the file name until we get to the first slash */ 701 p = Path + 1; 702 while ((*p) && ((*p != L'\\') && (*p != L'/'))) p++; 703 704 /* Whatever is in between those two is now the file name length */ 705 Length = p - Path; 706 707 /* 708 * Check if it is valid 709 * Note that !IsShortName != IsLongName, these two functions simply help 710 * us determine if a conversion is necessary or not. 711 * "Found" really means: "Is a conversion necessary?", hence the "!" 712 */ 713 Found = UseShort ? !IsShortName_U(Path, Length) : !IsLongName_U(Path, Length); 714 if (Found) 715 { 716 /* It is! did the caller request to know the markers? */ 717 if ((First) && (Last)) 718 { 719 /* Return them */ 720 *First = Path; 721 *Last = p; 722 } 723 break; 724 } 725 726 /* Is there anything else following this sub-path/filename? */ 727 if (*p == UNICODE_NULL) break; 728 729 /* Yes, keep going */ 730 Path = p + 1; 731 } 732 733 /* Return if anything was found and valid */ 734 return Found; 735 } 736 737 PWCHAR 738 WINAPI 739 SkipPathTypeIndicator_U(IN LPWSTR Path) 740 { 741 PWCHAR ReturnPath; 742 ULONG i; 743 744 /* Check what kind of path this is and how many slashes to skip */ 745 switch (RtlDetermineDosPathNameType_U(Path)) 746 { 747 case RtlPathTypeUncAbsolute: 748 case RtlPathTypeLocalDevice: 749 { 750 /* Keep going until we bypass the path indicators */ 751 for (ReturnPath = Path + 2, i = 2; (i > 0) && (*ReturnPath); ReturnPath++) 752 { 753 /* We look for 2 slashes, so keep at it until we find them */ 754 if ((*ReturnPath == L'\\') || (*ReturnPath == L'/')) i--; 755 } 756 757 return ReturnPath; 758 } 759 760 case RtlPathTypeDriveAbsolute: 761 return Path + 3; 762 763 case RtlPathTypeDriveRelative: 764 return Path + 2; 765 766 case RtlPathTypeRooted: 767 return Path + 1; 768 769 case RtlPathTypeRelative: 770 return Path; 771 772 case RtlPathTypeRootLocalDevice: 773 default: 774 return NULL; 775 } 776 } 777 778 BOOL 779 WINAPI 780 BasepIsCurDirAllowedForPlainExeNames(VOID) 781 { 782 NTSTATUS Status; 783 UNICODE_STRING EmptyString; 784 785 RtlInitEmptyUnicodeString(&EmptyString, NULL, 0); 786 Status = RtlQueryEnvironmentVariable_U(NULL, 787 &NoDefaultCurrentDirectoryInExePath, 788 &EmptyString); 789 return !NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL; 790 } 791 792 /* PUBLIC FUNCTIONS ***********************************************************/ 793 794 /* 795 * @implemented 796 */ 797 BOOL 798 WINAPI 799 SetDllDirectoryW(IN LPCWSTR lpPathName) 800 { 801 UNICODE_STRING OldDirectory, DllDirectory; 802 803 if (lpPathName) 804 { 805 if (wcschr(lpPathName, L';')) 806 { 807 SetLastError(ERROR_INVALID_PARAMETER); 808 return FALSE; 809 } 810 if (!RtlCreateUnicodeString(&DllDirectory, lpPathName)) 811 { 812 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 813 return FALSE; 814 } 815 } 816 else 817 { 818 RtlInitUnicodeString(&DllDirectory, NULL); 819 } 820 821 RtlEnterCriticalSection(&BaseDllDirectoryLock); 822 823 OldDirectory = BaseDllDirectory; 824 BaseDllDirectory = DllDirectory; 825 826 RtlLeaveCriticalSection(&BaseDllDirectoryLock); 827 828 RtlFreeUnicodeString(&OldDirectory); 829 return TRUE; 830 } 831 832 /* 833 * @implemented 834 */ 835 BOOL 836 WINAPI 837 SetDllDirectoryA(IN LPCSTR lpPathName) 838 { 839 ANSI_STRING AnsiDllDirectory; 840 UNICODE_STRING OldDirectory, DllDirectory; 841 NTSTATUS Status; 842 843 if (lpPathName) 844 { 845 if (strchr(lpPathName, ';')) 846 { 847 SetLastError(ERROR_INVALID_PARAMETER); 848 return FALSE; 849 } 850 851 Status = RtlInitAnsiStringEx(&AnsiDllDirectory, lpPathName); 852 if (NT_SUCCESS(Status)) 853 { 854 Status = Basep8BitStringToUnicodeString(&DllDirectory, 855 &AnsiDllDirectory, 856 TRUE); 857 } 858 859 if (!NT_SUCCESS(Status)) 860 { 861 BaseSetLastNTError(Status); 862 return FALSE; 863 } 864 } 865 else 866 { 867 RtlInitUnicodeString(&DllDirectory, NULL); 868 } 869 870 RtlEnterCriticalSection(&BaseDllDirectoryLock); 871 872 OldDirectory = BaseDllDirectory; 873 BaseDllDirectory = DllDirectory; 874 875 RtlLeaveCriticalSection(&BaseDllDirectoryLock); 876 877 RtlFreeUnicodeString(&OldDirectory); 878 return TRUE; 879 } 880 881 /* 882 * @implemented 883 */ 884 DWORD 885 WINAPI 886 GetDllDirectoryW(IN DWORD nBufferLength, 887 OUT LPWSTR lpBuffer) 888 { 889 ULONG Length; 890 891 RtlEnterCriticalSection(&BaseDllDirectoryLock); 892 893 if ((nBufferLength * sizeof(WCHAR)) > BaseDllDirectory.Length) 894 { 895 RtlCopyMemory(lpBuffer, BaseDllDirectory.Buffer, BaseDllDirectory.Length); 896 Length = BaseDllDirectory.Length / sizeof(WCHAR); 897 lpBuffer[Length] = UNICODE_NULL; 898 } 899 else 900 { 901 Length = (BaseDllDirectory.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR); 902 if (lpBuffer) *lpBuffer = UNICODE_NULL; 903 } 904 905 RtlLeaveCriticalSection(&BaseDllDirectoryLock); 906 return Length; 907 } 908 909 /* 910 * @implemented 911 */ 912 DWORD 913 WINAPI 914 GetDllDirectoryA(IN DWORD nBufferLength, 915 OUT LPSTR lpBuffer) 916 { 917 NTSTATUS Status; 918 ANSI_STRING AnsiDllDirectory; 919 ULONG Length; 920 921 RtlInitEmptyAnsiString(&AnsiDllDirectory, lpBuffer, (USHORT)nBufferLength); 922 923 RtlEnterCriticalSection(&BaseDllDirectoryLock); 924 925 Length = BasepUnicodeStringTo8BitSize(&BaseDllDirectory); 926 if (Length > nBufferLength) 927 { 928 Status = STATUS_SUCCESS; 929 if (lpBuffer) *lpBuffer = ANSI_NULL; 930 } 931 else 932 { 933 --Length; 934 Status = BasepUnicodeStringTo8BitString(&AnsiDllDirectory, 935 &BaseDllDirectory, 936 FALSE); 937 } 938 939 RtlLeaveCriticalSection(&BaseDllDirectoryLock); 940 941 if (!NT_SUCCESS(Status)) 942 { 943 BaseSetLastNTError(Status); 944 Length = 0; 945 if (lpBuffer) *lpBuffer = ANSI_NULL; 946 } 947 948 return Length; 949 } 950 951 /* 952 * @implemented 953 */ 954 BOOL 955 WINAPI 956 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName) 957 { 958 if (wcschr(ExeName, L'\\')) return TRUE; 959 960 return BasepIsCurDirAllowedForPlainExeNames(); 961 } 962 963 /* 964 * @implemented 965 */ 966 BOOL 967 WINAPI 968 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName) 969 { 970 if (strchr(ExeName, '\\')) return TRUE; 971 972 return BasepIsCurDirAllowedForPlainExeNames(); 973 } 974 975 /* 976 * @implemented 977 * 978 * NOTE: Many of these A functions may seem to do rather complex A<->W mapping 979 * beyond what you would usually expect. There are two main reasons: 980 * 981 * First, these APIs are subject to the ANSI/OEM File API selection status that 982 * the caller has chosen, so we must use the "8BitString" internal Base APIs. 983 * 984 * Secondly, the Wide APIs (coming from the 9x world) are coded to return the 985 * length of the paths in "ANSI" by dividing their internal Wide character count 986 * by two... this is usually correct when dealing with pure-ASCII codepages but 987 * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports 988 * for CJK, for example. 989 */ 990 DWORD 991 WINAPI 992 GetFullPathNameA(IN LPCSTR lpFileName, 993 IN DWORD nBufferLength, 994 OUT LPSTR lpBuffer, 995 OUT LPSTR *lpFilePart) 996 { 997 NTSTATUS Status; 998 PWCHAR Buffer = NULL; 999 ULONG PathSize, FilePartSize; 1000 ANSI_STRING AnsiString; 1001 UNICODE_STRING FileNameString, UniString; 1002 PWCHAR LocalFilePart; 1003 PWCHAR* FilePart; 1004 1005 /* If the caller wants filepart, use a local wide buffer since this is A */ 1006 FilePart = lpFilePart != NULL ? &LocalFilePart : NULL; 1007 1008 /* Initialize for Quickie */ 1009 FilePartSize = PathSize = 0; 1010 FileNameString.Buffer = NULL; 1011 1012 /* First get our string in Unicode */ 1013 Status = Basep8BitStringToDynamicUnicodeString(&FileNameString, lpFileName); 1014 if (!NT_SUCCESS(Status)) goto Quickie; 1015 1016 /* Allocate a buffer to hold teh path name */ 1017 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 1018 0, 1019 MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL)); 1020 if (!Buffer) 1021 { 1022 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES); 1023 goto Quickie; 1024 } 1025 1026 /* Call into RTL to get the full Unicode path name */ 1027 PathSize = RtlGetFullPathName_U(FileNameString.Buffer, 1028 MAX_PATH * sizeof(WCHAR), 1029 Buffer, 1030 FilePart); 1031 if (PathSize <= (MAX_PATH * sizeof(WCHAR))) 1032 { 1033 /* The buffer will fit, get the real ANSI string size now */ 1034 Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize); 1035 if (NT_SUCCESS(Status)) 1036 { 1037 /* Now check if the user wanted file part size as well */ 1038 if ((PathSize) && (lpFilePart) && (LocalFilePart)) 1039 { 1040 /* Yep, so in this case get the length of the file part too */ 1041 Status = RtlUnicodeToMultiByteSize(&FilePartSize, 1042 Buffer, 1043 (ULONG)(LocalFilePart - Buffer) * 1044 sizeof(WCHAR)); 1045 if (!NT_SUCCESS(Status)) 1046 { 1047 /* We failed to do that, so fail the whole call */ 1048 BaseSetLastNTError(Status); 1049 PathSize = 0; 1050 } 1051 } 1052 } 1053 } 1054 else 1055 { 1056 /* Reset the path size since the buffer is not large enough */ 1057 PathSize = 0; 1058 } 1059 1060 /* Either no path, or local buffer was too small, enter failure code */ 1061 if (!PathSize) goto Quickie; 1062 1063 /* If the *caller's* buffer was too small, fail, but add in space for NULL */ 1064 if (PathSize >= nBufferLength) 1065 { 1066 PathSize++; 1067 goto Quickie; 1068 } 1069 1070 /* So far so good, initialize a unicode string to convert back to ANSI/OEM */ 1071 RtlInitUnicodeString(&UniString, Buffer); 1072 Status = BasepUnicodeStringTo8BitString(&AnsiString, &UniString, TRUE); 1073 if (!NT_SUCCESS(Status)) 1074 { 1075 /* Final conversion failed, fail the call */ 1076 BaseSetLastNTError(Status); 1077 PathSize = 0; 1078 } 1079 else 1080 { 1081 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */ 1082 RtlCopyMemory(lpBuffer, AnsiString.Buffer, PathSize + 1); 1083 RtlFreeAnsiString(&AnsiString); 1084 1085 /* And finally, did the caller request file part information? */ 1086 if (lpFilePart) 1087 { 1088 /* Use the size we computed earlier and add it to the buffer */ 1089 *lpFilePart = LocalFilePart ? &lpBuffer[FilePartSize] : 0; 1090 } 1091 } 1092 1093 Quickie: 1094 /* Cleanup and return the path size */ 1095 if (FileNameString.Buffer) RtlFreeUnicodeString(&FileNameString); 1096 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 1097 return PathSize; 1098 } 1099 1100 /* 1101 * @implemented 1102 */ 1103 DWORD 1104 WINAPI 1105 GetFullPathNameW(IN LPCWSTR lpFileName, 1106 IN DWORD nBufferLength, 1107 OUT LPWSTR lpBuffer, 1108 OUT LPWSTR *lpFilePart) 1109 { 1110 /* Call Rtl to do the work */ 1111 return RtlGetFullPathName_U(lpFileName, 1112 nBufferLength * sizeof(WCHAR), 1113 lpBuffer, 1114 lpFilePart) / sizeof(WCHAR); 1115 } 1116 1117 /* 1118 * @implemented 1119 */ 1120 DWORD 1121 WINAPI 1122 SearchPathA(IN LPCSTR lpPath OPTIONAL, 1123 IN LPCSTR lpFileName, 1124 IN LPCSTR lpExtension OPTIONAL, 1125 IN DWORD nBufferLength, 1126 OUT LPSTR lpBuffer, 1127 OUT LPSTR *lpFilePart OPTIONAL) 1128 { 1129 PUNICODE_STRING FileNameString; 1130 UNICODE_STRING PathString, ExtensionString; 1131 NTSTATUS Status; 1132 ULONG PathSize, FilePartSize, AnsiLength; 1133 PWCHAR LocalFilePart, Buffer; 1134 PWCHAR* FilePart; 1135 1136 /* If the caller wants filepart, use a local wide buffer since this is A */ 1137 FilePart = lpFilePart != NULL ? &LocalFilePart : NULL; 1138 1139 /* Initialize stuff for Quickie */ 1140 PathSize = 0; 1141 Buffer = NULL; 1142 ExtensionString.Buffer = PathString.Buffer = NULL; 1143 1144 /* Get the UNICODE_STRING file name */ 1145 FileNameString = Basep8BitStringToStaticUnicodeString(lpFileName); 1146 if (!FileNameString) return 0; 1147 1148 /* Did the caller specify an extension */ 1149 if (lpExtension) 1150 { 1151 /* Yup, convert it into UNICODE_STRING */ 1152 Status = Basep8BitStringToDynamicUnicodeString(&ExtensionString, 1153 lpExtension); 1154 if (!NT_SUCCESS(Status)) goto Quickie; 1155 } 1156 1157 /* Did the caller specify a path */ 1158 if (lpPath) 1159 { 1160 /* Yup, convert it into UNICODE_STRING */ 1161 Status = Basep8BitStringToDynamicUnicodeString(&PathString, lpPath); 1162 if (!NT_SUCCESS(Status)) goto Quickie; 1163 } 1164 1165 /* Allocate our output buffer */ 1166 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength * sizeof(WCHAR)); 1167 if (!Buffer) 1168 { 1169 /* It failed, bail out */ 1170 BaseSetLastNTError(STATUS_NO_MEMORY); 1171 goto Quickie; 1172 } 1173 1174 /* Now run the Wide search with the input buffer lengths */ 1175 PathSize = SearchPathW(PathString.Buffer, 1176 FileNameString->Buffer, 1177 ExtensionString.Buffer, 1178 nBufferLength, 1179 Buffer, 1180 FilePart); 1181 if (PathSize <= nBufferLength) 1182 { 1183 /* It fits, but is it empty? If so, bail out */ 1184 if (!PathSize) goto Quickie; 1185 1186 /* The length above is inexact, we need it in ANSI */ 1187 Status = RtlUnicodeToMultiByteSize(&AnsiLength, Buffer, PathSize * sizeof(WCHAR)); 1188 if (!NT_SUCCESS(Status)) 1189 { 1190 /* Conversion failed, fail the call */ 1191 PathSize = 0; 1192 BaseSetLastNTError(Status); 1193 goto Quickie; 1194 } 1195 1196 /* If the correct ANSI size is too big, return required length plus a NULL */ 1197 if (AnsiLength >= nBufferLength) 1198 { 1199 PathSize = AnsiLength + 1; 1200 goto Quickie; 1201 } 1202 1203 /* Now apply the final conversion to ANSI */ 1204 Status = RtlUnicodeToMultiByteN(lpBuffer, 1205 nBufferLength - 1, 1206 &AnsiLength, 1207 Buffer, 1208 PathSize * sizeof(WCHAR)); 1209 if (!NT_SUCCESS(Status)) 1210 { 1211 /* Conversion failed, fail the whole call */ 1212 PathSize = 0; 1213 BaseSetLastNTError(STATUS_NO_MEMORY); 1214 goto Quickie; 1215 } 1216 1217 /* NULL-terminate and return the real ANSI length */ 1218 lpBuffer[AnsiLength] = ANSI_NULL; 1219 PathSize = AnsiLength; 1220 1221 /* Now check if the user wanted file part size as well */ 1222 if (lpFilePart) 1223 { 1224 /* If we didn't get a file part, clear the caller's */ 1225 if (!LocalFilePart) 1226 { 1227 *lpFilePart = NULL; 1228 } 1229 else 1230 { 1231 /* Yep, so in this case get the length of the file part too */ 1232 Status = RtlUnicodeToMultiByteSize(&FilePartSize, 1233 Buffer, 1234 (ULONG)(LocalFilePart - Buffer) * 1235 sizeof(WCHAR)); 1236 if (!NT_SUCCESS(Status)) 1237 { 1238 /* We failed to do that, so fail the whole call */ 1239 BaseSetLastNTError(Status); 1240 PathSize = 0; 1241 } 1242 1243 /* Return the file part buffer */ 1244 *lpFilePart = lpBuffer + FilePartSize; 1245 } 1246 } 1247 } 1248 else 1249 { 1250 /* Our initial buffer guess was too small, allocate a bigger one */ 1251 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 1252 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize * sizeof(WCHAR)); 1253 if (!Buffer) 1254 { 1255 /* Out of memory, fail everything */ 1256 BaseSetLastNTError(STATUS_NO_MEMORY); 1257 goto Quickie; 1258 } 1259 1260 /* Do the search again -- it will fail, we just want the path size */ 1261 PathSize = SearchPathW(PathString.Buffer, 1262 FileNameString->Buffer, 1263 ExtensionString.Buffer, 1264 PathSize, 1265 Buffer, 1266 FilePart); 1267 if (!PathSize) goto Quickie; 1268 1269 /* Convert it to a correct size */ 1270 Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize * sizeof(WCHAR)); 1271 if (NT_SUCCESS(Status)) 1272 { 1273 /* Make space for the NULL-char */ 1274 PathSize++; 1275 } 1276 else 1277 { 1278 /* Conversion failed for some reason, fail the call */ 1279 BaseSetLastNTError(Status); 1280 PathSize = 0; 1281 } 1282 } 1283 1284 Quickie: 1285 /* Cleanup/complete path */ 1286 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 1287 if (ExtensionString.Buffer) RtlFreeUnicodeString(&ExtensionString); 1288 if (PathString.Buffer) RtlFreeUnicodeString(&PathString); 1289 return PathSize; 1290 } 1291 1292 /* 1293 * @implemented 1294 */ 1295 DWORD 1296 WINAPI 1297 SearchPathW(IN LPCWSTR lpPath OPTIONAL, 1298 IN LPCWSTR lpFileName, 1299 IN LPCWSTR lpExtension OPTIONAL, 1300 IN DWORD nBufferLength, 1301 OUT LPWSTR lpBuffer, 1302 OUT LPWSTR *lpFilePart OPTIONAL) 1303 { 1304 UNICODE_STRING FileNameString, ExtensionString, PathString, CallerBuffer; 1305 ULONG Flags; 1306 SIZE_T LengthNeeded, FilePartSize; 1307 NTSTATUS Status; 1308 DWORD Result = 0; 1309 1310 /* Default flags for RtlDosSearchPath_Ustr */ 1311 Flags = 6; 1312 1313 /* Clear file part in case we fail */ 1314 if (lpFilePart) *lpFilePart = NULL; 1315 1316 /* Initialize path buffer for free later */ 1317 PathString.Buffer = NULL; 1318 1319 /* Convert filename to a unicode string and eliminate trailing spaces */ 1320 RtlInitUnicodeString(&FileNameString, lpFileName); 1321 while ((FileNameString.Length >= sizeof(WCHAR)) && 1322 (FileNameString.Buffer[(FileNameString.Length / sizeof(WCHAR)) - 1] == L' ')) 1323 { 1324 FileNameString.Length -= sizeof(WCHAR); 1325 } 1326 1327 /* Was it all just spaces? */ 1328 if (!FileNameString.Length) 1329 { 1330 /* Fail out */ 1331 BaseSetLastNTError(STATUS_INVALID_PARAMETER); 1332 goto Quickie; 1333 } 1334 1335 /* Convert extension to a unicode string */ 1336 RtlInitUnicodeString(&ExtensionString, lpExtension); 1337 1338 /* Check if the user sent a path */ 1339 if (lpPath) 1340 { 1341 /* Convert it to a unicode string too */ 1342 Status = RtlInitUnicodeStringEx(&PathString, lpPath); 1343 if (NT_ERROR(Status)) 1344 { 1345 /* Fail if it was too long */ 1346 BaseSetLastNTError(Status); 1347 goto Quickie; 1348 } 1349 } 1350 else 1351 { 1352 /* A path wasn't sent, so compute it ourselves */ 1353 PathString.Buffer = BaseComputeProcessSearchPath(); 1354 if (!PathString.Buffer) 1355 { 1356 /* Fail if we couldn't compute it */ 1357 BaseSetLastNTError(STATUS_NO_MEMORY); 1358 goto Quickie; 1359 } 1360 1361 /* See how big the computed path is */ 1362 LengthNeeded = lstrlenW(PathString.Buffer); 1363 if (LengthNeeded > UNICODE_STRING_MAX_CHARS) 1364 { 1365 /* Fail if it's too long */ 1366 BaseSetLastNTError(STATUS_NAME_TOO_LONG); 1367 goto Quickie; 1368 } 1369 1370 /* Set the path size now that we have it */ 1371 PathString.MaximumLength = PathString.Length = (USHORT)LengthNeeded * sizeof(WCHAR); 1372 1373 /* Request SxS isolation from RtlDosSearchPath_Ustr */ 1374 Flags |= 1; 1375 } 1376 1377 /* Create the string that describes the output buffer from the caller */ 1378 CallerBuffer.Length = 0; 1379 CallerBuffer.Buffer = lpBuffer; 1380 1381 /* How much space does the caller have? */ 1382 if (nBufferLength <= UNICODE_STRING_MAX_CHARS) 1383 { 1384 /* Add it into the string */ 1385 CallerBuffer.MaximumLength = (USHORT)nBufferLength * sizeof(WCHAR); 1386 } 1387 else 1388 { 1389 /* Caller wants too much, limit it to the maximum length of a string */ 1390 CallerBuffer.MaximumLength = UNICODE_STRING_MAX_BYTES; 1391 } 1392 1393 /* Call Rtl to do the work */ 1394 Status = RtlDosSearchPath_Ustr(Flags, 1395 &PathString, 1396 &FileNameString, 1397 &ExtensionString, 1398 &CallerBuffer, 1399 NULL, 1400 NULL, 1401 &FilePartSize, 1402 &LengthNeeded); 1403 if (NT_ERROR(Status)) 1404 { 1405 /* Check for unusual status codes */ 1406 if ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) 1407 { 1408 /* Print them out since maybe an app needs fixing */ 1409 DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n", 1410 __FUNCTION__, 1411 &FileNameString, 1412 Status); 1413 DbgPrint(" Path = %wZ\n", &PathString); 1414 } 1415 1416 /* Check if the failure was due to a small buffer */ 1417 if (Status == STATUS_BUFFER_TOO_SMALL) 1418 { 1419 /* Check if the length was actually too big for Rtl to work with */ 1420 Result = LengthNeeded / sizeof(WCHAR); 1421 if (Result > 0xFFFFFFFF) BaseSetLastNTError(STATUS_NAME_TOO_LONG); 1422 } 1423 else 1424 { 1425 /* Some other error, set the error code */ 1426 BaseSetLastNTError(Status); 1427 } 1428 } 1429 else 1430 { 1431 /* It worked! Write the file part now */ 1432 if (lpFilePart) *lpFilePart = &lpBuffer[FilePartSize]; 1433 1434 /* Convert the final result length */ 1435 Result = CallerBuffer.Length / sizeof(WCHAR); 1436 } 1437 1438 Quickie: 1439 /* Check if there was a dynamic path string to free */ 1440 if ((PathString.Buffer != lpPath) && (PathString.Buffer)) 1441 { 1442 /* And free it */ 1443 RtlFreeHeap(RtlGetProcessHeap(), 0, PathString.Buffer); 1444 } 1445 1446 /* Return the final result length */ 1447 return Result; 1448 } 1449 1450 /* 1451 * @implemented 1452 */ 1453 DWORD 1454 WINAPI 1455 GetLongPathNameW(IN LPCWSTR lpszShortPath, 1456 OUT LPWSTR lpszLongPath, 1457 IN DWORD cchBuffer) 1458 { 1459 PWCHAR Path, Original, First, Last, Buffer, Src, Dst; 1460 SIZE_T Length, ReturnLength; 1461 WCHAR LastChar; 1462 HANDLE FindHandle; 1463 ULONG ErrorMode; 1464 BOOLEAN Found = FALSE; 1465 WIN32_FIND_DATAW FindFileData; 1466 1467 /* Initialize so Quickie knows there's nothing to do */ 1468 Buffer = Original = NULL; 1469 ReturnLength = 0; 1470 1471 /* First check if the input path was obviously NULL */ 1472 if (!lpszShortPath) 1473 { 1474 /* Fail the request */ 1475 SetLastError(ERROR_INVALID_PARAMETER); 1476 return 0; 1477 } 1478 1479 /* We will be touching removed, removable drives -- don't warn the user */ 1480 ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); 1481 1482 /* Do a simple check to see if the path exists */ 1483 if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES) 1484 { 1485 /* It doesn't, so fail */ 1486 ReturnLength = 0; 1487 goto Quickie; 1488 } 1489 1490 /* Now get a pointer to the actual path, skipping indicators */ 1491 Path = SkipPathTypeIndicator_U((LPWSTR)lpszShortPath); 1492 1493 /* Is there any path or filename in there? */ 1494 if (!(Path) || 1495 (*Path == UNICODE_NULL) || 1496 !(FindLFNorSFN_U(Path, &First, &Last, FALSE))) 1497 { 1498 /* There isn't, so the long path is simply the short path */ 1499 ReturnLength = wcslen(lpszShortPath); 1500 1501 /* Is there space for it? */ 1502 if ((cchBuffer > ReturnLength) && (lpszLongPath)) 1503 { 1504 /* Make sure the pointers aren't already the same */ 1505 if (lpszLongPath != lpszShortPath) 1506 { 1507 /* They're not -- copy the short path into the long path */ 1508 RtlMoveMemory(lpszLongPath, 1509 lpszShortPath, 1510 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL)); 1511 } 1512 } 1513 else 1514 { 1515 /* Otherwise, let caller know we need a bigger buffer, include NULL */ 1516 ReturnLength++; 1517 } 1518 goto Quickie; 1519 } 1520 1521 /* We are still in the game -- compute the current size */ 1522 Length = wcslen(lpszShortPath) + sizeof(ANSI_NULL); 1523 Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR)); 1524 if (!Original) goto ErrorQuickie; 1525 1526 /* Make a copy of it */ 1527 RtlMoveMemory(Original, lpszShortPath, Length * sizeof(WCHAR)); 1528 1529 /* Compute the new first and last markers */ 1530 First = &Original[First - lpszShortPath]; 1531 Last = &Original[Last - lpszShortPath]; 1532 1533 /* Set the current destination pointer for a copy */ 1534 Dst = lpszLongPath; 1535 1536 /* 1537 * Windows allows the paths to overlap -- we have to be careful with this and 1538 * see if it's same to do so, and if not, allocate our own internal buffer 1539 * that we'll return at the end. 1540 * 1541 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory! 1542 */ 1543 if ((cchBuffer) && (lpszLongPath) && 1544 (((lpszLongPath >= lpszShortPath) && (lpszLongPath < &lpszShortPath[Length])) || 1545 ((lpszLongPath < lpszShortPath) && (&lpszLongPath[cchBuffer] >= lpszShortPath)))) 1546 { 1547 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR)); 1548 if (!Buffer) goto ErrorQuickie; 1549 1550 /* New destination */ 1551 Dst = Buffer; 1552 } 1553 1554 /* Prepare for the loop */ 1555 Src = Original; 1556 ReturnLength = 0; 1557 while (TRUE) 1558 { 1559 /* Current delta in the loop */ 1560 Length = First - Src; 1561 1562 /* Update the return length by it */ 1563 ReturnLength += Length; 1564 1565 /* Is there a delta? If so, is there space and buffer for it? */ 1566 if ((Length) && (cchBuffer > ReturnLength) && (lpszLongPath)) 1567 { 1568 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR)); 1569 Dst += Length; 1570 } 1571 1572 /* "Terminate" this portion of the path's substring so we can do a find */ 1573 LastChar = *Last; 1574 *Last = UNICODE_NULL; 1575 FindHandle = FindFirstFileW(Original, &FindFileData); 1576 *Last = LastChar; 1577 1578 /* This portion wasn't found, so fail */ 1579 if (FindHandle == INVALID_HANDLE_VALUE) 1580 { 1581 ReturnLength = 0; 1582 break; 1583 } 1584 1585 /* Close the find handle */ 1586 FindClose(FindHandle); 1587 1588 /* Now check the length of the long name */ 1589 Length = wcslen(FindFileData.cFileName); 1590 if (Length) 1591 { 1592 /* This is our new first marker */ 1593 First = FindFileData.cFileName; 1594 } 1595 else 1596 { 1597 /* Otherwise, the name is the delta between our current markers */ 1598 Length = Last - First; 1599 } 1600 1601 /* Update the return length with the short name length, if any */ 1602 ReturnLength += Length; 1603 1604 /* Once again check for appropriate space and buffer */ 1605 if ((cchBuffer > ReturnLength) && (lpszLongPath)) 1606 { 1607 /* And do the copy if there is */ 1608 RtlMoveMemory(Dst, First, Length * sizeof(WCHAR)); 1609 Dst += Length; 1610 } 1611 1612 /* Now update the source pointer */ 1613 Src = Last; 1614 if (*Src == UNICODE_NULL) break; 1615 1616 /* Are there more names in there? */ 1617 Found = FindLFNorSFN_U(Src, &First, &Last, FALSE); 1618 if (!Found) break; 1619 } 1620 1621 /* The loop is done, is there anything left? */ 1622 if (ReturnLength) 1623 { 1624 /* Get the length of the straggling path */ 1625 Length = wcslen(Src); 1626 ReturnLength += Length; 1627 1628 /* Once again check for appropriate space and buffer */ 1629 if ((cchBuffer > ReturnLength) && (lpszLongPath)) 1630 { 1631 /* And do the copy if there is -- accounting for NULL here */ 1632 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL)); 1633 1634 /* What about our buffer? */ 1635 if (Buffer) 1636 { 1637 /* Copy it into the caller's long path */ 1638 RtlMoveMemory(lpszLongPath, 1639 Buffer, 1640 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL)); 1641 } 1642 } 1643 else 1644 { 1645 /* Buffer is too small, let the caller know, making space for NULL */ 1646 ReturnLength++; 1647 } 1648 } 1649 1650 /* We're all done */ 1651 goto Quickie; 1652 1653 ErrorQuickie: 1654 /* This is the goto for memory failures */ 1655 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1656 1657 Quickie: 1658 /* General function end: free memory, restore error mode, return length */ 1659 if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original); 1660 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 1661 SetErrorMode(ErrorMode); 1662 return ReturnLength; 1663 } 1664 1665 /* 1666 * @implemented 1667 */ 1668 DWORD 1669 WINAPI 1670 GetLongPathNameA(IN LPCSTR lpszShortPath, 1671 OUT LPSTR lpszLongPath, 1672 IN DWORD cchBuffer) 1673 { 1674 ULONG Result, PathLength; 1675 PWCHAR LongPath; 1676 NTSTATUS Status; 1677 UNICODE_STRING LongPathUni, ShortPathUni; 1678 ANSI_STRING LongPathAnsi; 1679 WCHAR LongPathBuffer[MAX_PATH]; 1680 1681 LongPath = NULL; 1682 LongPathAnsi.Buffer = NULL; 1683 ShortPathUni.Buffer = NULL; 1684 Result = 0; 1685 1686 if (!lpszShortPath) 1687 { 1688 SetLastError(ERROR_INVALID_PARAMETER); 1689 return 0; 1690 } 1691 1692 Status = Basep8BitStringToDynamicUnicodeString(&ShortPathUni, lpszShortPath); 1693 if (!NT_SUCCESS(Status)) goto Quickie; 1694 1695 LongPath = LongPathBuffer; 1696 1697 PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPathBuffer, MAX_PATH); 1698 if (PathLength >= MAX_PATH) 1699 { 1700 LongPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR)); 1701 if (!LongPath) 1702 { 1703 PathLength = 0; 1704 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1705 } 1706 else 1707 { 1708 PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPath, PathLength); 1709 } 1710 } 1711 1712 if (!PathLength) goto Quickie; 1713 1714 ShortPathUni.MaximumLength = (USHORT)PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL); 1715 LongPathUni.Buffer = LongPath; 1716 LongPathUni.Length = (USHORT)PathLength * sizeof(WCHAR); 1717 1718 Status = BasepUnicodeStringTo8BitString(&LongPathAnsi, &LongPathUni, TRUE); 1719 if (!NT_SUCCESS(Status)) 1720 { 1721 BaseSetLastNTError(Status); 1722 Result = 0; 1723 } 1724 1725 Result = LongPathAnsi.Length; 1726 if ((lpszLongPath) && (cchBuffer > LongPathAnsi.Length)) 1727 { 1728 RtlMoveMemory(lpszLongPath, LongPathAnsi.Buffer, LongPathAnsi.Length); 1729 lpszLongPath[Result] = ANSI_NULL; 1730 } 1731 else 1732 { 1733 Result = LongPathAnsi.Length + sizeof(ANSI_NULL); 1734 } 1735 1736 Quickie: 1737 if (ShortPathUni.Buffer) RtlFreeUnicodeString(&ShortPathUni); 1738 if (LongPathAnsi.Buffer) RtlFreeAnsiString(&LongPathAnsi); 1739 if ((LongPath) && (LongPath != LongPathBuffer)) 1740 { 1741 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath); 1742 } 1743 return Result; 1744 } 1745 1746 /* 1747 * @implemented 1748 */ 1749 DWORD 1750 WINAPI 1751 GetShortPathNameA(IN LPCSTR lpszLongPath, 1752 OUT LPSTR lpszShortPath, 1753 IN DWORD cchBuffer) 1754 { 1755 ULONG Result, PathLength; 1756 PWCHAR ShortPath; 1757 NTSTATUS Status; 1758 UNICODE_STRING LongPathUni, ShortPathUni; 1759 ANSI_STRING ShortPathAnsi; 1760 WCHAR ShortPathBuffer[MAX_PATH]; 1761 1762 ShortPath = NULL; 1763 ShortPathAnsi.Buffer = NULL; 1764 LongPathUni.Buffer = NULL; 1765 Result = 0; 1766 1767 if (!lpszLongPath) 1768 { 1769 SetLastError(ERROR_INVALID_PARAMETER); 1770 return 0; 1771 } 1772 1773 Status = Basep8BitStringToDynamicUnicodeString(&LongPathUni, lpszLongPath); 1774 if (!NT_SUCCESS(Status)) goto Quickie; 1775 1776 ShortPath = ShortPathBuffer; 1777 1778 PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPathBuffer, MAX_PATH); 1779 if (PathLength >= MAX_PATH) 1780 { 1781 ShortPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR)); 1782 if (!ShortPath) 1783 { 1784 PathLength = 0; 1785 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 1786 } 1787 else 1788 { 1789 PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPath, PathLength); 1790 } 1791 } 1792 1793 if (!PathLength) goto Quickie; 1794 1795 LongPathUni.MaximumLength = (USHORT)PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL); 1796 ShortPathUni.Buffer = ShortPath; 1797 ShortPathUni.Length = (USHORT)PathLength * sizeof(WCHAR); 1798 1799 Status = BasepUnicodeStringTo8BitString(&ShortPathAnsi, &ShortPathUni, TRUE); 1800 if (!NT_SUCCESS(Status)) 1801 { 1802 BaseSetLastNTError(Status); 1803 Result = 0; 1804 } 1805 1806 Result = ShortPathAnsi.Length; 1807 if ((lpszShortPath) && (cchBuffer > ShortPathAnsi.Length)) 1808 { 1809 RtlMoveMemory(lpszShortPath, ShortPathAnsi.Buffer, ShortPathAnsi.Length); 1810 lpszShortPath[Result] = ANSI_NULL; 1811 } 1812 else 1813 { 1814 Result = ShortPathAnsi.Length + sizeof(ANSI_NULL); 1815 } 1816 1817 Quickie: 1818 if (LongPathUni.Buffer) RtlFreeUnicodeString(&LongPathUni); 1819 if (ShortPathAnsi.Buffer) RtlFreeAnsiString(&ShortPathAnsi); 1820 if ((ShortPath) && (ShortPath != ShortPathBuffer)) 1821 { 1822 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath); 1823 } 1824 return Result; 1825 } 1826 1827 /* 1828 * @implemented 1829 */ 1830 DWORD 1831 WINAPI 1832 GetShortPathNameW(IN LPCWSTR lpszLongPath, 1833 OUT LPWSTR lpszShortPath, 1834 IN DWORD cchBuffer) 1835 { 1836 PWCHAR Path, Original, First, Last, Buffer, Src, Dst; 1837 SIZE_T Length, ReturnLength; 1838 WCHAR LastChar; 1839 HANDLE FindHandle; 1840 ULONG ErrorMode; 1841 BOOLEAN Found = FALSE; 1842 WIN32_FIND_DATAW FindFileData; 1843 1844 /* Initialize so Quickie knows there's nothing to do */ 1845 Buffer = Original = NULL; 1846 ReturnLength = 0; 1847 1848 /* First check if the input path was obviously NULL */ 1849 if (!lpszLongPath) 1850 { 1851 /* Fail the request */ 1852 SetLastError(ERROR_INVALID_PARAMETER); 1853 return 0; 1854 } 1855 1856 /* We will be touching removed, removable drives -- don't warn the user */ 1857 ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); 1858 1859 /* Do a simple check to see if the path exists */ 1860 if (GetFileAttributesW(lpszLongPath) == INVALID_FILE_ATTRIBUTES) 1861 { 1862 /* Windows checks for an application compatibility flag to allow this */ 1863 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & GetShortPathNameNT4)) 1864 { 1865 /* It doesn't, so fail */ 1866 ReturnLength = 0; 1867 goto Quickie; 1868 } 1869 } 1870 1871 /* Now get a pointer to the actual path, skipping indicators */ 1872 Path = SkipPathTypeIndicator_U((LPWSTR)lpszLongPath); 1873 1874 /* Is there any path or filename in there? */ 1875 if (!(Path) || 1876 (*Path == UNICODE_NULL) || 1877 !(FindLFNorSFN_U(Path, &First, &Last, TRUE))) 1878 { 1879 /* There isn't, so the long path is simply the short path */ 1880 ReturnLength = wcslen(lpszLongPath); 1881 1882 /* Is there space for it? */ 1883 if ((cchBuffer > ReturnLength) && (lpszShortPath)) 1884 { 1885 /* Make sure the pointers aren't already the same */ 1886 if (lpszLongPath != lpszShortPath) 1887 { 1888 /* They're not -- copy the short path into the long path */ 1889 RtlMoveMemory(lpszShortPath, 1890 lpszLongPath, 1891 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL)); 1892 } 1893 } 1894 else 1895 { 1896 /* Otherwise, let caller know we need a bigger buffer, include NULL */ 1897 ReturnLength++; 1898 } 1899 goto Quickie; 1900 } 1901 1902 /* We are still in the game -- compute the current size */ 1903 Length = wcslen(lpszLongPath) + sizeof(ANSI_NULL); 1904 Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR)); 1905 if (!Original) goto ErrorQuickie; 1906 1907 /* Make a copy of it */ 1908 wcsncpy(Original, lpszLongPath, Length); 1909 1910 /* Compute the new first and last markers */ 1911 First = &Original[First - lpszLongPath]; 1912 Last = &Original[Last - lpszLongPath]; 1913 1914 /* Set the current destination pointer for a copy */ 1915 Dst = lpszShortPath; 1916 1917 /* 1918 * Windows allows the paths to overlap -- we have to be careful with this and 1919 * see if it's same to do so, and if not, allocate our own internal buffer 1920 * that we'll return at the end. 1921 * 1922 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory! 1923 */ 1924 if ((cchBuffer) && (lpszShortPath) && 1925 (((lpszShortPath >= lpszLongPath) && (lpszShortPath < &lpszLongPath[Length])) || 1926 ((lpszShortPath < lpszLongPath) && (&lpszShortPath[cchBuffer] >= lpszLongPath)))) 1927 { 1928 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR)); 1929 if (!Buffer) goto ErrorQuickie; 1930 1931 /* New destination */ 1932 Dst = Buffer; 1933 } 1934 1935 /* Prepare for the loop */ 1936 Src = Original; 1937 ReturnLength = 0; 1938 while (TRUE) 1939 { 1940 /* Current delta in the loop */ 1941 Length = First - Src; 1942 1943 /* Update the return length by it */ 1944 ReturnLength += Length; 1945 1946 /* Is there a delta? If so, is there space and buffer for it? */ 1947 if ((Length) && (cchBuffer > ReturnLength) && (lpszShortPath)) 1948 { 1949 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR)); 1950 Dst += Length; 1951 } 1952 1953 /* "Terminate" this portion of the path's substring so we can do a find */ 1954 LastChar = *Last; 1955 *Last = UNICODE_NULL; 1956 FindHandle = FindFirstFileW(Original, &FindFileData); 1957 *Last = LastChar; 1958 1959 /* This portion wasn't found, so fail */ 1960 if (FindHandle == INVALID_HANDLE_VALUE) 1961 { 1962 ReturnLength = 0; 1963 break; 1964 } 1965 1966 /* Close the find handle */ 1967 FindClose(FindHandle); 1968 1969 /* Now check the length of the short name */ 1970 Length = wcslen(FindFileData.cAlternateFileName); 1971 if (Length) 1972 { 1973 /* This is our new first marker */ 1974 First = FindFileData.cAlternateFileName; 1975 } 1976 else 1977 { 1978 /* Otherwise, the name is the delta between our current markers */ 1979 Length = Last - First; 1980 } 1981 1982 /* Update the return length with the short name length, if any */ 1983 ReturnLength += Length; 1984 1985 /* Once again check for appropriate space and buffer */ 1986 if ((cchBuffer > ReturnLength) && (lpszShortPath)) 1987 { 1988 /* And do the copy if there is */ 1989 RtlMoveMemory(Dst, First, Length * sizeof(WCHAR)); 1990 Dst += Length; 1991 } 1992 1993 /* Now update the source pointer */ 1994 Src = Last; 1995 if (*Src == UNICODE_NULL) break; 1996 1997 /* Are there more names in there? */ 1998 Found = FindLFNorSFN_U(Src, &First, &Last, TRUE); 1999 if (!Found) break; 2000 } 2001 2002 /* The loop is done, is there anything left? */ 2003 if (ReturnLength) 2004 { 2005 /* Get the length of the straggling path */ 2006 Length = wcslen(Src); 2007 ReturnLength += Length; 2008 2009 /* Once again check for appropriate space and buffer */ 2010 if ((cchBuffer > ReturnLength) && (lpszShortPath)) 2011 { 2012 /* And do the copy if there is -- accounting for NULL here */ 2013 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL)); 2014 2015 /* What about our buffer? */ 2016 if (Buffer) 2017 { 2018 /* Copy it into the caller's long path */ 2019 RtlMoveMemory(lpszShortPath, 2020 Buffer, 2021 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL)); 2022 } 2023 } 2024 else 2025 { 2026 /* Buffer is too small, let the caller know, making space for NULL */ 2027 ReturnLength++; 2028 } 2029 } 2030 2031 /* We're all done */ 2032 goto Quickie; 2033 2034 ErrorQuickie: 2035 /* This is the goto for memory failures */ 2036 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 2037 2038 Quickie: 2039 /* General function end: free memory, restore error mode, return length */ 2040 if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original); 2041 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 2042 SetErrorMode(ErrorMode); 2043 return ReturnLength; 2044 } 2045 2046 /* 2047 * @implemented 2048 * 2049 * NOTE: Windows returns a dos/short (8.3) path 2050 */ 2051 DWORD 2052 WINAPI 2053 GetTempPathA(IN DWORD nBufferLength, 2054 OUT LPSTR lpBuffer) 2055 { 2056 WCHAR BufferW[MAX_PATH]; 2057 DWORD ret; 2058 2059 ret = GetTempPathW(MAX_PATH, BufferW); 2060 2061 if (!ret) return 0; 2062 2063 if (ret > MAX_PATH) 2064 { 2065 SetLastError(ERROR_FILENAME_EXCED_RANGE); 2066 return 0; 2067 } 2068 2069 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1); 2070 } 2071 2072 /* 2073 * @implemented 2074 * 2075 * ripped from wine 2076 */ 2077 DWORD 2078 WINAPI 2079 GetTempPathW(IN DWORD count, 2080 OUT LPWSTR path) 2081 { 2082 static const WCHAR tmp[] = { 'T', 'M', 'P', 0 }; 2083 static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 }; 2084 static const WCHAR userprofile[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 }; 2085 WCHAR tmp_path[MAX_PATH]; 2086 WCHAR full_tmp_path[MAX_PATH]; 2087 UINT ret; 2088 2089 DPRINT("%u,%p\n", count, path); 2090 2091 if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )) && 2092 !(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )) && 2093 !(ret = GetEnvironmentVariableW( userprofile, tmp_path, MAX_PATH )) && 2094 !(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH ))) 2095 { 2096 return 0; 2097 } 2098 2099 if (ret > MAX_PATH) 2100 { 2101 SetLastError(ERROR_FILENAME_EXCED_RANGE); 2102 return 0; 2103 } 2104 2105 ret = GetFullPathNameW(tmp_path, MAX_PATH, full_tmp_path, NULL); 2106 if (!ret) return 0; 2107 2108 if (ret > MAX_PATH - 2) 2109 { 2110 SetLastError(ERROR_FILENAME_EXCED_RANGE); 2111 return 0; 2112 } 2113 2114 if (full_tmp_path[ret-1] != '\\') 2115 { 2116 full_tmp_path[ret++] = '\\'; 2117 full_tmp_path[ret] = '\0'; 2118 } 2119 2120 ret++; /* add space for terminating 0 */ 2121 2122 if (count >= ret) 2123 { 2124 lstrcpynW(path, full_tmp_path, count); 2125 /* the remaining buffer must be zeroed up to 32766 bytes in XP or 32767 2126 * bytes after it, we will assume the > XP behavior for now */ 2127 memset(path + ret, 0, (min(count, 32767) - ret) * sizeof(WCHAR)); 2128 ret--; /* return length without 0 */ 2129 } 2130 else if (count) 2131 { 2132 /* the buffer must be cleared if contents will not fit */ 2133 memset(path, 0, count * sizeof(WCHAR)); 2134 } 2135 2136 DPRINT("GetTempPathW returning %u, %S\n", ret, path); 2137 return ret; 2138 } 2139 2140 /* 2141 * @implemented 2142 */ 2143 DWORD 2144 WINAPI 2145 GetCurrentDirectoryA(IN DWORD nBufferLength, 2146 OUT LPSTR lpBuffer) 2147 { 2148 ANSI_STRING AnsiString; 2149 NTSTATUS Status; 2150 PUNICODE_STRING StaticString; 2151 ULONG MaxLength; 2152 2153 StaticString = &NtCurrentTeb()->StaticUnicodeString; 2154 2155 MaxLength = nBufferLength; 2156 if (nBufferLength >= UNICODE_STRING_MAX_BYTES) 2157 { 2158 MaxLength = UNICODE_STRING_MAX_BYTES - 1; 2159 } 2160 2161 StaticString->Length = (USHORT)RtlGetCurrentDirectory_U(StaticString->MaximumLength, 2162 StaticString->Buffer); 2163 Status = RtlUnicodeToMultiByteSize(&nBufferLength, 2164 StaticString->Buffer, 2165 StaticString->Length); 2166 if (!NT_SUCCESS(Status)) 2167 { 2168 BaseSetLastNTError(Status); 2169 return 0; 2170 } 2171 2172 if (MaxLength <= nBufferLength) 2173 { 2174 return nBufferLength + 1; 2175 } 2176 2177 AnsiString.Buffer = lpBuffer; 2178 AnsiString.MaximumLength = (USHORT)MaxLength; 2179 Status = BasepUnicodeStringTo8BitString(&AnsiString, StaticString, FALSE); 2180 if (!NT_SUCCESS(Status)) 2181 { 2182 BaseSetLastNTError(Status); 2183 return 0; 2184 } 2185 2186 return AnsiString.Length; 2187 } 2188 2189 /* 2190 * @implemented 2191 */ 2192 DWORD 2193 WINAPI 2194 GetCurrentDirectoryW(IN DWORD nBufferLength, 2195 OUT LPWSTR lpBuffer) 2196 { 2197 return RtlGetCurrentDirectory_U(nBufferLength * sizeof(WCHAR), lpBuffer) / sizeof(WCHAR); 2198 } 2199 2200 /* 2201 * @implemented 2202 */ 2203 BOOL 2204 WINAPI 2205 SetCurrentDirectoryA(IN LPCSTR lpPathName) 2206 { 2207 PUNICODE_STRING DirName; 2208 NTSTATUS Status; 2209 2210 if (!lpPathName) 2211 { 2212 BaseSetLastNTError(STATUS_INVALID_PARAMETER); 2213 return FALSE; 2214 } 2215 2216 DirName = Basep8BitStringToStaticUnicodeString(lpPathName); 2217 if (!DirName) return FALSE; 2218 2219 if (CheckForSameCurdir(DirName)) return TRUE; 2220 2221 Status = RtlSetCurrentDirectory_U(DirName); 2222 if (NT_SUCCESS(Status)) return TRUE; 2223 2224 if ((*DirName->Buffer != L'"') || (DirName->Length <= 2)) 2225 { 2226 BaseSetLastNTError(Status); 2227 return 0; 2228 } 2229 2230 DirName = Basep8BitStringToStaticUnicodeString(lpPathName + 1); 2231 if (!DirName) return FALSE; 2232 2233 Status = RtlSetCurrentDirectory_U(DirName); 2234 if (!NT_SUCCESS(Status)) 2235 { 2236 BaseSetLastNTError(Status); 2237 return FALSE; 2238 } 2239 2240 return TRUE; 2241 } 2242 2243 /* 2244 * @implemented 2245 */ 2246 BOOL 2247 WINAPI 2248 SetCurrentDirectoryW(IN LPCWSTR lpPathName) 2249 { 2250 NTSTATUS Status; 2251 UNICODE_STRING UnicodeString; 2252 2253 if (!lpPathName) 2254 { 2255 BaseSetLastNTError(STATUS_INVALID_PARAMETER); 2256 return FALSE; 2257 } 2258 2259 Status = RtlInitUnicodeStringEx(&UnicodeString, lpPathName); 2260 if (NT_SUCCESS(Status)) 2261 { 2262 if (!CheckForSameCurdir(&UnicodeString)) 2263 { 2264 Status = RtlSetCurrentDirectory_U(&UnicodeString); 2265 } 2266 } 2267 2268 if (!NT_SUCCESS(Status)) 2269 { 2270 BaseSetLastNTError(Status); 2271 return FALSE; 2272 } 2273 2274 return TRUE; 2275 } 2276 2277 /* 2278 * @implemented 2279 */ 2280 UINT 2281 WINAPI 2282 GetSystemDirectoryA(OUT LPSTR lpBuffer, 2283 IN UINT uSize) 2284 { 2285 ANSI_STRING AnsiString; 2286 NTSTATUS Status; 2287 ULONG AnsiLength; 2288 2289 /* Get the correct size of the Unicode Base directory */ 2290 Status = RtlUnicodeToMultiByteSize(&AnsiLength, 2291 BaseWindowsSystemDirectory.Buffer, 2292 BaseWindowsSystemDirectory.MaximumLength); 2293 if (!NT_SUCCESS(Status)) return 0; 2294 2295 if (uSize < AnsiLength) return AnsiLength; 2296 2297 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize); 2298 2299 Status = BasepUnicodeStringTo8BitString(&AnsiString, 2300 &BaseWindowsSystemDirectory, 2301 FALSE); 2302 if (!NT_SUCCESS(Status)) return 0; 2303 2304 return AnsiString.Length; 2305 } 2306 2307 /* 2308 * @implemented 2309 */ 2310 UINT 2311 WINAPI 2312 GetSystemDirectoryW(OUT LPWSTR lpBuffer, 2313 IN UINT uSize) 2314 { 2315 ULONG ReturnLength; 2316 2317 ReturnLength = BaseWindowsSystemDirectory.MaximumLength; 2318 if ((uSize * sizeof(WCHAR)) >= ReturnLength) 2319 { 2320 RtlCopyMemory(lpBuffer, 2321 BaseWindowsSystemDirectory.Buffer, 2322 BaseWindowsSystemDirectory.Length); 2323 lpBuffer[BaseWindowsSystemDirectory.Length / sizeof(WCHAR)] = ANSI_NULL; 2324 2325 ReturnLength = BaseWindowsSystemDirectory.Length; 2326 } 2327 2328 return ReturnLength / sizeof(WCHAR); 2329 } 2330 2331 /* 2332 * @implemented 2333 */ 2334 UINT 2335 WINAPI 2336 GetWindowsDirectoryA(OUT LPSTR lpBuffer, 2337 IN UINT uSize) 2338 { 2339 /* Is this a TS installation? */ 2340 if (gpTermsrvGetWindowsDirectoryA) UNIMPLEMENTED; 2341 2342 /* Otherwise, call the System API */ 2343 return GetSystemWindowsDirectoryA(lpBuffer, uSize); 2344 } 2345 2346 /* 2347 * @implemented 2348 */ 2349 UINT 2350 WINAPI 2351 GetWindowsDirectoryW(OUT LPWSTR lpBuffer, 2352 IN UINT uSize) 2353 { 2354 /* Is this a TS installation? */ 2355 if (gpTermsrvGetWindowsDirectoryW) UNIMPLEMENTED; 2356 2357 /* Otherwise, call the System API */ 2358 return GetSystemWindowsDirectoryW(lpBuffer, uSize); 2359 } 2360 2361 /* 2362 * @implemented 2363 */ 2364 UINT 2365 WINAPI 2366 GetSystemWindowsDirectoryA(OUT LPSTR lpBuffer, 2367 IN UINT uSize) 2368 { 2369 ANSI_STRING AnsiString; 2370 NTSTATUS Status; 2371 ULONG AnsiLength; 2372 2373 /* Get the correct size of the Unicode Base directory */ 2374 Status = RtlUnicodeToMultiByteSize(&AnsiLength, 2375 BaseWindowsDirectory.Buffer, 2376 BaseWindowsDirectory.MaximumLength); 2377 if (!NT_SUCCESS(Status)) return 0; 2378 2379 if (uSize < AnsiLength) return AnsiLength; 2380 2381 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize); 2382 2383 Status = BasepUnicodeStringTo8BitString(&AnsiString, 2384 &BaseWindowsDirectory, 2385 FALSE); 2386 if (!NT_SUCCESS(Status)) return 0; 2387 2388 return AnsiString.Length; 2389 } 2390 2391 /* 2392 * @implemented 2393 */ 2394 UINT 2395 WINAPI 2396 GetSystemWindowsDirectoryW(OUT LPWSTR lpBuffer, 2397 IN UINT uSize) 2398 { 2399 ULONG ReturnLength; 2400 2401 ReturnLength = BaseWindowsDirectory.MaximumLength; 2402 if ((uSize * sizeof(WCHAR)) >= ReturnLength) 2403 { 2404 RtlCopyMemory(lpBuffer, 2405 BaseWindowsDirectory.Buffer, 2406 BaseWindowsDirectory.Length); 2407 lpBuffer[BaseWindowsDirectory.Length / sizeof(WCHAR)] = ANSI_NULL; 2408 2409 ReturnLength = BaseWindowsDirectory.Length; 2410 } 2411 2412 return ReturnLength / sizeof(WCHAR); 2413 } 2414 2415 /* 2416 * @unimplemented 2417 */ 2418 UINT 2419 WINAPI 2420 GetSystemWow64DirectoryW(OUT LPWSTR lpBuffer, 2421 IN UINT uSize) 2422 { 2423 #ifdef _WIN64 2424 UNIMPLEMENTED; 2425 return 0; 2426 #else 2427 SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 2428 return 0; 2429 #endif 2430 } 2431 2432 /* 2433 * @unimplemented 2434 */ 2435 UINT 2436 WINAPI 2437 GetSystemWow64DirectoryA(OUT LPSTR lpBuffer, 2438 IN UINT uSize) 2439 { 2440 #ifdef _WIN64 2441 UNIMPLEMENTED; 2442 return 0; 2443 #else 2444 SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 2445 return 0; 2446 #endif 2447 } 2448 2449 /* EOF */ 2450