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