c22dcd80eaf42628fb6adaa31d804bc35a589485
[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;
240 while (TRUE);
241 }
242
243 /* We do not. Do we have the LDR_ENTRY for the executable? */
244 if (!BasepExeLdrEntry)
245 {
246 /* We do not. Grab it */
247 LdrEnumerateLoadedModules(0,
248 BasepLocateExeLdrEntry,
249 NtCurrentPeb()->ImageBaseAddress);
250 }
251
252 /* Now do we have it? */
253 if (BasepExeLdrEntry)
254 {
255 /* Yes, so read the name out of it */
256 AppName = BasepExeLdrEntry->FullDllName.Buffer;
257 }
258
259 /* Find out where the app name ends, to get only the directory */
260 if (AppName) AppNameEnd = BasepEndOfDirName(AppName);
261 }
262
263 /* So, do we have an application name and its directory? */
264 if ((AppName) && (AppNameEnd))
265 {
266 /* Add the size of the app's directory, plus the separator */
267 PathLengthInBytes += ((AppNameEnd - AppName) * sizeof(WCHAR)) + sizeof(L';');
268 }
269 break;
270
271 default:
272 break;
273 }
274 }
275
276 /* Bam, all done, we now have the final path size */
277 ASSERT(PathLengthInBytes > 0);
278 ASSERT(!(PathLengthInBytes & 1));
279
280 /* Allocate the buffer to hold it */
281 PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes);
282 if (!PathBuffer)
283 {
284 /* Failure path */
285 Status = STATUS_NO_MEMORY;
286 goto Quickie;
287 }
288
289 /* Now we loop again, this time to copy the data */
290 PathCurrent = PathBuffer;
291 for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) {
292 switch (*Order)
293 {
294 /* Add the DLL path */
295 case BaseSearchPathDll:
296 if (BaseDllDirectory.Length)
297 {
298 /* Copy it in the buffer, ASSERT there's enough space */
299 ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + BaseDllDirectory.Length) <= PathLengthInBytes);
300 RtlCopyMemory(PathCurrent,
301 BaseDllDirectory.Buffer,
302 BaseDllDirectory.Length);
303
304 /* Update the current pointer, add a separator */
305 PathCurrent += (BaseDllDirectory.Length / sizeof(WCHAR));
306 *PathCurrent++ = ';';
307 }
308 break;
309
310 /* Add the current applicaiton path */
311 case BaseSearchPathApp:
312 if ((AppName) && (AppNameEnd))
313 {
314 /* Copy it in the buffer, ASSERT there's enough space */
315 ASSERT(((PathCurrent - PathBuffer + 1 + (AppNameEnd - AppName)) * sizeof(WCHAR)) <= PathLengthInBytes);
316 RtlCopyMemory(PathCurrent,
317 AppName,
318 (AppNameEnd - AppName) * sizeof(WCHAR));
319
320 /* Update the current pointer, add a separator */
321 PathCurrent += AppNameEnd - AppName;
322 *PathCurrent++ = ';';
323 }
324 break;
325
326 /* Add the default search path */
327 case BaseSearchPathDefault:
328 /* Copy it in the buffer, ASSERT there's enough space */
329 ASSERT((((PathCurrent - PathBuffer) * sizeof(WCHAR)) + BaseDefaultPath.Length) <= PathLengthInBytes);
330 RtlCopyMemory(PathCurrent, BaseDefaultPath.Buffer, BaseDefaultPath.Length);
331
332 /* Update the current pointer. The default path already has a ";" */
333 PathCurrent += (BaseDefaultPath.Length / sizeof(WCHAR));
334 break;
335
336 /* Add the path in the PATH environment variable */
337 case BaseSearchPathEnv:
338 if (EnvPath.Length)
339 {
340 /* Copy it in the buffer, ASSERT there's enough space */
341 ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + EnvPath.Length) <= PathLengthInBytes);
342 RtlCopyMemory(PathCurrent, EnvPath.Buffer, EnvPath.Length);
343
344 /* Update the current pointer, add a separator */
345 PathCurrent += (EnvPath.Length / sizeof(WCHAR));
346 *PathCurrent++ = ';';
347 }
348 break;
349
350 /* Add the current dierctory */
351 case BaseSearchPathCurrent:
352
353 /* Copy it in the buffer, ASSERT there's enough space */
354 ASSERT(((PathCurrent - PathBuffer + 2) * sizeof(WCHAR)) <= PathLengthInBytes);
355 *PathCurrent++ = '.';
356
357 /* Add the path separator */
358 *PathCurrent++ = ';';
359 break;
360
361 default:
362 break;
363 }
364 }
365
366 /* Everything should've perfectly fit in there */
367 ASSERT((PathCurrent - PathBuffer) * sizeof(WCHAR) == PathLengthInBytes);
368 ASSERT(PathCurrent > PathBuffer);
369
370 /* Terminate the whole thing */
371 PathCurrent[-1] = UNICODE_NULL;
372
373 Quickie:
374 /* Exit path: free our buffers */
375 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
376 if (PathBuffer)
377 {
378 /* This only gets freed in the failure path, since caller wants it */
379 if (!NT_SUCCESS(Status))
380 {
381 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
382 PathBuffer = NULL;
383 }
384 }
385
386 /* Return the path! */
387 return PathBuffer;
388 }
389
390 LPWSTR
391 WINAPI
392 BaseComputeProcessSearchPath(VOID)
393 {
394 DPRINT("Computing Process Search path\n");
395
396 /* Compute the path using default process order */
397 return BasepComputeProcessPath(BaseProcessOrder, NULL, NULL);
398 }
399
400 LPWSTR
401 WINAPI
402 BaseComputeProcessExePath(IN LPWSTR FullPath)
403 {
404 PBASE_SEARCH_PATH_TYPE PathOrder;
405 DPRINT1("Computing EXE path: %wZ\n", FullPath);
406
407 /* Check if we should use the current directory */
408 PathOrder = NeedCurrentDirectoryForExePathW(FullPath) ?
409 BaseProcessOrder : BaseProcessOrderNoCurrent;
410
411 /* And now compute the path */
412 return BasepComputeProcessPath(PathOrder, NULL, NULL);
413 }
414
415 LPWSTR
416 WINAPI
417 BaseComputeProcessDllPath(IN LPWSTR FullPath,
418 IN PVOID Environment)
419 {
420 LPWSTR DllPath = NULL;
421 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
422 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"SafeDllSearchMode");
423 OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
424 KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
425 HANDLE KeyHandle;
426 NTSTATUS Status;
427 ULONG ResultLength;
428 BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement, OldCurrentDirPlacement;
429
430 /* Acquire DLL directory lock */
431 RtlEnterCriticalSection(&BaseDllDirectoryLock);
432
433 /* Check if we have a base dll directory */
434 if (BaseDllDirectory.Buffer)
435 {
436 /* Then compute the process path using DLL order (without curdir) */
437 DllPath = BasepComputeProcessPath(BaseDllOrderNoCurrent, FullPath, Environment);
438
439 /* Release DLL directory lock */
440 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
441
442 /* Return dll path */
443 return DllPath;
444 }
445
446 /* Release DLL directory lock */
447 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
448
449 /* Read the current placement */
450 CurrentDirPlacement = BasepDllCurrentDirPlacement;
451 if (CurrentDirPlacement == BaseCurrentDirPlacementInvalid)
452 {
453 /* Open the configuration key */
454 Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
455 if (NT_SUCCESS(Status))
456 {
457 /* Query if safe search is enabled */
458 Status = NtQueryValueKey(KeyHandle,
459 &ValueName,
460 KeyValuePartialInformation,
461 &PartialInfo,
462 sizeof(PartialInfo),
463 &ResultLength);
464 if (NT_SUCCESS(Status))
465 {
466 /* Read the value if the size is OK */
467 if (ResultLength == sizeof(PartialInfo))
468 {
469 CurrentDirPlacement = *(PULONG)PartialInfo.Data;
470 }
471 }
472
473 /* Close the handle */
474 NtClose(KeyHandle);
475
476 /* Validate the registry value */
477 if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
478 (CurrentDirPlacement >= BaseCurrentDirPlacementMax))
479 {
480 /* Default to safe search */
481 CurrentDirPlacement = BaseCurrentDirPlacementSafe;
482 }
483 }
484
485 /* Update the placement and read the old one */
486 OldCurrentDirPlacement = InterlockedCompareExchange((PLONG)&BasepDllCurrentDirPlacement,
487 CurrentDirPlacement,
488 BaseCurrentDirPlacementInvalid);
489 if (OldCurrentDirPlacement != BaseCurrentDirPlacementInvalid)
490 {
491 /* If there already was a placement, use it */
492 CurrentDirPlacement = OldCurrentDirPlacement;
493 }
494 }
495
496 /* Check if the placement is invalid or not set */
497 if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
498 (CurrentDirPlacement >= BaseCurrentDirPlacementMax))
499 {
500 /* Default to safe search */
501 CurrentDirPlacement = BaseCurrentDirPlacementSafe;
502 }
503
504 /* Compute the process path using either normal or safe search */
505 DllPath = BasepComputeProcessPath(BaseDllOrderCurrent[CurrentDirPlacement],
506 FullPath,
507 Environment);
508
509 /* Return dll path */
510 return DllPath;
511 }
512
513 BOOLEAN
514 WINAPI
515 CheckForSameCurdir(IN PUNICODE_STRING DirName)
516 {
517 PUNICODE_STRING CurDir;
518 USHORT CurLength;
519 BOOLEAN Result;
520 UNICODE_STRING CurDirCopy;
521
522 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
523
524 CurLength = CurDir->Length;
525 if (CurDir->Length <= 6)
526 {
527 if (CurLength != DirName->Length) return FALSE;
528 }
529 else
530 {
531 if ((CurLength - 2) != DirName->Length) return FALSE;
532 }
533
534 RtlAcquirePebLock();
535
536 CurDirCopy = *CurDir;
537 if (CurDirCopy.Length > 6) CurDirCopy.Length -= 2;
538
539 Result = 0;
540
541 if (RtlEqualUnicodeString(&CurDirCopy, DirName, TRUE)) Result = TRUE;
542
543 RtlReleasePebLock();
544
545 return Result;
546 }
547
548 /*
549 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
550 * identical (other than the Rtl can optionally check for spaces), however the
551 * Rtl will always convert to OEM, while kernel32 has two possible file modes
552 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get
553 * the correct compatible results
554 */
555 BOOL
556 WINAPI
557 IsShortName_U(IN PWCHAR Name,
558 IN ULONG Length)
559 {
560 BOOLEAN HasExtension;
561 WCHAR c;
562 NTSTATUS Status;
563 UNICODE_STRING UnicodeName;
564 ANSI_STRING AnsiName;
565 ULONG i, Dots;
566 CHAR AnsiBuffer[MAX_PATH];
567 ASSERT(Name);
568
569 /* What do you think 8.3 means? */
570 if (Length > 12) return FALSE;
571
572 /* Sure, any emtpy name is a short name */
573 if (!Length) return TRUE;
574
575 /* This could be . or .. or something else */
576 if (*Name == L'.')
577 {
578 /* Which one is it */
579 if ((Length == 1) || ((Length == 2) && *(Name + 1) == L'.'))
580 {
581 /* . or .., this is good */
582 return TRUE;
583 }
584
585 /* Some other bizare dot-based name, not good */
586 return FALSE;
587 }
588
589 /* Initialize our two strings */
590 RtlInitEmptyAnsiString(&AnsiName, AnsiBuffer, MAX_PATH);
591 RtlInitEmptyUnicodeString(&UnicodeName, Name, Length * sizeof(WCHAR));
592 UnicodeName.Length = UnicodeName.MaximumLength;
593
594 /* Now do the conversion */
595 Status = BasepUnicodeStringTo8BitString(&AnsiName, &UnicodeName, FALSE);
596 if (!NT_SUCCESS(Status)) return FALSE;
597
598 /* Now we loop the name */
599 HasExtension = FALSE;
600 for (i = 0, Dots = Length - 1; i < AnsiName.Length; i++, Dots--)
601 {
602 /* Read the current byte */
603 c = AnsiName.Buffer[i];
604
605 /* Is it DBCS? */
606 if (IsDBCSLeadByte(c))
607 {
608 /* If we're near the end of the string, we can't allow a DBCS */
609 if ((!(HasExtension) && (i >= 7)) || (i == AnsiName.Length - 1))
610 {
611 return FALSE;
612 }
613
614 /* Otherwise we skip over it */
615 continue;
616 }
617
618 /* Check for illegal characters */
619 if ((c > 0x7F) || (IllegalMask[c / 32] & (1 << (c % 32))))
620 {
621 return FALSE;
622 }
623
624 /* Check if this is perhaps an extension? */
625 if (c == '.')
626 {
627 /* Unless the extension is too large or there's more than one */
628 if ((HasExtension) || (Dots > 3)) return FALSE;
629
630 /* This looks like an extension */
631 HasExtension = TRUE;
632 }
633
634 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
635 if ((i >= 8) && !(HasExtension)) return FALSE;
636 }
637
638 /* You survived the loop, this is a good short name */
639 return TRUE;
640 }
641
642 BOOL
643 WINAPI
644 IsLongName_U(IN PWCHAR FileName,
645 IN ULONG Length)
646 {
647 BOOLEAN HasExtension;
648 ULONG i, Dots;
649
650 /* More than 8.3, any combination of dots, and NULL names are all long */
651 if (!(Length) || (Length > 12) || (*FileName == L'.')) return TRUE;
652
653 /* Otherwise, initialize our scanning loop */
654 HasExtension = FALSE;
655 for (i = 0, Dots = Length - 1; i < Length; i++, Dots--)
656 {
657 /* Check if this could be an extension */
658 if (FileName[i] == L'.')
659 {
660 /* Unlike the short case, we WANT more than one extension, or a long one */
661 if ((HasExtension) || (Dots > 3))
662 {
663 return TRUE;
664 }
665 HasExtension = TRUE;
666 }
667
668 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
669 if ((i >= 8) && (!HasExtension)) return TRUE;
670 }
671
672 /* The name *seems* to conform to 8.3 */
673 return FALSE;
674 }
675
676 BOOL
677 WINAPI
678 FindLFNorSFN_U(IN PWCHAR Path,
679 OUT PWCHAR *First,
680 OUT PWCHAR *Last,
681 IN BOOL UseShort)
682 {
683 PWCHAR p;
684 ULONG Length;
685 BOOL Found = 0;
686 ASSERT(Path);
687
688 /* Loop while there is something in the path */
689 while (TRUE)
690 {
691 /* Loop within the path skipping slashes */
692 while ((*Path == L'\\') || (*Path == L'/')) Path++;
693
694 /* Make sure there's something after the slashes too! */
695 if (*Path == UNICODE_NULL) break;
696
697 /* Now skip past the file name until we get to the first slash */
698 p = Path + 1;
699 while ((*p) && ((*p != L'\\') && (*p != L'/'))) p++;
700
701 /* Whatever is in between those two is now the file name length */
702 Length = p - Path;
703
704 /*
705 * Check if it is valid
706 * Note that !IsShortName != IsLongName, these two functions simply help
707 * us determine if a conversion is necessary or not.
708 * "Found" really means: "Is a conversion necessary?", hence the "!"
709 */
710 Found = UseShort ? !IsShortName_U(Path, Length) : !IsLongName_U(Path, Length);
711 if (Found)
712 {
713 /* It is! did the caller request to know the markers? */
714 if ((First) && (Last))
715 {
716 /* Return them */
717 *First = Path;
718 *Last = p;
719 }
720 break;
721 }
722
723 /* Is there anything else following this sub-path/filename? */
724 if (*p == UNICODE_NULL) break;
725
726 /* Yes, keep going */
727 Path = p + 1;
728 }
729
730 /* Return if anything was found and valid */
731 return Found;
732 }
733
734 PWCHAR
735 WINAPI
736 SkipPathTypeIndicator_U(IN LPWSTR Path)
737 {
738 PWCHAR ReturnPath;
739 ULONG i;
740
741 /* Check what kind of path this is and how many slashes to skip */
742 switch (RtlDetermineDosPathNameType_U(Path))
743 {
744 case RtlPathTypeDriveAbsolute:
745 return Path + 3;
746
747 case RtlPathTypeDriveRelative:
748 return Path + 2;
749
750 case RtlPathTypeRooted:
751 return Path + 1;
752
753 case RtlPathTypeRelative:
754 return Path;
755
756 case RtlPathTypeRootLocalDevice:
757 default:
758 return NULL;
759
760 case RtlPathTypeUncAbsolute:
761 case RtlPathTypeLocalDevice:
762
763 /* Keep going until we bypass the path indicators */
764 for (ReturnPath = Path + 2, i = 2; (i > 0) && (*ReturnPath); ReturnPath++)
765 {
766 /* We look for 2 slashes, so keep at it until we find them */
767 if ((*ReturnPath == L'\\') || (*ReturnPath == L'/')) i--;
768 }
769
770 return ReturnPath;
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, 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 IN LPSTR lpBuffer,
991 IN LPSTR *lpFilePart)
992 {
993 NTSTATUS Status;
994 PWCHAR Buffer;
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 IN LPWSTR lpBuffer,
1104 OUT LPWSTR *lpFilePart)
1105 {
1106 /* Call Rtl to do the work */
1107 return RtlGetFullPathName_U((LPWSTR)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 = 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 = 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 stirng 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 lenght */
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 = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
1711 LongPathUni.Buffer = LongPath;
1712 LongPathUni.Length = 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 = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
1792 ShortPathUni.Buffer = ShortPath;
1793 ShortPathUni.Length = 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 UINT ret;
2084
2085 DPRINT("%u,%p\n", count, path);
2086
2087 if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )) &&
2088 !(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )) &&
2089 !(ret = GetEnvironmentVariableW( userprofile, tmp_path, MAX_PATH )) &&
2090 !(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH )))
2091 return 0;
2092
2093 if (ret > MAX_PATH)
2094 {
2095 SetLastError(ERROR_FILENAME_EXCED_RANGE);
2096 return 0;
2097 }
2098
2099 ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
2100 if (!ret) return 0;
2101
2102 if (ret > MAX_PATH - 2)
2103 {
2104 SetLastError(ERROR_FILENAME_EXCED_RANGE);
2105 return 0;
2106 }
2107
2108 if (tmp_path[ret-1] != '\\')
2109 {
2110 tmp_path[ret++] = '\\';
2111 tmp_path[ret] = '\0';
2112 }
2113
2114 ret++; /* add space for terminating 0 */
2115
2116 if (count)
2117 {
2118 lstrcpynW(path, tmp_path, count);
2119 if (count >= ret)
2120 ret--; /* return length without 0 */
2121 else if (count < 4)
2122 path[0] = 0; /* avoid returning ambiguous "X:" */
2123 }
2124
2125 DPRINT("GetTempPathW returning %u, %S\n", ret, path);
2126 return ret;
2127 }
2128
2129 /*
2130 * @implemented
2131 */
2132 DWORD
2133 WINAPI
2134 GetCurrentDirectoryA(IN DWORD nBufferLength,
2135 IN LPSTR lpBuffer)
2136 {
2137 ANSI_STRING AnsiString;
2138 NTSTATUS Status;
2139 PUNICODE_STRING StaticString;
2140 ULONG MaxLength;
2141
2142 StaticString = &NtCurrentTeb()->StaticUnicodeString;
2143
2144 MaxLength = nBufferLength;
2145 if (nBufferLength >= UNICODE_STRING_MAX_BYTES)
2146 {
2147 MaxLength = UNICODE_STRING_MAX_BYTES - 1;
2148 }
2149
2150 StaticString->Length = RtlGetCurrentDirectory_U(StaticString->MaximumLength,
2151 StaticString->Buffer);
2152 Status = RtlUnicodeToMultiByteSize(&nBufferLength,
2153 StaticString->Buffer,
2154 StaticString->Length);
2155 if (!NT_SUCCESS(Status))
2156 {
2157 BaseSetLastNTError(Status);
2158 return 0;
2159 }
2160
2161 if (MaxLength <= nBufferLength)
2162 {
2163 return nBufferLength + 1;
2164 }
2165
2166 AnsiString.Buffer = lpBuffer;
2167 AnsiString.MaximumLength = MaxLength;
2168 Status = BasepUnicodeStringTo8BitString(&AnsiString, StaticString, FALSE);
2169 if (!NT_SUCCESS(Status))
2170 {
2171 BaseSetLastNTError(Status);
2172 return 0;
2173 }
2174
2175 return AnsiString.Length;
2176 }
2177
2178 /*
2179 * @implemented
2180 */
2181 DWORD
2182 WINAPI
2183 GetCurrentDirectoryW(IN DWORD nBufferLength,
2184 IN LPWSTR lpBuffer)
2185 {
2186 return RtlGetCurrentDirectory_U(nBufferLength * sizeof(WCHAR), lpBuffer) / sizeof(WCHAR);
2187 }
2188
2189 /*
2190 * @implemented
2191 */
2192 BOOL
2193 WINAPI
2194 SetCurrentDirectoryA(IN LPCSTR lpPathName)
2195 {
2196 PUNICODE_STRING DirName;
2197 NTSTATUS Status;
2198
2199 if (!lpPathName)
2200 {
2201 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
2202 return FALSE;
2203 }
2204
2205 DirName = Basep8BitStringToStaticUnicodeString(lpPathName);
2206 if (!DirName) return FALSE;
2207
2208 if (CheckForSameCurdir(DirName)) return TRUE;
2209
2210 Status = RtlSetCurrentDirectory_U(DirName);
2211 if (NT_SUCCESS(Status)) return TRUE;
2212
2213 if ((*DirName->Buffer != L'"') || (DirName->Length <= 2))
2214 {
2215 BaseSetLastNTError(Status);
2216 return 0;
2217 }
2218
2219 DirName = Basep8BitStringToStaticUnicodeString(lpPathName + 1);
2220 if (!DirName) return FALSE;
2221
2222 Status = RtlSetCurrentDirectory_U(DirName);
2223 if (!NT_SUCCESS(Status))
2224 {
2225 BaseSetLastNTError(Status);
2226 return FALSE;
2227 }
2228
2229 return TRUE;
2230 }
2231
2232 /*
2233 * @implemented
2234 */
2235 BOOL
2236 WINAPI
2237 SetCurrentDirectoryW(IN LPCWSTR lpPathName)
2238 {
2239 NTSTATUS Status;
2240 UNICODE_STRING UnicodeString;
2241
2242 if (!lpPathName)
2243 {
2244 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
2245 return FALSE;
2246 }
2247
2248 Status = RtlInitUnicodeStringEx(&UnicodeString, lpPathName);
2249 if (NT_SUCCESS(Status))
2250 {
2251 if (!CheckForSameCurdir(&UnicodeString))
2252 {
2253 Status = RtlSetCurrentDirectory_U(&UnicodeString);
2254 }
2255 }
2256
2257 if (!NT_SUCCESS(Status))
2258 {
2259 BaseSetLastNTError(Status);
2260 return FALSE;
2261 }
2262
2263 return TRUE;
2264 }
2265
2266 /*
2267 * @implemented
2268 */
2269 UINT
2270 WINAPI
2271 GetSystemDirectoryA(IN LPSTR lpBuffer,
2272 IN UINT uSize)
2273 {
2274 ANSI_STRING AnsiString;
2275 NTSTATUS Status;
2276 ULONG AnsiLength;
2277
2278 /* Get the correct size of the Unicode Base directory */
2279 Status = RtlUnicodeToMultiByteSize(&AnsiLength,
2280 BaseWindowsSystemDirectory.Buffer,
2281 BaseWindowsSystemDirectory.MaximumLength);
2282 if (!NT_SUCCESS(Status)) return 0;
2283
2284 if (uSize < AnsiLength) return AnsiLength;
2285
2286 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
2287
2288 Status = BasepUnicodeStringTo8BitString(&AnsiString,
2289 &BaseWindowsSystemDirectory,
2290 FALSE);
2291 if (!NT_SUCCESS(Status)) return 0;
2292
2293 return AnsiString.Length;
2294 }
2295
2296 /*
2297 * @implemented
2298 */
2299 UINT
2300 WINAPI
2301 GetSystemDirectoryW(IN LPWSTR lpBuffer,
2302 IN UINT uSize)
2303 {
2304 ULONG ReturnLength;
2305
2306 ReturnLength = BaseWindowsSystemDirectory.MaximumLength;
2307 if ((uSize * sizeof(WCHAR)) >= ReturnLength)
2308 {
2309 RtlCopyMemory(lpBuffer,
2310 BaseWindowsSystemDirectory.Buffer,
2311 BaseWindowsSystemDirectory.Length);
2312 lpBuffer[BaseWindowsSystemDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
2313
2314 ReturnLength = BaseWindowsSystemDirectory.Length;
2315 }
2316
2317 return ReturnLength / sizeof(WCHAR);
2318 }
2319
2320 /*
2321 * @implemented
2322 */
2323 UINT
2324 WINAPI
2325 GetWindowsDirectoryA(IN LPSTR lpBuffer,
2326 IN UINT uSize)
2327 {
2328 /* Is this a TS installation? */
2329 if (gpTermsrvGetWindowsDirectoryA) UNIMPLEMENTED;
2330
2331 /* Otherwise, call the System API */
2332 return GetSystemWindowsDirectoryA(lpBuffer, uSize);
2333 }
2334
2335 /*
2336 * @implemented
2337 */
2338 UINT
2339 WINAPI
2340 GetWindowsDirectoryW(IN LPWSTR lpBuffer,
2341 IN UINT uSize)
2342 {
2343 /* Is this a TS installation? */
2344 if (gpTermsrvGetWindowsDirectoryW) UNIMPLEMENTED;
2345
2346 /* Otherwise, call the System API */
2347 return GetSystemWindowsDirectoryW(lpBuffer, uSize);
2348 }
2349
2350 /*
2351 * @implemented
2352 */
2353 UINT
2354 WINAPI
2355 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer,
2356 IN UINT uSize)
2357 {
2358 ANSI_STRING AnsiString;
2359 NTSTATUS Status;
2360 ULONG AnsiLength;
2361
2362 /* Get the correct size of the Unicode Base directory */
2363 Status = RtlUnicodeToMultiByteSize(&AnsiLength,
2364 BaseWindowsDirectory.Buffer,
2365 BaseWindowsDirectory.MaximumLength);
2366 if (!NT_SUCCESS(Status)) return 0;
2367
2368 if (uSize < AnsiLength) return AnsiLength;
2369
2370 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
2371
2372 Status = BasepUnicodeStringTo8BitString(&AnsiString,
2373 &BaseWindowsDirectory,
2374 FALSE);
2375 if (!NT_SUCCESS(Status)) return 0;
2376
2377 return AnsiString.Length;
2378 }
2379
2380 /*
2381 * @implemented
2382 */
2383 UINT
2384 WINAPI
2385 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer,
2386 IN UINT uSize)
2387 {
2388 ULONG ReturnLength;
2389
2390 ReturnLength = BaseWindowsDirectory.MaximumLength;
2391 if ((uSize * sizeof(WCHAR)) >= ReturnLength)
2392 {
2393 RtlCopyMemory(lpBuffer,
2394 BaseWindowsDirectory.Buffer,
2395 BaseWindowsDirectory.Length);
2396 lpBuffer[BaseWindowsDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
2397
2398 ReturnLength = BaseWindowsDirectory.Length;
2399 }
2400
2401 return ReturnLength / sizeof(WCHAR);
2402 }
2403
2404 /*
2405 * @unimplemented
2406 */
2407 UINT
2408 WINAPI
2409 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer,
2410 IN UINT uSize)
2411 {
2412 #ifdef _WIN64
2413 UNIMPLEMENTED;
2414 return 0;
2415 #else
2416 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2417 return 0;
2418 #endif
2419 }
2420
2421 /*
2422 * @unimplemented
2423 */
2424 UINT
2425 WINAPI
2426 GetSystemWow64DirectoryA(IN LPSTR lpBuffer,
2427 IN UINT uSize)
2428 {
2429 #ifdef _WIN64
2430 UNIMPLEMENTED;
2431 return 0;
2432 #else
2433 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2434 return 0;
2435 #endif
2436 }
2437
2438 /* EOF */