[REACTX] Fix 64 bit issues
[reactos.git] / ntoskrnl / ps / quota.c
index 8814104..f955818 100644 (file)
 /* INCLUDES **************************************************************/
 
 #include <ntoskrnl.h>
+#include <ntintsafe.h>
 #define NDEBUG
 #include <debug.h>
 
 EPROCESS_QUOTA_BLOCK PspDefaultQuotaBlock;
+static LIST_ENTRY PspQuotaBlockList = {&PspQuotaBlockList, &PspQuotaBlockList};
+static KSPIN_LOCK PspQuotaLock;
 
-
-/* Define this macro to enable quota code testing. Once quota code is */
-/* stable and verified, remove this macro and checks for it. */
-/*#define PS_QUOTA_ENABLE_QUOTA_CODE*/
-
+#define TAG_QUOTA_BLOCK 'bQsP'
+#define VALID_QUOTA_FLAGS (QUOTA_LIMITS_HARDWS_MIN_ENABLE | \
+                           QUOTA_LIMITS_HARDWS_MIN_DISABLE | \
+                           QUOTA_LIMITS_HARDWS_MAX_ENABLE | \
+                           QUOTA_LIMITS_HARDWS_MAX_DISABLE)
 
 /* PRIVATE FUNCTIONS *******************************************************/
 
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
-
 /*
  * Private helper to charge the specified process quota.
  * ReturnsSTATUS_QUOTA_EXCEEDED on quota limit check failure.
@@ -49,7 +50,9 @@ PspChargeProcessQuotaSpecifiedPool(IN PEPROCESS Process,
     if (Process->QuotaUsage[PoolIndex] + Amount >
         Process->QuotaBlock->QuotaEntry[PoolIndex].Limit)
     {
-        return STATUS_QUOTA_EXCEEDED; /* caller raises the exception */
+        DPRINT1("Quota exceeded, but ROS will let it slide...\n");
+        return STATUS_SUCCESS;
+        //return STATUS_QUOTA_EXCEEDED; /* caller raises the exception */
     }
 
     InterlockedExchangeAdd((LONG*)&Process->QuotaUsage[PoolIndex], Amount);
@@ -63,7 +66,6 @@ PspChargeProcessQuotaSpecifiedPool(IN PEPROCESS Process,
     return STATUS_SUCCESS;
 }
 
-
 /*
  * Private helper to remove quota charge from the specified process quota.
  * TODO: Research and possibly add (the undocumented) enum type PS_QUOTA_TYPE
@@ -91,9 +93,6 @@ PspReturnProcessQuotaSpecifiedPool(IN PEPROCESS Process,
     }
 }
 
-#endif /* PS_QUOTA_ENABLE_QUOTA_CODE */
-
-
 /* FUNCTIONS ***************************************************************/
 
 VOID
@@ -126,19 +125,38 @@ PspInheritQuota(PEPROCESS Process, PEPROCESS ParentProcess)
         Process->QuotaBlock = &PspDefaultQuotaBlock;
 }
 
+VOID
+NTAPI
+PspInsertQuotaBlock(
+    PEPROCESS_QUOTA_BLOCK QuotaBlock)
+{
+    KIRQL OldIrql;
+
+    KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
+    InsertTailList(&PspQuotaBlockList, &QuotaBlock->QuotaList);
+    KeReleaseSpinLock(&PspQuotaLock, OldIrql);
+}
+
 VOID
 NTAPI
 PspDestroyQuotaBlock(PEPROCESS Process)
 {
     PEPROCESS_QUOTA_BLOCK QuotaBlock = Process->QuotaBlock;
+    KIRQL OldIrql;
 
     if (QuotaBlock != &PspDefaultQuotaBlock &&
         InterlockedDecrementUL(&QuotaBlock->ReferenceCount) == 0)
     {
+        KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
+        RemoveEntryList(&QuotaBlock->QuotaList);
+        KeReleaseSpinLock(&PspQuotaLock, OldIrql);
         ExFreePool(QuotaBlock);
     }
 }
 
