[NTOSKRNL] Don't need all access for file object
[reactos.git] / ntoskrnl / mm / pagefile.c
index 2859a35..9855578 100644 (file)
@@ -44,7 +44,7 @@
 PMMPAGING_FILE MmPagingFile[MAX_PAGING_FILES];
 
 /* Lock for examining the list of paging files */
-static KSPIN_LOCK PagingFileListLock;
+KGUARDED_MUTEX MmPageFileCreationLock;
 
 /* Number of paging files */
 ULONG MmNumberOfPagingFiles;
@@ -258,7 +258,7 @@ MmInitPagingFile(VOID)
 {
     ULONG i;
 
-    KeInitializeSpinLock(&PagingFileListLock);
+    KeInitializeGuardedMutex(&MmPageFileCreationLock);
 
     MiFreeSwapPages = 0;
     MiUsedSwapPages = 0;
@@ -271,98 +271,81 @@ MmInitPagingFile(VOID)
     MmNumberOfPagingFiles = 0;
 }
 
-static ULONG
-MiAllocPageFromPagingFile(PMMPAGING_FILE PagingFile)
-{
-    KIRQL oldIrql;
-    ULONG off;
-
-    KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
-    off = RtlFindClearBitsAndSet(PagingFile->AllocMap, 1, 0);
-    KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
-
-    return off;
-}
-
 VOID
 NTAPI
 MmFreeSwapPage(SWAPENTRY Entry)
 {
     ULONG i;
     ULONG_PTR off;
-    KIRQL oldIrql;
     PMMPAGING_FILE PagingFile;
 
     i = FILE_FROM_ENTRY(Entry);
     off = OFFSET_FROM_ENTRY(Entry) - 1;
 
-    KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
+    KeAcquireGuardedMutex(&MmPageFileCreationLock);
 
     PagingFile = MmPagingFile[i];
     if (PagingFile == NULL)
     {
         KeBugCheck(MEMORY_MANAGEMENT);
     }
-    KeAcquireSpinLockAtDpcLevel(&PagingFile->AllocMapLock);
 
-    RtlClearBit(PagingFile->AllocMap, off >> 5);
+    RtlClearBit(PagingFile->Bitmap, off >> 5);
 
-    PagingFile->FreePages++;
-    PagingFile->UsedPages--;
+    PagingFile->FreeSpace++;
+    PagingFile->CurrentUsage--;
 
     MiFreeSwapPages++;
     MiUsedSwapPages--;
 
-    KeReleaseSpinLockFromDpcLevel(&PagingFile->AllocMapLock);
-    KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+    KeReleaseGuardedMutex(&MmPageFileCreationLock);
 }
 
 SWAPENTRY
 NTAPI
 MmAllocSwapPage(VOID)
 {
-    KIRQL oldIrql;
     ULONG i;
     ULONG off;
     SWAPENTRY entry;
 
-    KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
+    KeAcquireGuardedMutex(&MmPageFileCreationLock);
 
     if (MiFreeSwapPages == 0)
     {
-        KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+        KeReleaseGuardedMutex(&MmPageFileCreationLock);
         return(0);
     }
 
     for (i = 0; i < MAX_PAGING_FILES; i++)
     {
         if (MmPagingFile[i] != NULL &&
-                MmPagingFile[i]->FreePages >= 1)
+                MmPagingFile[i]->FreeSpace >= 1)
         {
-            off = MiAllocPageFromPagingFile(MmPagingFile[i]);
+            off = RtlFindClearBitsAndSet(MmPagingFile[i]->Bitmap, 1, 0);
             if (off == 0xFFFFFFFF)
             {
                 KeBugCheck(MEMORY_MANAGEMENT);
-                KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+                KeReleaseGuardedMutex(&MmPageFileCreationLock);
                 return(STATUS_UNSUCCESSFUL);
             }
             MiUsedSwapPages++;
             MiFreeSwapPages--;
-            KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+            KeReleaseGuardedMutex(&MmPageFileCreationLock);
 
             entry = ENTRY_FROM_FILE_OFFSET(i, off + 1);
             return(entry);
         }
     }
 
