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