[SMSS] When determining page file size, keep more free disk space. CORE-6839
[reactos.git] / base / system / smss / pagefile.c
1 /*
2 * PROJECT: ReactOS Windows-Compatible Session Manager
3 * LICENSE: BSD 2-Clause License
4 * FILE: base/system/smss/pagefile.c
5 * PURPOSE: Main SMSS Code
6 * PROGRAMMERS: Alex Ionescu
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "smss.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
18 //
19 // Constants
20 //
21 #define STANDARD_PAGING_FILE_NAME L"\\??\\?:\\pagefile.sys"
22 #define STANDARD_DRIVE_LETTER_OFFSET 4
23 #define MEGABYTE 0x100000UL
24 #define MAXIMUM_PAGEFILE_SIZE (4095 * MEGABYTE)
25 /* This should be 32 MB, but we need more than that for 2nd stage setup */
26 #define MINIMUM_TO_KEEP_FREE (256 * MEGABYTE)
27 #define FUZZ_FACTOR (16 * MEGABYTE)
28
29 //
30 // Structure and flags describing each pagefile
31 //
32 #define SMP_PAGEFILE_CREATED 0x01
33 #define SMP_PAGEFILE_DEFAULT 0x02
34 #define SMP_PAGEFILE_SYSTEM_MANAGED 0x04
35 #define SMP_PAGEFILE_WAS_TOO_BIG 0x08
36 #define SMP_PAGEFILE_ON_ANY_DRIVE 0x10
37 #define SMP_PAGEFILE_EMERGENCY 0x20
38 #define SMP_PAGEFILE_DUMP_PROCESSED 0x40
39 typedef struct _SMP_PAGEFILE_DESCRIPTOR
40 {
41 LIST_ENTRY Entry;
42 UNICODE_STRING Name;
43 UNICODE_STRING Token;
44 LARGE_INTEGER MinSize;
45 LARGE_INTEGER MaxSize;
46 LARGE_INTEGER ActualMinSize;
47 LARGE_INTEGER ActualMaxSize;
48 ULONG Flags;
49 } SMP_PAGEFILE_DESCRIPTOR, *PSMP_PAGEFILE_DESCRIPTOR;
50
51 //
52 // Structure and flags describing each volume
53 //
54 #define SMP_VOLUME_INSERTED 0x01
55 #define SMP_VOLUME_PAGEFILE_CREATED 0x04
56 #define SMP_VOLUME_IS_BOOT 0x08
57 typedef struct _SMP_VOLUME_DESCRIPTOR
58 {
59 LIST_ENTRY Entry;
60 USHORT Flags;
61 USHORT PageFileCount;
62 WCHAR DriveLetter;
63 LARGE_INTEGER FreeSpace;
64 FILE_FS_DEVICE_INFORMATION DeviceInfo;
65 } SMP_VOLUME_DESCRIPTOR, *PSMP_VOLUME_DESCRIPTOR;
66
67 LIST_ENTRY SmpPagingFileDescriptorList, SmpVolumeDescriptorList;
68 BOOLEAN SmpRegistrySpecifierPresent;
69 ULONG SmpNumberOfPagingFiles;
70
71 /* FUNCTIONS ******************************************************************/
72
73 VOID
74 NTAPI
75 SmpPagingFileInitialize(VOID)
76 {
77 /* Initialize the two lists */
78 InitializeListHead(&SmpPagingFileDescriptorList);
79 InitializeListHead(&SmpVolumeDescriptorList);
80 }
81
82 NTSTATUS
83 NTAPI
84 SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken)
85 {
86 NTSTATUS Status;
87 ULONG MinSize = 0, MaxSize = 0;
88 BOOLEAN SystemManaged = FALSE, ZeroSize = TRUE;
89 PSMP_PAGEFILE_DESCRIPTOR Descriptor, ListDescriptor;
90 ULONG i;
91 WCHAR c;
92 PLIST_ENTRY NextEntry;
93 UNICODE_STRING PageFileName, Arguments, SecondArgument;
94
95 /* Make sure we don't have too many */
96 if (SmpNumberOfPagingFiles >= 16)
97 {
98 DPRINT1("SMSS:PFILE: Too many paging files specified - %lu\n",
99 SmpNumberOfPagingFiles);
100 return STATUS_TOO_MANY_PAGING_FILES;
101 }
102
103 /* Parse the specified and get the name and arguments out of it */
104 DPRINT("SMSS:PFILE: Paging file specifier `%wZ'\n", PageFileToken);
105 Status = SmpParseCommandLine(PageFileToken,
106 NULL,
107 &PageFileName,
108 NULL,
109 &Arguments);
110 if (!NT_SUCCESS(Status))
111 {
112 /* Fail */
113 DPRINT1("SMSS:PFILE: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
114 PageFileToken, Status);
115 return Status;
116 }
117
118 /* Set the variable to let everyone know we have a pagefile token */
119 SmpRegistrySpecifierPresent = TRUE;
120
121 /* Parse the arguments, if any */
122 if (Arguments.Buffer)
123 {
124 /* Parse the pagefile size */
125 for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++)
126 {
127 /* Check if it's zero */
128 c = Arguments.Buffer[i];
129 if ((c != L' ') && (c != L'\t') && (c != L'0'))
130 {
131 /* It isn't, break out */
132 ZeroSize = FALSE;
133 break;
134 }
135 }
136 }
137
138 /* Was a pagefile not specified, or was it specified with no size? */
139 if (!(Arguments.Buffer) || (ZeroSize))
140 {
141 /* In this case, the system will manage its size */
142 SystemManaged = TRUE;
143 }
144 else
145 {
146 /* We do have a size, so convert the arguments into a number */
147 Status = RtlUnicodeStringToInteger(&Arguments, 0, &MinSize);
148 if (!NT_SUCCESS(Status))
149 {
150 /* Fail */
151 RtlFreeUnicodeString(&PageFileName);
152 RtlFreeUnicodeString(&Arguments);
153 return Status;
154 }
155
156 /* Now advance to the next argument */
157 for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++)
158 {
159 /* Found a space -- second argument must start here */
160 if (Arguments.Buffer[i] == L' ')
161 {
162 /* Use the rest of the arguments as a maximum size */
163 SecondArgument.Buffer = &Arguments.Buffer[i];
164 SecondArgument.Length = (USHORT)(Arguments.Length -
165 i * sizeof(WCHAR));
166 SecondArgument.MaximumLength = (USHORT)(Arguments.MaximumLength -
167 i * sizeof(WCHAR));
168 Status = RtlUnicodeStringToInteger(&SecondArgument, 0, &MaxSize);
169 if (!NT_SUCCESS(Status))
170 {
171 /* Fail */
172 RtlFreeUnicodeString(&PageFileName);
173 RtlFreeUnicodeString(&Arguments);
174 return Status;
175 }
176
177 break;
178 }
179 }
180 }
181
182 /* We are done parsing arguments */
183 RtlFreeUnicodeString(&Arguments);
184
185 /* Now we can allocate our descriptor */
186 Descriptor = RtlAllocateHeap(RtlGetProcessHeap(),
187 HEAP_ZERO_MEMORY,
188 sizeof(SMP_PAGEFILE_DESCRIPTOR));
189 if (!Descriptor)
190 {
191 /* Fail if we couldn't */
192 RtlFreeUnicodeString(&PageFileName);
193 return STATUS_NO_MEMORY;
194 }
195
196 /* Capture all our data into the descriptor */
197 Descriptor->Token = *PageFileToken;
198 Descriptor->Name = PageFileName;
199 Descriptor->MinSize.QuadPart = MinSize * MEGABYTE;
200 Descriptor->MaxSize.QuadPart = MaxSize * MEGABYTE;
201 if (SystemManaged) Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED;
202 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] =
203 RtlUpcaseUnicodeChar(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
204 if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?')
205 {
206 Descriptor->Flags |= SMP_PAGEFILE_ON_ANY_DRIVE;
207 }
208
209 /* Now loop the existing descriptors */
210 NextEntry = SmpPagingFileDescriptorList.Flink;
211 do
212 {
213 /* Are there none, or have we looped back to the beginning? */
214 if (NextEntry == &SmpPagingFileDescriptorList)
215 {
216 /* This means no duplicates exist, so insert our descriptor! */
217 InsertTailList(&SmpPagingFileDescriptorList, &Descriptor->Entry);
218 SmpNumberOfPagingFiles++;
219 DPRINT("SMSS:PFILE: Created descriptor for `%wZ' (`%wZ')\n",
220 PageFileToken, &Descriptor->Name);
221 return STATUS_SUCCESS;
222 }
223
224 /* Keep going until we find a duplicate, unless we are in "any" mode */
225 ListDescriptor = CONTAINING_RECORD(NextEntry, SMP_PAGEFILE_DESCRIPTOR, Entry);
226 NextEntry = NextEntry->Flink;
227 } while (!(ListDescriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE) ||
228 !(Descriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE));
229
230 /* We found a duplicate, so skip this descriptor/pagefile and fail */
231 DPRINT1("SMSS:PFILE: Skipping duplicate specifier `%wZ'\n", PageFileToken);
232 RtlFreeUnicodeString(&PageFileName);
233 RtlFreeHeap(RtlGetProcessHeap(), 0, Descriptor);
234 return STATUS_INVALID_PARAMETER;
235 }
236
237 NTSTATUS
238 NTAPI
239 SmpGetPagingFileSize(IN PUNICODE_STRING FileName,
240 OUT PLARGE_INTEGER Size)
241 {
242 NTSTATUS Status;
243 OBJECT_ATTRIBUTES ObjectAttributes;
244 IO_STATUS_BLOCK IoStatusBlock;
245 HANDLE FileHandle;
246 FILE_STANDARD_INFORMATION StandardInfo;
247
248 DPRINT("SMSS:PFILE: Trying to get size for `%wZ'\n", FileName);
249 Size->QuadPart = 0;
250
251 InitializeObjectAttributes(&ObjectAttributes,
252 FileName,
253 OBJ_CASE_INSENSITIVE,
254 NULL,
255 NULL);
256 Status = NtOpenFile(&FileHandle,
257 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
258 &ObjectAttributes,
259 &IoStatusBlock,
260 FILE_SHARE_READ | FILE_SHARE_WRITE,
261 FILE_SYNCHRONOUS_IO_NONALERT);
262 if (!NT_SUCCESS(Status)) return Status;
263
264 Status = NtQueryInformationFile(FileHandle,
265 &IoStatusBlock,
266 &StandardInfo,
267 sizeof(StandardInfo),
268 FileStandardInformation);
269 if (!NT_SUCCESS(Status))
270 {
271 DPRINT1("SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X\n",
272 FileName, Status);
273 NtClose(FileHandle);
274 return Status;
275 }
276
277 NtClose(FileHandle);
278 Size->QuadPart = StandardInfo.AllocationSize.QuadPart;
279 return STATUS_SUCCESS;
280 }
281
282 NTSTATUS
283 NTAPI
284 SmpDeletePagingFile(IN PUNICODE_STRING FileName)
285 {
286 NTSTATUS Status;
287 OBJECT_ATTRIBUTES ObjectAttributes;
288 IO_STATUS_BLOCK IoStatusBlock;
289 HANDLE FileHandle;
290 FILE_DISPOSITION_INFORMATION Disposition;
291
292 /* Open the page file */
293 InitializeObjectAttributes(&ObjectAttributes,
294 FileName,
295 OBJ_CASE_INSENSITIVE,
296 NULL,
297 NULL);
298 Status = NtOpenFile(&FileHandle,
299 DELETE,
300 &ObjectAttributes,
301 &IoStatusBlock,
302 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
303 FILE_NON_DIRECTORY_FILE);
304 if (NT_SUCCESS(Status))
305 {
306 /* Delete it */
307 Disposition.DeleteFile = TRUE;
308 Status = NtSetInformationFile(FileHandle,
309 &IoStatusBlock,
310 &Disposition,
311 sizeof(Disposition),
312 FileDispositionInformation);
313 if (!NT_SUCCESS(Status))
314 {
315 DPRINT1("SMSS:PFILE: Failed to delete page file `%wZ' (status %X)\n",
316 FileName, Status);
317 }
318 else
319 {
320 DPRINT("SMSS:PFILE: Deleted stale paging file - %wZ\n", FileName);
321 }
322
323 /* Close the handle */
324 NtClose(FileHandle);
325 }
326 else
327 {
328 DPRINT1("SMSS:PFILE: Failed to open for deletion page file `%wZ' (status %X)\n",
329 FileName, Status);
330 }
331
332 /* All done */
333 return Status;
334 }
335
336 NTSTATUS
337 NTAPI
338 SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume)
339 {
340 NTSTATUS Status;
341 LARGE_INTEGER FreeSpace, FinalFreeSpace;
342 FILE_FS_SIZE_INFORMATION SizeInfo;
343 IO_STATUS_BLOCK IoStatusBlock;
344 OBJECT_ATTRIBUTES ObjectAttributes;
345 UNICODE_STRING VolumeName;
346 HANDLE VolumeHandle;
347 WCHAR PathString[32];
348 ASSERT(Volume->Flags & SMP_VOLUME_IS_BOOT); // ASSERT says "BootVolume == 1"
349
350 /* Build the standard path */
351 wcscpy(PathString, L"\\??\\A:\\");
352 RtlInitUnicodeString(&VolumeName, PathString);
353 VolumeName.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter;
354 DPRINT("SMSS:PFILE: Querying volume `%wZ' for free space\n", &VolumeName);
355
356 /* Open the volume */
357 InitializeObjectAttributes(&ObjectAttributes,
358 &VolumeName,
359 OBJ_CASE_INSENSITIVE,
360 NULL,
361 NULL);
362 Status = NtOpenFile(&VolumeHandle,
363 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
364 &ObjectAttributes,
365 &IoStatusBlock,
366 FILE_SHARE_READ | FILE_SHARE_WRITE,
367 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
368 if (!NT_SUCCESS(Status))
369 {
370 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n", &VolumeName, Status);
371 return Status;
372 }
373
374 /* Now get size information on the volume */
375 Status = NtQueryVolumeInformationFile(VolumeHandle,
376 &IoStatusBlock,
377 &SizeInfo,
378 sizeof(SizeInfo),
379 FileFsSizeInformation);
380 if (!NT_SUCCESS(Status))
381 {
382 /* We failed */
383 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
384 " with status %X\n",
385 &VolumeName,
386 VolumeHandle,
387 Status);
388 NtClose(VolumeHandle);
389 return Status;
390 }
391 NtClose(VolumeHandle);
392
393 /* Compute how much free space we have */
394 FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart *
395 SizeInfo.SectorsPerAllocationUnit;
396 FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector;
397
398 /* Check if there's less than 32MB free so we don't starve the disk */
399 if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
400 {
401 /* In this case, act as if there's no free space */
402 Volume->FreeSpace.QuadPart = 0;
403 }
404 else
405 {
406 /* Trim off 32MB to give the disk a bit of breathing room */
407 Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart -
408 MINIMUM_TO_KEEP_FREE;
409 }
410
411 return STATUS_SUCCESS;
412 }
413
414 PSMP_VOLUME_DESCRIPTOR
415 NTAPI
416 SmpSearchVolumeDescriptor(IN WCHAR DriveLetter)
417 {
418 WCHAR UpLetter;
419 PSMP_VOLUME_DESCRIPTOR Volume = NULL;
420 PLIST_ENTRY NextEntry;
421
422 /* Use upper case to reduce differences */
423 UpLetter = RtlUpcaseUnicodeChar(DriveLetter);
424
425 /* Loop each volume */
426 NextEntry = SmpVolumeDescriptorList.Flink;
427 while (NextEntry != &SmpVolumeDescriptorList)
428 {
429 /* Grab the entry */
430 Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry);
431
432 /* Make sure it's a valid entry with an uppcase drive letter */
433 ASSERT(Volume->Flags & SMP_VOLUME_INSERTED); // Volume->Initialized in ASSERT
434 ASSERT(Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z');
435
436 /* Break if it matches, if not, keep going */
437 if (Volume->DriveLetter == UpLetter) break;
438 NextEntry = NextEntry->Flink;
439 }
440
441 /* Return the volume if one was found */
442 if (NextEntry == &SmpVolumeDescriptorList) Volume = NULL;
443 return Volume;
444 }
445
446 NTSTATUS
447 NTAPI
448 SmpCreatePagingFile(IN PUNICODE_STRING Name,
449 IN PLARGE_INTEGER MinSize,
450 IN PLARGE_INTEGER MaxSize,
451 IN ULONG Priority)
452 {
453 NTSTATUS Status;
454
455 /* Tell the kernel to create the pagefile */
456 Status = NtCreatePagingFile(Name, MinSize, MaxSize, Priority);
457 if (NT_SUCCESS(Status))
458 {
459 DPRINT("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) succeeded.\n",
460 Name,
461 MinSize->QuadPart,
462 MaxSize->QuadPart);
463 }
464 else
465 {
466 DPRINT1("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) failed with %X\n",
467 Name,
468 MinSize->QuadPart,
469 MaxSize->QuadPart,
470 Status);
471 }
472
473 /* Return the status */
474 return Status;
475 }
476
477 NTSTATUS
478 NTAPI
479 SmpCreatePagingFileOnFixedDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
480 IN PLARGE_INTEGER FuzzFactor,
481 IN PLARGE_INTEGER MinimumSize)
482 {
483 PSMP_VOLUME_DESCRIPTOR Volume;
484 BOOLEAN ShouldDelete;
485 NTSTATUS Status;
486 LARGE_INTEGER PageFileSize;
487 ASSERT(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] != L'?');
488
489 /* Try to find the volume descriptor for this drive letter */
490 ShouldDelete = FALSE;
491 Volume = SmpSearchVolumeDescriptor(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
492 if (!Volume)
493 {
494 /* Couldn't find it, fail */
495 DPRINT1("SMSS:PFILE: No volume descriptor for `%wZ'\n",
496 &Descriptor->Name);
497 return STATUS_INVALID_PARAMETER;
498 }
499
500 /* Check if this is the boot volume */
501 if (Volume->Flags & SMP_VOLUME_IS_BOOT)
502 {
503 /* Check if we haven't yet processed a crash dump on this volume */
504 if (!(Descriptor->Flags & SMP_PAGEFILE_DUMP_PROCESSED))
505 {
506 /* Try to find a crash dump and extract it */
507 DPRINT("SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume\n",
508 &Descriptor->Name);
509 SmpCheckForCrashDump(&Descriptor->Name);
510
511 /* Update how much free space we have now that we extracted a dump */
512 Status = SmpGetVolumeFreeSpace(Volume);
513 if (!NT_SUCCESS(Status))
514 {
515 DPRINT1("SMSS:PFILE: Failed to query free space for boot volume `%wC'\n",
516 Volume->DriveLetter);
517 }
518 else
519 {
520 DPRINT("Queried free space for boot volume `%wC: %I64x'\n",
521 Volume->DriveLetter, Volume->FreeSpace.QuadPart);
522 }
523
524 /* Don't process crashdump on this volume anymore */
525 Descriptor->Flags |= SMP_PAGEFILE_DUMP_PROCESSED;
526 }
527 }
528 else
529 {
530 /* Crashdumps can only be on the boot volume */
531 DPRINT("SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot"
532 "volume `%wC'\n",
533 &Descriptor->Name,
534 Volume->DriveLetter);
535 }
536
537 /* Update the size after dump extraction */
538 Descriptor->ActualMinSize = Descriptor->MinSize;
539 Descriptor->ActualMaxSize = Descriptor->MaxSize;
540
541 /* Check how big we can make the pagefile */
542 Status = SmpGetPagingFileSize(&Descriptor->Name, &PageFileSize);
543 if (NT_SUCCESS(Status) && PageFileSize.QuadPart > 0) ShouldDelete = TRUE;
544 DPRINT("SMSS:PFILE: Detected size %I64X for future paging file `%wZ'\n",
545 PageFileSize,
546 &Descriptor->Name);
547 DPRINT("SMSS:PFILE: Free space on volume `%wC' is %I64X\n",
548 Volume->DriveLetter,
549 Volume->FreeSpace.QuadPart);
550
551 /* Now update our size and make sure none of these are too big */
552 PageFileSize.QuadPart += Volume->FreeSpace.QuadPart;
553 if (Descriptor->ActualMinSize.QuadPart > PageFileSize.QuadPart)
554 {
555 Descriptor->ActualMinSize = PageFileSize;
556 }
557 if (Descriptor->ActualMaxSize.QuadPart > PageFileSize.QuadPart)
558 {
559 Descriptor->ActualMaxSize = PageFileSize;
560 }
561 DPRINT("SMSS:PFILE: min %I64X, max %I64X, real min %I64X\n",
562 Descriptor->ActualMinSize.QuadPart,
563 Descriptor->ActualMaxSize.QuadPart,
564 MinimumSize->QuadPart);
565
566 /* Keep going until we've created a pagefile of the right size */
567 while (Descriptor->ActualMinSize.QuadPart >= MinimumSize->QuadPart)
568 {
569 /* Call NT to do it */
570 Status = SmpCreatePagingFile(&Descriptor->Name,
571 &Descriptor->ActualMinSize,
572 &Descriptor->ActualMaxSize,
573 0);
574 if (NT_SUCCESS(Status))
575 {
576 /* We're done, update flags and increase the count */
577 Descriptor->Flags |= SMP_PAGEFILE_CREATED;
578 Volume->Flags |= SMP_VOLUME_PAGEFILE_CREATED;
579 Volume->PageFileCount++;
580 break;
581 }
582
583 /* We failed, try a slightly smaller pagefile */
584 Descriptor->ActualMinSize.QuadPart -= FuzzFactor->QuadPart;
585 }
586
587 /* Check if we weren't able to create it */
588 if (Descriptor->ActualMinSize.QuadPart < MinimumSize->QuadPart)
589 {
590 /* Delete the current page file and fail */
591 if (ShouldDelete)
592 {
593 SmpDeletePagingFile(&Descriptor->Name);
594
595 /* FIXFIX: Windows Vista does this, and it seems like we should too, so try to see if this fixes KVM */
596 Volume->FreeSpace.QuadPart = PageFileSize.QuadPart;
597 }
598 DPRINT1("SMSS:PFILE: Failing for min %I64X, max %I64X, real min %I64X\n",
599 Descriptor->ActualMinSize.QuadPart,
600 Descriptor->ActualMaxSize.QuadPart,
601 MinimumSize->QuadPart);
602 Status = STATUS_DISK_FULL;
603 }
604
605 /* Return the status */
606 return Status;
607 }
608
609 NTSTATUS
610 NTAPI
611 SmpCreatePagingFileOnAnyDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
612 IN PLARGE_INTEGER FuzzFactor,
613 IN PLARGE_INTEGER MinimumSize)
614 {
615 PSMP_VOLUME_DESCRIPTOR Volume;
616 NTSTATUS Status = STATUS_DISK_FULL;
617 PLIST_ENTRY NextEntry;
618 ASSERT(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?');
619
620 /* Loop the volume list */
621 NextEntry = SmpVolumeDescriptorList.Flink;
622 while (NextEntry != &SmpVolumeDescriptorList)
623 {
624 /* Get the volume */
625 Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry);
626
627 /* Make sure it's inserted and on a valid drive letter */
628 ASSERT(Volume->Flags & SMP_VOLUME_INSERTED); // Volume->Initialized in ASSERT
629 ASSERT(Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z');
630
631 /* Write the drive letter to try creating it on this volume */
632 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter;
633 Status = SmpCreatePagingFileOnFixedDrive(Descriptor,
634 FuzzFactor,
635 MinimumSize);
636 if (NT_SUCCESS(Status)) break;
637
638 /* It didn't work, make it an any pagefile again and keep going */
639 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = L'?';
640 NextEntry = NextEntry->Flink;
641 }
642
643 /* Return disk full or success */
644 return Status;
645 }
646
647 VOID
648 NTAPI
649 SmpMakeDefaultPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
650 {
651 /* The default descriptor uses 128MB as a pagefile size */
652 Descriptor->Flags |= SMP_PAGEFILE_DEFAULT;
653 Descriptor->MinSize.QuadPart = 128 * MEGABYTE;
654 Descriptor->MaxSize.QuadPart = 128 * MEGABYTE;
655 }
656
657 VOID
658 NTAPI
659 SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
660 {
661 NTSTATUS Status;
662 LONGLONG MinimumSize, MaximumSize, Ram;
663 SYSTEM_BASIC_INFORMATION BasicInfo;
664
665 /* Query the page size of the system, and the amount of RAM */
666 Status = NtQuerySystemInformation(SystemBasicInformation,
667 &BasicInfo,
668 sizeof(BasicInfo),
669 NULL);
670 if (!NT_SUCCESS(Status))
671 {
672 /* If we failed, use defaults since we have no idea otherwise */
673 DPRINT1("SMSS:PFILE: NtQuerySystemInformation failed with %x\n", Status);
674 SmpMakeDefaultPagingFileDescriptor(Descriptor);
675 return;
676 }
677
678 /* Chekc how much RAM we have and set three times this amount as maximum */
679 Ram = BasicInfo.NumberOfPhysicalPages * BasicInfo.PageSize;
680 MaximumSize = 3 * Ram;
681
682 /* If we have more than 1GB, use that as minimum, otherwise, use 1.5X RAM */
683 MinimumSize = (Ram >= 1024 * MEGABYTE) ? Ram : MaximumSize / 2;
684
685 /* Write the new sizes in the descriptor and mark it as system managed */
686 Descriptor->MinSize.QuadPart = MinimumSize;
687 Descriptor->MaxSize.QuadPart = MaximumSize;
688 Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED;
689 }
690
691 NTSTATUS
692 NTAPI
693 SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
694 {
695 NTSTATUS Status = STATUS_SUCCESS;
696 ULONGLONG MinSize, MaxSize;
697 BOOLEAN WasTooBig = FALSE;
698
699 /* Capture the min and max */
700 MinSize = Descriptor->MinSize.QuadPart;
701 MaxSize = Descriptor->MaxSize.QuadPart;
702 DPRINT("SMSS:PFILE: Validating sizes for `%wZ' %I64X %I64X\n",
703 &Descriptor->Name, MinSize, MaxSize);
704
705 /* Don't let minimum be bigger than maximum */
706 if (MinSize > MaxSize) MaxSize = MinSize;
707
708 /* On PAE we can have bigger pagefiles... */
709 if (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED])
710 {
711 /* But we don't support that yet */
712 DPRINT1("ReactOS does not support PAE yet... assuming sizes OK\n");
713 }
714 else
715 {
716 /* Check if the minimum is more then 4095 MB */
717 if (MinSize > MAXIMUM_PAGEFILE_SIZE)
718 {
719 /* Trim it, this isn't allowed */
720 WasTooBig = TRUE;
721 MinSize = MAXIMUM_PAGEFILE_SIZE;
722 }
723
724 /* Check if the maximum is more then 4095 MB */
725 if (MaxSize > MAXIMUM_PAGEFILE_SIZE)
726 {
727 /* Trim it, this isn't allowed */
728 WasTooBig = TRUE;
729 MaxSize = MAXIMUM_PAGEFILE_SIZE;
730 }
731 }
732
733 /* Did we trim? */
734 if (WasTooBig)
735 {
736 /* Notify debugger output and write a flag in the descriptor */
737 DPRINT("SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed\n",
738 &Descriptor->Name);
739 Descriptor->Flags |= SMP_PAGEFILE_WAS_TOO_BIG;
740 }
741
742 /* Now write the (possibly trimmed) sizes back */
743 Descriptor->MinSize.QuadPart = MinSize;
744 Descriptor->MaxSize.QuadPart = MaxSize;
745 return Status;
746 }
747
748 NTSTATUS
749 NTAPI
750 SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
751 IN BOOLEAN DecreaseSize)
752 {
753 LARGE_INTEGER FuzzFactor, Size;
754
755 /* Make sure there's at least 1 paging file and that we are system-managed */
756 ASSERT(SmpNumberOfPagingFiles >= 1);
757 ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList));
758 ASSERT(Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED); // Descriptor->SystemManaged == 1 in ASSERT.
759
760 /* Keep decreasing the pagefile by this amount if we run out of space */
761 FuzzFactor.QuadPart = FUZZ_FACTOR;
762
763 /* Create the descriptor for it (mainly the right sizes) and validate */
764 SmpMakeSystemManagedPagingFileDescriptor(Descriptor);
765 SmpValidatePagingFileSizes(Descriptor);
766
767 /* Use either the minimum size in the descriptor, or 16MB in minimal mode */
768 Size.QuadPart = DecreaseSize ? 16 * MEGABYTE : Descriptor->MinSize.QuadPart;
769
770 /* Check if this should be a fixed pagefile or an any pagefile*/
771 if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?')
772 {
773 /* Find a disk for it */
774 return SmpCreatePagingFileOnAnyDrive(Descriptor, &FuzzFactor, &Size);
775 }
776
777 /* Use the disk that was given */
778 return SmpCreatePagingFileOnFixedDrive(Descriptor, &FuzzFactor, &Size);
779 }
780
781 NTSTATUS
782 NTAPI
783 SmpCreateEmergencyPagingFile(VOID)
784 {
785 PSMP_PAGEFILE_DESCRIPTOR Descriptor;
786 WCHAR Buffer[32];
787
788 /* Allocate a descriptor */
789 Descriptor = RtlAllocateHeap(RtlGetProcessHeap(),
790 HEAP_ZERO_MEMORY,
791 sizeof(SMP_PAGEFILE_DESCRIPTOR));
792 if (!Descriptor) return STATUS_NO_MEMORY;
793
794 /* Initialize it */
795 RtlInitUnicodeString(&Descriptor->Token, NULL);
796
797 /* Copy the default pagefile name */
798 ASSERT(sizeof(Buffer) >= sizeof(STANDARD_PAGING_FILE_NAME));
799 wcscpy(Buffer, STANDARD_PAGING_FILE_NAME);
800
801 /* Fill the rest of the descriptor out */
802 RtlInitUnicodeString(&Descriptor->Name, Buffer);
803 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = '?';
804 Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED |
805 SMP_PAGEFILE_EMERGENCY |
806 SMP_PAGEFILE_ON_ANY_DRIVE;
807
808 /* Insert it into the descriptor list */
809 InsertHeadList(&SmpPagingFileDescriptorList, &Descriptor->Entry);
810 SmpNumberOfPagingFiles++;
811
812 /* Go ahead and create it now, with the minimal size possible */
813 return SmpCreateSystemManagedPagingFile(Descriptor, TRUE);
814 }
815
816 NTSTATUS
817 NTAPI
818 SmpCreateVolumeDescriptors(VOID)
819 {
820 NTSTATUS Status;
821 UNICODE_STRING VolumePath;
822 BOOLEAN BootVolumeFound = FALSE;
823 WCHAR StartChar, Drive, DriveDiff;
824 HANDLE VolumeHandle;
825 OBJECT_ATTRIBUTES ObjectAttributes;
826 IO_STATUS_BLOCK IoStatusBlock;
827 PROCESS_DEVICEMAP_INFORMATION ProcessInformation;
828 FILE_FS_DEVICE_INFORMATION DeviceInfo;
829 FILE_FS_SIZE_INFORMATION SizeInfo;
830 PSMP_VOLUME_DESCRIPTOR Volume;
831 LARGE_INTEGER FreeSpace, FinalFreeSpace;
832 WCHAR Buffer[32];
833
834 /* We should be starting with an empty list */
835 ASSERT(IsListEmpty(&SmpVolumeDescriptorList));
836
837 /* Query the device map so we can get the drive letters */
838 Status = NtQueryInformationProcess(NtCurrentProcess(),
839 ProcessDeviceMap,
840 &ProcessInformation.Query,
841 sizeof(ProcessInformation.Query),
842 NULL);
843 if (!NT_SUCCESS(Status))
844 {
845 DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X\n",
846 Status);
847 return Status;
848 }
849
850 /* Build the volume string, starting with A: (we'll edit this in place) */
851 wcscpy(Buffer, L"\\??\\A:\\");
852 RtlInitUnicodeString(&VolumePath, Buffer);
853
854 /* Start with the C drive except on weird Japanese NECs... */
855 StartChar = SharedUserData->AlternativeArchitecture ? L'A' : L'C';
856 for (Drive = StartChar, DriveDiff = StartChar - L'A'; Drive <= L'Z'; Drive++, DriveDiff++)
857 {
858 /* Skip the disk if it's not in the drive map */
859 if (!((1 << DriveDiff) & ProcessInformation.Query.DriveMap)) continue;
860
861 /* Write the drive letter and try to open the volume */
862 VolumePath.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Drive;
863 InitializeObjectAttributes(&ObjectAttributes,
864 &VolumePath,
865 OBJ_CASE_INSENSITIVE,
866 NULL,
867 NULL);
868 Status = NtOpenFile(&VolumeHandle,
869 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
870 &ObjectAttributes,
871 &IoStatusBlock,
872 FILE_SHARE_READ | FILE_SHARE_WRITE,
873 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
874 if (!NT_SUCCESS(Status))
875 {
876 /* Skip the volume if we failed */
877 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n",
878 &VolumePath, Status);
879 continue;
880 }
881
882 /* Now query device information on the volume */
883 Status = NtQueryVolumeInformationFile(VolumeHandle,
884 &IoStatusBlock,
885 &DeviceInfo,
886 sizeof(DeviceInfo),
887 FileFsDeviceInformation);
888 if (!NT_SUCCESS(Status))
889 {
890 /* Move to the next volume if we failed */
891 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for device info"
892 " failed with status %X\n",
893 &VolumePath,
894 VolumeHandle,
895 Status);
896 NtClose(VolumeHandle);
897 continue;
898 }
899
900 /* Check if this is a fixed disk */
901 if (DeviceInfo.Characteristics & (FILE_FLOPPY_DISKETTE |
902 FILE_READ_ONLY_DEVICE |
903 FILE_REMOTE_DEVICE |
904 FILE_REMOVABLE_MEDIA))
905 {
906 /* It isn't, so skip it */
907 DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file\n",
908 &VolumePath,
909 DeviceInfo.Characteristics);
910 NtClose(VolumeHandle);
911 continue;
912 }
913
914 /* We found a fixed volume, allocate a descriptor for it */
915 Volume = RtlAllocateHeap(RtlGetProcessHeap(),
916 HEAP_ZERO_MEMORY,
917 sizeof(SMP_VOLUME_DESCRIPTOR));
918 if (!Volume)
919 {
920 /* Failed to allocate memory, try the next disk */
921 DPRINT1("SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes)\n",
922 sizeof(SMP_VOLUME_DESCRIPTOR));
923 NtClose(VolumeHandle);
924 continue;
925 }
926
927 /* Save the drive letter and device information */
928 Volume->DriveLetter = Drive;
929 Volume->DeviceInfo = DeviceInfo;
930
931 /* Check if this is the boot volume */
932 if (RtlUpcaseUnicodeChar(Drive) ==
933 RtlUpcaseUnicodeChar(SharedUserData->NtSystemRoot[0]))
934 {
935 /* Save it */
936 ASSERT(BootVolumeFound == FALSE);
937 Volume->Flags |= SMP_VOLUME_IS_BOOT;
938 BootVolumeFound = TRUE;
939 }
940
941 /* Now get size information on the volume */
942 Status = NtQueryVolumeInformationFile(VolumeHandle,
943 &IoStatusBlock,
944 &SizeInfo,
945 sizeof(SizeInfo),
946 FileFsSizeInformation);
947 if (!NT_SUCCESS(Status))
948 {
949 /* We failed -- keep going */
950 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
951 " with status %X\n",
952 &VolumePath,
953 VolumeHandle,
954 Status);
955 RtlFreeHeap(RtlGetProcessHeap(), 0, Volume);
956 NtClose(VolumeHandle);
957 continue;
958 }
959
960 /* Done querying volume information, close the handle */
961 NtClose(VolumeHandle);
962
963 /* Compute how much free space we have */
964 FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart *
965 SizeInfo.SectorsPerAllocationUnit;
966 FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector;
967
968 /* Check if there's less than 32MB free so we don't starve the disk */
969 if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
970 {
971 /* In this case, act as if there's no free space */
972 Volume->FreeSpace.QuadPart = 0;
973 }
974 else
975 {
976 /* Trim off 32MB to give the disk a bit of breathing room */
977 Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart -
978 MINIMUM_TO_KEEP_FREE;
979 }
980
981 /* All done, add this volume to our descriptor list */
982 InsertTailList(&SmpVolumeDescriptorList, &Volume->Entry);
983 Volume->Flags |= SMP_VOLUME_INSERTED;
984 DPRINT("SMSS:PFILE: Created volume descriptor for`%wZ'\n", &VolumePath);
985 }
986
987 /* We must've found at least the boot volume */
988 ASSERT(BootVolumeFound == TRUE);
989 ASSERT(!IsListEmpty(&SmpVolumeDescriptorList));
990 if (!IsListEmpty(&SmpVolumeDescriptorList)) return STATUS_SUCCESS;
991
992 /* Something is really messed up if we found no disks at all */
993 return STATUS_UNEXPECTED_IO_ERROR;
994 }
995
996 NTSTATUS
997 NTAPI
998 SmpCreatePagingFiles(VOID)
999 {
1000 NTSTATUS Status;
1001 PSMP_PAGEFILE_DESCRIPTOR Descriptor;
1002 LARGE_INTEGER Size, FuzzFactor;
1003 BOOLEAN Created = FALSE;
1004 PLIST_ENTRY NextEntry;
1005
1006 /* Check if no paging files were requested */
1007 if (!(SmpNumberOfPagingFiles) && !(SmpRegistrySpecifierPresent))
1008 {
1009 /* The list should be empty -- nothing to do */
1010 ASSERT(IsListEmpty(&SmpPagingFileDescriptorList));
1011 DPRINT1("SMSS:PFILE: No paging file was requested\n");
1012 return STATUS_SUCCESS;
1013 }
1014
1015 /* Initialize the volume descriptors so we can know what's available */
1016 Status = SmpCreateVolumeDescriptors();
1017 if (!NT_SUCCESS(Status))
1018 {
1019 /* We can't make decisions without this, so fail */
1020 DPRINT1("SMSS:PFILE: Failed to create volume descriptors (status %X)\n",
1021 Status);
1022 return Status;
1023 }
1024
1025 /* If we fail creating pagefiles, try to reduce by this much each time */
1026 FuzzFactor.QuadPart = FUZZ_FACTOR;
1027
1028 /* Loop the descriptor list */
1029 NextEntry = SmpPagingFileDescriptorList.Flink;
1030 while (NextEntry != &SmpPagingFileDescriptorList)
1031 {
1032 /* Check what kind of descriptor this is */
1033 Descriptor = CONTAINING_RECORD(NextEntry, SMP_PAGEFILE_DESCRIPTOR, Entry);
1034 if (Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED)
1035 {
1036 /* This is a system-managed descriptor. Create the correct file */
1037 DPRINT("SMSS:PFILE: Creating a system managed paging file (`%wZ')\n",
1038 &Descriptor->Name);
1039 Status = SmpCreateSystemManagedPagingFile(Descriptor, FALSE);
1040 if (!NT_SUCCESS(Status))
1041 {
1042 /* We failed -- try again, with size minimization this time */
1043 DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1044 &Descriptor->Name);
1045 Status = SmpCreateSystemManagedPagingFile(Descriptor, TRUE);
1046 }
1047 }
1048 else
1049 {
1050 /* This is a manually entered descriptor. Validate its size first */
1051 SmpValidatePagingFileSizes(Descriptor);
1052
1053 /* Check if this is an ANY pagefile or a FIXED pagefile */
1054 DPRINT("SMSS:PFILE: Creating a normal paging file (`%wZ')\n",
1055 &Descriptor->Name);
1056 if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?')
1057 {
1058 /* It's an any pagefile, try to create it wherever possible */
1059 Size = Descriptor->MinSize;
1060 Status = SmpCreatePagingFileOnAnyDrive(Descriptor,
1061 &FuzzFactor,
1062 &Size);
1063 if (!NT_SUCCESS(Status))
1064 {
1065 /* We failed to create it. Try again with a smaller size */
1066 DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1067 &Descriptor->Name);
1068 Size.QuadPart = 16 * MEGABYTE;
1069 Status = SmpCreatePagingFileOnAnyDrive(Descriptor,
1070 &FuzzFactor,
1071 &Size);
1072 }
1073 }
1074 else
1075 {
1076 /* It's a fixed pagefile: override the minimum and use ours */
1077 Size.QuadPart = 16 * MEGABYTE;
1078 Status = SmpCreatePagingFileOnFixedDrive(Descriptor,
1079 &FuzzFactor,
1080 &Size);
1081 }
1082 }
1083
1084 /* Go to the next descriptor */
1085 if (NT_SUCCESS(Status)) Created = TRUE;
1086 NextEntry = NextEntry->Flink;
1087 }
1088
1089 /* Check if none of the code in our loops above was able to create it */
1090 if (!Created)
1091 {
1092 /* Build an emergency pagefile ourselves */
1093 DPRINT1("SMSS:PFILE: Creating emergency paging file.\n");
1094 Status = SmpCreateEmergencyPagingFile();
1095 }
1096
1097 /* All done */
1098 return Status;
1099 }