Sync trunk.
[reactos.git] / base / system / smss / initpage.c
1 /*
2 * PROJECT: ReactOS Session Manager
3 * LICENSE: GPL v2 or later - See COPYING in the top level directory
4 * FILE: base/system/smss/initpage.c
5 * PURPOSE: Paging file support.
6 * PROGRAMMERS: ReactOS Development Team
7 */
8
9 /* INCLUDES ******************************************************************/
10 #include "smss.h"
11
12 #define NDEBUG
13 #include <debug.h>
14
15 #define GIGABYTE (1024 * 1024 * 1024) /* One Gigabyte */
16
17 static NTSTATUS NTAPI
18 SmpPagingFilesQueryRoutine(PWSTR ValueName,
19 ULONG ValueType,
20 PVOID ValueData,
21 ULONG ValueLength,
22 PVOID Context,
23 PVOID EntryContext)
24 {
25 UNICODE_STRING FileName;
26 LARGE_INTEGER InitialSize = {{0, 0}};
27 LARGE_INTEGER MaximumSize = {{0, 0}};
28 NTSTATUS Status = STATUS_SUCCESS;
29 PWSTR p, ValueString = (PWSTR)ValueData;
30 WCHAR RootDriveLetter[5] = {0};
31
32 if (ValueLength > 3 * sizeof(WCHAR) &&
33 (ValueLength % sizeof(WCHAR) != 0 ||
34 ValueString[(ValueLength / sizeof(WCHAR)) - 1] != L'\0'))
35 {
36 return STATUS_INVALID_PARAMETER;
37 }
38
39 if (ValueType != REG_SZ)
40 {
41 return STATUS_INVALID_PARAMETER_2;
42 }
43
44 /*
45 * Format: "<path>[ <initial_size>[ <maximum_size>]]"
46 */
47 if ((p = wcschr(ValueString, L' ')) != NULL)
48 {
49 *p = L'\0';
50 InitialSize.QuadPart = wcstoul(p + 1, &p, 0) * 256 * 4096;
51 if (*p == ' ')
52 {
53 MaximumSize.QuadPart = wcstoul(p + 1, NULL, 0) * 256 * 4096;
54 }
55 else
56 {
57 MaximumSize = InitialSize;
58 }
59 }
60
61 if (!RtlDosPathNameToNtPathName_U (ValueString,
62 &FileName,
63 NULL,
64 NULL))
65 {
66 return STATUS_OBJECT_PATH_INVALID;
67 }
68
69 /* If there is only a file name or if initial and max are both 0
70 * the system will pick the sizes. Then it makes intial the size of phyical memory
71 * and makes max the size of 1.5 * initial. If there isnt enough free space then it will
72 * fall back to intial 20% of free space and max 25%. There is a max of 1 gig before
73 * it doesnt make it bigger. */
74 if ((InitialSize.QuadPart == 0 && MaximumSize.QuadPart == 0) || p == NULL)
75 {
76 FILE_FS_SIZE_INFORMATION FileFsSize;
77 IO_STATUS_BLOCK IoStatusBlock;
78 HANDLE hFile;
79 SYSTEM_BASIC_INFORMATION SysBasicInfo;
80 UNICODE_STRING NtPathU;
81 LARGE_INTEGER FreeBytes = {{0, 0}};
82 OBJECT_ATTRIBUTES ObjectAttributes;
83
84 DPRINT("System managed pagefile...\n");
85 /* Make sure the path that is given for the file actually has the drive in it.
86 At this point if there is not file name, no sizes will be set therefore no page
87 file will be created */
88 if (wcslen(ValueString) <= 3 ||
89 ValueString[1] != L':' ||
90 ValueString[2] != L'\\')
91 {
92 DPRINT1("Invalid path for pagefile.\n");
93 goto Cleanup;
94 }
95
96 Status = NtQuerySystemInformation(SystemBasicInformation,
97 &SysBasicInfo,
98 sizeof(SysBasicInfo),
99 NULL);
100 if (!NT_SUCCESS(Status))
101 {
102 DPRINT1("Could not query for physical memory size.\n");
103 goto Cleanup;
104 }
105 DPRINT("PageSize: %d, PhysicalPages: %d, TotalMem: %d\n", SysBasicInfo.PageSize, SysBasicInfo.NumberOfPhysicalPages, (SysBasicInfo.NumberOfPhysicalPages * SysBasicInfo.PageSize) / 1024);
106
107 InitialSize.QuadPart = SysBasicInfo.NumberOfPhysicalPages *
108 SysBasicInfo.PageSize;
109 MaximumSize.QuadPart = InitialSize.QuadPart * 2;
110
111 DPRINT("InitialSize: %I64d PhysicalPages: %lu PageSize: %lu\n",InitialSize.QuadPart,SysBasicInfo.NumberOfPhysicalPages,SysBasicInfo.PageSize);
112
113 /* copy the drive letter, the colon and the slash,
114 tack a null on the end */
115 RootDriveLetter[0] = ValueString[0];
116 RootDriveLetter[1] = L':';
117 RootDriveLetter[2] = L'\\';
118 RootDriveLetter[3] = L'\0';
119 DPRINT("Root drive X:\\...\"%S\"\n",RootDriveLetter);
120
121 if (!RtlDosPathNameToNtPathName_U(RootDriveLetter,
122 &NtPathU,
123 NULL,
124 NULL))
125 {
126 DPRINT1("Invalid path to root of drive\n");
127 Status = STATUS_OBJECT_PATH_INVALID;
128 goto Cleanup;
129 }
130
131 InitializeObjectAttributes(&ObjectAttributes,
132 &NtPathU,
133 OBJ_CASE_INSENSITIVE,
134 NULL,
135 NULL);
136
137 /* Get a handle to the root to find the free space on the drive */
138 Status = NtCreateFile(&hFile,
139 0,
140 &ObjectAttributes,
141 &IoStatusBlock,
142 NULL,
143 0,
144 FILE_SHARE_READ | FILE_SHARE_WRITE,
145 FILE_OPEN,
146 0,
147 NULL,
148 0);
149
150 RtlFreeHeap(RtlGetProcessHeap(),
151 0,
152 NtPathU.Buffer);
153
154 if (!NT_SUCCESS(Status))
155 {
156 DPRINT1("Could not open a handle to the volume.\n");
157 goto Cleanup;
158 }
159
160 Status = NtQueryVolumeInformationFile(hFile,
161 &IoStatusBlock,
162 &FileFsSize,
163 sizeof(FILE_FS_SIZE_INFORMATION),
164 FileFsSizeInformation);
165
166 NtClose(hFile);
167
168 if (!NT_SUCCESS(Status))
169 {
170 DPRINT1("Querying the volume free space failed!\n");
171 goto Cleanup;
172 }
173
174 FreeBytes.QuadPart = FileFsSize.BytesPerSector *
175 FileFsSize.SectorsPerAllocationUnit *
176 FileFsSize.AvailableAllocationUnits.QuadPart;
177
178 DPRINT("Free bytes: %I64d Inital Size based on memory: %I64d \n",FreeBytes.QuadPart,InitialSize.QuadPart);
179
180
181 if (InitialSize.QuadPart > (FreeBytes.QuadPart / 4) || InitialSize.QuadPart == 0)
182 {
183 DPRINT("Inital Size took more then 25%% of free space\n");
184 /* Set by percentage of free space
185 * intial is 20%, and max is 25% */
186 InitialSize.QuadPart = FreeBytes.QuadPart / 5;
187 MaximumSize.QuadPart = FreeBytes.QuadPart / 4;
188 /* The page file is more then a gig, size it down */
189 if (InitialSize.QuadPart > GIGABYTE)
190 {
191 InitialSize.QuadPart = GIGABYTE;
192 MaximumSize.QuadPart = GIGABYTE * 1.5;
193 }
194 }
195
196
197 }
198
199 /* Make sure that max is not smaller then initial */
200 if (InitialSize.QuadPart > MaximumSize.QuadPart)
201 {
202 DPRINT("Max page file size was bigger then inital.\n");
203 MaximumSize.QuadPart = InitialSize.QuadPart;
204 }
205
206 DPRINT("Creating paging file %wZ with size %I64d KB\n",
207 &FileName, InitialSize.QuadPart / 1024);
208
209 Status = NtCreatePagingFile(&FileName,
210 &InitialSize,
211 &MaximumSize,
212 0);
213 if (! NT_SUCCESS(Status))
214 {
215 PrintString("Creation of paging file %wZ with size %I64d KB failed (status 0x%x)\n",
216 &FileName, InitialSize.QuadPart / 1024, Status);
217 }
218
219 Cleanup:
220 RtlFreeHeap(RtlGetProcessHeap(),
221 0,
222 FileName.Buffer);
223
224 return STATUS_SUCCESS;
225 }
226
227
228 static NTSTATUS
229 SmpGetFreeDiskSpace(IN PWSTR PageFileName,
230 OUT PLARGE_INTEGER FreeDiskSpaceInMB)
231 {
232 FILE_FS_SIZE_INFORMATION FileFsSize;
233 IO_STATUS_BLOCK IoStatusBlock;
234 HANDLE hFile;
235 UNICODE_STRING NtPathU;
236 LARGE_INTEGER FreeBytes;
237 OBJECT_ATTRIBUTES ObjectAttributes;
238 WCHAR RootPath[5];
239 NTSTATUS Status;
240
241 /*
242 * copy the drive letter, the colon and the slash,
243 * tack a null on the end
244 */
245 RootPath[0] = PageFileName[0];
246 RootPath[1] = L':';
247 RootPath[2] = L'\\';
248 RootPath[3] = L'\0';
249
250 DPRINT("Root drive X:\\...\"%S\"\n",RootPath);
251
252 if (!RtlDosPathNameToNtPathName_U(RootPath,
253 &NtPathU,
254 NULL,
255 NULL))
256 {
257 DPRINT1("Invalid path to root of drive\n");
258 return STATUS_OBJECT_PATH_INVALID;
259 }
260
261 InitializeObjectAttributes(&ObjectAttributes,
262 &NtPathU,
263 OBJ_CASE_INSENSITIVE,
264 NULL,
265 NULL);
266
267 /* Get a handle to the root to find the free space on the drive */
268 Status = NtCreateFile(&hFile,
269 0,
270 &ObjectAttributes,
271 &IoStatusBlock,
272 NULL,
273 0,
274 FILE_SHARE_READ | FILE_SHARE_WRITE,
275 FILE_OPEN,
276 0,
277 NULL,
278 0);
279
280 RtlFreeHeap(RtlGetProcessHeap(),
281 0,
282 NtPathU.Buffer);
283
284 if (!NT_SUCCESS(Status))
285 {
286 DPRINT1("Could not open a handle to the volume.\n");
287 return Status;
288 }
289
290 Status = NtQueryVolumeInformationFile(hFile,
291 &IoStatusBlock,
292 &FileFsSize,
293 sizeof(FILE_FS_SIZE_INFORMATION),
294 FileFsSizeInformation);
295
296 NtClose(hFile);
297
298 if (!NT_SUCCESS(Status))
299 {
300 DPRINT1("Querying the volume free space failed!\n");
301 return Status;
302 }
303
304 FreeBytes.QuadPart = FileFsSize.BytesPerSector *
305 FileFsSize.SectorsPerAllocationUnit *
306 FileFsSize.AvailableAllocationUnits.QuadPart;
307
308 FreeDiskSpaceInMB->QuadPart = FreeBytes.QuadPart >> 20;
309
310 return STATUS_SUCCESS;
311 }
312
313
314 static NTSTATUS
315 SmpSetDefaultPageFileData(IN PWSTR PageFileName,
316 IN PLARGE_INTEGER InitialSizeInMB,
317 IN PLARGE_INTEGER MaximumSizeInMB)
318 {
319 WCHAR ValueString[MAX_PATH * 2];
320 ULONG ValueLength;
321
322 /* Format the value string */
323 swprintf(ValueString,
324 L"%s %I64u %I64u",
325 PageFileName,
326 InitialSizeInMB->QuadPart,
327 MaximumSizeInMB->QuadPart);
328
329 /*
330 * Append another zero character because it is a multi string value
331 * (REG_MULTI_SZ) and calculate the total string length.
332 */
333 ValueLength = wcslen(ValueString) + 1;
334 ValueString[ValueLength] = 0;
335 ValueLength++;
336
337 /* Write the page file data */
338 return RtlWriteRegistryValue(RTL_REGISTRY_CONTROL,
339 L"\\Session Manager\\Memory Management",
340 L"PagingFiles",
341 REG_MULTI_SZ,
342 ValueString,
343 ValueLength * sizeof(WCHAR));
344 }
345
346
347 static NTSTATUS
348 SmpCreatePageFile(IN PWSTR PageFileName,
349 IN PLARGE_INTEGER InitialSizeInMB,
350 IN PLARGE_INTEGER MaximumSizeInMB)
351 {
352 LARGE_INTEGER InitialSize;
353 LARGE_INTEGER MaximumSize;
354 UNICODE_STRING FileName;
355 NTSTATUS Status;
356
357 /* Get the NT path name of the page file */
358 if (!RtlDosPathNameToNtPathName_U(PageFileName,
359 &FileName,
360 NULL,
361 NULL))
362 {
363 return STATUS_OBJECT_PATH_INVALID;
364 }
365
366 /* Convert sizes in megabytes to sizes in bytes */
367 InitialSize.QuadPart = InitialSizeInMB->QuadPart << 20;
368 MaximumSize.QuadPart = MaximumSizeInMB->QuadPart << 20;
369
370 /* Create the pageing file */
371 Status = NtCreatePagingFile(&FileName,
372 &InitialSize,
373 &MaximumSize,
374 0);
375 if (! NT_SUCCESS(Status))
376 {
377 DPRINT("Creation of paging file %wZ with size %I64d MB failed (status 0x%x)\n",
378 &FileName, InitialSizeInMB->QuadPart, Status);
379 }
380
381 /* Release the file name */
382 RtlFreeHeap(RtlGetProcessHeap(),
383 0,
384 FileName.Buffer);
385
386 return Status;
387 }
388
389
390 static NTSTATUS
391 SmpCreateDefaultPagingFile(VOID)
392 {
393 SYSTEM_BASIC_INFORMATION SysBasicInfo;
394 LARGE_INTEGER MemorySizeInMB;
395 LARGE_INTEGER FreeDiskSpaceInMB;
396 LARGE_INTEGER InitialSizeInMB;
397 LARGE_INTEGER MaximumSizeInMB;
398 NTSTATUS Status = STATUS_SUCCESS;
399 WCHAR PageFileName[MAX_PATH];
400
401 DPRINT("Creating a default paging file\n");
402
403 Status = NtQuerySystemInformation(SystemBasicInformation,
404 &SysBasicInfo,
405 sizeof(SysBasicInfo),
406 NULL);
407 if (!NT_SUCCESS(Status))
408 {
409 DPRINT1("Could not query for physical memory size.\n");
410 return Status;
411 }
412
413 DPRINT("PageSize: %d, PhysicalPages: %d, TotalMem: %d\n",
414 SysBasicInfo.PageSize,
415 SysBasicInfo.NumberOfPhysicalPages,
416 (SysBasicInfo.NumberOfPhysicalPages * SysBasicInfo.PageSize) / 1024);
417
418 MemorySizeInMB.QuadPart = (SysBasicInfo.NumberOfPhysicalPages * SysBasicInfo.PageSize) >> 20;
419
420 DPRINT("MemorySize %I64u MB\n",
421 MemorySizeInMB.QuadPart);
422
423 /* Build the default page file name */
424 PageFileName[0] = SharedUserData->NtSystemRoot[0];
425 PageFileName[1] = 0;
426 wcscat(PageFileName, L":\\pagefile.sys");
427
428 Status = SmpGetFreeDiskSpace(PageFileName,
429 &FreeDiskSpaceInMB);
430 if (!NT_SUCCESS(Status))
431 {
432 return Status;
433 }
434
435 DPRINT("FreeDiskSpace %I64u MB\n",
436 FreeDiskSpaceInMB.QuadPart);
437
438 InitialSizeInMB.QuadPart = MemorySizeInMB.QuadPart + (MemorySizeInMB.QuadPart / 2);
439 MaximumSizeInMB.QuadPart = InitialSizeInMB.QuadPart * 2;
440
441 if (InitialSizeInMB.QuadPart > (FreeDiskSpaceInMB.QuadPart / 4))
442 {
443 DPRINT("Inital Size took more then 25%% of free disk space\n");
444
445 /*
446 * Set by percentage of free space
447 * intial is 20%, and max is 25%
448 */
449 InitialSizeInMB.QuadPart = FreeDiskSpaceInMB.QuadPart / 5;
450 MaximumSizeInMB.QuadPart = FreeDiskSpaceInMB.QuadPart / 4;
451
452 /* The page file is more then a gig, size it down */
453 if (InitialSizeInMB.QuadPart > 1024)
454 {
455 InitialSizeInMB.QuadPart = 1024; /* 1GB */
456 MaximumSizeInMB.QuadPart = 1536; /* 1.5GB */
457 }
458 }
459
460 DPRINT("InitialSize %I64u MB MaximumSize %I64u MB\n",
461 InitialSizeInMB.QuadPart,
462 MaximumSizeInMB.QuadPart);
463
464 Status = SmpSetDefaultPageFileData(PageFileName,
465 &InitialSizeInMB,
466 &MaximumSizeInMB);
467 if (!NT_SUCCESS(Status))
468 {
469 return Status;
470 }
471
472 return SmpCreatePageFile(PageFileName,
473 &InitialSizeInMB,
474 &MaximumSizeInMB);
475 }
476
477
478 NTSTATUS
479 SmCreatePagingFiles(VOID)
480 {
481 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
482 NTSTATUS Status;
483
484 DPRINT("creating system paging files\n");
485
486 /* Disable paging file on MiniNT/Live CD. */
487 if (RtlCheckRegistryKey(RTL_REGISTRY_CONTROL, L"MiniNT") == STATUS_SUCCESS)
488 {
489 return STATUS_SUCCESS;
490 }
491
492 RtlZeroMemory(&QueryTable,
493 sizeof(QueryTable));
494
495 QueryTable[0].Name = L"PagingFiles";
496 QueryTable[0].QueryRoutine = SmpPagingFilesQueryRoutine;
497 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
498
499 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
500 L"\\Session Manager\\Memory Management",
501 QueryTable,
502 NULL,
503 NULL);
504 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
505 {
506 Status = SmpCreateDefaultPagingFile();
507 }
508
509 return Status;
510 }
511
512
513 /* EOF */