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