mirror of
https://github.com/reactos/reactos.git
synced 2024-11-19 05:22:59 +00:00
bcb9abc133
[SYSDM] Prevent my german-teacher from getting a heart-attack by that misplaced comma. [SMSS] Prevent myself from getting a heart-attack by that superfluous dot. Furthermore this addendum serves the purpose of actually linking both PRs and their 4 previous commits to the actual JIRA ticket CORE-18754, which wasn't the case before: 0.4.15-dev-5392-g04b2d35f5b
0.4.15-dev-5391-ga8e06d92e8
0.4.15-dev-5390-ga4274ad548
0.4.15-dev-5389-g5dc43c0f32
1150 lines
41 KiB
C
1150 lines
41 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 MAX_PAGING_FILES 16 // See also ntoskrnl/include/internal/mm.h
|
|
#define MEGABYTE (1024 * 1024)
|
|
|
|
/* Minimum pagefile size is 256 pages (1 MB) */
|
|
// #define MINIMUM_PAGEFILE_SIZE (256ULL * PAGE_SIZE)
|
|
|
|
/* Maximum pagefile sizes for different architectures */
|
|
#define GIGABYTE (1024ULL * MEGABYTE)
|
|
#define TERABYTE (1024ULL * GIGABYTE)
|
|
|
|
// NOTE: No changes for NTDDI_WIN10
|
|
#if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81
|
|
#define MAXIMUM_PAGEFILE_SIZE32 ((1ULL * 1024 * 1024 - 1) * PAGE_SIZE)
|
|
// PAGE_ROUND_DOWN(4ULL * GIGABYTE - 1)
|
|
#else
|
|
/* 4095 MB */
|
|
#define MAXIMUM_PAGEFILE_SIZE32 (4095ULL * MEGABYTE)
|
|
#endif
|
|
|
|
// NOTE: No changes for NTDDI_WIN10
|
|
#if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81
|
|
#define MAXIMUM_PAGEFILE_SIZE64 ((4ULL * 1024 * 1024 * 1024 - 1) * PAGE_SIZE)
|
|
// PAGE_ROUND_DOWN(16ULL * TERABYTE - 1)
|
|
#else
|
|
/* 16 TB */
|
|
#define MAXIMUM_PAGEFILE_SIZE64 (16ULL * TERABYTE)
|
|
#endif
|
|
|
|
#if defined(_M_IX86)
|
|
#define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32
|
|
/* PAE uses the same size as x64 */
|
|
#define MAXIMUM_PAGEFILE_SIZE_PAE MAXIMUM_PAGEFILE_SIZE64
|
|
#elif defined (_M_AMD64) || defined(_M_ARM64)
|
|
#define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE64
|
|
#elif defined (_M_IA64)
|
|
/* 32 TB */
|
|
#define MAXIMUM_PAGEFILE_SIZE (32ULL * TERABYTE)
|
|
#elif defined(_M_ARM)
|
|
/* Around 2 GB */
|
|
// NOTE: No changes for NTDDI_WIN10
|
|
#if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81
|
|
#define MAXIMUM_PAGEFILE_SIZE ((512ULL * 1024 - 1) * PAGE_SIZE)
|
|
// PAGE_ROUND_DOWN(2ULL * GIGABYTE - 1)
|
|
#else
|
|
/* 4095 MB */
|
|
#define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32
|
|
#endif
|
|
#else
|
|
/* On unknown architectures, default to either one of the 32 or 64 bit sizes */
|
|
#pragma message("Unknown architecture")
|
|
#ifdef _WIN64
|
|
#define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE64
|
|
#else
|
|
#define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32
|
|
#endif
|
|
#endif
|
|
|
|
/* This should be 32 MB, but we need more than that for 2nd stage setup */
|
|
#define MINIMUM_TO_KEEP_FREE (256 * 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 >= MAX_PAGING_FILES)
|
|
{
|
|
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 is less than 32 MB free so we don't starve the disk */
|
|
if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
|
|
{
|
|
/* In this case, act as if there is no free space */
|
|
Volume->FreeSpace.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Trim off 32 MB 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, 0x%I64X, 0x%I64X) succeeded\n",
|
|
Name,
|
|
MinSize->QuadPart,
|
|
MaxSize->QuadPart);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("SMSS:PFILE: NtCreatePagingFile(%wZ, 0x%I64X, 0x%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: 0x%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 0x%I64X for future paging file `%wZ'\n",
|
|
PageFileSize,
|
|
&Descriptor->Name);
|
|
DPRINT("SMSS:PFILE: Free space on volume `%wC' is 0x%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 0x%I64X, max 0x%I64X, real min 0x%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 0x%I64X, max 0x%I64X, real min 0x%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 128 MB 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;
|
|
ULONGLONG 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;
|
|
}
|
|
|
|
/* Check 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;
|
|
BOOLEAN WasTooBig = FALSE;
|
|
ULONGLONG MinSize, MaxSize;
|
|
#ifdef _M_IX86
|
|
ULONGLONG MaxPageFileSize =
|
|
(SharedUserData->ProcessorFeatures[PF_PAE_ENABLED])
|
|
? MAXIMUM_PAGEFILE_SIZE_PAE : MAXIMUM_PAGEFILE_SIZE;
|
|
#else
|
|
static const ULONGLONG MaxPageFileSize = MAXIMUM_PAGEFILE_SIZE;
|
|
#endif
|
|
|
|
/* Capture the min and max */
|
|
MinSize = Descriptor->MinSize.QuadPart;
|
|
MaxSize = Descriptor->MaxSize.QuadPart;
|
|
|
|
DPRINT("SMSS:PFILE: Validating sizes for `%wZ' 0x%I64X 0x%I64X\n",
|
|
&Descriptor->Name, MinSize, MaxSize);
|
|
|
|
/* Don't let minimum be bigger than maximum */
|
|
if (MinSize > MaxSize)
|
|
MaxSize = MinSize;
|
|
|
|
/* Validate the minimum and maximum and trim them if they are too large */
|
|
if (MinSize > MaxPageFileSize)
|
|
{
|
|
WasTooBig = TRUE;
|
|
MinSize = MaxPageFileSize;
|
|
}
|
|
if (MaxSize > MaxPageFileSize)
|
|
{
|
|
WasTooBig = TRUE;
|
|
MaxSize = MaxPageFileSize;
|
|
}
|
|
|
|
/* If we trimmed, write a flag in the descriptor */
|
|
if (WasTooBig)
|
|
{
|
|
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 is at least 1 paging file and that we are system-managed */
|
|
ASSERT(SmpNumberOfPagingFiles >= 1);
|
|
ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList));
|
|
ASSERT(Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED);
|
|
|
|
/* 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 16 MB 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 NEC PC-98 */
|
|
StartChar = IsNEC_98 ? 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 is less than 32 MB free so we don't starve the disk */
|
|
if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
|
|
{
|
|
/* In this case, act as if there is no free space */
|
|
Volume->FreeSpace.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Trim off 32 MB 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;
|
|
}
|