/* 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.
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);
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
}
}
-#endif /* PS_QUOTA_ENABLE_QUOTA_CODE */
-
-
/* FUNCTIONS ***************************************************************/
VOID
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,
/* 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
}
/*
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;
/* 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
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
/* 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
/* 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,
/* 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 */