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