-    KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+    KeReleaseGuardedMutex(&MmPageFileCreationLock);
     KeBugCheck(MEMORY_MANAGEMENT);
     return(0);
 }
 
 NTSTATUS NTAPI
 NtCreatePagingFile(IN PUNICODE_STRING FileName,
-                   IN PLARGE_INTEGER InitialSize,
+                   IN PLARGE_INTEGER MinimumSize,
                    IN PLARGE_INTEGER MaximumSize,
                    IN ULONG Reserved)
 {
@@ -372,18 +355,20 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     IO_STATUS_BLOCK IoStatus;
     PFILE_OBJECT FileObject;
     PMMPAGING_FILE PagingFile;
-    KIRQL oldIrql;
     ULONG AllocMapSize;
     ULONG Count;
     KPROCESSOR_MODE PreviousMode;
-    UNICODE_STRING CapturedFileName;
-    LARGE_INTEGER SafeInitialSize, SafeMaximumSize, AllocationSize;
+    UNICODE_STRING PageFileName;
+    LARGE_INTEGER SafeMinimumSize, SafeMaximumSize, AllocationSize;
     FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
     SECURITY_DESCRIPTOR SecurityDescriptor;
     PACL Dacl;
+    PWSTR Buffer;
 
-    DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
-           FileName, InitialSize->QuadPart);
+    DPRINT("NtCreatePagingFile(FileName %wZ, MinimumSize %I64d)\n",
+           FileName, MinimumSize->QuadPart);
+
+    PAGED_CODE();
 
     if (MmNumberOfPagingFiles >= MAX_PAGING_FILES)
     {
@@ -401,8 +386,12 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
 
         _SEH2_TRY
         {
-            SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
+            SafeMinimumSize = ProbeForReadLargeInteger(MinimumSize);
             SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
+
+            PageFileName.Length = FileName->Length;
+            PageFileName.MaximumLength = FileName->MaximumLength;
+            PageFileName.Buffer = FileName->Buffer;
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
@@ -413,13 +402,17 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     }
     else
     {
-        SafeInitialSize = *InitialSize;
+        SafeMinimumSize = *MinimumSize;
         SafeMaximumSize = *MaximumSize;
+
+        PageFileName.Length = FileName->Length;
+        PageFileName.MaximumLength = FileName->MaximumLength;
+        PageFileName.Buffer = FileName->Buffer;
     }
 
     /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
        smaller than the maximum */
-    if (0 != SafeInitialSize.u.HighPart)
+    if (0 != SafeMinimumSize.u.HighPart)
     {
         return STATUS_INVALID_PARAMETER_2;
     }
@@ -427,25 +420,60 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     {
         return STATUS_INVALID_PARAMETER_3;
     }
-    if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
+    if (SafeMaximumSize.u.LowPart < SafeMinimumSize.u.LowPart)
     {
         return STATUS_INVALID_PARAMETER_MIX;
     }
 
-    Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
-                                          PreviousMode,
-                                          FileName);
-    if (!NT_SUCCESS(Status))
+    /* Validate name length */
+    if (PageFileName.Length > 128 * sizeof(WCHAR))
     {
-        return(Status);
+        return STATUS_OBJECT_NAME_INVALID;
     }
 
+    /* We won't care about any potential UNICODE_NULL */
+    PageFileName.MaximumLength = PageFileName.Length;
+    /* Allocate a buffer to keep name copy */
+    Buffer = ExAllocatePoolWithTag(PagedPool, PageFileName.Length, TAG_MM);
+    if (Buffer == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Copy name */
+    if (PreviousMode != KernelMode)
+    {
+        _SEH2_TRY
+        {
+            if (PageFileName.Length != 0)
+            {
+                ProbeForRead(PageFileName.Buffer, PageFileName.Length, sizeof(WCHAR));
+            }
+
+            RtlCopyMemory(Buffer, PageFileName.Buffer, PageFileName.Length);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            ExFreePoolWithTag(Buffer, TAG_MM);
+
+            /* Return the exception code */
+            _SEH2_YIELD(return _SEH2_GetExceptionCode());
+        }
+        _SEH2_END;
+    }
+    else
+    {
+        RtlCopyMemory(Buffer, PageFileName.Buffer, PageFileName.Length);
+    }
+
+    /* Erase caller's buffer with ours */
+    PageFileName.Buffer = Buffer;
+
     /* Create the security descriptor for the page file */
     Status = RtlCreateSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
     if (!NT_SUCCESS(Status))
     {
-        ReleaseCapturedUnicodeString(&CapturedFileName,
-                                     PreviousMode);
+        ExFreePoolWithTag(Buffer, TAG_MM);
         return Status;
     }
 
@@ -455,8 +483,7 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     Dacl = ExAllocatePoolWithTag(PagedPool, Count, 'lcaD');
     if (Dacl == NULL)
     {
-        ReleaseCapturedUnicodeString(&CapturedFileName,
-                                     PreviousMode);
+        ExFreePoolWithTag(Buffer, TAG_MM);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -465,8 +492,7 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(Dacl, 'lcaD');
-        ReleaseCapturedUnicodeString(&CapturedFileName,
-                                     PreviousMode);
+        ExFreePoolWithTag(Buffer, TAG_MM);
         return Status;
     }
 
@@ -475,8 +501,7 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(Dacl, 'lcaD');
-        ReleaseCapturedUnicodeString(&CapturedFileName,
-                                     PreviousMode);
+        ExFreePoolWithTag(Buffer, TAG_MM);
         return Status;
     }
 
