+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS NT User Mode Library
+ * FILE: dll/ntdll/ldr/ldrapi.c
+ * PURPOSE: PE Loader Public APIs
+ * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
+ * Aleksey Bragin (aleksey@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include <ntdll.h>
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS *******************************************************************/
+
+#define LDR_LOCK_HELD 0x2
+#define LDR_LOCK_FREE 0x1
+
+LONG LdrpLoaderLockAcquisitonCount;
+
+/* FUNCTIONS *****************************************************************/
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+LdrUnlockLoaderLock(IN ULONG Flags,
+ IN ULONG Cookie OPTIONAL)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DPRINT("LdrUnlockLoaderLock(%x %x)\n", Flags, Cookie);
+
+ /* Check for valid flags */
+ if (Flags & ~1)
+ {
+ /* Flags are invalid, check how to fail */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS)
+ {
+ /* The caller wants us to raise status */
+ RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
+ }
+ else
+ {
+ /* A normal failure */
+ return STATUS_INVALID_PARAMETER_1;
+ }
+ }
+
+ /* If we don't have a cookie, just return */
+ if (!Cookie) return STATUS_SUCCESS;
+
+ /* Validate the cookie */
+ if ((Cookie & 0xF0000000) ||
+ ((Cookie >> 16) ^ ((ULONG)(NtCurrentTeb()->RealClientId.UniqueThread) & 0xFFF)))
+ {
+ DPRINT1("LdrUnlockLoaderLock() called with an invalid cookie!\n");
+
+ /* Invalid cookie, check how to fail */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS)
+ {
+ /* The caller wants us to raise status */
+ RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
+ }
+ else
+ {
+ /* A normal failure */
+ return STATUS_INVALID_PARAMETER_2;
+ }
+ }
+
+ /* Ready to release the lock */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS)
+ {
+ /* Do a direct leave */
+ RtlLeaveCriticalSection(&LdrpLoaderLock);
+ }
+ else
+ {
+ /* Wrap this in SEH, since we're not supposed to raise */
+ _SEH2_TRY
+ {
+ /* Leave the lock */
+ RtlLeaveCriticalSection(&LdrpLoaderLock);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* We should use the LDR Filter instead */
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ }
+
+ /* All done */
+ return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+LdrLockLoaderLock(IN ULONG Flags,
+ OUT PULONG Result OPTIONAL,
+ OUT PULONG Cookie OPTIONAL)
+{
+ LONG OldCount;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN InInit = FALSE; // FIXME
+ //BOOLEAN InInit = LdrpInLdrInit;
+
+ DPRINT("LdrLockLoaderLock(%x %p %p)\n", Flags, Result, Cookie);
+
+ /* Zero out the outputs */
+ if (Result) *Result = 0;
+ if (Cookie) *Cookie = 0;
+
+ /* Validate the flags */
+ if (Flags & ~(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS |
+ LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY))
+ {
+ /* Flags are invalid, check how to fail */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS)
+ {
+ /* The caller wants us to raise status */
+ RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
+ }
+
+ /* A normal failure */
+ return STATUS_INVALID_PARAMETER_1;
+ }
+
+ /* Make sure we got a cookie */
+ if (!Cookie)
+ {
+ /* No cookie check how to fail */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS)
+ {
+ /* The caller wants us to raise status */
+ RtlRaiseStatus(STATUS_INVALID_PARAMETER_3);
+ }
+
+ /* A normal failure */
+ return STATUS_INVALID_PARAMETER_3;
+ }
+
+ /* If the flag is set, make sure we have a valid pointer to use */
+ if ((Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY) && !(Result))
+ {
+ /* No pointer to return the data to */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS)
+ {
+ /* The caller wants us to raise status */
+ RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
+ }
+
+ /* Fail */
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ /* Return now if we are in the init phase */
+ if (InInit) return STATUS_SUCCESS;
+
+ /* Check what locking semantic to use */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_STATUS)
+ {
+ /* Check if we should enter or simply try */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
+ {
+ /* Do a try */
+ if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
+ {
+ /* It's locked */
+ *Result = LDR_LOCK_HELD;
+ goto Quickie;
+ }
+ else
+ {
+ /* It worked */
+ *Result = LDR_LOCK_FREE;
+ }
+ }
+ else
+ {
+ /* Do a enter */
+ RtlEnterCriticalSection(&LdrpLoaderLock);
+
+ /* See if result was requested */
+ if (Result) *Result = LDR_LOCK_FREE;
+ }
+
+ /* Increase the acquisition count */
+ OldCount = _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount);
+
+ /* Generate a cookie */
+ *Cookie = (((ULONG)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) | OldCount;
+ }
+ else
+ {
+ /* Wrap this in SEH, since we're not supposed to raise */
+ _SEH2_TRY
+ {
+ /* Check if we should enter or simply try */
+ if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
+ {
+ /* Do a try */
+ if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
+ {
+ /* It's locked */
+ *Result = LDR_LOCK_HELD;
+ _SEH2_YIELD(return STATUS_SUCCESS);
+ }
+ else
+ {
+ /* It worked */
+ *Result = LDR_LOCK_FREE;
+ }
+ }
+ else
+ {
+ /* Do an enter */
+ RtlEnterCriticalSection(&LdrpLoaderLock);
+
+ /* See if result was requested */
+ if (Result) *Result = LDR_LOCK_FREE;
+ }
+
+ /* Increase the acquisition count */
+ OldCount = _InterlockedIncrement(&LdrpLoaderLockAcquisitonCount);
+
+ /* Generate a cookie */
+ *Cookie = (((ULONG)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) | OldCount;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* We should use the LDR Filter instead */
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
+ }
+
+Quickie:
+ return Status;
+}
+
+/* EOF */