+/*
+ * @implemented
+ */
 NTSTATUS
 NTAPI
 PsChargeProcessPageFileQuota(IN PEPROCESS Process,
@@ -147,13 +165,7 @@ PsChargeProcessPageFileQuota(IN PEPROCESS Process,
     /* Don't do anything for the system process */
     if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
 
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
     return PspChargeProcessQuotaSpecifiedPool(Process, 2, Amount);
-#else
-    /* Otherwise, not implemented */
-    UNIMPLEMENTED;
-    return STATUS_SUCCESS;
-#endif
 }
 
 /*
@@ -166,12 +178,7 @@ PsChargePoolQuota(IN PEPROCESS Process,
                   IN SIZE_T Amount)
 {
     NTSTATUS Status;
-
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
-    /* MS-documented IRQL requirement. Not yet enabled as it */
-    /* needs verification that it does not break ReactOS, */
     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
-#endif
 
     /* Don't do anything for the system process */
     if (Process == PsInitialSystemProcess) return;
@@ -217,18 +224,13 @@ PsChargeProcessPoolQuota(IN PEPROCESS Process,
     /* Don't do anything for the system process */
     if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
 
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
     return PspChargeProcessQuotaSpecifiedPool(Process,
                                               (PoolType & PAGED_POOL_MASK),
                                               Amount);
-#else
-    UNIMPLEMENTED;
-    return STATUS_SUCCESS;
-#endif
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -236,26 +238,16 @@ PsReturnPoolQuota(IN PEPROCESS Process,
                   IN POOL_TYPE PoolType,
                   IN SIZE_T Amount)
 {
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
-    /* MS-documented IRQL requirement. Not yet enabled as it */
-    /* needs verification that it does not break ReactOS, */
-    ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
-#endif
-
     /* Don't do anything for the system process */
     if (Process == PsInitialSystemProcess) return;
 
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
     PspReturnProcessQuotaSpecifiedPool(Process,
                                        (PoolType & PAGED_POOL_MASK),
                                        Amount);
-#else
-    UNIMPLEMENTED;
-#endif
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -265,15 +257,11 @@ PsReturnProcessNonPagedPoolQuota(IN PEPROCESS Process,
     /* Don't do anything for the system process */
     if (Process == PsInitialSystemProcess) return;
 
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
     PsReturnPoolQuota(Process, NonPagedPool, Amount);
-#else
-    UNIMPLEMENTED;
-#endif
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -283,13 +271,12 @@ PsReturnProcessPagedPoolQuota(IN PEPROCESS Process,
     /* Don't do anything for the system process */
     if (Process == PsInitialSystemProcess) return;
 
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
     PsReturnPoolQuota(Process, PagedPool, Amount);
-#else
-    UNIMPLEMENTED;
-#endif
 }
 
+/*
+ * @implemented
+ */
 NTSTATUS
 NTAPI
 PsReturnProcessPageFileQuota(IN PEPROCESS Process,
@@ -298,13 +285,172 @@ PsReturnProcessPageFileQuota(IN PEPROCESS Process,
     /* Don't do anything for the system process */
     if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
 
-#ifdef PS_QUOTA_ENABLE_QUOTA_CODE
     PspReturnProcessQuotaSpecifiedPool(Process, 2, Amount);
-#else
-    /* Otherwise, not implemented */
-    UNIMPLEMENTED;
-#endif
     return STATUS_SUCCESS;
 }
 
+NTSTATUS
+NTAPI
+PspSetQuotaLimits(
+    _In_ PEPROCESS Process,
+    _In_ ULONG Unused,
+    _In_ PVOID QuotaLimits,
+    _In_ ULONG QuotaLimitsLength,
+    _In_ KPROCESSOR_MODE PreviousMode)
+{
+    QUOTA_LIMITS_EX CapturedQuotaLimits;
+    PEPROCESS_QUOTA_BLOCK QuotaBlock, OldQuotaBlock;
+    BOOLEAN IncreaseOkay;
+    KAPC_STATE SavedApcState;
+    NTSTATUS Status;
+
+    UNREFERENCED_PARAMETER(Unused);
+
+    _SEH2_TRY
+    {
+        ProbeForRead(QuotaLimits, QuotaLimitsLength, sizeof(ULONG));
+
+        /* Check if we have the basic or extended structure */
+        if (QuotaLimitsLength == sizeof(QUOTA_LIMITS))
+        {
+            /* Copy the basic structure, zero init the remaining fields */
+            RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS));
+            CapturedQuotaLimits.WorkingSetLimit = 0;
+            CapturedQuotaLimits.Reserved2 = 0;
+            CapturedQuotaLimits.Reserved3 = 0;
+            CapturedQuotaLimits.Reserved4 = 0;
+            CapturedQuotaLimits.CpuRateLimit.RateData = 0;
+            CapturedQuotaLimits.Flags = 0;
+        }
+        else if (QuotaLimitsLength == sizeof(QUOTA_LIMITS_EX))
+        {
+            /* Copy the full structure */
+            RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS_EX));
+
+            /* Verify that the caller passed valid flags */
+            if ((CapturedQuotaLimits.Flags & ~VALID_QUOTA_FLAGS) ||
+                ((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_ENABLE) &&
+                 (CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_DISABLE)) ||
+                ((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_ENABLE) &&
+                 (CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_DISABLE)))
+            {
+                DPRINT1("Invalid quota flags: 0x%lx\n", CapturedQuotaLimits.Flags);
+                _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
+            }
+
+            /* Verify that the caller didn't pass reserved values */
+            if ((CapturedQuotaLimits.WorkingSetLimit != 0) ||
+                (CapturedQuotaLimits.Reserved2 != 0) ||
+                (CapturedQuotaLimits.Reserved3 != 0) ||
+                (CapturedQuotaLimits.Reserved4 != 0) ||
+                (CapturedQuotaLimits.CpuRateLimit.RateData != 0))
+            {
+                DPRINT1("Invalid value: (%lx,%lx,%lx,%lx,%lx)\n",
+                        CapturedQuotaLimits.WorkingSetLimit,
+                        CapturedQuotaLimits.Reserved2,
+                        CapturedQuotaLimits.Reserved3,
+                        CapturedQuotaLimits.Reserved4,
+                        CapturedQuotaLimits.CpuRateLimit.RateData);
+                _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
+            }
+        }
+        else
+        {
+            DPRINT1("Invalid quota size: 0x%lx\n", QuotaLimitsLength);
+            _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH);
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        DPRINT1("Exception while copying data\n");
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    /* Check the caller changes the working set size limits */
+    if ((CapturedQuotaLimits.MinimumWorkingSetSize != 0) &&
+        (CapturedQuotaLimits.MaximumWorkingSetSize != 0))
+    {
+        /* Check for special case: trimming the WS */
+        if ((CapturedQuotaLimits.MinimumWorkingSetSize == SIZE_T_MAX) &&
+            (CapturedQuotaLimits.MaximumWorkingSetSize == SIZE_T_MAX))
+        {
+            /* No increase allowed */
+            IncreaseOkay = FALSE;
+        }
+        else
+        {
+            /* Check if the caller has the required privilege */
+            IncreaseOkay = SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege,
+                                                  PreviousMode);
+        }
+
+        /* Attach to the target process and disable APCs */
+        KeStackAttachProcess(&Process->Pcb, &SavedApcState);
+        KeEnterGuardedRegion();
+
+        /* Call Mm to adjust the process' working set size */
+        Status = MmAdjustWorkingSetSize(CapturedQuotaLimits.MinimumWorkingSetSize,
+                                        CapturedQuotaLimits.MaximumWorkingSetSize,
+                                        0,
+                                        IncreaseOkay);
+
+        /* Bring back APCs and detach from the process */
+        KeLeaveGuardedRegion();
+        KeUnstackDetachProcess(&SavedApcState);
+    }
+    else if (Process->QuotaBlock == &PspDefaultQuotaBlock)
+    {
+        /* Check if the caller has the required privilege */
+        if (!SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege, PreviousMode))
+        {
+            return STATUS_PRIVILEGE_NOT_HELD;
+        }
+
+        /* Allocate a new quota block */
+        QuotaBlock = ExAllocatePoolWithTag(NonPagedPool,
+                                           sizeof(EPROCESS_QUOTA_BLOCK),
+                                           TAG_QUOTA_BLOCK);
+        if (QuotaBlock == NULL)
+        {
+            ObDereferenceObject(Process);
+            return STATUS_NO_MEMORY;
+        }
+
+        /* Initialize the quota block */
+        QuotaBlock->ReferenceCount = 1;
+        QuotaBlock->ProcessCount = 1;
+        QuotaBlock->QuotaEntry[0].Peak = Process->QuotaPeak[0];
+        QuotaBlock->QuotaEntry[1].Peak = Process->QuotaPeak[1];
+        QuotaBlock->QuotaEntry[2].Peak = Process->QuotaPeak[2];
+        QuotaBlock->QuotaEntry[0].Limit = PspDefaultQuotaBlock.QuotaEntry[0].Limit;
+        QuotaBlock->QuotaEntry[1].Limit = PspDefaultQuotaBlock.QuotaEntry[1].Limit;
+        QuotaBlock->QuotaEntry[2].Limit = PspDefaultQuotaBlock.QuotaEntry[2].Limit;
+
+        /* Try to exchange the quota block, if that failed, just drop it */
+        OldQuotaBlock = InterlockedCompareExchangePointer((PVOID*)&Process->QuotaBlock,
+                                                          QuotaBlock,
+                                                          &PspDefaultQuotaBlock);
+        if (OldQuotaBlock == &PspDefaultQuotaBlock)
+        {
+            /* Success, insert the new quota block */
+            PspInsertQuotaBlock(QuotaBlock);
+        }
+        else
+        {
+            /* Failed, free the quota block and ignore it */
+            ExFreePoolWithTag(QuotaBlock, TAG_QUOTA_BLOCK);
+        }
+
+        Status = STATUS_SUCCESS;
+    }
+    else
+    {
+        Status = STATUS_SUCCESS;
+    }
+
+    return Status;
+}
+
+
 /* EOF */