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