@@ -485,8 +510,7 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(Dacl, 'lcaD');
-        ReleaseCapturedUnicodeString(&CapturedFileName,
-                                     PreviousMode);
+        ExFreePoolWithTag(Buffer, TAG_MM);
         return Status;
     }
 
@@ -495,13 +519,12 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(Dacl, 'lcaD');
-        ReleaseCapturedUnicodeString(&CapturedFileName,
-                                     PreviousMode);
+        ExFreePoolWithTag(Buffer, TAG_MM);
         return Status;
     }
 
     InitializeObjectAttributes(&ObjectAttributes,
-                               &CapturedFileName,
+                               &PageFileName,
                                OBJ_KERNEL_HANDLE,
                                NULL,
                                &SecurityDescriptor);
@@ -512,7 +535,7 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
      * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
      * paging file but of another file. We can't write a complete page (4096
      * bytes) to the physical location of cluster 3042 then. */
-    AllocationSize.QuadPart = SafeInitialSize.QuadPart + PAGE_SIZE;
+    AllocationSize.QuadPart = SafeMinimumSize.QuadPart + PAGE_SIZE;
 
     /* First, attempt to replace the page file, if existing */
     Status = IoCreateFile(&FileHandle,
@@ -532,9 +555,12 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     /* If we failed, relax a bit constraints, someone may be already holding the
      * the file, so share write, don't attempt to replace and don't delete on close
      * (basically, don't do anything conflicting)
+     * This can happen if the caller attempts to extend a page file.
      */
     if (!NT_SUCCESS(Status))
     {
+        ULONG i;
+
         Status = IoCreateFile(&FileHandle,
                               SYNCHRONIZE | FILE_WRITE_DATA,
                               &ObjectAttributes,
@@ -549,15 +575,103 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
                               CreateFileTypeNone,
                               NULL,
                               SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
+        if (!NT_SUCCESS(Status))
+        {
+            ExFreePoolWithTag(Dacl, 'lcaD');
+            ExFreePoolWithTag(Buffer, TAG_MM);
+            return Status;
+        }
+
+        /* We opened it! Check we are that "someone" ;-)
+         * First, get the opened file object.
+         */
+        Status = ObReferenceObjectByHandle(FileHandle,
+                                           FILE_READ_DATA | FILE_WRITE_DATA,
+                                           IoFileObjectType,
+                                           KernelMode,
+                                           (PVOID*)&FileObject,
+                                           NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            ZwClose(FileHandle);
+            ExFreePoolWithTag(Dacl, 'lcaD');
+            ExFreePoolWithTag(Buffer, TAG_MM);
+            return Status;
+        }
+
+        /* Find if it matches a previous page file */
+        PagingFile = NULL;
+
+        /* FIXME: should be calling unsafe instead,
+         * we should already be in a guarded region
+         */
+        KeAcquireGuardedMutex(&MmPageFileCreationLock);
+        if (MmNumberOfPagingFiles > 0)
+        {
+            i = 0;
+
+            while (MmPagingFile[i]->FileObject->SectionObjectPointer != FileObject->SectionObjectPointer)
+            {
+                ++i;
+                if (i >= MmNumberOfPagingFiles)
+                {
+                    break;
+                }
+            }
+
+            /* This is the matching page file */
+            PagingFile = MmPagingFile[i];
+        }
+
+        /* If we didn't find the page file, fail */
+        if (PagingFile == NULL)
+        {
+            KeReleaseGuardedMutex(&MmPageFileCreationLock);
+            ObDereferenceObject(FileObject);
+            ZwClose(FileHandle);
+            ExFreePoolWithTag(Dacl, 'lcaD');
+            ExFreePoolWithTag(Buffer, TAG_MM);
+            return STATUS_NOT_FOUND;
+        }
+
+        /* Don't allow page file shrinking */
+        if (PagingFile->MinimumSize > (SafeMinimumSize.QuadPart >> PAGE_SHIFT))
+        {
+            KeReleaseGuardedMutex(&MmPageFileCreationLock);
+            ObDereferenceObject(FileObject);
+            ZwClose(FileHandle);
+            ExFreePoolWithTag(Dacl, 'lcaD');
+            ExFreePoolWithTag(Buffer, TAG_MM);
+            return STATUS_INVALID_PARAMETER_2;
+        }
+
+        if ((SafeMaximumSize.QuadPart >> PAGE_SHIFT) < PagingFile->MaximumSize)
+        {
+            KeReleaseGuardedMutex(&MmPageFileCreationLock);
+            ObDereferenceObject(FileObject);
+            ZwClose(FileHandle);
+            ExFreePoolWithTag(Dacl, 'lcaD');
+            ExFreePoolWithTag(Buffer, TAG_MM);
+            return STATUS_INVALID_PARAMETER_3;
+        }
+
+        /* FIXME: implement parameters checking and page file extension */
+        UNIMPLEMENTED;
+
+        KeReleaseGuardedMutex(&MmPageFileCreationLock);
+        ObDereferenceObject(FileObject);
+        ZwClose(FileHandle);
+        ExFreePoolWithTag(Dacl, 'lcaD');
+        ExFreePoolWithTag(Buffer, TAG_MM);
+        return STATUS_NOT_IMPLEMENTED;
     }
 
-    ReleaseCapturedUnicodeString(&CapturedFileName,
-                                 PreviousMode);
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("Failed creating page file: %lx\n", Status);
         ExFreePoolWithTag(Dacl, 'lcaD');
-        return(Status);
+        ExFreePoolWithTag(Buffer, TAG_MM);
+        return Status;
     }
 
     /* Set the security descriptor */
@@ -568,6 +682,7 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
         {
             ExFreePoolWithTag(Dacl, 'lcaD');
             ZwClose(FileHandle);
+            ExFreePoolWithTag(Buffer, TAG_MM);
             return Status;
         }
     }
