Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpagefile.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Windows-Compatible Session Manager 00003 * LICENSE: BSD 2-Clause License 00004 * FILE: base/system/smss/smss.c 00005 * PURPOSE: Main SMSS Code 00006 * PROGRAMMERS: Alex Ionescu 00007 */ 00008 00009 /* INCLUDES *******************************************************************/ 00010 00011 #include "smss.h" 00012 #define NDEBUG 00013 #include "debug.h" 00014 00015 /* GLOBALS ********************************************************************/ 00016 00017 // 00018 // Constants 00019 // 00020 #define STANDARD_PAGING_FILE_NAME L"\\??\\?:\\pagefile.sys" 00021 #define STANDARD_DRIVE_LETTER_OFFSET 4 00022 #define MEGABYTE 0x100000UL 00023 #define MAXIMUM_PAGEFILE_SIZE (4095 * MEGABYTE) 00024 /* This should be 32 MB, but we need more than that for 2nd stage setup */ 00025 #define MINIMUM_TO_KEEP_FREE (64 * MEGABYTE) 00026 #define FUZZ_FACTOR (16 * MEGABYTE) 00027 00028 // 00029 // Structure and flags describing each pagefile 00030 // 00031 #define SMP_PAGEFILE_CREATED 0x01 00032 #define SMP_PAGEFILE_DEFAULT 0x02 00033 #define SMP_PAGEFILE_SYSTEM_MANAGED 0x04 00034 #define SMP_PAGEFILE_WAS_TOO_BIG 0x08 00035 #define SMP_PAGEFILE_ON_ANY_DRIVE 0x10 00036 #define SMP_PAGEFILE_EMERGENCY 0x20 00037 #define SMP_PAGEFILE_DUMP_PROCESSED 0x40 00038 typedef struct _SMP_PAGEFILE_DESCRIPTOR 00039 { 00040 LIST_ENTRY Entry; 00041 UNICODE_STRING Name; 00042 UNICODE_STRING Token; 00043 LARGE_INTEGER MinSize; 00044 LARGE_INTEGER MaxSize; 00045 LARGE_INTEGER ActualMinSize; 00046 LARGE_INTEGER ActualMaxSize; 00047 ULONG Flags; 00048 } SMP_PAGEFILE_DESCRIPTOR, *PSMP_PAGEFILE_DESCRIPTOR; 00049 00050 // 00051 // Structure and flags describing each volume 00052 // 00053 #define SMP_VOLUME_INSERTED 0x01 00054 #define SMP_VOLUME_PAGEFILE_CREATED 0x04 00055 #define SMP_VOLUME_IS_BOOT 0x08 00056 typedef struct _SMP_VOLUME_DESCRIPTOR 00057 { 00058 LIST_ENTRY Entry; 00059 USHORT Flags; 00060 USHORT PageFileCount; 00061 WCHAR DriveLetter; 00062 LARGE_INTEGER FreeSpace; 00063 FILE_FS_DEVICE_INFORMATION DeviceInfo; 00064 } SMP_VOLUME_DESCRIPTOR, *PSMP_VOLUME_DESCRIPTOR; 00065 00066 LIST_ENTRY SmpPagingFileDescriptorList, SmpVolumeDescriptorList; 00067 BOOLEAN SmpRegistrySpecifierPresent; 00068 ULONG SmpNumberOfPagingFiles; 00069 00070 /* FUNCTIONS ******************************************************************/ 00071 00072 VOID 00073 NTAPI 00074 SmpPagingFileInitialize(VOID) 00075 { 00076 /* Initialize the two lists */ 00077 InitializeListHead(&SmpPagingFileDescriptorList); 00078 InitializeListHead(&SmpVolumeDescriptorList); 00079 } 00080 00081 NTSTATUS 00082 NTAPI 00083 SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken) 00084 { 00085 NTSTATUS Status; 00086 ULONG MinSize = 0, MaxSize = 0; 00087 BOOLEAN SystemManaged = FALSE, ZeroSize = TRUE; 00088 PSMP_PAGEFILE_DESCRIPTOR Descriptor, ListDescriptor; 00089 ULONG i; 00090 WCHAR c; 00091 PLIST_ENTRY NextEntry; 00092 UNICODE_STRING PageFileName, Arguments, SecondArgument; 00093 00094 /* Make sure we don't have too many */ 00095 if (SmpNumberOfPagingFiles >= 16) 00096 { 00097 DPRINT1("SMSS:PFILE: Too many paging files specified - %d\n", 00098 SmpNumberOfPagingFiles); 00099 return STATUS_TOO_MANY_PAGING_FILES; 00100 } 00101 00102 /* Parse the specified and get the name and arguments out of it */ 00103 DPRINT("SMSS:PFILE: Paging file specifier `%wZ' \n", PageFileToken); 00104 Status = SmpParseCommandLine(PageFileToken, 00105 NULL, 00106 &PageFileName, 00107 NULL, 00108 &Arguments); 00109 if (!NT_SUCCESS(Status)) 00110 { 00111 /* Fail */ 00112 DPRINT1("SMSS:PFILE: SmpParseCommandLine(%wZ) failed with status %X \n", 00113 PageFileToken, Status); 00114 return Status; 00115 } 00116 00117 /* Set the variable to let everyone know we have a pagefile token */ 00118 SmpRegistrySpecifierPresent = TRUE; 00119 00120 /* Parse the arguments, if any */ 00121 if (Arguments.Buffer) 00122 { 00123 /* Parse the pagefile size */ 00124 for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++) 00125 { 00126 /* Check if it's zero */ 00127 c = Arguments.Buffer[i]; 00128 if ((c != L' ') && (c != L'\t') && (c != L'0')) 00129 { 00130 /* It isn't, break out */ 00131 ZeroSize = FALSE; 00132 break; 00133 } 00134 } 00135 } 00136 00137 /* Was a pagefile not specified, or was it specified with no size? */ 00138 if (!(Arguments.Buffer) || (ZeroSize)) 00139 { 00140 /* In this case, the system will manage its size */ 00141 SystemManaged = TRUE; 00142 } 00143 else 00144 { 00145 /* We do have a size, so convert the arguments into a number */ 00146 Status = RtlUnicodeStringToInteger(&Arguments, 0, &MinSize); 00147 if (!NT_SUCCESS(Status)) 00148 { 00149 /* Fail */ 00150 RtlFreeUnicodeString(&PageFileName); 00151 RtlFreeUnicodeString(&Arguments); 00152 return Status; 00153 } 00154 00155 /* Now advance to the next argument */ 00156 for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++) 00157 { 00158 /* Found a space -- second argument must start here */ 00159 if (Arguments.Buffer[i] == L' ') 00160 { 00161 /* Use the rest of the arguments as a maximum size */ 00162 SecondArgument.Buffer = &Arguments.Buffer[i]; 00163 SecondArgument.Length = Arguments.Length - 00164 i * sizeof(WCHAR); 00165 SecondArgument.MaximumLength = Arguments.MaximumLength - 00166 i * sizeof(WCHAR); 00167 Status = RtlUnicodeStringToInteger(&SecondArgument, 0, &MaxSize); 00168 if (!NT_SUCCESS(Status)) 00169 { 00170 /* Fail */ 00171 RtlFreeUnicodeString(&PageFileName); 00172 RtlFreeUnicodeString(&Arguments); 00173 return Status; 00174 } 00175 00176 break; 00177 } 00178 } 00179 } 00180 00181 /* We are done parsing arguments */ 00182 RtlFreeUnicodeString(&Arguments); 00183 00184 /* Now we can allocate our descriptor */ 00185 Descriptor = RtlAllocateHeap(RtlGetProcessHeap(), 00186 HEAP_ZERO_MEMORY, 00187 sizeof(SMP_PAGEFILE_DESCRIPTOR)); 00188 if (!Descriptor) 00189 { 00190 /* Fail if we couldn't */ 00191 RtlFreeUnicodeString(&PageFileName); 00192 return STATUS_NO_MEMORY; 00193 } 00194 00195 /* Capture all our data into the descriptor */ 00196 Descriptor->Token = *PageFileToken; 00197 Descriptor->Name = PageFileName; 00198 Descriptor->MinSize.QuadPart = MinSize * MEGABYTE; 00199 Descriptor->MaxSize.QuadPart = MaxSize * MEGABYTE; 00200 if (SystemManaged) Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED; 00201 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = 00202 RtlUpcaseUnicodeChar(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]); 00203 if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?') 00204 { 00205 Descriptor->Flags |= SMP_PAGEFILE_ON_ANY_DRIVE; 00206 } 00207 00208 /* Now loop the existing descriptors */ 00209 NextEntry = SmpPagingFileDescriptorList.Flink; 00210 do 00211 { 00212 /* Are there none, or have we looped back to the beginning? */ 00213 if (NextEntry == &SmpPagingFileDescriptorList) 00214 { 00215 /* This means no duplicates exist, so insert our descriptor! */ 00216 InsertTailList(&SmpPagingFileDescriptorList, &Descriptor->Entry); 00217 SmpNumberOfPagingFiles++; 00218 DPRINT("SMSS:PFILE: Created descriptor for `%wZ' (`%wZ') \n", 00219 PageFileToken, &Descriptor->Name); 00220 return STATUS_SUCCESS; 00221 } 00222 00223 /* Keep going until we find a duplicate, unless we are in "any" mode */ 00224 ListDescriptor = CONTAINING_RECORD(NextEntry, SMP_PAGEFILE_DESCRIPTOR, Entry); 00225 NextEntry = NextEntry->Flink; 00226 } while (!(ListDescriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE) || 00227 !(Descriptor->Flags & SMP_PAGEFILE_ON_ANY_DRIVE)); 00228 00229 /* We found a duplicate, so skip this descriptor/pagefile and fail */ 00230 DPRINT1("SMSS:PFILE: Skipping duplicate specifier `%wZ' \n", PageFileToken); 00231 RtlFreeUnicodeString(&PageFileName); 00232 RtlFreeHeap(RtlGetProcessHeap(), 0, Descriptor); 00233 return STATUS_INVALID_PARAMETER; 00234 } 00235 00236 NTSTATUS 00237 NTAPI 00238 SmpGetPagingFileSize(IN PUNICODE_STRING FileName, 00239 OUT PLARGE_INTEGER Size) 00240 { 00241 NTSTATUS Status; 00242 OBJECT_ATTRIBUTES ObjectAttributes; 00243 IO_STATUS_BLOCK IoStatusBlock; 00244 HANDLE FileHandle; 00245 FILE_STANDARD_INFORMATION StandardInfo; 00246 00247 DPRINT("SMSS:PFILE: Trying to get size for `%wZ'\n", FileName); 00248 Size->QuadPart = 0; 00249 00250 InitializeObjectAttributes(&ObjectAttributes, 00251 FileName, 00252 OBJ_CASE_INSENSITIVE, 00253 NULL, 00254 NULL); 00255 Status = NtOpenFile(&FileHandle, 00256 FILE_READ_ATTRIBUTES | SYNCHRONIZE, 00257 &ObjectAttributes, 00258 &IoStatusBlock, 00259 FILE_SHARE_READ | FILE_SHARE_WRITE, 00260 FILE_SYNCHRONOUS_IO_NONALERT); 00261 if (!NT_SUCCESS(Status)) return Status; 00262 00263 Status = NtQueryInformationFile(FileHandle, 00264 &IoStatusBlock, 00265 &StandardInfo, 00266 sizeof(StandardInfo), 00267 FileStandardInformation); 00268 if (!NT_SUCCESS(Status)) 00269 { 00270 DPRINT1("SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X \n", 00271 FileName, Status); 00272 NtClose(FileHandle); 00273 return Status; 00274 } 00275 00276 NtClose(FileHandle); 00277 Size->QuadPart = StandardInfo.AllocationSize.QuadPart; 00278 return STATUS_SUCCESS; 00279 } 00280 00281 NTSTATUS 00282 NTAPI 00283 SmpDeletePagingFile(IN PUNICODE_STRING FileName) 00284 { 00285 NTSTATUS Status; 00286 OBJECT_ATTRIBUTES ObjectAttributes; 00287 IO_STATUS_BLOCK IoStatusBlock; 00288 HANDLE FileHandle; 00289 FILE_DISPOSITION_INFORMATION Disposition; 00290 00291 /* Open the page file */ 00292 InitializeObjectAttributes(&ObjectAttributes, 00293 FileName, 00294 OBJ_CASE_INSENSITIVE, 00295 NULL, 00296 NULL); 00297 Status = NtOpenFile(&FileHandle, 00298 DELETE, 00299 &ObjectAttributes, 00300 &IoStatusBlock, 00301 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 00302 FILE_NON_DIRECTORY_FILE); 00303 if (NT_SUCCESS(Status)) 00304 { 00305 /* Delete it */ 00306 Disposition.DeleteFile = TRUE; 00307 Status = NtSetInformationFile(FileHandle, 00308 &IoStatusBlock, 00309 &Disposition, 00310 sizeof(Disposition), 00311 FileDispositionInformation); 00312 if (!NT_SUCCESS(Status)) 00313 { 00314 DPRINT1("SMSS:PFILE: Failed to delete page file `%wZ' (status %X)\n", 00315 FileName, Status); 00316 } 00317 else 00318 { 00319 DPRINT1("SMSS:PFILE: Deleted stale paging file - %wZ\n", FileName); 00320 } 00321 00322 /* Close the handle */ 00323 NtClose(FileHandle); 00324 } 00325 else 00326 { 00327 DPRINT1("SMSS:PFILE: Failed to open for deletion page file `%wZ' (status %X)\n", 00328 FileName, Status); 00329 } 00330 00331 /* All done */ 00332 return Status; 00333 } 00334 00335 NTSTATUS 00336 NTAPI 00337 SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume) 00338 { 00339 NTSTATUS Status; 00340 LARGE_INTEGER FreeSpace, FinalFreeSpace; 00341 FILE_FS_SIZE_INFORMATION SizeInfo; 00342 IO_STATUS_BLOCK IoStatusBlock; 00343 OBJECT_ATTRIBUTES ObjectAttributes; 00344 UNICODE_STRING VolumeName; 00345 HANDLE VolumeHandle; 00346 WCHAR PathString[32]; 00347 ASSERT(Volume->Flags & SMP_VOLUME_IS_BOOT); // ASSERT says "BootVolume == 1" 00348 00349 /* Build the standard path */ 00350 wcscpy(PathString, L"\\??\\A:\\"); 00351 RtlInitUnicodeString(&VolumeName, PathString); 00352 VolumeName.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter; 00353 DPRINT("SMSS:PFILE: Querying volume `%wZ' for free space \n", &VolumeName); 00354 00355 /* Open the volume */ 00356 InitializeObjectAttributes(&ObjectAttributes, 00357 &VolumeName, 00358 OBJ_CASE_INSENSITIVE, 00359 NULL, 00360 NULL); 00361 Status = NtOpenFile(&VolumeHandle, 00362 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, 00363 &ObjectAttributes, 00364 &IoStatusBlock, 00365 FILE_SHARE_READ | FILE_SHARE_WRITE, 00366 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE); 00367 if (!NT_SUCCESS(Status)) 00368 { 00369 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X \n", &VolumeName, Status); 00370 return Status; 00371 } 00372 00373 /* Now get size information on the volume */ 00374 Status = NtQueryVolumeInformationFile(VolumeHandle, 00375 &IoStatusBlock, 00376 &SizeInfo, 00377 sizeof(SizeInfo), 00378 FileFsSizeInformation); 00379 if (!NT_SUCCESS(Status)) 00380 { 00381 /* We failed */ 00382 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed" 00383 " with status %X \n", 00384 &VolumeName, 00385 VolumeHandle, 00386 Status); 00387 NtClose(VolumeHandle); 00388 return Status; 00389 } 00390 NtClose(VolumeHandle); 00391 00392 /* Compute how much free space we have */ 00393 FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart * 00394 SizeInfo.SectorsPerAllocationUnit; 00395 FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector; 00396 00397 /* Check if there's less than 32MB free so we don't starve the disk */ 00398 if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE) 00399 { 00400 /* In this case, act as if there's no free space */ 00401 Volume->FreeSpace.QuadPart = 0; 00402 } 00403 else 00404 { 00405 /* Trim off 32MB to give the disk a bit of breathing room */ 00406 Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart - 00407 MINIMUM_TO_KEEP_FREE; 00408 } 00409 00410 return STATUS_SUCCESS; 00411 } 00412 00413 PSMP_VOLUME_DESCRIPTOR 00414 NTAPI 00415 SmpSearchVolumeDescriptor(IN WCHAR DriveLetter) 00416 { 00417 WCHAR UpLetter; 00418 PSMP_VOLUME_DESCRIPTOR Volume = NULL; 00419 PLIST_ENTRY NextEntry; 00420 00421 /* Use upper case to reduce differences */ 00422 UpLetter = RtlUpcaseUnicodeChar(DriveLetter); 00423 00424 /* Loop each volume */ 00425 NextEntry = SmpVolumeDescriptorList.Flink; 00426 while (NextEntry != &SmpVolumeDescriptorList) 00427 { 00428 /* Grab the entry */ 00429 Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry); 00430 00431 /* Make sure it's a valid entry with an uppcase drive letter */ 00432 ASSERT(Volume->Flags & SMP_VOLUME_INSERTED); // Volume->Initialized in ASSERT 00433 ASSERT(Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z'); 00434 00435 /* Break if it matches, if not, keep going */ 00436 if (Volume->DriveLetter == UpLetter) break; 00437 NextEntry = NextEntry->Flink; 00438 } 00439 00440 /* Return the volume if one was found */ 00441 if (NextEntry == &SmpVolumeDescriptorList) Volume = NULL; 00442 return Volume; 00443 } 00444 00445 NTSTATUS 00446 NTAPI 00447 SmpCreatePagingFile(IN PUNICODE_STRING Name, 00448 IN PLARGE_INTEGER MinSize, 00449 IN PLARGE_INTEGER MaxSize, 00450 IN ULONG Priority) 00451 { 00452 NTSTATUS Status; 00453 00454 /* Tell the kernel to create the pagefile */ 00455 Status = NtCreatePagingFile(Name, MinSize, MaxSize, Priority); 00456 if (NT_SUCCESS(Status)) 00457 { 00458 DPRINT("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) succeeded. \n", 00459 Name, 00460 MinSize->QuadPart, 00461 MaxSize->QuadPart); 00462 } 00463 else 00464 { 00465 DPRINT1("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) failed with %X \n", 00466 Name, 00467 MinSize->QuadPart, 00468 MaxSize->QuadPart, 00469 Status); 00470 } 00471 00472 /* Return the status */ 00473 return Status; 00474 } 00475 00476 NTSTATUS 00477 NTAPI 00478 SmpCreatePagingFileOnFixedDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor, 00479 IN PLARGE_INTEGER FuzzFactor, 00480 IN PLARGE_INTEGER MinimumSize) 00481 { 00482 PSMP_VOLUME_DESCRIPTOR Volume; 00483 BOOLEAN ShouldDelete; 00484 NTSTATUS Status; 00485 LARGE_INTEGER PageFileSize; 00486 ASSERT(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] != L'?'); 00487 00488 /* Try to find the volume descriptor for this drive letter */ 00489 ShouldDelete = FALSE; 00490 Volume = SmpSearchVolumeDescriptor(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]); 00491 if (!Volume) 00492 { 00493 /* Couldn't find it, fail */ 00494 DPRINT1("SMSS:PFILE: No volume descriptor for `%wZ' \n", 00495 &Descriptor->Name); 00496 return STATUS_INVALID_PARAMETER; 00497 } 00498 00499 /* Check if this is the boot volume */ 00500 if (Volume->Flags & SMP_VOLUME_IS_BOOT) 00501 { 00502 /* Check if we haven't yet processed a crash dump on this volume */ 00503 if (!(Descriptor->Flags & SMP_PAGEFILE_DUMP_PROCESSED)) 00504 { 00505 /* Try to find a crash dump and extract it */ 00506 DPRINT("SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume \n", 00507 &Descriptor->Name); 00508 SmpCheckForCrashDump(&Descriptor->Name); 00509 00510 /* Update how much free space we have now that we extracted a dump */ 00511 Status = SmpGetVolumeFreeSpace(Volume); 00512 if (!NT_SUCCESS(Status)) 00513 { 00514 DPRINT1("SMSS:PFILE: Failed to query free space for boot volume `%wC'\n", 00515 Volume->DriveLetter); 00516 } 00517 else 00518 { 00519 DPRINT("Queried free space for boot volume `%wC: %I64x'\n", 00520 Volume->DriveLetter, Volume->FreeSpace.QuadPart); 00521 } 00522 00523 /* Don't process crashdump on this volume anymore */ 00524 Descriptor->Flags |= SMP_PAGEFILE_DUMP_PROCESSED; 00525 } 00526 } 00527 else 00528 { 00529 /* Crashdumps can only be on the boot volume */ 00530 DPRINT("SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot" 00531 "volume `%wC' \n", 00532 &Descriptor->Name, 00533 Volume->DriveLetter); 00534 } 00535 00536 /* Update the size after dump extraction */ 00537 Descriptor->ActualMinSize = Descriptor->MinSize; 00538 Descriptor->ActualMaxSize = Descriptor->MaxSize; 00539 00540 /* Check how big we can make the pagefile */ 00541 Status = SmpGetPagingFileSize(&Descriptor->Name, &PageFileSize); 00542 if (NT_SUCCESS(Status) && PageFileSize.QuadPart > 0) ShouldDelete = TRUE; 00543 DPRINT("SMSS:PFILE: Detected size %I64X for future paging file `%wZ'\n", 00544 PageFileSize, 00545 &Descriptor->Name); 00546 DPRINT("SMSS:PFILE: Free space on volume `%wC' is %I64X \n", 00547 Volume->DriveLetter, 00548 Volume->FreeSpace.QuadPart); 00549 00550 /* Now update our size and make sure none of these are too big */ 00551 PageFileSize.QuadPart += Volume->FreeSpace.QuadPart; 00552 if (Descriptor->ActualMinSize.QuadPart > PageFileSize.QuadPart) 00553 { 00554 Descriptor->ActualMinSize = PageFileSize; 00555 } 00556 if (Descriptor->ActualMaxSize.QuadPart > PageFileSize.QuadPart) 00557 { 00558 Descriptor->ActualMaxSize = PageFileSize; 00559 } 00560 DPRINT("SMSS:PFILE: min %I64X, max %I64X, real min %I64X \n", 00561 Descriptor->ActualMinSize.QuadPart, 00562 Descriptor->ActualMaxSize.QuadPart, 00563 MinimumSize->QuadPart); 00564 00565 /* Keep going until we've created a pagefile of the right size */ 00566 while (Descriptor->ActualMinSize.QuadPart >= MinimumSize->QuadPart) 00567 { 00568 /* Call NT to do it */ 00569 Status = SmpCreatePagingFile(&Descriptor->Name, 00570 &Descriptor->ActualMinSize, 00571 &Descriptor->ActualMaxSize, 00572 0); 00573 if (NT_SUCCESS(Status)) 00574 { 00575 /* We're done, update flags and increase the count */ 00576 Descriptor->Flags |= SMP_PAGEFILE_CREATED; 00577 Volume->Flags |= SMP_VOLUME_PAGEFILE_CREATED; 00578 Volume->PageFileCount++; 00579 break; 00580 } 00581 00582 /* We failed, try a slighly smaller pagefile */ 00583 Descriptor->ActualMinSize.QuadPart -= FuzzFactor->QuadPart; 00584 } 00585 00586 /* Check if we weren't able to create it */ 00587 if (Descriptor->ActualMinSize.QuadPart < MinimumSize->QuadPart) 00588 { 00589 /* Delete the current page file and fail */ 00590 if (ShouldDelete) 00591 { 00592 SmpDeletePagingFile(&Descriptor->Name); 00593 00594 /* FIXFIX: Windows Vista does this, and it seems like we should too, so try to see if this fixes KVM */ 00595 Volume->FreeSpace.QuadPart = PageFileSize.QuadPart; 00596 } 00597 DPRINT1("SMSS:PFILE: Failing for min %I64X, max %I64X, real min %I64X \n", 00598 Descriptor->ActualMinSize.QuadPart, 00599 Descriptor->ActualMaxSize.QuadPart, 00600 MinimumSize->QuadPart); 00601 Status = STATUS_DISK_FULL; 00602 } 00603 00604 /* Return the status */ 00605 return Status; 00606 } 00607 00608 NTSTATUS 00609 NTAPI 00610 SmpCreatePagingFileOnAnyDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor, 00611 IN PLARGE_INTEGER FuzzFactor, 00612 IN PLARGE_INTEGER MinimumSize) 00613 { 00614 PSMP_VOLUME_DESCRIPTOR Volume; 00615 NTSTATUS Status = STATUS_DISK_FULL; 00616 PLIST_ENTRY NextEntry; 00617 ASSERT(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?'); 00618 00619 /* Loop the volume list */ 00620 NextEntry = SmpVolumeDescriptorList.Flink; 00621 while (NextEntry != &SmpVolumeDescriptorList) 00622 { 00623 /* Get the volume */ 00624 Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry); 00625 00626 /* Make sure it's inserted and on a valid drive letter */ 00627 ASSERT(Volume->Flags & SMP_VOLUME_INSERTED); // Volume->Initialized in ASSERT 00628 ASSERT(Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z'); 00629 00630 /* Write the drive letter to try creating it on this volume */ 00631 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter; 00632 Status = SmpCreatePagingFileOnFixedDrive(Descriptor, 00633 FuzzFactor, 00634 MinimumSize); 00635 if (NT_SUCCESS(Status)) break; 00636 00637 /* It didn't work, make it an any pagefile again and keep going */ 00638 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = L'?'; 00639 NextEntry = NextEntry->Flink; 00640 } 00641 00642 /* Return disk full or success */ 00643 return Status; 00644 } 00645 00646 VOID 00647 NTAPI 00648 SmpMakeDefaultPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor) 00649 { 00650 /* The default descriptor uses 128MB as a pagefile size */ 00651 Descriptor->Flags |= SMP_PAGEFILE_DEFAULT; 00652 Descriptor->MinSize.QuadPart = 128 * MEGABYTE; 00653 Descriptor->MaxSize.QuadPart = 128 * MEGABYTE; 00654 } 00655 00656 VOID 00657 NTAPI 00658 SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor) 00659 { 00660 NTSTATUS Status; 00661 LONGLONG MinimumSize, MaximumSize, Ram; 00662 SYSTEM_BASIC_INFORMATION BasicInfo; 00663 00664 /* Query the page size of the system, and the amount of RAM */ 00665 Status = NtQuerySystemInformation(SystemBasicInformation, 00666 &BasicInfo, 00667 sizeof(BasicInfo), 00668 NULL); 00669 if (!NT_SUCCESS(Status)) 00670 { 00671 /* If we failed, use defaults since we have no idea otherwise */ 00672 DPRINT1("SMSS:PFILE: NtQuerySystemInformation failed with %x \n", Status); 00673 SmpMakeDefaultPagingFileDescriptor(Descriptor); 00674 return; 00675 } 00676 00677 /* Chekc how much RAM we have and set three times this amount as maximum */ 00678 Ram = BasicInfo.NumberOfPhysicalPages * BasicInfo.PageSize; 00679 MaximumSize = 3 * Ram; 00680 00681 /* If we have more than 1GB, use that as minimum, otherwise, use 1.5X RAM */ 00682 MinimumSize = (Ram >= 1024 * MEGABYTE) ? Ram : MaximumSize / 2; 00683 00684 /* Write the new sizes in the descriptor and mark it as system managed */ 00685 Descriptor->MinSize.QuadPart = MinimumSize; 00686 Descriptor->MaxSize.QuadPart = MaximumSize; 00687 Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED; 00688 } 00689 00690 NTSTATUS 00691 NTAPI 00692 SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor) 00693 { 00694 NTSTATUS Status = STATUS_SUCCESS; 00695 ULONGLONG MinSize, MaxSize; 00696 BOOLEAN WasTooBig = FALSE; 00697 00698 /* Capture the min and max */ 00699 MinSize = Descriptor->MinSize.QuadPart; 00700 MaxSize = Descriptor->MaxSize.QuadPart; 00701 DPRINT("SMSS:PFILE: Validating sizes for `%wZ' %I64X %I64X\n", 00702 &Descriptor->Name, MinSize, MaxSize); 00703 00704 /* Don't let minimum be bigger than maximum */ 00705 if (MinSize > MaxSize) MaxSize = MinSize; 00706 00707 /* On PAE we can have bigger pagefiles... */ 00708 if (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED]) 00709 { 00710 /* But we don't support that yet */ 00711 DPRINT1("ReactOS does not support PAE yet... assuming sizes OK\n"); 00712 } 00713 else 00714 { 00715 /* Check if the minimum is more then 4095 MB */ 00716 if (MinSize > MAXIMUM_PAGEFILE_SIZE) 00717 { 00718 /* Trim it, this isn't allowed */ 00719 WasTooBig = TRUE; 00720 MinSize = MAXIMUM_PAGEFILE_SIZE; 00721 } 00722 00723 /* Check if the maximum is more then 4095 MB */ 00724 if (MaxSize > MAXIMUM_PAGEFILE_SIZE) 00725 { 00726 /* Trim it, this isn't allowed */ 00727 WasTooBig = TRUE; 00728 MaxSize = MAXIMUM_PAGEFILE_SIZE; 00729 } 00730 } 00731 00732 /* Did we trim? */ 00733 if (WasTooBig) 00734 { 00735 /* Notify debugger output and write a flag in the descriptor */ 00736 DPRINT("SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed \n", 00737 &Descriptor->Name); 00738 Descriptor->Flags |= SMP_PAGEFILE_WAS_TOO_BIG; 00739 } 00740 00741 /* Now write the (possibly trimmed) sizes back */ 00742 Descriptor->MinSize.QuadPart = MinSize; 00743 Descriptor->MaxSize.QuadPart = MaxSize; 00744 return Status; 00745 } 00746 00747 NTSTATUS 00748 NTAPI 00749 SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor, 00750 IN BOOLEAN DecreaseSize) 00751 { 00752 LARGE_INTEGER FuzzFactor, Size; 00753 00754 /* Make sure there's at least 1 paging file and that we are system-managed */ 00755 ASSERT(SmpNumberOfPagingFiles >= 1); 00756 ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList)); 00757 ASSERT(Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED); // Descriptor->SystemManaged == 1 in ASSERT. 00758 00759 /* Keep decreasing the pagefile by this amount if we run out of space */ 00760 FuzzFactor.QuadPart = FUZZ_FACTOR; 00761 00762 /* Create the descriptor for it (mainly the right sizes) and validate */ 00763 SmpMakeSystemManagedPagingFileDescriptor(Descriptor); 00764 SmpValidatePagingFileSizes(Descriptor); 00765 00766 /* Use either the minimum size in the descriptor, or 16MB in minimal mode */ 00767 Size.QuadPart = DecreaseSize ? 16 * MEGABYTE : Descriptor->MinSize.QuadPart; 00768 00769 /* Check if this should be a fixed pagefile or an any pagefile*/ 00770 if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?') 00771 { 00772 /* Find a disk for it */ 00773 return SmpCreatePagingFileOnAnyDrive(Descriptor, &FuzzFactor, &Size); 00774 } 00775 00776 /* Use the disk that was given */ 00777 return SmpCreatePagingFileOnFixedDrive(Descriptor, &FuzzFactor, &Size); 00778 } 00779 00780 NTSTATUS 00781 NTAPI 00782 SmpCreateEmergencyPagingFile(VOID) 00783 { 00784 PSMP_PAGEFILE_DESCRIPTOR Descriptor; 00785 WCHAR Buffer[32]; 00786 00787 /* Allocate a descriptor */ 00788 Descriptor = RtlAllocateHeap(RtlGetProcessHeap(), 00789 HEAP_ZERO_MEMORY, 00790 sizeof(SMP_PAGEFILE_DESCRIPTOR)); 00791 if (!Descriptor) return STATUS_NO_MEMORY; 00792 00793 /* Initialize it */ 00794 RtlInitUnicodeString(&Descriptor->Token, NULL); 00795 00796 /* Copy the default pagefile name */ 00797 ASSERT(sizeof(Buffer) >= sizeof(STANDARD_PAGING_FILE_NAME)); 00798 wcscpy(Buffer, STANDARD_PAGING_FILE_NAME); 00799 00800 /* Fill the rest of the descriptor out */ 00801 RtlInitUnicodeString(&Descriptor->Name, Buffer); 00802 Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = '?'; 00803 Descriptor->Flags |= SMP_PAGEFILE_SYSTEM_MANAGED | 00804 SMP_PAGEFILE_EMERGENCY | 00805 SMP_PAGEFILE_ON_ANY_DRIVE; 00806 00807 /* Insert it into the descriptor list */ 00808 InsertHeadList(&SmpPagingFileDescriptorList, &Descriptor->Entry); 00809 SmpNumberOfPagingFiles++; 00810 00811 /* Go ahead and create it now, with the minimal size possible */ 00812 return SmpCreateSystemManagedPagingFile(Descriptor, TRUE); 00813 } 00814 00815 NTSTATUS 00816 NTAPI 00817 SmpCreateVolumeDescriptors(VOID) 00818 { 00819 NTSTATUS Status; 00820 UNICODE_STRING VolumePath; 00821 BOOLEAN BootVolumeFound = FALSE; 00822 WCHAR StartChar, Drive, DriveDiff; 00823 HANDLE VolumeHandle; 00824 OBJECT_ATTRIBUTES ObjectAttributes; 00825 IO_STATUS_BLOCK IoStatusBlock; 00826 PROCESS_DEVICEMAP_INFORMATION ProcessInformation; 00827 FILE_FS_DEVICE_INFORMATION DeviceInfo; 00828 FILE_FS_SIZE_INFORMATION SizeInfo; 00829 PSMP_VOLUME_DESCRIPTOR Volume; 00830 LARGE_INTEGER FreeSpace, FinalFreeSpace; 00831 WCHAR Buffer[32]; 00832 00833 /* We should be starting with an empty list */ 00834 ASSERT(IsListEmpty(&SmpVolumeDescriptorList)); 00835 00836 /* Query the device map so we can get the drive letters */ 00837 Status = NtQueryInformationProcess(NtCurrentProcess(), 00838 ProcessDeviceMap, 00839 &ProcessInformation, 00840 sizeof(ProcessInformation), 00841 NULL); 00842 if (!NT_SUCCESS(Status)) 00843 { 00844 DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X \n", 00845 Status); 00846 return Status; 00847 } 00848 00849 /* Build the volume string, starting with A: (we'll edit this in place) */ 00850 wcscpy(Buffer, L"\\??\\A:\\"); 00851 RtlInitUnicodeString(&VolumePath, Buffer); 00852 00853 /* Start with the C drive except on weird Japanese NECs... */ 00854 StartChar = SharedUserData->AlternativeArchitecture ? L'A' : L'C'; 00855 for (Drive = StartChar, DriveDiff = StartChar - L'A'; Drive <= L'Z'; Drive++, DriveDiff++) 00856 { 00857 /* Skip the disk if it's not in the drive map */ 00858 if (!((1 << DriveDiff) & ProcessInformation.Query.DriveMap)) continue; 00859 00860 /* Write the drive letter and try to open the volume */ 00861 VolumePath.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Drive; 00862 InitializeObjectAttributes(&ObjectAttributes, 00863 &VolumePath, 00864 OBJ_CASE_INSENSITIVE, 00865 NULL, 00866 NULL); 00867 Status = NtOpenFile(&VolumeHandle, 00868 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, 00869 &ObjectAttributes, 00870 &IoStatusBlock, 00871 FILE_SHARE_READ | FILE_SHARE_WRITE, 00872 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE); 00873 if (!NT_SUCCESS(Status)) 00874 { 00875 /* Skip the volume if we failed */ 00876 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X \n", 00877 &VolumePath, Status); 00878 continue; 00879 } 00880 00881 /* Now query device information on the volume */ 00882 Status = NtQueryVolumeInformationFile(VolumeHandle, 00883 &IoStatusBlock, 00884 &DeviceInfo, 00885 sizeof(DeviceInfo), 00886 FileFsDeviceInformation); 00887 if (!NT_SUCCESS(Status)) 00888 { 00889 /* Move to the next volume if we failed */ 00890 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for device info" 00891 " failed with status %X \n", 00892 &VolumePath, 00893 VolumeHandle, 00894 Status); 00895 NtClose(VolumeHandle); 00896 continue; 00897 } 00898 00899 /* Check if this is a fixed disk */ 00900 if (DeviceInfo.Characteristics & (FILE_FLOPPY_DISKETTE | 00901 FILE_READ_ONLY_DEVICE | 00902 FILE_REMOTE_DEVICE | 00903 FILE_REMOVABLE_MEDIA)) 00904 { 00905 /* It isn't, so skip it */ 00906 DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file \n", 00907 &VolumePath, 00908 DeviceInfo.Characteristics); 00909 NtClose(VolumeHandle); 00910 continue; 00911 } 00912 00913 /* We found a fixed volume, allocate a descriptor for it */ 00914 Volume = RtlAllocateHeap(RtlGetProcessHeap(), 00915 HEAP_ZERO_MEMORY, 00916 sizeof(SMP_VOLUME_DESCRIPTOR)); 00917 if (!Volume) 00918 { 00919 /* Failed to allocate memory, try the next disk */ 00920 DPRINT1("SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes) \n", 00921 sizeof(SMP_VOLUME_DESCRIPTOR)); 00922 NtClose(VolumeHandle); 00923 continue; 00924 } 00925 00926 /* Save the drive letter and device information */ 00927 Volume->DriveLetter = Drive; 00928 Volume->DeviceInfo = DeviceInfo; 00929 00930 /* Check if this is the boot volume */ 00931 if (RtlUpcaseUnicodeChar(Drive) == 00932 RtlUpcaseUnicodeChar(SharedUserData->NtSystemRoot[0])) 00933 { 00934 /* Save it */ 00935 ASSERT(BootVolumeFound == FALSE); 00936 Volume->Flags |= SMP_VOLUME_IS_BOOT; 00937 BootVolumeFound = TRUE; 00938 } 00939 00940 /* Now get size information on the volume */ 00941 Status = NtQueryVolumeInformationFile(VolumeHandle, 00942 &IoStatusBlock, 00943 &SizeInfo, 00944 sizeof(SizeInfo), 00945 FileFsSizeInformation); 00946 if (!NT_SUCCESS(Status)) 00947 { 00948 /* We failed -- keep going */ 00949 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed" 00950 " with status %X \n", 00951 &VolumePath, 00952 VolumeHandle, 00953 Status); 00954 RtlFreeHeap(RtlGetProcessHeap(), 0, Volume); 00955 NtClose(VolumeHandle); 00956 continue; 00957 } 00958 00959 /* Done querying volume information, close the handle */ 00960 NtClose(VolumeHandle); 00961 00962 /* Compute how much free space we have */ 00963 FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart * 00964 SizeInfo.SectorsPerAllocationUnit; 00965 FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector; 00966 00967 /* Check if there's less than 32MB free so we don't starve the disk */ 00968 if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE) 00969 { 00970 /* In this case, act as if there's no free space */ 00971 Volume->FreeSpace.QuadPart = 0; 00972 } 00973 else 00974 { 00975 /* Trim off 32MB to give the disk a bit of breathing room */ 00976 Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart - 00977 MINIMUM_TO_KEEP_FREE; 00978 } 00979 00980 /* All done, add this volume to our descriptor list */ 00981 InsertTailList(&SmpVolumeDescriptorList, &Volume->Entry); 00982 Volume->Flags |= SMP_VOLUME_INSERTED; 00983 DPRINT("SMSS:PFILE: Created volume descriptor for`%wZ' \n", &VolumePath); 00984 } 00985 00986 /* We must've found at least the boot volume */ 00987 ASSERT(BootVolumeFound == TRUE); 00988 ASSERT(!IsListEmpty(&SmpVolumeDescriptorList)); 00989 if (!IsListEmpty(&SmpVolumeDescriptorList)) return STATUS_SUCCESS; 00990 00991 /* Something is really messed up if we found no disks at all */ 00992 return STATUS_UNEXPECTED_IO_ERROR; 00993 } 00994 00995 NTSTATUS 00996 NTAPI 00997 SmpCreatePagingFiles(VOID) 00998 { 00999 NTSTATUS Status; 01000 PSMP_PAGEFILE_DESCRIPTOR Descriptor; 01001 LARGE_INTEGER Size, FuzzFactor; 01002 BOOLEAN Created = FALSE; 01003 PLIST_ENTRY NextEntry; 01004 01005 /* Check if no paging files were requested */ 01006 if (!(SmpNumberOfPagingFiles) && !(SmpRegistrySpecifierPresent)) 01007 { 01008 /* The list should be empty -- nothign to do */ 01009 ASSERT(IsListEmpty(&SmpPagingFileDescriptorList)); 01010 DPRINT1("SMSS:PFILE: No paging file was requested \n"); 01011 return STATUS_SUCCESS; 01012 } 01013 01014 /* Initialize the volume descriptors so we can know what's available */ 01015 Status = SmpCreateVolumeDescriptors(); 01016 if (!NT_SUCCESS(Status)) 01017 { 01018 /* We can't make decisions without this, so fail */ 01019 DPRINT1("SMSS:PFILE: Failed to create volume descriptors (status %X)\n", 01020 Status); 01021 return Status; 01022 } 01023 01024 /* If we fail creating pagefiles, try to reduce by this much each time */ 01025 FuzzFactor.QuadPart = FUZZ_FACTOR; 01026 01027 /* Loop the descriptor list */ 01028 NextEntry = SmpPagingFileDescriptorList.Flink; 01029 while (NextEntry != &SmpPagingFileDescriptorList) 01030 { 01031 /* Check what kind of descriptor this is */ 01032 Descriptor = CONTAINING_RECORD(NextEntry, SMP_PAGEFILE_DESCRIPTOR, Entry); 01033 if (Descriptor->Flags & SMP_PAGEFILE_SYSTEM_MANAGED) 01034 { 01035 /* This is a system-managed descriptor. Create the correct file */ 01036 DPRINT("SMSS:PFILE: Creating a system managed paging file (`%wZ')\n", 01037 &Descriptor->Name); 01038 Status = SmpCreateSystemManagedPagingFile(Descriptor, FALSE); 01039 if (!NT_SUCCESS(Status)) 01040 { 01041 /* We failed -- try again, with size minimization this time */ 01042 DPRINT1("SMSS:PFILE: Trying lower sizes for (`%wZ') \n", 01043 &Descriptor->Name); 01044 Status = SmpCreateSystemManagedPagingFile(Descriptor, TRUE); 01045 } 01046 } 01047 else 01048 { 01049 /* This is a manually entered descriptor. Validate its size first */ 01050 SmpValidatePagingFileSizes(Descriptor); 01051 01052 /* Check if this is an ANY pagefile or a FIXED pagefile */ 01053 DPRINT("SMSS:PFILE: Creating a normal paging file (`%wZ') \n", 01054 &Descriptor->Name); 01055 if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?') 01056 { 01057 /* It's an any pagefile, try to create it wherever possible */ 01058 Size = Descriptor->MinSize; 01059 Status = SmpCreatePagingFileOnAnyDrive(Descriptor, 01060 &FuzzFactor, 01061 &Size); 01062 if (!NT_SUCCESS(Status)) 01063 { 01064 /* We failed to create it. Try again with a smaller size */ 01065 DPRINT1("SMSS:PFILE: Trying lower sizes for (`%wZ') \n", 01066 &Descriptor->Name); 01067 Size.QuadPart = 16 * MEGABYTE; 01068 Status = SmpCreatePagingFileOnAnyDrive(Descriptor, 01069 &FuzzFactor, 01070 &Size); 01071 } 01072 } 01073 else 01074 { 01075 /* It's a fixed pagefile: override the minimum and use ours */ 01076 Size.QuadPart = 16 * MEGABYTE; 01077 Status = SmpCreatePagingFileOnFixedDrive(Descriptor, 01078 &FuzzFactor, 01079 &Size); 01080 } 01081 } 01082 01083 /* Go to the next descriptor */ 01084 if (NT_SUCCESS(Status)) Created = TRUE; 01085 NextEntry = NextEntry->Flink; 01086 } 01087 01088 /* Check if none of the code in our loops above was able to create it */ 01089 if (!Created) 01090 { 01091 /* Build an emergency pagefile ourselves */ 01092 DPRINT1("SMSS:PFILE: Creating emergency paging file. \n"); 01093 Status = SmpCreateEmergencyPagingFile(); 01094 } 01095 01096 /* All done */ 01097 return Status; 01098 } Generated on Sat May 26 2012 04:17:44 for ReactOS by
1.7.6.1
|