1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include "stdafx.h"
6#include "windows.h"
7#include "longfilepathwrappers.h"
8#include "sstring.h"
9#include "ex.h"
10
11class LongFile
12{
13private:
14#ifndef FEATURE_PAL
15 static const WCHAR* ExtendedPrefix;
16 static const WCHAR* DevicePathPrefix;
17 static const WCHAR* UNCPathPrefix;
18 static const WCHAR* UNCExtendedPathPrefix;
19 static const WCHAR VolumeSeparatorChar;
20 #define UNCPATHPREFIX W("\\\\")
21#endif //FEATURE_PAL
22 static const WCHAR DirectorySeparatorChar;
23 static const WCHAR AltDirectorySeparatorChar;
24public:
25 static BOOL IsExtended(SString & path);
26 static BOOL IsUNCExtended(SString & path);
27 static BOOL ContainsDirectorySeparator(SString & path);
28 static BOOL IsDirectorySeparator(WCHAR c);
29 static BOOL IsPathNotFullyQualified(SString & path);
30 static BOOL IsDevice(SString & path);
31
32 static HRESULT NormalizePath(SString& path);
33};
34
35HMODULE
36LoadLibraryExWrapper(
37 LPCWSTR lpLibFileName,
38 HANDLE hFile,
39 DWORD dwFlags
40 )
41{
42 CONTRACTL
43 {
44 NOTHROW;
45 SO_TOLERANT;
46 }
47 CONTRACTL_END;
48
49 HRESULT hr = S_OK;
50 HMODULE ret = NULL;
51 DWORD lastError;
52
53 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return NULL;)
54 EX_TRY
55 {
56
57 LongPathString path(LongPathString::Literal, lpLibFileName);
58
59 if (LongFile::IsPathNotFullyQualified(path) || SUCCEEDED(LongFile::NormalizePath(path)))
60 {
61#ifndef FEATURE_PAL
62 //Adding the assert to ensure relative paths which are not just filenames are not used for LoadLibrary Calls
63 _ASSERTE(!LongFile::IsPathNotFullyQualified(path) || !LongFile::ContainsDirectorySeparator(path));
64#endif //FEATURE_PAL
65
66 ret = LoadLibraryExW(path.GetUnicode(), hFile, dwFlags);
67 }
68
69 lastError = GetLastError();
70 }
71 EX_CATCH_HRESULT(hr);
72 END_SO_INTOLERANT_CODE
73
74 if (hr != S_OK)
75 {
76 SetLastError(hr);
77 }
78 else if(ret == NULL)
79 {
80 SetLastError(lastError);
81 }
82
83 return ret;
84}
85
86HANDLE
87CreateFileWrapper(
88 _In_ LPCWSTR lpFileName,
89 _In_ DWORD dwDesiredAccess,
90 _In_ DWORD dwShareMode,
91 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
92 _In_ DWORD dwCreationDisposition,
93 _In_ DWORD dwFlagsAndAttributes,
94 _In_opt_ HANDLE hTemplateFile
95 )
96{
97 CONTRACTL
98 {
99 NOTHROW;
100 SO_TOLERANT;
101 }
102 CONTRACTL_END;
103
104 HRESULT hr = S_OK;
105 DWORD lastError;
106 HANDLE ret = INVALID_HANDLE_VALUE;
107
108 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return NULL;)
109
110 EX_TRY
111 {
112 LongPathString path(LongPathString::Literal, lpFileName);
113
114 if (SUCCEEDED(LongFile::NormalizePath(path)))
115 {
116 ret = CreateFileW(path.GetUnicode(),
117 dwDesiredAccess,
118 dwShareMode,
119 lpSecurityAttributes,
120 dwCreationDisposition,
121 dwFlagsAndAttributes,
122 hTemplateFile);
123
124 }
125
126 lastError = GetLastError();
127 }
128 EX_CATCH_HRESULT(hr);
129 END_SO_INTOLERANT_CODE
130
131 if (hr != S_OK )
132 {
133 SetLastError(hr);
134 }
135 else if(ret == INVALID_HANDLE_VALUE)
136 {
137 SetLastError(lastError);
138 }
139
140 return ret;
141}
142
143BOOL
144SetFileAttributesWrapper(
145 _In_ LPCWSTR lpFileName,
146 _In_ DWORD dwFileAttributes
147 )
148{
149 CONTRACTL
150 {
151 NOTHROW;
152 SO_TOLERANT;
153 }
154 CONTRACTL_END;
155
156 HRESULT hr = S_OK;
157 BOOL ret = FALSE;
158 DWORD lastError;
159
160 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
161
162 EX_TRY
163 {
164 LongPathString path(LongPathString::Literal, lpFileName);
165
166 if (SUCCEEDED(LongFile::NormalizePath(path)))
167 {
168 ret = SetFileAttributesW(
169 path.GetUnicode(),
170 dwFileAttributes
171 );
172 }
173
174 lastError = GetLastError();
175 }
176 EX_CATCH_HRESULT(hr);
177 END_SO_INTOLERANT_CODE
178
179 if (hr != S_OK )
180 {
181 SetLastError(hr);
182 }
183 else if(ret == FALSE)
184 {
185 SetLastError(lastError);
186 }
187
188 return ret;
189}
190
191DWORD
192GetFileAttributesWrapper(
193 _In_ LPCWSTR lpFileName
194 )
195{
196 CONTRACTL
197 {
198 NOTHROW;
199 SO_TOLERANT;
200 }
201 CONTRACTL_END;
202
203 HRESULT hr = S_OK;
204 DWORD ret = INVALID_FILE_ATTRIBUTES;
205 DWORD lastError;
206
207 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return INVALID_FILE_ATTRIBUTES;)
208
209 EX_TRY
210 {
211 LongPathString path(LongPathString::Literal, lpFileName);
212
213 if (SUCCEEDED(LongFile::NormalizePath(path)))
214 {
215 ret = GetFileAttributesW(
216 path.GetUnicode()
217 );
218 }
219
220 lastError = GetLastError();
221 }
222 EX_CATCH_HRESULT(hr);
223 END_SO_INTOLERANT_CODE
224
225 if (hr != S_OK )
226 {
227 SetLastError(hr);
228 }
229 else if(ret == INVALID_FILE_ATTRIBUTES)
230 {
231 SetLastError(lastError);
232 }
233
234 return ret;
235}
236
237BOOL
238GetFileAttributesExWrapper(
239 _In_ LPCWSTR lpFileName,
240 _In_ GET_FILEEX_INFO_LEVELS fInfoLevelId,
241 _Out_writes_bytes_(sizeof(WIN32_FILE_ATTRIBUTE_DATA)) LPVOID lpFileInformation
242 )
243{
244 CONTRACTL
245 {
246 NOTHROW;
247 SO_TOLERANT;
248 }
249 CONTRACTL_END;
250
251 HRESULT hr = S_OK;
252 BOOL ret = FALSE;
253 DWORD lastError;
254
255 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
256
257 EX_TRY
258 {
259 LongPathString path(LongPathString::Literal, lpFileName);
260
261 if (SUCCEEDED(LongFile::NormalizePath(path)))
262 {
263 ret = GetFileAttributesExW(
264 path.GetUnicode(),
265 fInfoLevelId,
266 lpFileInformation
267 );
268
269 }
270
271 lastError = GetLastError();
272 }
273 EX_CATCH_HRESULT(hr);
274 END_SO_INTOLERANT_CODE
275
276 if (hr != S_OK )
277 {
278 SetLastError(hr);
279 }
280 else if(ret == FALSE)
281 {
282 SetLastError(lastError);
283 }
284
285 return ret;
286}
287
288BOOL
289DeleteFileWrapper(
290 _In_ LPCWSTR lpFileName
291 )
292{
293 CONTRACTL
294 {
295 NOTHROW;
296 SO_TOLERANT;
297 }
298 CONTRACTL_END;
299
300 HRESULT hr = S_OK;
301 BOOL ret = FALSE;
302 DWORD lastError;
303
304 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
305
306 EX_TRY
307 {
308 LongPathString path(LongPathString::Literal, lpFileName);
309
310 if (SUCCEEDED(LongFile::NormalizePath(path)))
311 {
312 ret = DeleteFileW(
313 path.GetUnicode()
314 );
315 }
316
317 lastError = GetLastError();
318 }
319 EX_CATCH_HRESULT(hr);
320 END_SO_INTOLERANT_CODE
321
322 if (hr != S_OK )
323 {
324 SetLastError(hr);
325 }
326 else if(ret == FALSE)
327 {
328 SetLastError(lastError);
329 }
330
331 return ret;
332}
333
334
335BOOL
336CopyFileWrapper(
337 _In_ LPCWSTR lpExistingFileName,
338 _In_ LPCWSTR lpNewFileName,
339 _In_ BOOL bFailIfExists
340 )
341{
342 CONTRACTL
343 {
344 NOTHROW;
345 SO_TOLERANT;
346 }
347 CONTRACTL_END;
348
349 HRESULT hr = S_OK;
350 BOOL ret = FALSE;
351 DWORD lastError;
352
353 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
354
355 EX_TRY
356 {
357 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
358 LongPathString Newpath(LongPathString::Literal, lpNewFileName);
359
360 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
361 {
362 ret = CopyFileW(
363 Existingpath.GetUnicode(),
364 Newpath.GetUnicode(),
365 bFailIfExists
366 );
367 }
368
369 lastError = GetLastError();
370 }
371 EX_CATCH_HRESULT(hr);
372 END_SO_INTOLERANT_CODE
373
374 if (hr != S_OK )
375 {
376 SetLastError(hr);
377 }
378 else if(ret == FALSE)
379 {
380 SetLastError(lastError);
381 }
382
383 return ret;
384}
385
386BOOL
387MoveFileExWrapper(
388 _In_ LPCWSTR lpExistingFileName,
389 _In_opt_ LPCWSTR lpNewFileName,
390 _In_ DWORD dwFlags
391 )
392{
393 CONTRACTL
394 {
395 NOTHROW;
396 SO_TOLERANT;
397 }
398 CONTRACTL_END;
399
400 HRESULT hr = S_OK;
401 BOOL ret = FALSE;
402 DWORD lastError;
403
404 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
405
406 EX_TRY
407 {
408 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
409 LongPathString Newpath(LongPathString::Literal, lpNewFileName);
410
411 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
412 {
413 ret = MoveFileExW(
414 Existingpath.GetUnicode(),
415 Newpath.GetUnicode(),
416 dwFlags
417 );
418 }
419
420 lastError = GetLastError();
421 }
422 EX_CATCH_HRESULT(hr);
423 END_SO_INTOLERANT_CODE
424
425 if (hr != S_OK )
426 {
427 SetLastError(hr);
428 }
429 else if(ret == FALSE)
430 {
431 SetLastError(lastError);
432 }
433
434 return ret;
435
436}
437
438DWORD
439SearchPathWrapper(
440 _In_opt_ LPCWSTR lpPath,
441 _In_ LPCWSTR lpFileName,
442 _In_opt_ LPCWSTR lpExtension,
443 _In_ BOOL getPath,
444 SString& lpBuffer,
445 _Out_opt_ LPWSTR * lpFilePart
446 )
447{
448 CONTRACTL
449 {
450 NOTHROW;
451 SO_TOLERANT;
452 }
453 CONTRACTL_END;
454
455 HRESULT hr = S_OK;
456 DWORD ret = 0;
457 DWORD lastError;
458
459 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
460
461 EX_TRY
462 {
463 LongPathString Existingpath(LongPathString::Literal, lpPath);
464
465 if (lpPath != NULL)
466 {
467 if (FAILED(LongFile::NormalizePath(Existingpath)))
468 {
469 ret = FALSE;
470 }
471 else
472 {
473 lpPath = Existingpath.GetUnicode();
474 }
475 }
476
477 if (!getPath)
478 {
479 ret = SearchPathW(
480 lpPath,
481 lpFileName,
482 lpExtension,
483 0,
484 NULL,
485 NULL
486 );
487 }
488 else
489 {
490 COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
491
492 ret = SearchPathW(
493 lpPath,
494 lpFileName,
495 lpExtension,
496 size,
497 lpBuffer.OpenUnicodeBuffer(size - 1),
498 lpFilePart
499 );
500
501 if (ret > size)
502 {
503 lpBuffer.CloseBuffer();
504 ret = SearchPathW(
505 lpPath,
506 lpFileName,
507 lpExtension,
508 ret,
509 lpBuffer.OpenUnicodeBuffer(ret - 1),
510 lpFilePart
511 );
512 }
513
514 lpBuffer.CloseBuffer(ret);
515
516 }
517
518 lastError = GetLastError();
519 }
520 EX_CATCH_HRESULT(hr);
521 END_SO_INTOLERANT_CODE
522
523 if (hr != S_OK)
524 {
525 SetLastError(hr);
526 }
527 else if (ret == 0)
528 {
529 SetLastError(lastError);
530 }
531
532 return ret;
533
534}
535
536DWORD
537GetShortPathNameWrapper(
538 _In_ LPCWSTR lpszLongPath,
539 SString& lpszShortPath
540 )
541{
542 CONTRACTL
543 {
544 NOTHROW;
545 SO_TOLERANT;
546 }
547 CONTRACTL_END;
548
549 DWORD ret = 0;
550 HRESULT hr = S_OK;
551 DWORD lastError;
552
553 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
554
555 EX_TRY
556 {
557 LongPathString longPath(LongPathString::Literal, lpszLongPath);
558
559 if (SUCCEEDED(LongFile::NormalizePath(longPath)))
560 {
561 COUNT_T size = lpszShortPath.GetUnicodeAllocation() + 1;
562
563 ret = GetShortPathNameW(
564 longPath.GetUnicode(),
565 lpszShortPath.OpenUnicodeBuffer(size - 1),
566 (DWORD)size
567 );
568
569 if (ret > size)
570 {
571 lpszShortPath.CloseBuffer();
572 ret = GetShortPathNameW(
573 longPath.GetUnicode(),
574 lpszShortPath.OpenUnicodeBuffer(ret -1),
575 ret
576 );
577 }
578
579 lpszShortPath.CloseBuffer(ret);
580 }
581
582 lastError = GetLastError();
583 }
584 EX_CATCH_HRESULT(hr);
585 END_SO_INTOLERANT_CODE
586
587 if (hr != S_OK )
588 {
589 SetLastError(hr);
590 }
591 else if(ret == 0)
592 {
593 SetLastError(lastError);
594 }
595
596 return ret;
597}
598
599DWORD
600GetLongPathNameWrapper(
601 _In_ LPCWSTR lpszShortPath,
602 SString& lpszLongPath
603 )
604{
605 CONTRACTL
606 {
607 NOTHROW;
608 SO_TOLERANT;
609 }
610 CONTRACTL_END;
611
612 DWORD ret = 0;
613 HRESULT hr = S_OK;
614 DWORD lastError;
615
616 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
617
618 EX_TRY
619 {
620 LongPathString shortPath(LongPathString::Literal, lpszShortPath);
621
622 if (SUCCEEDED(LongFile::NormalizePath(shortPath)))
623 {
624 COUNT_T size = lpszLongPath.GetUnicodeAllocation() + 1;
625
626 ret = GetLongPathNameW(
627 shortPath.GetUnicode(),
628 lpszLongPath.OpenUnicodeBuffer(size - 1),
629 (DWORD)size
630 );
631
632 if (ret > size)
633 {
634 lpszLongPath.CloseBuffer();
635 ret = GetLongPathNameW(
636 shortPath.GetUnicode(),
637 lpszLongPath.OpenUnicodeBuffer(ret - 1),
638 ret
639 );
640
641 }
642
643 lpszLongPath.CloseBuffer(ret);
644 }
645
646 lastError = GetLastError();
647 }
648 EX_CATCH_HRESULT(hr);
649 END_SO_INTOLERANT_CODE
650
651 if (hr != S_OK )
652 {
653 SetLastError(hr);
654 }
655 else if(ret == 0)
656 {
657 SetLastError(lastError);
658 }
659
660 return ret;
661}
662
663BOOL
664CreateDirectoryWrapper(
665 _In_ LPCWSTR lpPathName,
666 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
667 )
668{
669 CONTRACTL
670 {
671 NOTHROW;
672 SO_TOLERANT;
673 }
674 CONTRACTL_END;
675
676 HRESULT hr = S_OK;
677 BOOL ret = FALSE;
678 DWORD lastError;
679
680 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
681
682 EX_TRY
683 {
684 LongPathString path(LongPathString::Literal, lpPathName);
685
686 if (SUCCEEDED(LongFile::NormalizePath(path)))
687 {
688 ret = CreateDirectoryW(
689 path.GetUnicode(),
690 lpSecurityAttributes
691 );
692 }
693
694 lastError = GetLastError();
695 }
696 EX_CATCH_HRESULT(hr);
697 END_SO_INTOLERANT_CODE
698
699 if (hr != S_OK )
700 {
701 SetLastError(hr);
702 }
703 else if(ret == FALSE)
704 {
705 SetLastError(lastError);
706 }
707
708 return ret;
709}
710
711BOOL
712RemoveDirectoryWrapper(
713 _In_ LPCWSTR lpPathName
714 )
715{
716 CONTRACTL
717 {
718 NOTHROW;
719 SO_TOLERANT;
720 }
721 CONTRACTL_END;
722
723 HRESULT hr = S_OK;
724 BOOL ret = FALSE;
725 DWORD lastError;
726
727 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
728
729 EX_TRY
730 {
731 LongPathString path(LongPathString::Literal, lpPathName);
732
733 if (SUCCEEDED(LongFile::NormalizePath(path)))
734 {
735 ret = RemoveDirectoryW(
736 path.GetUnicode()
737 );
738 }
739
740 lastError = GetLastError();
741 }
742 EX_CATCH_HRESULT(hr);
743 END_SO_INTOLERANT_CODE
744
745 if (hr != S_OK )
746 {
747 SetLastError(hr);
748 }
749 else if(ret == FALSE)
750 {
751 SetLastError(lastError);
752 }
753
754 return ret;
755}
756DWORD
757GetModuleFileNameWrapper(
758 _In_opt_ HMODULE hModule,
759 SString& buffer
760 )
761{
762 CONTRACTL
763 {
764 NOTHROW;
765 SO_TOLERANT;
766 }
767 CONTRACTL_END;
768
769 HRESULT hr = S_OK;
770 DWORD ret = 0;
771 DWORD lastError;
772
773 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
774
775 EX_TRY
776 {
777 COUNT_T size = buffer.GetUnicodeAllocation() + 1;
778
779 ret = GetModuleFileNameW(
780 hModule,
781 buffer.OpenUnicodeBuffer(size - 1),
782 (DWORD)size
783 );
784
785
786 while (ret == size )
787 {
788 buffer.CloseBuffer();
789 size = size * 2;
790 ret = GetModuleFileNameW(
791 hModule,
792 buffer.OpenUnicodeBuffer(size - 1),
793 (DWORD)size
794 );
795
796 }
797
798
799 lastError = GetLastError();
800 buffer.CloseBuffer(ret);
801 }
802 EX_CATCH_HRESULT(hr);
803 END_SO_INTOLERANT_CODE
804
805 if (hr != S_OK)
806 {
807 SetLastError(hr);
808 }
809 else if (ret == 0)
810 {
811 SetLastError(lastError);
812 }
813
814 return ret;
815}
816
817UINT WINAPI GetTempFileNameWrapper(
818 _In_ LPCTSTR lpPathName,
819 _In_ LPCTSTR lpPrefixString,
820 _In_ UINT uUnique,
821 SString& lpTempFileName
822 )
823{
824 CONTRACTL
825 {
826 NOTHROW;
827 SO_TOLERANT;
828 }
829 CONTRACTL_END;
830
831 HRESULT hr = S_OK;
832 UINT ret = 0;
833 DWORD lastError;
834
835 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
836
837 EX_TRY
838 {
839 //Change the behaviour in Redstone to retry
840 COUNT_T size = MAX_LONGPATH;
841 WCHAR* buffer = lpTempFileName.OpenUnicodeBuffer(size - 1);
842 ret = GetTempFileNameW(
843 lpPathName,
844 lpPrefixString,
845 uUnique,
846 buffer
847 );
848
849 lastError = GetLastError();
850 size = (COUNT_T)wcslen(buffer);
851 lpTempFileName.CloseBuffer(size);
852
853 }
854 EX_CATCH_HRESULT(hr);
855 END_SO_INTOLERANT_CODE
856
857 if (hr != S_OK)
858 {
859 SetLastError(hr);
860 }
861 else if (ret == 0)
862 {
863 SetLastError(lastError);
864 }
865
866 return ret;
867}
868DWORD WINAPI GetTempPathWrapper(
869 SString& lpBuffer
870 )
871{
872 CONTRACTL
873 {
874 NOTHROW;
875 SO_TOLERANT;
876 }
877 CONTRACTL_END;
878
879 HRESULT hr = S_OK;
880 DWORD ret = 0;
881 DWORD lastError;
882
883 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
884
885 EX_TRY
886 {
887 //Change the behaviour in Redstone to retry
888 COUNT_T size = MAX_LONGPATH;
889
890 ret = GetTempPathW(
891 size,
892 lpBuffer.OpenUnicodeBuffer(size - 1)
893 );
894
895 lastError = GetLastError();
896 lpBuffer.CloseBuffer(ret);
897 }
898 EX_CATCH_HRESULT(hr);
899 END_SO_INTOLERANT_CODE
900
901 if (hr != S_OK)
902 {
903 SetLastError(hr);
904 }
905 else if (ret == 0)
906 {
907 SetLastError(lastError);
908 }
909
910 return ret;
911}
912
913DWORD WINAPI GetCurrentDirectoryWrapper(
914 SString& lpBuffer
915 )
916{
917 CONTRACTL
918 {
919 NOTHROW;
920 SO_TOLERANT;
921 }
922 CONTRACTL_END;
923
924 HRESULT hr = S_OK;
925 DWORD ret = 0;
926 DWORD lastError;
927
928 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
929
930 EX_TRY
931 {
932 //Change the behaviour in Redstone to retry
933 COUNT_T size = MAX_LONGPATH;
934
935 ret = GetCurrentDirectoryW(
936 size,
937 lpBuffer.OpenUnicodeBuffer(size - 1)
938 );
939
940 lastError = GetLastError();
941 lpBuffer.CloseBuffer(ret);
942 }
943 EX_CATCH_HRESULT(hr);
944 END_SO_INTOLERANT_CODE
945
946 if (hr != S_OK)
947 {
948 SetLastError(hr);
949 }
950 else if (ret == 0)
951 {
952 SetLastError(lastError);
953 }
954
955 return ret;
956}
957
958DWORD WINAPI GetEnvironmentVariableWrapper(
959 _In_opt_ LPCTSTR lpName,
960 _Out_opt_ SString& lpBuffer
961 )
962{
963 CONTRACTL
964 {
965 NOTHROW;
966 SO_TOLERANT;
967 }
968 CONTRACTL_END;
969
970 HRESULT hr = S_OK;
971 DWORD ret = 0;
972 DWORD lastError;
973
974 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return 0;)
975
976 EX_TRY
977 {
978
979 COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
980
981 ret = GetEnvironmentVariableW(
982 lpName,
983 lpBuffer.OpenUnicodeBuffer(size - 1),
984 size
985 );
986
987 // We loop round getting the length of the env var and then trying to copy
988 // the value into a the allocated buffer. Usually we'll go through this loop
989 // precisely once, but the caution is ncessary in case the variable mutates
990 // beneath us, as the environment variable can be modified by another thread
991 //between two calls to GetEnvironmentVariableW
992
993 while (ret > size)
994 {
995 size = ret;
996 lpBuffer.CloseBuffer();
997 ret = GetEnvironmentVariableW(
998 lpName,
999 lpBuffer.OpenUnicodeBuffer(size - 1),
1000 size);
1001 }
1002
1003 lastError = GetLastError();
1004 lpBuffer.CloseBuffer(ret);
1005 }
1006 EX_CATCH_HRESULT(hr);
1007 END_SO_INTOLERANT_CODE
1008
1009 if (hr != S_OK)
1010 {
1011 SetLastError(hr);
1012 }
1013 else if (ret == 0)
1014 {
1015 SetLastError(lastError);
1016 }
1017
1018 return ret;
1019}
1020
1021
1022#ifndef FEATURE_PAL
1023
1024BOOL
1025CreateHardLinkWrapper(
1026 _In_ LPCWSTR lpFileName,
1027 _In_ LPCWSTR lpExistingFileName,
1028 _Reserved_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
1029 )
1030{
1031 CONTRACTL
1032 {
1033 NOTHROW;
1034 SO_TOLERANT;
1035 }
1036 CONTRACTL_END;
1037
1038 HRESULT hr = S_OK;
1039 BOOL ret = FALSE;
1040 DWORD lastError;
1041
1042 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
1043
1044 EX_TRY
1045 {
1046 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
1047 LongPathString FileName(LongPathString::Literal, lpFileName);
1048
1049 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(FileName)))
1050 {
1051 ret = CreateHardLinkW(
1052 Existingpath.GetUnicode(),
1053 FileName.GetUnicode(),
1054 lpSecurityAttributes
1055 );
1056 }
1057
1058 lastError = GetLastError();
1059 }
1060 EX_CATCH_HRESULT(hr);
1061 END_SO_INTOLERANT_CODE
1062
1063 if (hr != S_OK )
1064 {
1065 SetLastError(hr);
1066 }
1067 else if(ret == FALSE)
1068 {
1069 SetLastError(lastError);
1070 }
1071
1072 return ret;
1073}
1074
1075BOOL
1076CopyFileExWrapper(
1077 _In_ LPCWSTR lpExistingFileName,
1078 _In_ LPCWSTR lpNewFileName,
1079 _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine,
1080 _In_opt_ LPVOID lpData,
1081 _When_(pbCancel != NULL, _Pre_satisfies_(*pbCancel == FALSE))
1082 _Inout_opt_ LPBOOL pbCancel,
1083 _In_ DWORD dwCopyFlags
1084 )
1085{
1086 CONTRACTL
1087 {
1088 NOTHROW;
1089 SO_TOLERANT;
1090 }
1091 CONTRACTL_END;
1092
1093 HRESULT hr = S_OK;
1094 BOOL ret = FALSE;
1095 DWORD lastError;
1096
1097 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
1098
1099 EX_TRY
1100 {
1101 LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
1102 LongPathString Newpath(LongPathString::Literal, lpNewFileName);
1103
1104 if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
1105 {
1106 ret = CopyFileExW(
1107 Existingpath.GetUnicode(),
1108 Newpath.GetUnicode(),
1109 lpProgressRoutine,
1110 lpData,
1111 pbCancel,
1112 dwCopyFlags
1113 );
1114 }
1115
1116 lastError = GetLastError();
1117 }
1118 EX_CATCH_HRESULT(hr);
1119 END_SO_INTOLERANT_CODE
1120
1121 if (hr != S_OK )
1122 {
1123 SetLastError(hr);
1124 }
1125 else if(ret == FALSE)
1126 {
1127 SetLastError(lastError);
1128 }
1129
1130 return ret;
1131}
1132
1133HANDLE
1134FindFirstFileExWrapper(
1135 _In_ LPCWSTR lpFileName,
1136 _In_ FINDEX_INFO_LEVELS fInfoLevelId,
1137 _Out_writes_bytes_(sizeof(WIN32_FIND_DATAW)) LPVOID lpFindFileData,
1138 _In_ FINDEX_SEARCH_OPS fSearchOp,
1139 _Reserved_ LPVOID lpSearchFilter,
1140 _In_ DWORD dwAdditionalFlags
1141 )
1142{
1143 CONTRACTL
1144 {
1145 NOTHROW;
1146 SO_TOLERANT;
1147 }
1148 CONTRACTL_END;
1149
1150 HRESULT hr = S_OK;
1151 HANDLE ret = INVALID_HANDLE_VALUE;
1152 DWORD lastError;
1153
1154 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return FALSE;)
1155
1156 EX_TRY
1157 {
1158 LongPathString path(LongPathString::Literal, lpFileName);
1159
1160 if (SUCCEEDED(LongFile::NormalizePath(path)))
1161 {
1162 ret = FindFirstFileExW(
1163 path.GetUnicode(),
1164 fInfoLevelId,
1165 lpFindFileData,
1166 fSearchOp,
1167 lpSearchFilter,
1168 dwAdditionalFlags
1169 );
1170 }
1171
1172 lastError = GetLastError();
1173 }
1174 EX_CATCH_HRESULT(hr);
1175 END_SO_INTOLERANT_CODE
1176
1177 if (hr != S_OK )
1178 {
1179 SetLastError(hr);
1180 }
1181 else if(ret == INVALID_HANDLE_VALUE)
1182 {
1183 SetLastError(lastError);
1184 }
1185
1186 return ret;
1187}
1188#endif //!FEATURE_PAL
1189
1190
1191#ifndef FEATURE_PAL
1192
1193#if ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1194extern HINSTANCE g_pMSCorEE;
1195#endif// ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1196
1197BOOL PAL_GetPALDirectoryWrapper(SString& pbuffer)
1198{
1199
1200 HRESULT hr = S_OK;
1201
1202 PathString pPath;
1203 DWORD dwPath;
1204 HINSTANCE hinst = NULL;
1205
1206#if ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1207 hinst = g_pMSCorEE;
1208#endif// ! defined(DACCESS_COMPILE) && !defined(SELF_NO_HOST)
1209
1210#ifndef CROSSGEN_COMPILE
1211 _ASSERTE(hinst != NULL);
1212#endif
1213
1214 dwPath = WszGetModuleFileName(hinst, pPath);
1215
1216 if(dwPath == 0)
1217 {
1218 hr = HRESULT_FROM_GetLastErrorNA();
1219 }
1220 else
1221 {
1222 hr = CopySystemDirectory(pPath, pbuffer);
1223 }
1224
1225 return (hr == S_OK);
1226}
1227
1228#else
1229
1230BOOL PAL_GetPALDirectoryWrapper(SString& pbuffer)
1231{
1232 BOOL retval = FALSE;
1233 COUNT_T size = MAX_LONGPATH;
1234
1235 if(!(retval = PAL_GetPALDirectoryW(pbuffer.OpenUnicodeBuffer(size - 1), &size)))
1236 {
1237 pbuffer.CloseBuffer(0);
1238 retval = PAL_GetPALDirectoryW(pbuffer.OpenUnicodeBuffer(size - 1), &size);
1239 }
1240
1241 pbuffer.CloseBuffer(size);
1242
1243 return retval;
1244}
1245
1246#endif // FEATURE_PAL
1247
1248
1249//Implementation of LongFile Helpers
1250const WCHAR LongFile::DirectorySeparatorChar = W('\\');
1251const WCHAR LongFile::AltDirectorySeparatorChar = W('/');
1252#ifndef FEATURE_PAL
1253const WCHAR LongFile::VolumeSeparatorChar = W(':');
1254const WCHAR* LongFile::ExtendedPrefix = W("\\\\?\\");
1255const WCHAR* LongFile::DevicePathPrefix = W("\\\\.\\");
1256const WCHAR* LongFile::UNCExtendedPathPrefix = W("\\\\?\\UNC\\");
1257const WCHAR* LongFile::UNCPathPrefix = UNCPATHPREFIX;
1258
1259BOOL LongFile::IsExtended(SString & path)
1260{
1261 return path.BeginsWith(ExtendedPrefix);
1262}
1263
1264BOOL LongFile::IsUNCExtended(SString & path)
1265{
1266
1267 return path.BeginsWith(UNCExtendedPathPrefix);
1268}
1269
1270// Relative here means it could be relative to current directory on the relevant drive
1271// NOTE: Relative segments ( \..\) are not considered relative
1272// Returns true if the path specified is relative to the current drive or working directory.
1273// Returns false if the path is fixed to a specific drive or UNC path. This method does no
1274// validation of the path (URIs will be returned as relative as a result).
1275// Handles paths that use the alternate directory separator. It is a frequent mistake to
1276// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
1277
1278BOOL LongFile::IsPathNotFullyQualified(SString & path)
1279{
1280 if (path.GetCount() < 2)
1281 {
1282 return TRUE; // It isn't fixed, it must be relative. There is no way to specify a fixed path with one character (or less).
1283 }
1284
1285 if (IsDirectorySeparator(path[0]))
1286 {
1287 return !IsDirectorySeparator(path[1]); // There is no valid way to specify a relative path with two initial slashes
1288 }
1289
1290 return !((path.GetCount() >= 3) //The only way to specify a fixed path that doesn't begin with two slashes is the drive, colon, slash format- i.e. "C:\"
1291 && (path[1] == VolumeSeparatorChar)
1292 && IsDirectorySeparator(path[2]));
1293}
1294
1295BOOL LongFile::IsDevice(SString & path)
1296{
1297 return path.BeginsWith(DevicePathPrefix);
1298}
1299
1300// This function will normalize paths if the path length exceeds MAX_PATH
1301// The normalization examples are :
1302// C:\foo\<long>\bar => \\?\C:\foo\<long>\bar
1303// \\server\<long>\bar => \\?\UNC\server\<long>\bar
1304HRESULT LongFile::NormalizePath(SString & path)
1305{
1306 HRESULT hr = S_OK;
1307 DWORD ret = 0;
1308 COUNT_T prefixLen = 0;
1309 if (path.IsEmpty()|| IsDevice(path) || IsExtended(path) || IsUNCExtended(path))
1310 return S_OK;
1311
1312 if (!IsPathNotFullyQualified(path) && path.GetCount() < MAX_LONGPATH)
1313 return S_OK;
1314
1315 //Now the path will be normalized
1316
1317 SString originalPath(path);
1318 SString prefix(ExtendedPrefix);
1319 prefixLen = prefix.GetCount();
1320
1321 if (path.BeginsWith(UNCPathPrefix))
1322 {
1323 prefix.Set(UNCExtendedPathPrefix);
1324 //In this case if path is \\server the extended syntax should be like \\?\UNC\server
1325 //The below logic populates the path from prefixLen offset from the start. This ensures that first 2 characters are overwritten
1326 //
1327 prefixLen = prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX);
1328 _ASSERTE(prefixLen > 0 );
1329 }
1330
1331
1332 COUNT_T size = path.GetUnicodeAllocation() + 1;
1333 WCHAR* buffer = path.OpenUnicodeBuffer(size - 1);
1334
1335 ret = GetFullPathNameW(
1336 originalPath.GetUnicode(),
1337 size - prefixLen, //memory avilable for path after reserving for prefix
1338 (buffer + prefixLen), //reserve memory for prefix
1339 NULL
1340 );
1341
1342 if (ret == 0)
1343 {
1344 return E_FAIL;
1345 }
1346
1347 if (ret > size - prefixLen)
1348 {
1349 path.CloseBuffer();
1350 size = ret + prefixLen;
1351 buffer = path.OpenUnicodeBuffer(size -1);
1352
1353 ret = GetFullPathNameW(
1354 originalPath.GetUnicode(),
1355 ret, // memory required for the path
1356 (buffer + prefixLen), //reserve memory for prefix
1357 NULL
1358 );
1359
1360 _ASSERTE(ret < size - prefixLen);
1361
1362 if (ret == 0)
1363 {
1364 return E_FAIL;
1365 }
1366 }
1367
1368 SString fullpath(SString::Literal,buffer + prefixLen);
1369
1370 //Check if the resolved path is a UNC. By default we assume relative path to resolve to disk
1371 if (fullpath.BeginsWith(UNCPathPrefix) && prefixLen != prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX))
1372 {
1373
1374 //Remove the leading '\\' from the UNC path to be replaced with UNCExtendedPathPrefix
1375 fullpath.Replace(fullpath.Begin(), (COUNT_T)wcslen(UNCPATHPREFIX), UNCExtendedPathPrefix);
1376 path.CloseBuffer();
1377 path.Set(fullpath);
1378 }
1379 else
1380 {
1381 //wcscpy_s always termintes with NULL, so we are saving the character that will be overwriiten
1382 WCHAR temp = buffer[prefix.GetCount()];
1383 wcscpy_s(buffer, prefix.GetCount() + 1, prefix.GetUnicode());
1384 buffer[prefix.GetCount()] = temp;
1385 path.CloseBuffer(ret + prefixLen);
1386 }
1387
1388 return S_OK;
1389}
1390#else
1391BOOL LongFile::IsExtended(SString & path)
1392{
1393 return FALSE;
1394}
1395
1396BOOL LongFile::IsUNCExtended(SString & path)
1397{
1398 return FALSE;
1399}
1400
1401BOOL LongFile::IsPathNotFullyQualified(SString & path)
1402{
1403 return TRUE;
1404}
1405
1406BOOL LongFile::IsDevice(SString & path)
1407{
1408 return FALSE;
1409}
1410
1411//Don't need to do anything For XPlat
1412HRESULT LongFile::NormalizePath(SString & path)
1413{
1414 return S_OK;
1415}
1416#endif //FEATURE_PAL
1417
1418BOOL LongFile::ContainsDirectorySeparator(SString & path)
1419{
1420 return path.Find(path.Begin(), DirectorySeparatorChar) || path.Find(path.Begin(), AltDirectorySeparatorChar);
1421}
1422
1423BOOL LongFile::IsDirectorySeparator(WCHAR c)
1424{
1425 return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
1426}
1427
1428
1429
1430