/* * 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 /* 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; }