@@ -575,20 +690,32 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     /* DACL is no longer needed, free it */
     ExFreePoolWithTag(Dacl, 'lcaD');
 
-    /* Set its end of file to initial size */
+    /* FIXME: To enable once page file managment is moved to ARM3 */
+#if 0
+    /* Check we won't overflow commit limit with the page file */
+    if (MmTotalCommitLimitMaximum + (SafeMaximumSize.QuadPart >> PAGE_SHIFT) <= MmTotalCommitLimitMaximum)
+    {
+        ZwClose(FileHandle);
+        ExFreePoolWithTag(Buffer, TAG_MM);
+        return STATUS_INVALID_PARAMETER_3;
+    }
+#endif
+
+    /* Set its end of file to minimal size */
     Status = ZwSetInformationFile(FileHandle,
                                   &IoStatus,
-                                  &SafeInitialSize,
+                                  &SafeMinimumSize,
                                   sizeof(LARGE_INTEGER),
                                   FileEndOfFileInformation);
     if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status))
     {
         ZwClose(FileHandle);
-        return(Status);
+        ExFreePoolWithTag(Buffer, TAG_MM);
+        return Status;
     }
 
     Status = ObReferenceObjectByHandle(FileHandle,
-                                       FILE_ALL_ACCESS,
+                                       FILE_READ_DATA | FILE_WRITE_DATA,
                                        IoFileObjectType,
                                        KernelMode,
                                        (PVOID*)&FileObject,
@@ -596,7 +723,8 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     if (!NT_SUCCESS(Status))
     {
         ZwClose(FileHandle);
-        return(Status);
+        ExFreePoolWithTag(Buffer, TAG_MM);
+        return Status;
     }
 
     /* Deny page file creation on a floppy disk */
@@ -606,54 +734,65 @@ NtCreatePagingFile(IN PUNICODE_STRING FileName,
     {
         ObDereferenceObject(FileObject);
         ZwClose(FileHandle);
+        ExFreePoolWithTag(Buffer, TAG_MM);
         return STATUS_FLOPPY_VOLUME;
     }
 
-    PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
+    PagingFile = ExAllocatePoolWithTag(NonPagedPool, sizeof(*PagingFile), TAG_MM);
     if (PagingFile == NULL)
     {
         ObDereferenceObject(FileObject);
         ZwClose(FileHandle);
-        return(STATUS_NO_MEMORY);
+        ExFreePoolWithTag(Buffer, TAG_MM);
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
 
     RtlZeroMemory(PagingFile, sizeof(*PagingFile));
 
     PagingFile->FileHandle = FileHandle;
     PagingFile->FileObject = FileObject;
-    PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
-    PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
-    PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
-    PagingFile->UsedPages = 0;
-    KeInitializeSpinLock(&PagingFile->AllocMapLock);
-
-    AllocMapSize = sizeof(RTL_BITMAP) + (((PagingFile->FreePages + 31) / 32) * sizeof(ULONG));
-    PagingFile->AllocMap = ExAllocatePoolWithTag(NonPagedPool,
-                                                 AllocMapSize,
-                                                 TAG_MM);
-    if (PagingFile->AllocMap == NULL)
-    {
-        ExFreePool(PagingFile);
+    PagingFile->MaximumSize = (SafeMaximumSize.QuadPart >> PAGE_SHIFT);
+    PagingFile->Size = (SafeMinimumSize.QuadPart >> PAGE_SHIFT);
+    PagingFile->MinimumSize = (SafeMinimumSize.QuadPart >> PAGE_SHIFT);
+    /* First page is never used: it's the header
+     * TODO: write it
+     */
+    PagingFile->FreeSpace = (ULONG)(SafeMinimumSize.QuadPart / PAGE_SIZE) - 1;
+    PagingFile->CurrentUsage = 0;
+    PagingFile->PageFileName = PageFileName;
+    ASSERT(PagingFile->Size == PagingFile->FreeSpace + PagingFile->CurrentUsage + 1);
+
+    AllocMapSize = sizeof(RTL_BITMAP) + (((PagingFile->MaximumSize + 31) / 32) * sizeof(ULONG));
+    PagingFile->Bitmap = ExAllocatePoolWithTag(NonPagedPool,
+                                               AllocMapSize,
+                                               TAG_MM);
+    if (PagingFile->Bitmap == NULL)
+    {
+        ExFreePoolWithTag(PagingFile, TAG_MM);
         ObDereferenceObject(FileObject);
         ZwClose(FileHandle);
-        return(STATUS_NO_MEMORY);
+        ExFreePoolWithTag(Buffer, TAG_MM);
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    RtlInitializeBitMap(PagingFile->AllocMap,
-                        (PULONG)(PagingFile->AllocMap + 1),
-                        (ULONG)(PagingFile->FreePages));
-    RtlClearAllBits(PagingFile->AllocMap);
+    RtlInitializeBitMap(PagingFile->Bitmap,
+                        (PULONG)(PagingFile->Bitmap + 1),
+                        (ULONG)(PagingFile->MaximumSize));
+    RtlClearAllBits(PagingFile->Bitmap);
 
-    KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
+    /* FIXME: should be calling unsafe instead,
+     * we should already be in a guarded region
+     */
+    KeAcquireGuardedMutex(&MmPageFileCreationLock);
     ASSERT(MmPagingFile[MmNumberOfPagingFiles] == NULL);
     MmPagingFile[MmNumberOfPagingFiles] = PagingFile;
     MmNumberOfPagingFiles++;
-    MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
-    KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+    MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreeSpace;
+    KeReleaseGuardedMutex(&MmPageFileCreationLock);
 
     MmSwapSpaceMessage = FALSE;
 
-    return(STATUS_SUCCESS);
+    return STATUS_SUCCESS;
 }
 
 /* EOF */