Reactos
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
18UNICODE_STRING NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath");
19
20UNICODE_STRING BaseWindowsSystemDirectory, BaseWindowsDirectory;
21UNICODE_STRING BaseDefaultPathAppend, BaseDefaultPath, BaseDllDirectory;
22
23PVOID gpTermsrvGetWindowsDirectoryA;
24PVOID gpTermsrvGetWindowsDirectoryW;
25
26/* This is bitmask for each illegal filename character */
27/* If someone has time, please feel free to use 0b notation */
28DWORD 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
36BASE_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
54BASE_SEARCH_PATH_TYPE BaseProcessOrderNoCurrent[BaseSearchPathMax] =
55{
56 BaseSearchPathApp,
57 BaseSearchPathDefault,
58 BaseSearchPathEnv,
59 BaseSearchPathInvalid,
60 BaseSearchPathInvalid
61};
62
63BASE_SEARCH_PATH_TYPE BaseDllOrderNoCurrent[BaseSearchPathMax] =
64{
65 BaseSearchPathApp,
66 BaseSearchPathDll,
67 BaseSearchPathDefault,
68 BaseSearchPathEnv,
69 BaseSearchPathInvalid
70};
71
72BASE_SEARCH_PATH_TYPE BaseProcessOrder[BaseSearchPathMax] =
73{
74 BaseSearchPathApp,
75 BaseSearchPathCurrent,
76 BaseSearchPathDefault,
77 BaseSearchPathEnv,
78 BaseSearchPathInvalid
79};
80
81BASE_CURRENT_DIR_PLACEMENT BasepDllCurrentDirPlacement = BaseCurrentDirPlacementInvalid;
82
83extern UNICODE_STRING BasePathVariableName;
84
85/* PRIVATE FUNCTIONS **********************************************************/
86
87PWCHAR
88WINAPI
89BasepEndOfDirName(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
114LPWSTR
115WINAPI
116BasepComputeProcessPath(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
376Quickie:
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
393LPWSTR
394WINAPI
395BaseComputeProcessSearchPath(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
403LPWSTR
404WINAPI
405BaseComputeProcessExePath(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
418LPWSTR
419WINAPI
420BaseComputeProcessDllPath(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
517BOOLEAN
518WINAPI
519CheckForSameCurdir(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 */
559BOOL
560WINAPI
561IsShortName_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
646BOOL
647WINAPI
648IsLongName_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
680BOOL
681WINAPI
682FindLFNorSFN_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
738PWCHAR
739WINAPI
740SkipPathTypeIndicator_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
779BOOL
780WINAPI
781BasepIsCurDirAllowedForPlainExeNames(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 */
798BOOL
799WINAPI
800SetDllDirectoryW(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 */
836BOOL
837WINAPI
838SetDllDirectoryA(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 */
885DWORD
886WINAPI
887GetDllDirectoryW(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 */
913DWORD
914WINAPI
915GetDllDirectoryA(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 */
955BOOL
956WINAPI
957NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName)
958{
959 if (wcschr(ExeName, L'\\')) return TRUE;
960
961 return BasepIsCurDirAllowedForPlainExeNames();
962}
963
964/*
965 * @implemented
966 */
967BOOL
968WINAPI
969NeedCurrentDirectoryForExePathA(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 */
991DWORD
992WINAPI
993GetFullPathNameA(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
1094Quickie:
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 */
1104DWORD
1105WINAPI
1106GetFullPathNameW(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 */
1121DWORD
1122WINAPI
1123SearchPathA(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
1285Quickie:
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 */
1296DWORD
1297WINAPI
1298SearchPathW(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
1439Quickie:
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 */
1454DWORD
1455WINAPI
1456GetLongPathNameW(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
1654ErrorQuickie:
1655 /* This is the goto for memory failures */
1656 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1657
1658Quickie:
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 */
1669DWORD
1670WINAPI
1671GetLongPathNameA(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
1737Quickie:
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 */
1750DWORD
1751WINAPI
1752GetShortPathNameW(IN LPCWSTR lpszLongPath,
1753 OUT LPWSTR lpszShortPath,
1754 IN DWORD cchBuffer)
1755{
1756 PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
1757 SIZE_T Length, ReturnLength;
1758 WCHAR LastChar;
1759 HANDLE FindHandle;
1760 ULONG ErrorMode;
1761 BOOLEAN Found = FALSE;
1762 WIN32_FIND_DATAW FindFileData;
1763
1764 /* Initialize so Quickie knows there's nothing to do */
1765 Buffer = Original = NULL;
1766 ReturnLength = 0;
1767
1768 /* First check if the input path was obviously NULL */
1769 if (!lpszLongPath)
1770 {
1771 /* Fail the request */
1772 SetLastError(ERROR_INVALID_PARAMETER);
1773 return 0;
1774 }
1775
1776 /* We will be touching removed, removable drives -- don't warn the user */
1777 ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
1778
1779 /* Do a simple check to see if the path exists */
1780 if (GetFileAttributesW(lpszLongPath) == INVALID_FILE_ATTRIBUTES)
1781 {
1782 /* Windows checks for an application compatibility flag to allow this */
1783 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & GetShortPathNameNT4))
1784 {
1785 /* It doesn't, so fail */
1786 ReturnLength = 0;
1787 goto Quickie;
1788 }
1789 }
1790
1791 /* Now get a pointer to the actual path, skipping indicators */
1792 Path = SkipPathTypeIndicator_U((LPWSTR)lpszLongPath);
1793
1794 /* Is there any path or filename in there? */
1795 if (!(Path) ||
1796 (*Path == UNICODE_NULL) ||
1797 !(FindLFNorSFN_U(Path, &First, &Last, TRUE)))
1798 {
1799 /* There isn't, so the long path is simply the short path */
1800 ReturnLength = wcslen(lpszLongPath);
1801
1802 /* Is there space for it? */
1803 if ((cchBuffer > ReturnLength) && (lpszShortPath))
1804 {
1805 /* Make sure the pointers aren't already the same */
1806 if (lpszLongPath != lpszShortPath)
1807 {
1808 /* They're not -- copy the short path into the long path */
1809 RtlMoveMemory(lpszShortPath,
1810 lpszLongPath,
1811 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1812 }
1813 }
1814 else
1815 {
1816 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1817 ReturnLength++;
1818 }
1819 goto Quickie;
1820 }
1821
1822 /* We are still in the game -- compute the current size */
1823 Length = wcslen(lpszLongPath) + sizeof(ANSI_NULL);
1824 Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
1825 if (!Original) goto ErrorQuickie;
1826
1827 /* Make a copy of it */
1828 wcsncpy(Original, lpszLongPath, Length);
1829
1830 /* Compute the new first and last markers */
1831 First = &Original[First - lpszLongPath];
1832 Last = &Original[Last - lpszLongPath];
1833
1834 /* Set the current destination pointer for a copy */
1835 Dst = lpszShortPath;
1836
1837 /*
1838 * Windows allows the paths to overlap -- we have to be careful with this and
1839 * see if it's same to do so, and if not, allocate our own internal buffer
1840 * that we'll return at the end.
1841 *
1842 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1843 */
1844 if ((cchBuffer) && (lpszShortPath) &&
1845 (((lpszShortPath >= lpszLongPath) && (lpszShortPath < &lpszLongPath[Length])) ||
1846 ((lpszShortPath < lpszLongPath) && (&lpszShortPath[cchBuffer] >= lpszLongPath))))
1847 {
1848 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
1849 if (!Buffer) goto ErrorQuickie;
1850
1851 /* New destination */
1852 Dst = Buffer;
1853 }
1854
1855 /* Prepare for the loop */
1856 Src = Original;
1857 ReturnLength = 0;
1858 while (TRUE)
1859 {
1860 /* Current delta in the loop */
1861 Length = First - Src;
1862
1863 /* Update the return length by it */
1864 ReturnLength += Length;
1865
1866 /* Is there a delta? If so, is there space and buffer for it? */
1867 if ((Length) && (cchBuffer > ReturnLength) && (lpszShortPath))
1868 {
1869 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
1870 Dst += Length;
1871 }
1872
1873 /* "Terminate" this portion of the path's substring so we can do a find */
1874 LastChar = *Last;
1875 *Last = UNICODE_NULL;
1876 FindHandle = FindFirstFileW(Original, &FindFileData);
1877 *Last = LastChar;
1878
1879 /* This portion wasn't found, so fail */
1880 if (FindHandle == INVALID_HANDLE_VALUE)
1881 {
1882 ReturnLength = 0;
1883 break;
1884 }
1885
1886 /* Close the find handle */
1887 FindClose(FindHandle);
1888
1889 /* Now check the length of the short name */
1890 Length = wcslen(FindFileData.cAlternateFileName);
1891 if (Length)
1892 {
1893 /* This is our new first marker */
1894 First = FindFileData.cAlternateFileName;
1895 }
1896 else
1897 {
1898 /* Otherwise, the name is the delta between our current markers */
1899 Length = Last - First;
1900 }
1901
1902 /* Update the return length with the short name length, if any */
1903 ReturnLength += Length;
1904
1905 /* Once again check for appropriate space and buffer */
1906 if ((cchBuffer > ReturnLength) && (lpszShortPath))
1907 {
1908 /* And do the copy if there is */
1909 RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
1910 Dst += Length;
1911 }
1912
1913 /* Now update the source pointer */
1914 Src = Last;
1915 if (*Src == UNICODE_NULL) break;
1916
1917 /* Are there more names in there? */
1918 Found = FindLFNorSFN_U(Src, &First, &Last, TRUE);
1919 if (!Found) break;
1920 }
1921
1922 /* The loop is done, is there anything left? */
1923 if (ReturnLength)
1924 {
1925 /* Get the length of the straggling path */
1926 Length = wcslen(Src);
1927 ReturnLength += Length;
1928
1929 /* Once again check for appropriate space and buffer */
1930 if ((cchBuffer > ReturnLength) && (lpszShortPath))
1931 {
1932 /* And do the copy if there is -- accounting for NULL here */
1933 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1934
1935 /* What about our buffer? */
1936 if (Buffer)
1937 {
1938 /* Copy it into the caller's long path */
1939 RtlMoveMemory(lpszShortPath,
1940 Buffer,
1941 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1942 }
1943 }
1944 else
1945 {
1946 /* Buffer is too small, let the caller know, making space for NULL */
1947 ReturnLength++;
1948 }
1949 }
1950
1951 /* We're all done */
1952 goto Quickie;
1953
1954ErrorQuickie:
1955 /* This is the goto for memory failures */
1956 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1957
1958Quickie:
1959 /* General function end: free memory, restore error mode, return length */
1960 if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original);
1961 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1962 SetErrorMode(ErrorMode);
1963 return ReturnLength;
1964}
1965
1966/*
1967 * @implemented
1968 *
1969 * NOTE: Windows returns a dos/short (8.3) path
1970 */
1971DWORD
1972WINAPI
1973GetTempPathA(IN DWORD nBufferLength,
1974 OUT LPSTR lpBuffer)
1975{
1976 WCHAR BufferW[MAX_PATH];
1977 DWORD ret;
1978
1979 ret = GetTempPathW(MAX_PATH, BufferW);
1980
1981 if (!ret) return 0;
1982
1983 if (ret > MAX_PATH)
1984 {
1985 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1986 return 0;
1987 }
1988
1989 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
1990}
1991
1992/*
1993 * @implemented
1994 *
1995 * ripped from wine
1996 */
1997DWORD
1998WINAPI
1999GetTempPathW(IN DWORD count,
2000 OUT LPWSTR path)
2001{
2002 static const WCHAR tmp[] = { 'T', 'M', 'P', 0 };
2003 static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
2004 static const WCHAR userprofile[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
2005 WCHAR tmp_path[MAX_PATH];
2006 WCHAR full_tmp_path[MAX_PATH];
2007 UINT ret;
2008
2009 DPRINT("%u,%p\n", count, path);
2010
2011 if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )) &&
2012 !(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )) &&
2013 !(ret = GetEnvironmentVariableW( userprofile, tmp_path, MAX_PATH )) &&
2014 !(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH )))
2015 {
2016 return 0;
2017 }
2018
2019 if (ret > MAX_PATH)
2020 {
2021 SetLastError(ERROR_FILENAME_EXCED_RANGE);
2022 return 0;
2023 }
2024
2025 ret = GetFullPathNameW(tmp_path, MAX_PATH, full_tmp_path, NULL);
2026 if (!ret) return 0;
2027
2028 if (ret > MAX_PATH - 2)
2029 {
2030 SetLastError(ERROR_FILENAME_EXCED_RANGE);
2031 return 0;
2032 }
2033
2034 if (full_tmp_path[ret-1] != '\\')
2035 {
2036 full_tmp_path[ret++] = '\\';
2037 full_tmp_path[ret] = '\0';
2038 }
2039
2040 ret++; /* add space for terminating 0 */
2041
2042 if (count >= ret)
2043 {
2044 lstrcpynW(path, full_tmp_path, count);
2045 /* the remaining buffer must be zeroed up to 32766 bytes in XP or 32767
2046 * bytes after it, we will assume the > XP behavior for now */
2047 memset(path + ret, 0, (min(count, 32767) - ret) * sizeof(WCHAR));
2048 ret--; /* return length without 0 */
2049 }
2050 else if (count)
2051 {
2052 /* the buffer must be cleared if contents will not fit */
2053 memset(path, 0, count * sizeof(WCHAR));
2054 }
2055
2056 DPRINT("GetTempPathW returning %u, %S\n", ret, path);
2057 return ret;
2058}
2059
2060/*
2061 * @implemented
2062 */
2063DWORD
2064WINAPI
2065GetCurrentDirectoryA(IN DWORD nBufferLength,
2066 OUT LPSTR lpBuffer)
2067{
2068 ANSI_STRING AnsiString;
2069 NTSTATUS Status;
2070 PUNICODE_STRING StaticString;
2071 ULONG MaxLength;
2072
2073 StaticString = &NtCurrentTeb()->StaticUnicodeString;
2074
2075 MaxLength = nBufferLength;
2076 if (nBufferLength >= UNICODE_STRING_MAX_BYTES)
2077 {
2078 MaxLength = UNICODE_STRING_MAX_BYTES - 1;
2079 }
2080
2081 StaticString->Length = (USHORT)RtlGetCurrentDirectory_U(StaticString->MaximumLength,
2082 StaticString->Buffer);
2083 Status = RtlUnicodeToMultiByteSize(&nBufferLength,
2084 StaticString->Buffer,
2085 StaticString->Length);
2086 if (!NT_SUCCESS(Status))
2087 {
2088 BaseSetLastNTError(Status);
2089 return 0;
2090 }
2091
2092 if (MaxLength <= nBufferLength)
2093 {
2094 return nBufferLength + 1;
2095 }
2096
2097 AnsiString.Buffer = lpBuffer;
2098 AnsiString.MaximumLength = (USHORT)MaxLength;
2099 Status = BasepUnicodeStringTo8BitString(&AnsiString, StaticString, FALSE);
2100 if (!NT_SUCCESS(Status))
2101 {
2102 BaseSetLastNTError(Status);
2103 return 0;
2104 }
2105
2106 return AnsiString.Length;
2107}
2108
2109/*
2110 * @implemented
2111 */
2112DWORD
2113WINAPI
2114GetCurrentDirectoryW(IN DWORD nBufferLength,
2115 OUT LPWSTR lpBuffer)
2116{
2117 return RtlGetCurrentDirectory_U(nBufferLength * sizeof(WCHAR), lpBuffer) / sizeof(WCHAR);
2118}
2119
2120/*
2121 * @implemented
2122 */
2123BOOL
2124WINAPI
2125SetCurrentDirectoryA(IN LPCSTR lpPathName)
2126{
2127 PUNICODE_STRING DirName;
2128 NTSTATUS Status;
2129
2130 if (!lpPathName)
2131 {
2132 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
2133 return FALSE;
2134 }
2135
2136 DirName = Basep8BitStringToStaticUnicodeString(lpPathName);
2137 if (!DirName) return FALSE;
2138
2139 if (CheckForSameCurdir(DirName)) return TRUE;
2140
2141 Status = RtlSetCurrentDirectory_U(DirName);
2142 if (NT_SUCCESS(Status)) return TRUE;
2143
2144 if ((*DirName->Buffer != L'"') || (DirName->Length <= 2))
2145 {
2146 BaseSetLastNTError(Status);
2147 return 0;
2148 }
2149
2150 DirName = Basep8BitStringToStaticUnicodeString(lpPathName + 1);
2151 if (!DirName) return FALSE;
2152
2153 Status = RtlSetCurrentDirectory_U(DirName);
2154 if (!NT_SUCCESS(Status))
2155 {
2156 BaseSetLastNTError(Status);
2157 return FALSE;
2158 }
2159
2160 return TRUE;
2161}
2162
2163/*
2164 * @implemented
2165 */
2166BOOL
2167WINAPI
2168SetCurrentDirectoryW(IN LPCWSTR lpPathName)
2169{
2170 NTSTATUS Status;
2171 UNICODE_STRING UnicodeString;
2172
2173 if (!lpPathName)
2174 {
2175 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
2176 return FALSE;
2177 }
2178
2179 Status = RtlInitUnicodeStringEx(&UnicodeString, lpPathName);
2180 if (NT_SUCCESS(Status))
2181 {
2182 if (!CheckForSameCurdir(&UnicodeString))
2183 {
2184 Status = RtlSetCurrentDirectory_U(&UnicodeString);
2185 }
2186 }
2187
2188 if (!NT_SUCCESS(Status))
2189 {
2190 BaseSetLastNTError(Status);
2191 return FALSE;
2192 }
2193
2194 return TRUE;
2195}
2196
2197/*
2198 * @implemented
2199 */
2200UINT
2201WINAPI
2202GetSystemDirectoryA(OUT LPSTR lpBuffer,
2203 IN UINT uSize)
2204{
2205 ANSI_STRING AnsiString;
2206 NTSTATUS Status;
2207 ULONG AnsiLength;
2208
2209 /* Get the correct size of the Unicode Base directory */
2210 Status = RtlUnicodeToMultiByteSize(&AnsiLength,
2211 BaseWindowsSystemDirectory.Buffer,
2212 BaseWindowsSystemDirectory.MaximumLength);
2213 if (!NT_SUCCESS(Status)) return 0;
2214
2215 if (uSize < AnsiLength) return AnsiLength;
2216
2217 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
2218
2219 Status = BasepUnicodeStringTo8BitString(&AnsiString,
2220 &BaseWindowsSystemDirectory,
2221 FALSE);
2222 if (!NT_SUCCESS(Status)) return 0;
2223
2224 return AnsiString.Length;
2225}
2226
2227/*
2228 * @implemented
2229 */
2230UINT
2231WINAPI
2232GetSystemDirectoryW(OUT LPWSTR lpBuffer,
2233 IN UINT uSize)
2234{
2235 ULONG ReturnLength;
2236
2237 ReturnLength = BaseWindowsSystemDirectory.MaximumLength;
2238 if ((uSize * sizeof(WCHAR)) >= ReturnLength)
2239 {
2240 RtlCopyMemory(lpBuffer,
2241 BaseWindowsSystemDirectory.Buffer,
2242 BaseWindowsSystemDirectory.Length);
2243 lpBuffer[BaseWindowsSystemDirectory.Length / sizeof(WCHAR)] = UNICODE_NULL;
2244
2245 ReturnLength = BaseWindowsSystemDirectory.Length;
2246 }
2247
2248 return ReturnLength / sizeof(WCHAR);
2249}
2250
2251/*
2252 * @implemented
2253 */
2254UINT
2255WINAPI
2256GetWindowsDirectoryA(OUT LPSTR lpBuffer,
2257 IN UINT uSize)
2258{
2259 /* Is this a TS installation? */
2260 if (gpTermsrvGetWindowsDirectoryA) UNIMPLEMENTED;
2261
2262 /* Otherwise, call the System API */
2263 return GetSystemWindowsDirectoryA(lpBuffer, uSize);
2264}
2265
2266/*
2267 * @implemented
2268 */
2269UINT
2270WINAPI
2271GetWindowsDirectoryW(OUT LPWSTR lpBuffer,
2272 IN UINT uSize)
2273{
2274 /* Is this a TS installation? */
2275 if (gpTermsrvGetWindowsDirectoryW) UNIMPLEMENTED;
2276
2277 /* Otherwise, call the System API */
2278 return GetSystemWindowsDirectoryW(lpBuffer, uSize);
2279}
2280
2281/*
2282 * @implemented
2283 */
2284UINT
2285WINAPI
2286GetSystemWindowsDirectoryA(OUT LPSTR lpBuffer,
2287 IN UINT uSize)
2288{
2289 ANSI_STRING AnsiString;
2290 NTSTATUS Status;
2291 ULONG AnsiLength;
2292
2293 /* Get the correct size of the Unicode Base directory */
2294 Status = RtlUnicodeToMultiByteSize(&AnsiLength,
2295 BaseWindowsDirectory.Buffer,
2296 BaseWindowsDirectory.MaximumLength);
2297 if (!NT_SUCCESS(Status)) return 0;
2298
2299 if (uSize < AnsiLength) return AnsiLength;
2300
2301 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
2302
2303 Status = BasepUnicodeStringTo8BitString(&AnsiString,
2304 &BaseWindowsDirectory,
2305 FALSE);
2306 if (!NT_SUCCESS(Status)) return 0;
2307
2308 return AnsiString.Length;
2309}
2310
2311/*
2312 * @implemented
2313 */
2314UINT
2315WINAPI
2316GetSystemWindowsDirectoryW(OUT LPWSTR lpBuffer,
2317 IN UINT uSize)
2318{
2319 ULONG ReturnLength;
2320
2321 ReturnLength = BaseWindowsDirectory.MaximumLength;
2322 if ((uSize * sizeof(WCHAR)) >= ReturnLength)
2323 {
2324 RtlCopyMemory(lpBuffer,
2325 BaseWindowsDirectory.Buffer,
2326 BaseWindowsDirectory.Length);
2327 lpBuffer[BaseWindowsDirectory.Length / sizeof(WCHAR)] = UNICODE_NULL;
2328
2329 ReturnLength = BaseWindowsDirectory.Length;
2330 }
2331
2332 return ReturnLength / sizeof(WCHAR);
2333}
2334
2335/*
2336 * @unimplemented
2337 */
2338UINT
2339WINAPI
2340GetSystemWow64DirectoryW(OUT LPWSTR lpBuffer,
2341 IN UINT uSize)
2342{
2343#ifdef _WIN64
2344 UNIMPLEMENTED;
2345 return 0;
2346#else
2347 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2348 return 0;
2349#endif
2350}
2351
2352/*
2353 * @unimplemented
2354 */
2355UINT
2356WINAPI
2357GetSystemWow64DirectoryA(OUT LPSTR lpBuffer,
2358 IN UINT uSize)
2359{
2360#ifdef _WIN64
2361 UNIMPLEMENTED;
2362 return 0;
2363#else
2364 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2365 return 0;
2366#endif
2367}
2368
2369/* EOF */