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