reactos/base/system/smss/pagefile.c
Timo Kreuzer 094a90ad4e [NTOS:PS] Fix an issue with PROCESS_DEVICEMAP_INFORMATION size on 64 bit builds
The PROCESS_DEVICEMAP_INFORMATION  union has 2 fields, one is a handle, the other one is a structure of 36 bytes (independent of architecture). The handle forces 64 bit alignment on 64 bit builds, making the structure 4 bytes bigger than on 32 bit builds. The site is checked in NtQueryInformationProcess (case ProcessDeviceMap). The expected size on x64 is the size of the Query structure without alignment. autocheck correctly passes the site of the Query union member, while smss passes the full size of PROCESS_DEVICEMAP_INFORMATION. Packing the structure is not an option, since it is defined in public headers without packing. Using the original headers sizeof(PROCESS_DEVICEMAP_INFORMATION) is 0x28, sizeof(PROCESS_DEVICEMAP_INFORMATION::Query) is 0x24.
2018-08-17 22:08:37 +02:00

1100 lines
39 KiB
C

/*
* PROJECT: ReactOS Windows-Compatible Session Manager
* LICENSE: BSD 2-Clause License
* FILE: base/system/smss/pagefile.c
* PURPOSE: Main SMSS Code
* PROGRAMMERS: Alex Ionescu
*/
/* INCLUDES *******************************************************************/
#include "smss.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
//
// Constants
//
#define STANDARD_PAGING_FILE_NAME L"\\??\\?:\\pagefile.sys"
#define STANDARD_DRIVE_LETTER_OFFSET 4
#define MEGABYTE 0x100000UL
#define MAXIMUM_PAGEFILE_SIZE (4095 * MEGABYTE)
/* This should be 32 MB, but we need more than that for 2nd stage setup */
#define MINIMUM_TO_KEEP_FREE (64 * MEGABYTE)
#define FUZZ_FACTOR (16 * MEGABYTE)
//
// Structure and flags describing each pagefile
//
#define SMP_PAGEFILE_CREATED 0x01
#define SMP_PAGEFILE_DEFAULT 0x02
#define SMP_PAGEFILE_SYSTEM_MANAGED 0x04
#define SMP_PAGEFILE_WAS_TOO_BIG 0x08
#define SMP_PAGEFILE_ON_ANY_DRIVE 0x10
#define SMP_PAGEFILE_EMERGENCY 0x20
#define SMP_PAGEFILE_DUMP_PROCESSED 0x40
typedef struct _SMP_PAGEFILE_DESCRIPTOR
{
LIST_ENTRY Entry;
UNICODE_STRING Name;
UNICODE_STRING Token;
LARGE_INTEGER MinSize;
LARGE_INTEGER MaxSize;
LARGE_INTEGER ActualMinSize;
LARGE_INTEGER ActualMaxSize;
ULONG Flags;
} SMP_PAGEFILE_DESCRIPTOR, *PSMP_PAGEFILE_DESCRIPTOR;
//
// Structure and flags describing each volume
//
#define SMP_VOLUME_INSERTED 0x01
#define SMP_VOLUME_PAGEFILE_CREATED 0x04
#define SMP_VOLUME_IS_BOOT 0x08
typedef struct _SMP_VOLUME_DESCRIPTOR
{
LIST_ENTRY Entry;
USHORT Flags;
USHORT PageFileCount;
WCHAR DriveLetter;
LARGE_INTEGER FreeSpace;
FILE_FS_DEVICE_INFORMATION DeviceInfo;
} SMP_VOLUME_DESCRIPTOR, *PSMP_VOLUME_DESCRIPTOR;
LIST_ENTRY SmpPagingFileDescriptorList, SmpVolumeDescriptorList;
BOOLEAN SmpRegistrySpecifierPresent;
ULONG SmpNumberOfPagingFiles;
/* FUNCTIONS ******************************************************************/
VOID
NTAPI
SmpPagingFileInitialize(VOID)
{
/* Initialize the two lists */
InitializeListHead(&SmpPagingFileDescriptorList);
InitializeListHead(&SmpVolumeDescriptorList);
}
NTSTATUS
NTAPI
SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken)
{
NTSTATUS Status;
ULONG MinSize = 0, MaxSize = 0;
BOOLEAN SystemManaged = FALSE, ZeroSize = TRUE;
PSMP_PAGEFILE_DESCRIPTOR Descriptor, ListDescriptor;
ULONG i;
WCHAR c;
PLIST_ENTRY NextEntry;
UNICODE_STRING PageFileName, Arguments, SecondArgument;
/* Make sure we don't have too many */
if (SmpNumberOfPagingFiles >= 16)
{
DPRINT1("SMSS:PFILE: Too many paging files specified - %lu\n",
SmpNumberOfPagingFiles);
return STATUS_TOO_MANY_PAGING_FILES;
}
/* Parse the specified and get the name and arguments out of it */
DPRINT("SMSS:PFILE: Paging file specifier `%wZ'\n", PageFileToken);
Status = SmpParseCommandLine(PageFileToken,
NULL,
&PageFileName,
NULL,
&Arguments);
if (!NT_SUCCESS(Status))
{
/* Fail */
DPRINT1("SMSS:PFILE: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
PageFileToken, Status);
return Status;
}
/* Set the variable to let everyone know we have a pagefile token */
SmpRegistrySpecifierPresent = TRUE;
/* Parse the arguments, if any */
if (Arguments.Buffer)
{
/* Parse the pagefile size */
for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++)
{
/* Check if it's zero */
c = Arguments.Buffer[i];
if ((c != L' ') && (c != L'\t') && (c != L'0'))
{
/* It isn't, break out */
ZeroSize = FALSE;
break;
}
}
}
/* Was a pagefile not specified, or was it specified with no size? */
if (!(Arguments.Buffer) || (ZeroSize))
{
/* In this case, the system will manage its size */
SystemManaged = TRUE;
}
else
{
/* We do have a size, so convert the arguments into a number */
Status = RtlUnicodeStringToInteger(&Arguments, 0, &MinSize);
if (!NT_SUCCESS(Status))
{
/* Fail */
RtlFreeUnicodeString(&PageFileName);
RtlFreeUnicodeString(&Arguments);
return Status;
}
/* Now advance to the next argument */
for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++)
{
/* Found a space -- second argument must start here */
if (Arguments.Buffer[i] == L' ')
{
/* Use the rest of the arguments as a maximum size */
SecondArgument.Buffer = &Arguments.Buffer[i];
SecondArgument.Length = (USHORT)(Arguments.Length -
i * sizeof(WCHAR));
SecondArgument.MaximumLength = (USHORT)(Arguments.MaximumLength -
i * sizeof(WCHAR));
Status = RtlUnicodeStringToInteger(&SecondArgument, 0, &MaxSize);
if (!NT_SUCCESS(Status))
{
/* Fail */
RtlFreeUnicodeString(&PageFileName);
RtlFreeUnicodeString(&Arguments);
return Status;
}
break;
}
}
}
/* We are done parsing arguments */
RtlFreeUnicodeString(&Arguments);
/* Now we can allocate our descriptor */
Descriptor = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SMP_PAGEFILE_DESCRIPTOR));
if (!Descriptor)
{
/* Fail if we couldn't */
RtlFreeUnicodeString(&PageFileName);
return STATUS_NO_MEMORY;
}
/* Capture all our data into the descriptor */
Descriptor->Token = *PageFileToken;
Descriptor->Name = PageFileName;
Descriptor->MinSize.QuadPart = MinSize * MEGABYTE;
Descriptor->MaxSize.QuadPart = MaxSize * MEGABYTE;
if (SystemManaged) Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED;
Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] =
RtlUpcaseUnicodeChar(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?')
{
Descriptor->Flags |= SMP_PAGEFILE_ON_ANY_DRIVE;
}
/* Now loop the existing descriptors */
NextEntry = SmpPagingFileDescriptorList.Flink;
do
{
/* Are there none, or have we looped back to the beginning? */
if (NextEntry == &SmpPagingFileDescriptorList)
{
/* This means no duplicates exist, so insert our descriptor! */
InsertTailList(&SmpPagingFileDescriptorList, &Descriptor->Entry);
SmpNumberOfPagingFiles++;
DPRINT("SMSS:PFILE: Created descriptor for `%wZ' (`%wZ')\n",
PageFileToken, &Descriptor->Name);
return STATUS_SUCCESS;
}
/* Keep going until we find a duplicate, unless we are in "any" mode */
ListDescriptor = CONTAINING_RECORD(NextEntry, SMP_PAGEFILE_DESCRIPTOR, Entry);
NextEntry = NextEntry->Flink;
} while (!(ListDescriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE) ||
!(Descriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE));
/* We found a duplicate, so skip this descriptor/pagefile and fail */
DPRINT1("SMSS:PFILE: Skipping duplicate specifier `%wZ'\n", PageFileToken);
RtlFreeUnicodeString(&PageFileName);
RtlFreeHeap(RtlGetProcessHeap(), 0, Descriptor);
return STATUS_INVALID_PARAMETER;
}
NTSTATUS
NTAPI
SmpGetPagingFileSize(IN PUNICODE_STRING FileName,
OUT PLARGE_INTEGER Size)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE FileHandle;
FILE_STANDARD_INFORMATION StandardInfo;
DPRINT("SMSS:PFILE: Trying to get size for `%wZ'\n", FileName);
Size->QuadPart = 0;
InitializeObjectAttributes(&ObjectAttributes,
FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&FileHandle,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status)) return Status;
Status = NtQueryInformationFile(FileHandle,
&IoStatusBlock,
&StandardInfo,
sizeof(StandardInfo),
FileStandardInformation);
if (!NT_SUCCESS(Status))
{
DPRINT1("SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X\n",
FileName, Status);
NtClose(FileHandle);
return Status;
}
NtClose(FileHandle);
Size->QuadPart = StandardInfo.AllocationSize.QuadPart;
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
SmpDeletePagingFile(IN PUNICODE_STRING FileName)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE FileHandle;
FILE_DISPOSITION_INFORMATION Disposition;
/* Open the page file */
InitializeObjectAttributes(&ObjectAttributes,
FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&FileHandle,
DELETE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_NON_DIRECTORY_FILE);
if (NT_SUCCESS(Status))
{
/* Delete it */
Disposition.DeleteFile = TRUE;
Status = NtSetInformationFile(FileHandle,
&IoStatusBlock,
&Disposition,
sizeof(Disposition),
FileDispositionInformation);
if (!NT_SUCCESS(Status))
{
DPRINT1("SMSS:PFILE: Failed to delete page file `%wZ' (status %X)\n",
FileName, Status);
}
else
{
DPRINT("SMSS:PFILE: Deleted stale paging file - %wZ\n", FileName);
}
/* Close the handle */
NtClose(FileHandle);
}
else
{
DPRINT1("SMSS:PFILE: Failed to open for deletion page file `%wZ' (status %X)\n",
FileName, Status);
}
/* All done */
return Status;
}
NTSTATUS
NTAPI
SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume)
{
NTSTATUS Status;
LARGE_INTEGER FreeSpace, FinalFreeSpace;
FILE_FS_SIZE_INFORMATION SizeInfo;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING VolumeName;
HANDLE VolumeHandle;
WCHAR PathString[32];
ASSERT(Volume->Flags & SMP_VOLUME_IS_BOOT); // ASSERT says "BootVolume == 1"
/* Build the standard path */
wcscpy(PathString, L"\\??\\A:\\");
RtlInitUnicodeString(&VolumeName, PathString);
VolumeName.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter;
DPRINT("SMSS:PFILE: Querying volume `%wZ' for free space\n", &VolumeName);
/* Open the volume */
InitializeObjectAttributes(&ObjectAttributes,
&VolumeName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&VolumeHandle,
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
if (!NT_SUCCESS(Status))
{
DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n", &VolumeName, Status);
return Status;
}
/* Now get size information on the volume */
Status = NtQueryVolumeInformationFile(VolumeHandle,
&IoStatusBlock,
&SizeInfo,
sizeof(SizeInfo),
FileFsSizeInformation);
if (!NT_SUCCESS(Status))
{
/* We failed */
DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
" with status %X\n",
&VolumeName,
VolumeHandle,
Status);
NtClose(VolumeHandle);
return Status;
}
NtClose(VolumeHandle);
/* Compute how much free space we have */
FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart *
SizeInfo.SectorsPerAllocationUnit;
FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector;
/* Check if there's less than 32MB free so we don't starve the disk */
if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
{
/* In this case, act as if there's no free space */
Volume->FreeSpace.QuadPart = 0;
}
else
{
/* Trim off 32MB to give the disk a bit of breathing room */
Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart -
MINIMUM_TO_KEEP_FREE;
}
return STATUS_SUCCESS;
}
PSMP_VOLUME_DESCRIPTOR
NTAPI
SmpSearchVolumeDescriptor(IN WCHAR DriveLetter)
{
WCHAR UpLetter;
PSMP_VOLUME_DESCRIPTOR Volume = NULL;
PLIST_ENTRY NextEntry;
/* Use upper case to reduce differences */
UpLetter = RtlUpcaseUnicodeChar(DriveLetter);
/* Loop each volume */
NextEntry = SmpVolumeDescriptorList.Flink;
while (NextEntry != &SmpVolumeDescriptorList)
{
/* Grab the entry */
Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry);
/* Make sure it's a valid entry with an uppcase drive letter */
ASSERT(Volume->Flags & SMP_VOLUME_INSERTED); // Volume->Initialized in ASSERT
ASSERT(Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z');
/* Break if it matches, if not, keep going */
if (Volume->DriveLetter == UpLetter) break;
NextEntry = NextEntry->Flink;
}
/* Return the volume if one was found */
if (NextEntry == &SmpVolumeDescriptorList) Volume = NULL;
return Volume;
}
NTSTATUS
NTAPI
SmpCreatePagingFile(IN PUNICODE_STRING Name,
IN PLARGE_INTEGER MinSize,
IN PLARGE_INTEGER MaxSize,
IN ULONG Priority)
{
NTSTATUS Status;
/* Tell the kernel to create the pagefile */
Status = NtCreatePagingFile(Name, MinSize, MaxSize, Priority);
if (NT_SUCCESS(Status))
{
DPRINT("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) succeeded.\n",
Name,
MinSize->QuadPart,
MaxSize->QuadPart);
}
else
{
DPRINT1("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) failed with %X\n",
Name,
MinSize->QuadPart,
MaxSize->QuadPart,
Status);
}
/* Return the status */
return Status;
}
NTSTATUS
NTAPI
SmpCreatePagingFileOnFixedDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
IN PLARGE_INTEGER FuzzFactor,
IN PLARGE_INTEGER MinimumSize)
{
PSMP_VOLUME_DESCRIPTOR Volume;
BOOLEAN ShouldDelete;
NTSTATUS Status;
LARGE_INTEGER PageFileSize;
ASSERT(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] != L'?');
/* Try to find the volume descriptor for this drive letter */
ShouldDelete = FALSE;
Volume = SmpSearchVolumeDescriptor(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
if (!Volume)
{
/* Couldn't find it, fail */
DPRINT1("SMSS:PFILE: No volume descriptor for `%wZ'\n",
&Descriptor->Name);
return STATUS_INVALID_PARAMETER;
}
/* Check if this is the boot volume */
if (Volume->Flags & SMP_VOLUME_IS_BOOT)
{
/* Check if we haven't yet processed a crash dump on this volume */
if (!(Descriptor->Flags & SMP_PAGEFILE_DUMP_PROCESSED))
{
/* Try to find a crash dump and extract it */
DPRINT("SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume\n",
&Descriptor->Name);
SmpCheckForCrashDump(&Descriptor->Name);
/* Update how much free space we have now that we extracted a dump */
Status = SmpGetVolumeFreeSpace(Volume);
if (!NT_SUCCESS(Status))
{
DPRINT1("SMSS:PFILE: Failed to query free space for boot volume `%wC'\n",
Volume->DriveLetter);
}
else
{
DPRINT("Queried free space for boot volume `%wC: %I64x'\n",
Volume->DriveLetter, Volume->FreeSpace.QuadPart);
}
/* Don't process crashdump on this volume anymore */
Descriptor->Flags |= SMP_PAGEFILE_DUMP_PROCESSED;
}
}
else
{
/* Crashdumps can only be on the boot volume */
DPRINT("SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot"
"volume `%wC'\n",
&Descriptor->Name,
Volume->DriveLetter);
}
/* Update the size after dump extraction */
Descriptor->ActualMinSize = Descriptor->MinSize;
Descriptor->ActualMaxSize = Descriptor->MaxSize;
/* Check how big we can make the pagefile */
Status = SmpGetPagingFileSize(&Descriptor->Name, &PageFileSize);
if (NT_SUCCESS(Status) && PageFileSize.QuadPart > 0) ShouldDelete = TRUE;
DPRINT("SMSS:PFILE: Detected size %I64X for future paging file `%wZ'\n",
PageFileSize,
&Descriptor->Name);
DPRINT("SMSS:PFILE: Free space on volume `%wC' is %I64X\n",
Volume->DriveLetter,
Volume->FreeSpace.QuadPart);
/* Now update our size and make sure none of these are too big */
PageFileSize.QuadPart += Volume->FreeSpace.QuadPart;
if (Descriptor->ActualMinSize.QuadPart > PageFileSize.QuadPart)
{
Descriptor->ActualMinSize = PageFileSize;
}
if (Descriptor->ActualMaxSize.QuadPart > PageFileSize.QuadPart)
{
Descriptor->ActualMaxSize = PageFileSize;
}
DPRINT("SMSS:PFILE: min %I64X, max %I64X, real min %I64X\n",
Descriptor->ActualMinSize.QuadPart,
Descriptor->ActualMaxSize.QuadPart,
MinimumSize->QuadPart);
/* Keep going until we've created a pagefile of the right size */
while (Descriptor->ActualMinSize.QuadPart >= MinimumSize->QuadPart)
{
/* Call NT to do it */
Status = SmpCreatePagingFile(&Descriptor->Name,
&Descriptor->ActualMinSize,
&Descriptor->ActualMaxSize,
0);
if (NT_SUCCESS(Status))
{
/* We're done, update flags and increase the count */
Descriptor->Flags |= SMP_PAGEFILE_CREATED;
Volume->Flags |= SMP_VOLUME_PAGEFILE_CREATED;
Volume->PageFileCount++;
break;
}
/* We failed, try a slightly smaller pagefile */
Descriptor->ActualMinSize.QuadPart -= FuzzFactor->QuadPart;
}
/* Check if we weren't able to create it */
if (Descriptor->ActualMinSize.QuadPart < MinimumSize->QuadPart)
{
/* Delete the current page file and fail */
if (ShouldDelete)
{
SmpDeletePagingFile(&Descriptor->Name);
/* FIXFIX: Windows Vista does this, and it seems like we should too, so try to see if this fixes KVM */
Volume->FreeSpace.QuadPart = PageFileSize.QuadPart;
}
DPRINT1("SMSS:PFILE: Failing for min %I64X, max %I64X, real min %I64X\n",
Descriptor->ActualMinSize.QuadPart,
Descriptor->ActualMaxSize.QuadPart,
MinimumSize->QuadPart);
Status = STATUS_DISK_FULL;
}
/* Return the status */
return Status;
}
NTSTATUS
NTAPI
SmpCreatePagingFileOnAnyDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
IN PLARGE_INTEGER FuzzFactor,
IN PLARGE_INTEGER MinimumSize)
{
PSMP_VOLUME_DESCRIPTOR Volume;
NTSTATUS Status = STATUS_DISK_FULL;
PLIST_ENTRY NextEntry;
ASSERT(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?');
/* Loop the volume list */
NextEntry = SmpVolumeDescriptorList.Flink;
while (NextEntry != &SmpVolumeDescriptorList)
{
/* Get the volume */
Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry);
/* Make sure it's inserted and on a valid drive letter */
ASSERT(Volume->Flags & SMP_VOLUME_INSERTED); // Volume->Initialized in ASSERT
ASSERT(Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z');
/* Write the drive letter to try creating it on this volume */
Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter;
Status = SmpCreatePagingFileOnFixedDrive(Descriptor,
FuzzFactor,
MinimumSize);
if (NT_SUCCESS(Status)) break;
/* It didn't work, make it an any pagefile again and keep going */
Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = L'?';
NextEntry = NextEntry->Flink;
}
/* Return disk full or success */
return Status;
}
VOID
NTAPI
SmpMakeDefaultPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
{
/* The default descriptor uses 128MB as a pagefile size */
Descriptor->Flags |= SMP_PAGEFILE_DEFAULT;
Descriptor->MinSize.QuadPart = 128 * MEGABYTE;
Descriptor->MaxSize.QuadPart = 128 * MEGABYTE;
}
VOID
NTAPI
SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
{
NTSTATUS Status;
LONGLONG MinimumSize, MaximumSize, Ram;
SYSTEM_BASIC_INFORMATION BasicInfo;
/* Query the page size of the system, and the amount of RAM */
Status = NtQuerySystemInformation(SystemBasicInformation,
&BasicInfo,
sizeof(BasicInfo),
NULL);
if (!NT_SUCCESS(Status))
{
/* If we failed, use defaults since we have no idea otherwise */
DPRINT1("SMSS:PFILE: NtQuerySystemInformation failed with %x\n", Status);
SmpMakeDefaultPagingFileDescriptor(Descriptor);
return;
}
/* Chekc how much RAM we have and set three times this amount as maximum */
Ram = BasicInfo.NumberOfPhysicalPages * BasicInfo.PageSize;
MaximumSize = 3 * Ram;
/* If we have more than 1GB, use that as minimum, otherwise, use 1.5X RAM */
MinimumSize = (Ram >= 1024 * MEGABYTE) ? Ram : MaximumSize / 2;
/* Write the new sizes in the descriptor and mark it as system managed */
Descriptor->MinSize.QuadPart = MinimumSize;
Descriptor->MaxSize.QuadPart = MaximumSize;
Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED;
}
NTSTATUS
NTAPI
SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONGLONG MinSize, MaxSize;
BOOLEAN WasTooBig = FALSE;
/* Capture the min and max */
MinSize = Descriptor->MinSize.QuadPart;
MaxSize = Descriptor->MaxSize.QuadPart;
DPRINT("SMSS:PFILE: Validating sizes for `%wZ' %I64X %I64X\n",
&Descriptor->Name, MinSize, MaxSize);
/* Don't let minimum be bigger than maximum */
if (MinSize > MaxSize) MaxSize = MinSize;
/* On PAE we can have bigger pagefiles... */
if (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED])
{
/* But we don't support that yet */
DPRINT1("ReactOS does not support PAE yet... assuming sizes OK\n");
}
else
{
/* Check if the minimum is more then 4095 MB */
if (MinSize > MAXIMUM_PAGEFILE_SIZE)
{
/* Trim it, this isn't allowed */
WasTooBig = TRUE;
MinSize = MAXIMUM_PAGEFILE_SIZE;
}
/* Check if the maximum is more then 4095 MB */
if (MaxSize > MAXIMUM_PAGEFILE_SIZE)
{
/* Trim it, this isn't allowed */
WasTooBig = TRUE;
MaxSize = MAXIMUM_PAGEFILE_SIZE;
}
}
/* Did we trim? */
if (WasTooBig)
{
/* Notify debugger output and write a flag in the descriptor */
DPRINT("SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed\n",
&Descriptor->Name);
Descriptor->Flags |= SMP_PAGEFILE_WAS_TOO_BIG;
}
/* Now write the (possibly trimmed) sizes back */
Descriptor->MinSize.QuadPart = MinSize;
Descriptor->MaxSize.QuadPart = MaxSize;
return Status;
}
NTSTATUS
NTAPI
SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
IN BOOLEAN DecreaseSize)
{
LARGE_INTEGER FuzzFactor, Size;
/* Make sure there's at least 1 paging file and that we are system-managed */
ASSERT(SmpNumberOfPagingFiles >= 1);
ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList));
ASSERT(Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED); // Descriptor->SystemManaged == 1 in ASSERT.
/* Keep decreasing the pagefile by this amount if we run out of space */
FuzzFactor.QuadPart = FUZZ_FACTOR;
/* Create the descriptor for it (mainly the right sizes) and validate */
SmpMakeSystemManagedPagingFileDescriptor(Descriptor);
SmpValidatePagingFileSizes(Descriptor);
/* Use either the minimum size in the descriptor, or 16MB in minimal mode */
Size.QuadPart = DecreaseSize ? 16 * MEGABYTE : Descriptor->MinSize.QuadPart;
/* Check if this should be a fixed pagefile or an any pagefile*/
if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?')
{
/* Find a disk for it */
return SmpCreatePagingFileOnAnyDrive(Descriptor, &FuzzFactor, &Size);
}
/* Use the disk that was given */
return SmpCreatePagingFileOnFixedDrive(Descriptor, &FuzzFactor, &Size);
}
NTSTATUS
NTAPI
SmpCreateEmergencyPagingFile(VOID)
{
PSMP_PAGEFILE_DESCRIPTOR Descriptor;
WCHAR Buffer[32];
/* Allocate a descriptor */
Descriptor = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SMP_PAGEFILE_DESCRIPTOR));
if (!Descriptor) return STATUS_NO_MEMORY;
/* Initialize it */
RtlInitUnicodeString(&Descriptor->Token, NULL);
/* Copy the default pagefile name */
ASSERT(sizeof(Buffer) >= sizeof(STANDARD_PAGING_FILE_NAME));
wcscpy(Buffer, STANDARD_PAGING_FILE_NAME);
/* Fill the rest of the descriptor out */
RtlInitUnicodeString(&Descriptor->Name, Buffer);
Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = '?';
Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED |
SMP_PAGEFILE_EMERGENCY |
SMP_PAGEFILE_ON_ANY_DRIVE;
/* Insert it into the descriptor list */
InsertHeadList(&SmpPagingFileDescriptorList, &Descriptor->Entry);
SmpNumberOfPagingFiles++;
/* Go ahead and create it now, with the minimal size possible */
return SmpCreateSystemManagedPagingFile(Descriptor, TRUE);
}
NTSTATUS
NTAPI
SmpCreateVolumeDescriptors(VOID)
{
NTSTATUS Status;
UNICODE_STRING VolumePath;
BOOLEAN BootVolumeFound = FALSE;
WCHAR StartChar, Drive, DriveDiff;
HANDLE VolumeHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PROCESS_DEVICEMAP_INFORMATION ProcessInformation;
FILE_FS_DEVICE_INFORMATION DeviceInfo;
FILE_FS_SIZE_INFORMATION SizeInfo;
PSMP_VOLUME_DESCRIPTOR Volume;
LARGE_INTEGER FreeSpace, FinalFreeSpace;
WCHAR Buffer[32];
/* We should be starting with an empty list */
ASSERT(IsListEmpty(&SmpVolumeDescriptorList));
/* Query the device map so we can get the drive letters */
Status = NtQueryInformationProcess(NtCurrentProcess(),
ProcessDeviceMap,
&ProcessInformation.Query,
sizeof(ProcessInformation.Query),
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X\n",
Status);
return Status;
}
/* Build the volume string, starting with A: (we'll edit this in place) */
wcscpy(Buffer, L"\\??\\A:\\");
RtlInitUnicodeString(&VolumePath, Buffer);
/* Start with the C drive except on weird Japanese NECs... */
StartChar = SharedUserData->AlternativeArchitecture ? L'A' : L'C';
for (Drive = StartChar, DriveDiff = StartChar - L'A'; Drive <= L'Z'; Drive++, DriveDiff++)
{
/* Skip the disk if it's not in the drive map */
if (!((1 << DriveDiff) & ProcessInformation.Query.DriveMap)) continue;
/* Write the drive letter and try to open the volume */
VolumePath.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Drive;
InitializeObjectAttributes(&ObjectAttributes,
&VolumePath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&VolumeHandle,
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
if (!NT_SUCCESS(Status))
{
/* Skip the volume if we failed */
DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n",
&VolumePath, Status);
continue;
}
/* Now query device information on the volume */
Status = NtQueryVolumeInformationFile(VolumeHandle,
&IoStatusBlock,
&DeviceInfo,
sizeof(DeviceInfo),
FileFsDeviceInformation);
if (!NT_SUCCESS(Status))
{
/* Move to the next volume if we failed */
DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for device info"
" failed with status %X\n",
&VolumePath,
VolumeHandle,
Status);
NtClose(VolumeHandle);
continue;
}
/* Check if this is a fixed disk */
if (DeviceInfo.Characteristics & (FILE_FLOPPY_DISKETTE |
FILE_READ_ONLY_DEVICE |
FILE_REMOTE_DEVICE |
FILE_REMOVABLE_MEDIA))
{
/* It isn't, so skip it */
DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file\n",
&VolumePath,
DeviceInfo.Characteristics);
NtClose(VolumeHandle);
continue;
}
/* We found a fixed volume, allocate a descriptor for it */
Volume = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SMP_VOLUME_DESCRIPTOR));
if (!Volume)
{
/* Failed to allocate memory, try the next disk */
DPRINT1("SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes)\n",
sizeof(SMP_VOLUME_DESCRIPTOR));
NtClose(VolumeHandle);
continue;
}
/* Save the drive letter and device information */
Volume->DriveLetter = Drive;
Volume->DeviceInfo = DeviceInfo;
/* Check if this is the boot volume */
if (RtlUpcaseUnicodeChar(Drive) ==
RtlUpcaseUnicodeChar(SharedUserData->NtSystemRoot[0]))
{
/* Save it */
ASSERT(BootVolumeFound == FALSE);
Volume->Flags |= SMP_VOLUME_IS_BOOT;
BootVolumeFound = TRUE;
}
/* Now get size information on the volume */
Status = NtQueryVolumeInformationFile(VolumeHandle,
&IoStatusBlock,
&SizeInfo,
sizeof(SizeInfo),
FileFsSizeInformation);
if (!NT_SUCCESS(Status))
{
/* We failed -- keep going */
DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
" with status %X\n",
&VolumePath,
VolumeHandle,
Status);
RtlFreeHeap(RtlGetProcessHeap(), 0, Volume);
NtClose(VolumeHandle);
continue;
}
/* Done querying volume information, close the handle */
NtClose(VolumeHandle);
/* Compute how much free space we have */
FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart *
SizeInfo.SectorsPerAllocationUnit;
FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector;
/* Check if there's less than 32MB free so we don't starve the disk */
if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
{
/* In this case, act as if there's no free space */
Volume->FreeSpace.QuadPart = 0;
}
else
{
/* Trim off 32MB to give the disk a bit of breathing room */
Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart -
MINIMUM_TO_KEEP_FREE;
}
/* All done, add this volume to our descriptor list */
InsertTailList(&SmpVolumeDescriptorList, &Volume->Entry);
Volume->Flags |= SMP_VOLUME_INSERTED;
DPRINT("SMSS:PFILE: Created volume descriptor for`%wZ'\n", &VolumePath);
}
/* We must've found at least the boot volume */
ASSERT(BootVolumeFound == TRUE);
ASSERT(!IsListEmpty(&SmpVolumeDescriptorList));
if (!IsListEmpty(&SmpVolumeDescriptorList)) return STATUS_SUCCESS;
/* Something is really messed up if we found no disks at all */
return STATUS_UNEXPECTED_IO_ERROR;
}
NTSTATUS
NTAPI
SmpCreatePagingFiles(VOID)
{
NTSTATUS Status;
PSMP_PAGEFILE_DESCRIPTOR Descriptor;
LARGE_INTEGER Size, FuzzFactor;
BOOLEAN Created = FALSE;
PLIST_ENTRY NextEntry;
/* Check if no paging files were requested */
if (!(SmpNumberOfPagingFiles) && !(SmpRegistrySpecifierPresent))
{
/* The list should be empty -- nothing to do */
ASSERT(IsListEmpty(&SmpPagingFileDescriptorList));
DPRINT1("SMSS:PFILE: No paging file was requested\n");
return STATUS_SUCCESS;
}
/* Initialize the volume descriptors so we can know what's available */
Status = SmpCreateVolumeDescriptors();
if (!NT_SUCCESS(Status))
{
/* We can't make decisions without this, so fail */
DPRINT1("SMSS:PFILE: Failed to create volume descriptors (status %X)\n",
Status);
return Status;
}
/* If we fail creating pagefiles, try to reduce by this much each time */
FuzzFactor.QuadPart = FUZZ_FACTOR;
/* Loop the descriptor list */
NextEntry = SmpPagingFileDescriptorList.Flink;
while (NextEntry != &SmpPagingFileDescriptorList)
{
/* Check what kind of descriptor this is */
Descriptor = CONTAINING_RECORD(NextEntry, SMP_PAGEFILE_DESCRIPTOR, Entry);
if (Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED)
{
/* This is a system-managed descriptor. Create the correct file */
DPRINT("SMSS:PFILE: Creating a system managed paging file (`%wZ')\n",
&Descriptor->Name);
Status = SmpCreateSystemManagedPagingFile(Descriptor, FALSE);
if (!NT_SUCCESS(Status))
{
/* We failed -- try again, with size minimization this time */
DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
&Descriptor->Name);
Status = SmpCreateSystemManagedPagingFile(Descriptor, TRUE);
}
}
else
{
/* This is a manually entered descriptor. Validate its size first */
SmpValidatePagingFileSizes(Descriptor);
/* Check if this is an ANY pagefile or a FIXED pagefile */
DPRINT("SMSS:PFILE: Creating a normal paging file (`%wZ')\n",
&Descriptor->Name);
if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?')
{
/* It's an any pagefile, try to create it wherever possible */
Size = Descriptor->MinSize;
Status = SmpCreatePagingFileOnAnyDrive(Descriptor,
&FuzzFactor,
&Size);
if (!NT_SUCCESS(Status))
{
/* We failed to create it. Try again with a smaller size */
DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
&Descriptor->Name);
Size.QuadPart = 16 * MEGABYTE;
Status = SmpCreatePagingFileOnAnyDrive(Descriptor,
&FuzzFactor,
&Size);
}
}
else
{
/* It's a fixed pagefile: override the minimum and use ours */
Size.QuadPart = 16 * MEGABYTE;
Status = SmpCreatePagingFileOnFixedDrive(Descriptor,
&FuzzFactor,
&Size);
}
}
/* Go to the next descriptor */
if (NT_SUCCESS(Status)) Created = TRUE;
NextEntry = NextEntry->Flink;
}
/* Check if none of the code in our loops above was able to create it */
if (!Created)
{
/* Build an emergency pagefile ourselves */
DPRINT1("SMSS:PFILE: Creating emergency paging file.\n");
Status = SmpCreateEmergencyPagingFile();
}
/* All done */
return Status;
}