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