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