xref: /reactos/dll/ntdll/rtl/libsupp.c (revision d6eebaa4)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS NT User-Mode DLL
4  * FILE:            lib/ntdll/rtl/libsup.c
5  * PURPOSE:         RTL Support Routines
6  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
7  *                  Gunnar Dalsnes
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ntdll.h>
13 #include <apisets.h>
14 #include <compat_undoc.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 SIZE_T RtlpAllocDeallocQueryBufferSize = PAGE_SIZE;
20 PTEB LdrpTopLevelDllBeingLoadedTeb = NULL;
21 PVOID MmHighestUserAddress = (PVOID)MI_HIGHEST_USER_ADDRESS;
22 
23 /* FUNCTIONS ***************************************************************/
24 
25 BOOLEAN
26 NTAPI
27 RtlpCheckForActiveDebugger(VOID)
28 {
29     /* Return the flag in the PEB */
30     return NtCurrentPeb()->BeingDebugged;
31 }
32 
33 BOOLEAN
34 NTAPI
35 RtlpSetInDbgPrint(VOID)
36 {
37     /* Check if it's already set and return TRUE if so */
38     if (NtCurrentTeb()->InDbgPrint) return TRUE;
39 
40     /* Set it and return */
41     NtCurrentTeb()->InDbgPrint = TRUE;
42     return FALSE;
43 }
44 
45 VOID
46 NTAPI
47 RtlpClearInDbgPrint(VOID)
48 {
49     /* Clear the flag */
50     NtCurrentTeb()->InDbgPrint = FALSE;
51 }
52 
53 KPROCESSOR_MODE
54 NTAPI
55 RtlpGetMode(VOID)
56 {
57    return UserMode;
58 }
59 
60 /*
61  * @implemented
62  */
63 PPEB
64 NTAPI
65 RtlGetCurrentPeb(VOID)
66 {
67     return NtCurrentPeb();
68 }
69 
70 /*
71  * @implemented
72  */
73 VOID NTAPI
74 RtlAcquirePebLock(VOID)
75 {
76    PPEB Peb = NtCurrentPeb ();
77    RtlEnterCriticalSection(Peb->FastPebLock);
78 }
79 
80 /*
81  * @implemented
82  */
83 VOID NTAPI
84 RtlReleasePebLock(VOID)
85 {
86    PPEB Peb = NtCurrentPeb ();
87    RtlLeaveCriticalSection(Peb->FastPebLock);
88 }
89 
90 /*
91 * @implemented
92 */
93 ULONG
94 NTAPI
95 RtlGetNtGlobalFlags(VOID)
96 {
97     PPEB pPeb = NtCurrentPeb();
98     return pPeb->NtGlobalFlag;
99 }
100 
101 NTSTATUS
102 NTAPI
103 RtlDeleteHeapLock(IN OUT PHEAP_LOCK Lock)
104 {
105     return RtlDeleteCriticalSection(&Lock->CriticalSection);
106 }
107 
108 NTSTATUS
109 NTAPI
110 RtlEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive)
111 {
112     UNREFERENCED_PARAMETER(Exclusive);
113 
114     return RtlEnterCriticalSection(&Lock->CriticalSection);
115 }
116 
117 BOOLEAN
118 NTAPI
119 RtlTryEnterHeapLock(IN OUT PHEAP_LOCK Lock, IN BOOLEAN Exclusive)
120 {
121     UNREFERENCED_PARAMETER(Exclusive);
122 
123     return RtlTryEnterCriticalSection(&Lock->CriticalSection);
124 }
125 
126 NTSTATUS
127 NTAPI
128 RtlInitializeHeapLock(IN OUT PHEAP_LOCK *Lock)
129 {
130     return RtlInitializeCriticalSection(&(*Lock)->CriticalSection);
131 }
132 
133 NTSTATUS
134 NTAPI
135 RtlLeaveHeapLock(IN OUT PHEAP_LOCK Lock)
136 {
137     return RtlLeaveCriticalSection(&Lock->CriticalSection);
138 }
139 
140 PVOID
141 NTAPI
142 RtlpAllocateMemory(UINT Bytes,
143                    ULONG Tag)
144 {
145     UNREFERENCED_PARAMETER(Tag);
146 
147     return RtlAllocateHeap(RtlGetProcessHeap(),
148                            0,
149                            Bytes);
150 }
151 
152 
153 VOID
154 NTAPI
155 RtlpFreeMemory(PVOID Mem,
156                ULONG Tag)
157 {
158     UNREFERENCED_PARAMETER(Tag);
159 
160     RtlFreeHeap(RtlGetProcessHeap(),
161                 0,
162                 Mem);
163 }
164 
165 
166 #if DBG
167 VOID FASTCALL
168 CHECK_PAGED_CODE_RTL(char *file, int line)
169 {
170   /* meaningless in user mode */
171 }
172 #endif
173 
174 VOID
175 NTAPI
176 RtlpSetHeapParameters(IN PRTL_HEAP_PARAMETERS Parameters)
177 {
178     PPEB Peb;
179 
180     /* Get PEB */
181     Peb = RtlGetCurrentPeb();
182 
183     /* Apply defaults for non-set parameters */
184     if (!Parameters->SegmentCommit) Parameters->SegmentCommit = Peb->HeapSegmentCommit;
185     if (!Parameters->SegmentReserve) Parameters->SegmentReserve = Peb->HeapSegmentReserve;
186     if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = Peb->HeapDeCommitFreeBlockThreshold;
187     if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = Peb->HeapDeCommitTotalFreeThreshold;
188 }
189 
190 BOOLEAN
191 NTAPI
192 RtlpHandleDpcStackException(IN PEXCEPTION_REGISTRATION_RECORD RegistrationFrame,
193                             IN ULONG_PTR RegistrationFrameEnd,
194                             IN OUT PULONG_PTR StackLow,
195                             IN OUT PULONG_PTR StackHigh)
196 {
197     /* There's no such thing as a DPC stack in user-mode */
198     return FALSE;
199 }
200 
201 VOID
202 NTAPI
203 RtlpCheckLogException(IN PEXCEPTION_RECORD ExceptionRecord,
204                       IN PCONTEXT ContextRecord,
205                       IN PVOID ContextData,
206                       IN ULONG Size)
207 {
208     /* Exception logging is not done in user-mode */
209 }
210 
211 BOOLEAN
212 NTAPI
213 RtlpCaptureStackLimits(IN ULONG_PTR Ebp,
214                        IN ULONG_PTR *StackBegin,
215                        IN ULONG_PTR *StackEnd)
216 {
217     /* FIXME: Verify */
218     *StackBegin = (ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit;
219     *StackEnd = (ULONG_PTR)NtCurrentTeb()->NtTib.StackBase;
220     return TRUE;
221 }
222 
223 #ifndef _M_AMD64
224 /*
225  * @implemented
226  */
227 ULONG
228 NTAPI
229 RtlWalkFrameChain(OUT PVOID *Callers,
230                   IN ULONG Count,
231                   IN ULONG Flags)
232 {
233     ULONG_PTR Stack, NewStack, StackBegin, StackEnd = 0;
234     ULONG Eip;
235     BOOLEAN Result, StopSearch = FALSE;
236     ULONG i = 0;
237 
238     /* Get current EBP */
239 #if defined(_M_IX86)
240 #if defined __GNUC__
241     __asm__("mov %%ebp, %0" : "=r" (Stack) : );
242 #elif defined(_MSC_VER)
243     __asm mov Stack, ebp
244 #endif
245 #elif defined(_M_MIPS)
246         __asm__("move $sp, %0" : "=r" (Stack) : );
247 #elif defined(_M_PPC)
248     __asm__("mr %0,1" : "=r" (Stack) : );
249 #elif defined(_M_ARM)
250 #if defined __GNUC__
251     __asm__("mov sp, %0" : "=r"(Stack) : );
252 #elif defined(_MSC_VER)
253     // FIXME: Hack. Probably won't work if this ever actually manages to run someday.
254     Stack = (ULONG_PTR)&Stack;
255 #endif
256 #else
257 #error Unknown architecture
258 #endif
259 
260     /* Set it as the stack begin limit as well */
261     StackBegin = (ULONG_PTR)Stack;
262 
263     /* Check if we're called for non-logging mode */
264     if (!Flags)
265     {
266         /* Get the actual safe limits */
267         Result = RtlpCaptureStackLimits((ULONG_PTR)Stack,
268                                         &StackBegin,
269                                         &StackEnd);
270         if (!Result) return 0;
271     }
272 
273     /* Use a SEH block for maximum protection */
274     _SEH2_TRY
275     {
276         /* Loop the frames */
277         for (i = 0; i < Count; i++)
278         {
279             /*
280              * Leave if we're past the stack,
281              * if we're before the stack,
282              * or if we've reached ourselves.
283              */
284             if ((Stack >= StackEnd) ||
285                 (!i ? (Stack < StackBegin) : (Stack <= StackBegin)) ||
286                 ((StackEnd - Stack) < (2 * sizeof(ULONG_PTR))))
287             {
288                 /* We're done or hit a bad address */
289                 break;
290             }
291 
292             /* Get new stack and EIP */
293             NewStack = *(PULONG_PTR)Stack;
294             Eip = *(PULONG_PTR)(Stack + sizeof(ULONG_PTR));
295 
296             /* Check if the new pointer is above the oldone and past the end */
297             if (!((Stack < NewStack) && (NewStack < StackEnd)))
298             {
299                 /* Stop searching after this entry */
300                 StopSearch = TRUE;
301             }
302 
303             /* Also make sure that the EIP isn't a stack address */
304             if ((StackBegin < Eip) && (Eip < StackEnd)) break;
305 
306             /* FIXME: Check that EIP is inside a loaded module */
307 
308             /* Save this frame */
309             Callers[i] = (PVOID)Eip;
310 
311             /* Check if we should continue */
312             if (StopSearch)
313             {
314                 /* Return the next index */
315                 i++;
316                 break;
317             }
318 
319             /* Move to the next stack */
320             Stack = NewStack;
321         }
322     }
323     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
324     {
325         /* No index */
326         i = 0;
327     }
328     _SEH2_END;
329 
330     /* Return frames parsed */
331     return i;
332 }
333 #endif
334 
335 VOID
336 NTAPI
337 RtlpGetStackLimits(
338     OUT PULONG_PTR LowLimit,
339     OUT PULONG_PTR HighLimit)
340 {
341     *LowLimit = (ULONG_PTR)NtCurrentTeb()->NtTib.StackLimit;
342     *HighLimit = (ULONG_PTR)NtCurrentTeb()->NtTib.StackBase;
343 }
344 
345 BOOLEAN
346 NTAPI
347 RtlIsThreadWithinLoaderCallout(VOID)
348 {
349     return LdrpTopLevelDllBeingLoadedTeb == NtCurrentTeb();
350 }
351 
352 /* RTL Atom Tables ************************************************************/
353 
354 typedef struct _RTL_ATOM_HANDLE
355 {
356    RTL_HANDLE_TABLE_ENTRY Handle;
357    PRTL_ATOM_TABLE_ENTRY AtomEntry;
358 } RTL_ATOM_HANDLE, *PRTL_ATOM_HANDLE;
359 
360 NTSTATUS
361 RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable)
362 {
363    RtlInitializeCriticalSection(&AtomTable->CriticalSection);
364    return STATUS_SUCCESS;
365 }
366 
367 
368 VOID
369 RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable)
370 {
371    RtlDeleteCriticalSection(&AtomTable->CriticalSection);
372 }
373 
374 
375 BOOLEAN
376 RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable)
377 {
378    RtlEnterCriticalSection(&AtomTable->CriticalSection);
379    return TRUE;
380 }
381 
382 
383 VOID
384 RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable)
385 {
386    RtlLeaveCriticalSection(&AtomTable->CriticalSection);
387 }
388 
389 
390 /* handle functions */
391 
392 BOOLEAN
393 RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
394 {
395    RtlInitializeHandleTable(0xCFFF,
396                             sizeof(RTL_ATOM_HANDLE),
397                             &AtomTable->RtlHandleTable);
398 
399    return TRUE;
400 }
401 
402 VOID
403 RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
404 {
405    RtlDestroyHandleTable(&AtomTable->RtlHandleTable);
406 }
407 
408 PRTL_ATOM_TABLE
409 RtlpAllocAtomTable(ULONG Size)
410 {
411    return (PRTL_ATOM_TABLE)RtlAllocateHeap(RtlGetProcessHeap(),
412                                            HEAP_ZERO_MEMORY,
413                                            Size);
414 }
415 
416 VOID
417 RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable)
418 {
419    RtlFreeHeap(RtlGetProcessHeap(),
420                0,
421                AtomTable);
422 }
423 
424 PRTL_ATOM_TABLE_ENTRY
425 RtlpAllocAtomTableEntry(ULONG Size)
426 {
427    return (PRTL_ATOM_TABLE_ENTRY)RtlAllocateHeap(RtlGetProcessHeap(),
428                                                  HEAP_ZERO_MEMORY,
429                                                  Size);
430 }
431 
432 VOID
433 RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry)
434 {
435    RtlFreeHeap(RtlGetProcessHeap(),
436                0,
437                Entry);
438 }
439 
440 VOID
441 RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
442 {
443    PRTL_HANDLE_TABLE_ENTRY RtlHandleEntry;
444 
445    if (RtlIsValidIndexHandle(&AtomTable->RtlHandleTable,
446                              (ULONG)Entry->HandleIndex,
447                              &RtlHandleEntry))
448    {
449       RtlFreeHandle(&AtomTable->RtlHandleTable,
450                     RtlHandleEntry);
451    }
452 }
453 
454 BOOLEAN
455 RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry)
456 {
457    ULONG HandleIndex;
458    PRTL_HANDLE_TABLE_ENTRY RtlHandle;
459 
460    RtlHandle = RtlAllocateHandle(&AtomTable->RtlHandleTable,
461                                  &HandleIndex);
462    if (RtlHandle != NULL)
463    {
464       PRTL_ATOM_HANDLE AtomHandle = (PRTL_ATOM_HANDLE)RtlHandle;
465 
466       /* FIXME - Handle Indexes >= 0xC000 ?! */
467       if (HandleIndex < 0xC000)
468       {
469          Entry->HandleIndex = (USHORT)HandleIndex;
470          Entry->Atom = 0xC000 + (USHORT)HandleIndex;
471 
472          AtomHandle->AtomEntry = Entry;
473          AtomHandle->Handle.Flags = RTL_HANDLE_VALID;
474 
475          return TRUE;
476       }
477       else
478       {
479          /* set the valid flag, otherwise RtlFreeHandle will fail! */
480          AtomHandle->Handle.Flags = RTL_HANDLE_VALID;
481 
482          RtlFreeHandle(&AtomTable->RtlHandleTable,
483                        RtlHandle);
484       }
485    }
486 
487    return FALSE;
488 }
489 
490 PRTL_ATOM_TABLE_ENTRY
491 RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index)
492 {
493    PRTL_HANDLE_TABLE_ENTRY RtlHandle;
494 
495    if (RtlIsValidIndexHandle(&AtomTable->RtlHandleTable,
496                              Index,
497                              &RtlHandle))
498    {
499       PRTL_ATOM_HANDLE AtomHandle = (PRTL_ATOM_HANDLE)RtlHandle;
500 
501       return AtomHandle->AtomEntry;
502    }
503 
504    return NULL;
505 }
506 
507 /* Ldr SEH-Protected access to IMAGE_NT_HEADERS */
508 
509 /* Rtl SEH-Free version of this */
510 NTSTATUS
511 NTAPI
512 RtlpImageNtHeaderEx(
513     _In_ ULONG Flags,
514     _In_ PVOID Base,
515     _In_ ULONG64 Size,
516     _Out_ PIMAGE_NT_HEADERS *OutHeaders);
517 
518 
519 /*
520  * @implemented
521  * @note: This is here, so that we do not drag SEH into rosload, freeldr and bootmgfw
522  */
523 NTSTATUS
524 NTAPI
525 RtlImageNtHeaderEx(
526     _In_ ULONG Flags,
527     _In_ PVOID Base,
528     _In_ ULONG64 Size,
529     _Out_ PIMAGE_NT_HEADERS *OutHeaders)
530 {
531     NTSTATUS Status;
532 
533     /* Assume failure. This is also done in RtlpImageNtHeaderEx, but this is guarded by SEH. */
534     if (OutHeaders != NULL)
535         *OutHeaders = NULL;
536 
537     _SEH2_TRY
538     {
539         Status = RtlpImageNtHeaderEx(Flags, Base, Size, OutHeaders);
540     }
541     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
542     {
543         /* Fail with the SEH error */
544         Status = _SEH2_GetExceptionCode();
545     }
546     _SEH2_END;
547 
548     return Status;
549 }
550 
551 /*
552  * Ldr Resource support code
553  */
554 
555 IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir,
556                                               LPCWSTR name, void *root,
557                                               int want_dir );
558 IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir,
559                                             WORD id, void *root, int want_dir );
560 IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir,
561                                             void *root, int want_dir );
562 int push_language( USHORT *list, ULONG pos, WORD lang );
563 
564 /**********************************************************************
565  *  find_entry
566  *
567  * Find a resource entry
568  */
569 NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info,
570                      ULONG level, void **ret, int want_dir )
571 {
572     ULONG size;
573     void *root;
574     IMAGE_RESOURCE_DIRECTORY *resdirptr;
575     USHORT list[9];  /* list of languages to try */
576     int i, pos = 0;
577     LCID user_lcid, system_lcid;
578 
579     root = RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size );
580     if (!root) return STATUS_RESOURCE_DATA_NOT_FOUND;
581     if (size < sizeof(*resdirptr)) return STATUS_RESOURCE_DATA_NOT_FOUND;
582     resdirptr = root;
583 
584     if (!level--) goto done;
585     if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Type, root, want_dir || level )))
586         return STATUS_RESOURCE_TYPE_NOT_FOUND;
587     if (!level--) return STATUS_SUCCESS;
588 
589     resdirptr = *ret;
590     if (!(*ret = find_entry_by_name( resdirptr, (LPCWSTR)info->Name, root, want_dir || level )))
591         return STATUS_RESOURCE_NAME_NOT_FOUND;
592     if (!level--) return STATUS_SUCCESS;
593     if (level) return STATUS_INVALID_PARAMETER;  /* level > 3 */
594 
595     /* 1. specified language */
596     pos = push_language( list, pos, info->Language );
597 
598     /* 2. specified language with neutral sublanguage */
599     pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(info->Language), SUBLANG_NEUTRAL ) );
600 
601     /* 3. neutral language with neutral sublanguage */
602     pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) );
603 
604     /* if no explicitly specified language, try some defaults */
605     if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
606     {
607         /* user defaults, unless SYS_DEFAULT sublanguage specified  */
608         if (SUBLANGID(info->Language) != SUBLANG_SYS_DEFAULT)
609         {
610             /* 4. current thread locale language */
611             pos = push_language( list, pos, LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale) );
612 
613             if (NT_SUCCESS(NtQueryDefaultLocale(TRUE, &user_lcid)))
614             {
615                 /* 5. user locale language */
616                 pos = push_language( list, pos, LANGIDFROMLCID(user_lcid) );
617 
618                 /* 6. user locale language with neutral sublanguage  */
619                 pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(user_lcid), SUBLANG_NEUTRAL ) );
620             }
621         }
622 
623         /* now system defaults */
624 
625         if (NT_SUCCESS(NtQueryDefaultLocale(FALSE, &system_lcid)))
626         {
627             /* 7. system locale language */
628             pos = push_language( list, pos, LANGIDFROMLCID( system_lcid ) );
629 
630             /* 8. system locale language with neutral sublanguage */
631             pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(system_lcid), SUBLANG_NEUTRAL ) );
632         }
633 
634         /* 9. English */
635         pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
636     }
637 
638     resdirptr = *ret;
639     for (i = 0; i < pos; i++)
640         if ((*ret = find_entry_by_id( resdirptr, list[i], root, want_dir ))) return STATUS_SUCCESS;
641 
642     /* if no explicitly specified language, return the first entry */
643     if (PRIMARYLANGID(info->Language) == LANG_NEUTRAL)
644     {
645         if ((*ret = find_first_entry( resdirptr, root, want_dir ))) return STATUS_SUCCESS;
646     }
647     return STATUS_RESOURCE_LANG_NOT_FOUND;
648 
649 done:
650     *ret = resdirptr;
651     return STATUS_SUCCESS;
652 }
653 
654 /*
655  * @implemented
656  */
657 PVOID NTAPI
658 RtlPcToFileHeader(IN PVOID PcValue,
659                   PVOID* BaseOfImage)
660 {
661     PLIST_ENTRY ModuleListHead;
662     PLIST_ENTRY Entry;
663     PLDR_DATA_TABLE_ENTRY Module;
664     PVOID ImageBase = NULL;
665 
666     RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock);
667     ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
668     Entry = ModuleListHead->Flink;
669     while (Entry != ModuleListHead)
670     {
671         Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
672 
673         if ((ULONG_PTR)PcValue >= (ULONG_PTR)Module->DllBase &&
674                 (ULONG_PTR)PcValue < (ULONG_PTR)Module->DllBase + Module->SizeOfImage)
675         {
676             ImageBase = Module->DllBase;
677             break;
678         }
679         Entry = Entry->Flink;
680     }
681     RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock);
682 
683     *BaseOfImage = ImageBase;
684     return ImageBase;
685 }
686 
687 NTSTATUS get_buffer(LPWSTR *buffer, SIZE_T needed, PUNICODE_STRING CallerBuffer, BOOLEAN bAllocateBuffer)
688 {
689     WCHAR *p;
690 
691     if (CallerBuffer && CallerBuffer->MaximumLength > needed)
692     {
693         p = CallerBuffer->Buffer;
694         CallerBuffer->Length = needed - sizeof(WCHAR);
695     }
696     else
697     {
698         if (!bAllocateBuffer)
699             return STATUS_BUFFER_TOO_SMALL;
700 
701         if (CallerBuffer)
702             CallerBuffer->Buffer[0] = 0;
703 
704         p = RtlAllocateHeap(RtlGetProcessHeap(), 0, needed );
705         if (!p)
706             return STATUS_NO_MEMORY;
707     }
708     *buffer = p;
709 
710     return STATUS_SUCCESS;
711 }
712 
713 /* NOTE: Remove this one once our actctx support becomes better */
714 NTSTATUS find_actctx_dll( PUNICODE_STRING pnameW, LPWSTR *fullname, PUNICODE_STRING CallerBuffer, BOOLEAN bAllocateBuffer)
715 {
716     static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\'};
717     static const WCHAR dotManifestW[] = {'.','m','a','n','i','f','e','s','t',0};
718 
719     ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *info;
720     ACTCTX_SECTION_KEYED_DATA data;
721     NTSTATUS status;
722     SIZE_T needed, size = 1024;
723     WCHAR *p;
724 
725     data.cbSize = sizeof(data);
726     status = RtlFindActivationContextSectionString( FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL,
727                                                     ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
728                                                     pnameW, &data );
729     if (status != STATUS_SUCCESS)
730     {
731         //DPRINT1("RtlFindActivationContextSectionString returned 0x%x for %wZ\n", status, pnameW);
732         return status;
733     }
734 
735     for (;;)
736     {
737         if (!(info = RtlAllocateHeap( RtlGetProcessHeap(), 0, size )))
738         {
739             status = STATUS_NO_MEMORY;
740             goto done;
741         }
742         status = RtlQueryInformationActivationContext( 0, data.hActCtx, &data.ulAssemblyRosterIndex,
743                                                        AssemblyDetailedInformationInActivationContext,
744                                                        info, size, &needed );
745         if (status == STATUS_SUCCESS) break;
746         if (status != STATUS_BUFFER_TOO_SMALL) goto done;
747         RtlFreeHeap( RtlGetProcessHeap(), 0, info );
748         size = needed;
749     }
750 
751     DPRINT("manifestpath === %S\n", info->lpAssemblyManifestPath);
752     DPRINT("DirectoryName === %S\n", info->lpAssemblyDirectoryName);
753     if (!info->lpAssemblyManifestPath /*|| !info->lpAssemblyDirectoryName*/)
754     {
755         status = STATUS_SXS_KEY_NOT_FOUND;
756         goto done;
757     }
758 
759     if ((p = wcsrchr( info->lpAssemblyManifestPath, '\\' )))
760     {
761         DWORD dirlen = info->ulAssemblyDirectoryNameLength / sizeof(WCHAR);
762 
763         p++;
764         if (!info->lpAssemblyDirectoryName || _wcsnicmp( p, info->lpAssemblyDirectoryName, dirlen ) || _wcsicmp( p + dirlen, dotManifestW ))
765         {
766             /* manifest name does not match directory name, so it's not a global
767              * windows/winsxs manifest; use the manifest directory name instead */
768             dirlen = p - info->lpAssemblyManifestPath;
769             needed = (dirlen + 1) * sizeof(WCHAR) + pnameW->Length;
770 
771             status = get_buffer(fullname, needed, CallerBuffer, bAllocateBuffer);
772             if (!NT_SUCCESS(status))
773                 goto done;
774 
775             p = *fullname;
776 
777             memcpy( p, info->lpAssemblyManifestPath, dirlen * sizeof(WCHAR) );
778             p += dirlen;
779             memcpy( p, pnameW->Buffer, pnameW->Length);
780             p += (pnameW->Length / sizeof(WCHAR));
781             *p = L'\0';
782 
783             goto done;
784         }
785     }
786 
787     needed = (wcslen(SharedUserData->NtSystemRoot) * sizeof(WCHAR) +
788               sizeof(winsxsW) + info->ulAssemblyDirectoryNameLength + pnameW->Length + 2*sizeof(WCHAR));
789 
790     status = get_buffer(fullname, needed, CallerBuffer, bAllocateBuffer);
791     if (!NT_SUCCESS(status))
792         goto done;
793 
794     p = *fullname;
795 
796     wcscpy( p, SharedUserData->NtSystemRoot );
797     p += wcslen(p);
798     memcpy( p, winsxsW, sizeof(winsxsW) );
799     p += sizeof(winsxsW) / sizeof(WCHAR);
800     memcpy( p, info->lpAssemblyDirectoryName, info->ulAssemblyDirectoryNameLength );
801     p += info->ulAssemblyDirectoryNameLength / sizeof(WCHAR);
802     *p++ = L'\\';
803     memcpy( p, pnameW->Buffer, pnameW->Length);
804     p += (pnameW->Length / sizeof(WCHAR));
805     *p = L'\0';
806 
807 done:
808     RtlFreeHeap( RtlGetProcessHeap(), 0, info );
809     RtlReleaseActivationContext( data.hActCtx );
810     DPRINT("%S\n", fullname);
811     return status;
812 }
813 
814 /*
815  * @unimplemented
816  */
817 NTSYSAPI
818 NTSTATUS
819 NTAPI
820 RtlDosApplyFileIsolationRedirection_Ustr(IN ULONG Flags,
821                                          IN PUNICODE_STRING OriginalName,
822                                          IN PUNICODE_STRING Extension,
823                                          IN OUT PUNICODE_STRING StaticString,
824                                          IN OUT PUNICODE_STRING DynamicString,
825                                          IN OUT PUNICODE_STRING *NewName,
826                                          IN PULONG NewFlags,
827                                          IN PSIZE_T FileNameSize,
828                                          IN PSIZE_T RequiredLength)
829 {
830     NTSTATUS Status;
831     LPWSTR fullname;
832     WCHAR buffer [MAX_PATH];
833     UNICODE_STRING localStr, localStr2, *pstrParam;
834     WCHAR *p;
835     BOOLEAN GotExtension;
836     WCHAR c;
837     C_ASSERT(sizeof(UNICODE_NULL) == sizeof(WCHAR));
838 
839 
840     /* Check for invalid parameters */
841     if (!OriginalName)
842     {
843         return STATUS_INVALID_PARAMETER;
844     }
845 
846     if (!DynamicString && !StaticString)
847     {
848         return STATUS_INVALID_PARAMETER;
849     }
850 
851     if ((DynamicString) && (StaticString) && !(NewName))
852     {
853         return STATUS_INVALID_PARAMETER;
854     }
855 
856     if (!OriginalName->Buffer || OriginalName->Length == 0)
857     {
858         return STATUS_SXS_KEY_NOT_FOUND;
859     }
860 
861     if (StaticString && (OriginalName == StaticString || OriginalName->Buffer == StaticString->Buffer))
862     {
863         return STATUS_SXS_KEY_NOT_FOUND;
864     }
865 
866     if ((Flags & RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL) &&
867         NtCurrentPeb()->ProcessParameters &&
868         (NtCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROCESS_PARAMETERS_PRIVATE_DLL_PATH))
869     {
870         UNICODE_STRING RealName, LocalName;
871         WCHAR RealNameBuf[MAX_PATH], LocalNameBuf[MAX_PATH];
872 
873         RtlInitEmptyUnicodeString(&RealName, RealNameBuf, sizeof(RealNameBuf));
874         RtlInitEmptyUnicodeString(&LocalName, LocalNameBuf, sizeof(LocalNameBuf));
875 
876         Status = RtlComputePrivatizedDllName_U(OriginalName, &RealName, &LocalName);
877         if (!NT_SUCCESS(Status))
878         {
879             DPRINT1("RtlComputePrivatizedDllName_U failed for %wZ: 0x%lx\n", OriginalName, Status);
880             return Status;
881         }
882 
883         if (RtlDoesFileExists_UStr(&LocalName))
884         {
885             Status = get_buffer(&fullname, LocalName.Length + sizeof(UNICODE_NULL), StaticString, DynamicString != NULL);
886             if (NT_SUCCESS(Status))
887             {
888                 RtlCopyMemory(fullname, LocalName.Buffer, LocalName.Length + sizeof(UNICODE_NULL));
889             }
890             else
891             {
892                 DPRINT1("Error while retrieving buffer for %wZ: 0x%lx\n", OriginalName, Status);
893             }
894         }
895         else if (RtlDoesFileExists_UStr(&RealName))
896         {
897             Status = get_buffer(&fullname, RealName.Length + sizeof(UNICODE_NULL), StaticString, DynamicString != NULL);
898             if (NT_SUCCESS(Status))
899             {
900                 RtlCopyMemory(fullname, RealName.Buffer, RealName.Length + sizeof(UNICODE_NULL));
901             }
902             else
903             {
904                 DPRINT1("Error while retrieving buffer for %wZ: 0x%lx\n", OriginalName, Status);
905             }
906         }
907         else
908         {
909             Status = STATUS_NOT_FOUND;
910         }
911 
912         if (RealName.Buffer != RealNameBuf)
913             RtlFreeUnicodeString(&RealName);
914         if (LocalName.Buffer != LocalNameBuf)
915             RtlFreeUnicodeString(&LocalName);
916 
917         if (NT_SUCCESS(Status))
918         {
919             DPRINT("Redirecting %wZ to %S\n", OriginalName, fullname);
920             if (!StaticString || StaticString->Buffer != fullname)
921             {
922                 RtlInitUnicodeString(DynamicString, fullname);
923                 *NewName = DynamicString;
924             }
925             else
926             {
927                 *NewName = StaticString;
928             }
929             return Status;
930         }
931     }
932 
933     pstrParam = OriginalName;
934 
935     /* Get the file name with an extension */
936     p = OriginalName->Buffer + OriginalName->Length / sizeof(WCHAR) - 1;
937     GotExtension = FALSE;
938     while (p >= OriginalName->Buffer)
939     {
940         c = *p--;
941         if (c == L'.')
942         {
943             GotExtension = TRUE;
944         }
945         else if (c == L'\\')
946         {
947             localStr.Buffer = p + 2;
948             localStr.Length = OriginalName->Length - ((ULONG_PTR)localStr.Buffer - (ULONG_PTR)OriginalName->Buffer);
949             localStr.MaximumLength = OriginalName->MaximumLength - ((ULONG_PTR)localStr.Buffer - (ULONG_PTR)OriginalName->Buffer);
950             pstrParam = &localStr;
951             break;
952         }
953     }
954 
955     if (!GotExtension)
956     {
957         if (!Extension)
958         {
959             return STATUS_SXS_KEY_NOT_FOUND;
960         }
961 
962         if (pstrParam->Length + Extension->Length > sizeof(buffer))
963         {
964             //FIXME!
965             return STATUS_NO_MEMORY;
966         }
967 
968         RtlInitEmptyUnicodeString(&localStr2, buffer, sizeof(buffer));
969         RtlAppendUnicodeStringToString(&localStr2, pstrParam);
970         RtlAppendUnicodeStringToString(&localStr2, Extension);
971         pstrParam = &localStr2;
972     }
973 
974     /* Use wine's function as long as we use wine's sxs implementation in ntdll */
975     Status = find_actctx_dll(pstrParam, &fullname, StaticString, DynamicString != NULL);
976     if (!NT_SUCCESS(Status))
977     {
978         return Status;
979     }
980 
981     DPRINT("Redirecting %wZ to %S\n", OriginalName, fullname);
982 
983     if (!StaticString || StaticString->Buffer != fullname)
984     {
985         RtlInitUnicodeString(DynamicString, fullname);
986         *NewName = DynamicString;
987     }
988     else
989     {
990         *NewName = StaticString;
991     }
992 
993     return Status;
994 }
995 
996 #ifndef TAG_USTR
997 #define TAG_USTR 'RTSU'
998 #endif
999 #ifndef RtlpAllocateStringMemory
1000 #define RtlpAllocateStringMemory(Bytes, Tag) RtlpAllocateMemory(Bytes, Tag)
1001 #endif
1002 
1003 static DWORD
1004 LdrpApisetVersion(VOID)
1005 {
1006     static DWORD CachedApisetVersion = ~0u;
1007 
1008     if (CachedApisetVersion == ~0u)
1009     {
1010         DWORD CompatVersion = RosGetProcessCompatVersion();
1011 
1012         switch (CompatVersion)
1013         {
1014             case 0:
1015                 break;
1016             case _WIN32_WINNT_VISTA:
1017                 /* No apisets in vista yet*/
1018                 CachedApisetVersion = 0;
1019                 break;
1020             case _WIN32_WINNT_WIN7:
1021                 CachedApisetVersion = APISET_WIN7;
1022                 DPRINT1("Activating apisets for Win7\n");
1023                 break;
1024             case _WIN32_WINNT_WIN8:
1025                 CachedApisetVersion = APISET_WIN8;
1026                 DPRINT1("Activating apisets for Win8\n");
1027                 break;
1028             case _WIN32_WINNT_WINBLUE:
1029                 CachedApisetVersion = APISET_WIN81;
1030                 DPRINT1("Activating apisets for Win8.1\n");
1031                 break;
1032             case _WIN32_WINNT_WIN10:
1033                 CachedApisetVersion = APISET_WIN10;
1034                 DPRINT1("Activating apisets for Win10\n");
1035                 break;
1036             default:
1037                 DPRINT1("Unknown version 0x%x\n", CompatVersion);
1038                 CachedApisetVersion = 0;
1039                 break;
1040         }
1041     }
1042 
1043     return CachedApisetVersion;
1044 }
1045 
1046 NTSYSAPI
1047 NTSTATUS
1048 NTAPI
1049 LdrpApplyFileNameRedirection(
1050     _In_ PUNICODE_STRING OriginalName,
1051     _In_ PUNICODE_STRING Extension,
1052     _Inout_opt_ PUNICODE_STRING StaticString,
1053     _Inout_opt_ PUNICODE_STRING DynamicString,
1054     _Inout_ PUNICODE_STRING *NewName,
1055     _Out_ PBOOLEAN RedirectedDll)
1056 {
1057 
1058     /* Check for invalid parameters */
1059     if (!OriginalName)
1060     {
1061         return STATUS_INVALID_PARAMETER;
1062     }
1063 
1064     if (!DynamicString && !StaticString)
1065     {
1066         return STATUS_INVALID_PARAMETER;
1067     }
1068 
1069     if (!NewName)
1070     {
1071         return STATUS_INVALID_PARAMETER;
1072     }
1073 
1074     *RedirectedDll = FALSE;
1075 
1076     PCUNICODE_STRING PrevNewName = *NewName;
1077     UNICODE_STRING ApisetName = {0};
1078     NTSTATUS Status = STATUS_SUCCESS;
1079 
1080     DWORD ApisetVersion = LdrpApisetVersion();
1081     if (ApisetVersion)
1082     {
1083         Status = ApiSetResolveToHost(ApisetVersion, OriginalName, RedirectedDll, &ApisetName);
1084         if (!NT_SUCCESS(Status))
1085         {
1086             DPRINT1("ApiSetResolveToHost FAILED: (Status 0x%x)\n", Status);
1087             return Status;
1088         }
1089     }
1090 
1091     if (*RedirectedDll)
1092     {
1093         UNICODE_STRING NtSystemRoot;
1094         static const UNICODE_STRING System32 = RTL_CONSTANT_STRING(L"\\System32\\");
1095         PUNICODE_STRING ResultPath = NULL;
1096 
1097         /* This is an apiset we can use */
1098         RtlInitUnicodeString(&NtSystemRoot, SharedUserData->NtSystemRoot);
1099 
1100         SIZE_T Needed = System32.Length + ApisetName.Length + NtSystemRoot.Length + sizeof(UNICODE_NULL);
1101 
1102         if (StaticString && StaticString->MaximumLength >= (USHORT)Needed)
1103         {
1104             StaticString->Length = 0;
1105             ResultPath = StaticString;
1106         }
1107         else if (DynamicString)
1108         {
1109             DynamicString->Buffer = RtlpAllocateStringMemory(Needed, TAG_USTR);
1110             if (DynamicString->Buffer == NULL)
1111             {
1112                 DPRINT1("LdrpApplyFileNameRedirection out of memory\n");
1113                 return STATUS_NO_MEMORY;
1114             }
1115             DynamicString->MaximumLength = (USHORT)Needed;
1116             DynamicString->Length = 0;
1117 
1118             ResultPath = DynamicString;
1119         }
1120         else
1121         {
1122             DPRINT1("ERROR: LdrpApplyFileNameRedirection no inputbuffer valid\n");
1123             return STATUS_INVALID_PARAMETER;
1124         }
1125 
1126         RtlAppendUnicodeStringToString(ResultPath, &NtSystemRoot);
1127         RtlAppendUnicodeStringToString(ResultPath, &System32);
1128         RtlAppendUnicodeStringToString(ResultPath, &ApisetName);
1129         DPRINT1("ApiSetResolveToHost redirected %wZ to %wZ\n", OriginalName, ResultPath);
1130         *NewName = ResultPath;
1131     }
1132     else
1133     {
1134         /* Check if the SxS Assemblies specify another file */
1135         Status = RtlDosApplyFileIsolationRedirection_Ustr(RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL, OriginalName, Extension, StaticString, DynamicString, NewName, NULL, NULL, NULL);
1136 
1137         /* Check success */
1138         if (NT_SUCCESS(Status))
1139         {
1140             /* Let Ldrp know */
1141             *RedirectedDll = TRUE;
1142         }
1143         else if (Status == STATUS_SXS_KEY_NOT_FOUND)
1144         {
1145             ASSERT(*NewName == PrevNewName);
1146             Status = STATUS_SUCCESS;
1147         }
1148         else
1149         {
1150             /* Unrecoverable SxS failure; did we get a string? */
1151             if (DynamicString && DynamicString->Buffer)
1152                 RtlFreeUnicodeString(DynamicString);
1153             return Status;
1154         }
1155     }
1156 
1157     return Status;
1158 }
1159 
1160 /*
1161  * @implemented
1162  */
1163 NTSYSAPI
1164 NTSTATUS
1165 NTAPI
1166 RtlWow64EnableFsRedirection(IN BOOLEAN Wow64FsEnableRedirection)
1167 {
1168     /* This is what Windows returns on x86 */
1169     return STATUS_NOT_IMPLEMENTED;
1170 }
1171 
1172 /*
1173  * @implemented
1174  */
1175 NTSYSAPI
1176 NTSTATUS
1177 NTAPI
1178 RtlWow64EnableFsRedirectionEx(IN PVOID Wow64FsEnableRedirection,
1179                               OUT PVOID *OldFsRedirectionLevel)
1180 {
1181     /* This is what Windows returns on x86 */
1182     return STATUS_NOT_IMPLEMENTED;
1183 }
1184 
1185 /*
1186  * @unimplemented
1187  */
1188 NTSYSAPI
1189 NTSTATUS
1190 NTAPI
1191 RtlComputeImportTableHash(IN HANDLE FileHandle,
1192                           OUT PCHAR Hash,
1193                           IN ULONG ImportTableHashSize)
1194 {
1195     UNIMPLEMENTED;
1196     return STATUS_NOT_IMPLEMENTED;
1197 }
1198 
1199 NTSTATUS
1200 NTAPI
1201 RtlpSafeCopyMemory(
1202    _Out_writes_bytes_all_(Length) VOID UNALIGNED *Destination,
1203    _In_reads_bytes_(Length) CONST VOID UNALIGNED *Source,
1204    _In_ SIZE_T Length)
1205 {
1206     _SEH2_TRY
1207     {
1208         RtlCopyMemory(Destination, Source, Length);
1209     }
1210     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1211     {
1212         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1213     }
1214     _SEH2_END;
1215 
1216     return STATUS_SUCCESS;
1217 }
1218 
1219 /* FIXME: code duplication with kernel32/client/time.c */
1220 ULONG
1221 NTAPI
1222 RtlGetTickCount(VOID)
1223 {
1224     ULARGE_INTEGER TickCount;
1225 
1226 #ifdef _WIN64
1227     TickCount.QuadPart = *((volatile ULONG64*)&SharedUserData->TickCount);
1228 #else
1229     while (TRUE)
1230     {
1231         TickCount.HighPart = (ULONG)SharedUserData->TickCount.High1Time;
1232         TickCount.LowPart = SharedUserData->TickCount.LowPart;
1233 
1234         if (TickCount.HighPart == (ULONG)SharedUserData->TickCount.High2Time)
1235             break;
1236 
1237         YieldProcessor();
1238     }
1239 #endif
1240 
1241     return (ULONG)((UInt32x32To64(TickCount.LowPart,
1242                                   SharedUserData->TickCountMultiplier) >> 24) +
1243                     UInt32x32To64((TickCount.HighPart << 8) & 0xFFFFFFFF,
1244                                   SharedUserData->TickCountMultiplier));
1245 }
1246 
1247 /* EOF */
1248