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