--- /dev/null
+/*
+ * 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,
+ sizeof(ProcessInformation),
+ 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;
+}