- Fix NtSetInformationFile to use correct sync/async semantics and use deferred I...
authorAlex Ionescu <aionescu@gmail.com>
Tue, 4 Jul 2006 16:50:56 +0000 (16:50 +0000)
committerAlex Ionescu <aionescu@gmail.com>
Tue, 4 Jul 2006 16:50:56 +0000 (16:50 +0000)
svn path=/trunk/; revision=22837

reactos/ntoskrnl/io/iomgr/iofunc.c

index 672b35d..0a077ea 100644 (file)
 ///\r
 //\r
 // TODO:\r
-// - Update to new semantics:\r
-//  - Lock/Unlock <= DONE\r
-//  - Query/Set Volume Info <= DONE\r
-//  - Read/Write file <= DONE\r
-//  - QueryDirectoryFile <= DONE\r
-//  - Query/Set File Info\r
 // - Add SEH to some places where it's missing (MDLs, etc)\r
 // - Add a generic Cleanup/Exception Routine\r
 // - Add probe/alignment checks for Query/Set routines\r
@@ -2014,312 +2008,312 @@ NtSetEaFile(IN HANDLE FileHandle,
  */\r
 NTSTATUS\r
 NTAPI\r
-NtSetInformationFile(HANDLE FileHandle,\r
-                     PIO_STATUS_BLOCK IoStatusBlock,\r
-                     PVOID FileInformation,\r
-                     ULONG Length,\r
-                     FILE_INFORMATION_CLASS FileInformationClass)\r
+NtSetInformationFile(IN HANDLE FileHandle,\r
+                     IN PIO_STATUS_BLOCK IoStatusBlock,\r
+                     IN PVOID FileInformation,\r
+                     IN ULONG Length,\r
+                     IN FILE_INFORMATION_CLASS FileInformationClass)\r
 {\r
-    OBJECT_HANDLE_INFORMATION HandleInformation;\r
-    PIO_STACK_LOCATION StackPtr;\r
     PFILE_OBJECT FileObject;\r
-    PDEVICE_OBJECT DeviceObject;\r
-    PIRP Irp;\r
-    KEVENT Event;\r
-    BOOLEAN LocalEvent = FALSE;\r
     NTSTATUS Status = STATUS_SUCCESS;\r
+    PIRP Irp;\r
+    PDEVICE_OBJECT DeviceObject;\r
+    PIO_STACK_LOCATION StackPtr;\r
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();\r
-    BOOLEAN Failed = FALSE;\r
+    PKEVENT Event = NULL;\r
+    BOOLEAN LocalEvent = FALSE;\r
+    PKNORMAL_ROUTINE NormalRoutine;\r
+    PVOID NormalContext;\r
+    KIRQL OldIrql;\r
+    IO_STATUS_BLOCK KernelIosb;\r
+    PVOID Queue;\r
+    PFILE_COMPLETION_INFORMATION CompletionInfo = FileInformation;\r
+    PIO_COMPLETION_CONTEXT Context;\r
 \r
+    /* Check if we're called from user mode */\r
     if (PreviousMode != KernelMode)\r
     {\r
+        /* Enter SEH for probing */\r
         _SEH_TRY\r
         {\r
-            if (IoStatusBlock)\r
-            {\r
-                ProbeForWrite(IoStatusBlock,\r
-                              sizeof(IO_STATUS_BLOCK),\r
-                              sizeof(ULONG));\r
-            }\r
+            /* Probe the I/O Status block */\r
+            ProbeForWrite(IoStatusBlock,\r
+                          sizeof(IO_STATUS_BLOCK),\r
+                          sizeof(ULONG));\r
 \r
+            /* Probe the information */\r
             if (Length) ProbeForRead(FileInformation, Length, 1);\r
         }\r
         _SEH_HANDLE\r
         {\r
+            /* Get the exception code */\r
             Status = _SEH_GetExceptionCode();\r
         }\r
         _SEH_END;\r
-        \r
-        if (!NT_SUCCESS(Status))\r
-        {\r
-            return Status;\r
-        }\r
+\r
+        /* Check if probing failed */\r
+        if (!NT_SUCCESS(Status)) return Status;\r
     }\r
 \r
-    /* Get the file object from the file handle */\r
+    /* Reference the Handle */\r
     Status = ObReferenceObjectByHandle(FileHandle,\r
-                                       0,\r
+                                       0, // FIXME\r
                                        IoFileObjectType,\r
                                        PreviousMode,\r
                                        (PVOID *)&FileObject,\r
-                                       &HandleInformation);\r
+                                       NULL);\r
     if (!NT_SUCCESS(Status)) return Status;\r
 \r
-    /* Check information class specific access rights */\r
-    switch (FileInformationClass)\r
-    {\r
-        case FileBasicInformation:\r
-            if (!(HandleInformation.GrantedAccess & FILE_WRITE_ATTRIBUTES))\r
-                Failed = TRUE;\r
-            break;\r
-\r
-        case FileDispositionInformation:\r
-            if (!(HandleInformation.GrantedAccess & DELETE))\r
-                Failed = TRUE;\r
-            break;\r
-\r
-        case FilePositionInformation:\r
-            if (!(HandleInformation.GrantedAccess & (FILE_READ_DATA | FILE_WRITE_DATA)) ||\r
-                !(FileObject->Flags & FO_SYNCHRONOUS_IO))\r
-                Failed = TRUE;\r
-            break;\r
-\r
-        case FileEndOfFileInformation:\r
-            if (!(HandleInformation.GrantedAccess & FILE_WRITE_DATA))\r
-                Failed = TRUE;\r
-            break;\r
-\r
-        default:\r
-            break;\r
-    }\r
-\r
-    if (Failed)\r
-    {\r
-        ObDereferenceObject(FileObject);\r
-        return STATUS_ACCESS_DENIED;\r
-    }\r
-\r
-    if (FileInformationClass == FilePositionInformation)\r
-    {\r
-       if (Length < sizeof(FILE_POSITION_INFORMATION))\r
-       {\r
-          Status = STATUS_BUFFER_OVERFLOW;\r
-       }\r
-       else\r
-       {\r
-          _SEH_TRY\r
-          {\r
-             FileObject->CurrentByteOffset = ((PFILE_POSITION_INFORMATION)FileInformation)->CurrentByteOffset;\r
-             IoStatusBlock->Information = 0;\r
-             Status = IoStatusBlock->Status = STATUS_SUCCESS;\r
-          }\r
-          _SEH_HANDLE\r
-          {\r
-             Status = _SEH_GetExceptionCode();\r
-          }\r
-          _SEH_END;\r
-       }\r
-       ObDereferenceObject(FileObject);\r
-       return Status;\r
-    }\r
-\r
-    /* FIXME: Later, we can implement a lot of stuff here and avoid a driver call */\r
-    /* Handle IO Completion Port quickly */\r
-    if (FileInformationClass == FileCompletionInformation)\r
-    {\r
-        PVOID Queue;\r
-        PFILE_COMPLETION_INFORMATION CompletionInfo = FileInformation;\r
-        PIO_COMPLETION_CONTEXT Context;\r
-        \r
-        if (FileObject->Flags & FO_SYNCHRONOUS_IO || FileObject->CompletionContext != NULL)\r
-        {\r
-            Status = STATUS_INVALID_PARAMETER;\r
-        }\r
-        else\r
-        {\r
-            if (Length < sizeof(FILE_COMPLETION_INFORMATION))\r
-            {\r
-                Status = STATUS_INFO_LENGTH_MISMATCH;\r
-            }\r
-            else\r
-            {\r
-                /* Reference the Port */\r
-                Status = ObReferenceObjectByHandle(CompletionInfo->Port, /* FIXME - protect with SEH! */\r
-                                                   IO_COMPLETION_MODIFY_STATE,\r
-                                                   IoCompletionType,\r
-                                                   PreviousMode,\r
-                                                   (PVOID*)&Queue,\r
-                                                   NULL);\r
-                if (NT_SUCCESS(Status))\r
-                {\r
-                    /* Allocate the Context */\r
-                    Context = ExAllocatePoolWithTag(PagedPool,\r
-                                                    sizeof(IO_COMPLETION_CONTEXT),\r
-                                                    TAG('I', 'o', 'C', 'p'));\r
-\r
-                    if (Context != NULL)\r
-                    {\r
-                        /* Set the Data */\r
-                        Context->Key = CompletionInfo->Key; /* FIXME - protect with SEH! */\r
-                        Context->Port = Queue;\r
-                        \r
-                        if (InterlockedCompareExchangePointer(&FileObject->CompletionContext,\r
-                                                              Context,\r
-                                                              NULL) != NULL)\r
-                        {\r
-                            /* someone else set the completion port in the\r
-                               meanwhile, fail */\r
-                            ExFreePool(Context);\r
-                            ObDereferenceObject(Queue);\r
-                            Status = STATUS_INVALID_PARAMETER;\r
-                        }\r
-                    }\r
-                    else\r
-                    {\r
-                        /* Dereference the Port now */\r
-                        ObDereferenceObject(Queue);\r
-                        Status = STATUS_INSUFFICIENT_RESOURCES;\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        /* Complete the I/O */\r
-        ObDereferenceObject(FileObject);\r
-        return Status;\r
-    }\r
-\r
     /* Check if this is a direct open or not */\r
     if (FileObject->Flags & FO_DIRECT_DEVICE_OPEN)\r
     {\r
+        /* Get the device object */\r
         DeviceObject = IoGetAttachedDevice(FileObject->DeviceObject);\r
     }\r
     else\r
     {\r
+        /* Get the device object */\r
         DeviceObject = IoGetRelatedDeviceObject(FileObject);\r
     }\r
 \r
-    /* Check if we should use Sync IO or not */\r
+    /* Check if this is a file that was opened for Synch I/O */\r
     if (FileObject->Flags & FO_SYNCHRONOUS_IO)\r
     {\r
-        /* Use File Object event */\r
-        KeClearEvent(&FileObject->Event);\r
-    }\r
-    else\r
-    {\r
-        /* Use local event */\r
-        KeInitializeEvent(&Event, SynchronizationEvent, FALSE);\r
-        LocalEvent = TRUE;\r
-    }\r
+        /* Lock it */\r
+        IopLockFileObject(FileObject);\r
 \r
-    /* Allocate the IRP */\r
-    if (!(Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE)))\r
-    {\r
-        ObDereferenceObject(FileObject);\r
-        return STATUS_INSUFFICIENT_RESOURCES;\r
-    }\r
+        /* Check if the caller just wants the position */\r
+        if (FileInformationClass == FilePositionInformation)\r
+        {\r
+            /* Protect write in SEH */\r
+            _SEH_TRY\r
+            {\r
+                /* Write the offset */\r
+                FileObject->CurrentByteOffset =\r
+                    ((PFILE_POSITION_INFORMATION)FileInformation)->\r
+                    CurrentByteOffset;\r
 \r
-    /* Allocate the System Buffer */\r
-    Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool,\r
-                                                            Length,\r
-                                                            TAG_SYSB);\r
-    if (!Irp->AssociatedIrp.SystemBuffer)\r
-    {\r
-        Status = STATUS_INSUFFICIENT_RESOURCES;\r
-        goto failfreeirp;\r
-    }\r
+                /* Fill out the I/O Status Block */\r
+                IoStatusBlock->Information = 0;\r
+                Status = IoStatusBlock->Status = STATUS_SUCCESS;\r
+            }\r
+            _SEH_HANDLE\r
+            {\r
+                /* Get the exception code */\r
+                Status = _SEH_GetExceptionCode();\r
+            }\r
+            _SEH_END;\r
 \r
-    /* Copy the data inside */\r
-    if (PreviousMode != KernelMode)\r
-    {\r
-        _SEH_TRY\r
-        {\r
-            /* no need to probe again */\r
-            RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,\r
-                          FileInformation,\r
-                          Length);\r
-        }\r
-        _SEH_HANDLE\r
-        {\r
-            Status = _SEH_GetExceptionCode();\r
-        }\r
-        _SEH_END;\r
-        \r
-        if (!NT_SUCCESS(Status))\r
-        {\r
-            ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer,\r
-                              TAG_SYSB);\r
-            Irp->AssociatedIrp.SystemBuffer = NULL;\r
-failfreeirp:\r
-            IoFreeIrp(Irp);\r
+            /* Release the file lock, dereference the file and return */\r
+            IopUnlockFileObject(FileObject);\r
             ObDereferenceObject(FileObject);\r
             return Status;\r
         }\r
     }\r
     else\r
     {\r
-        RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,\r
-                      FileInformation,\r
-                      Length);\r
+        /* Use local event */\r
+        Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_IO);\r
+        KeInitializeEvent(Event, SynchronizationEvent, FALSE);\r
+        LocalEvent = TRUE;\r
     }\r
 \r
-    /* Set up the IRP */\r
+    /* Clear the File Object event */\r
+    KeClearEvent(&FileObject->Event);\r
+\r
+    /* Allocate the IRP */\r
+    Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE);\r
+    if (!Irp) return IopCleanupFailedIrp(FileObject, NULL);\r
+\r
+    /* Set the IRP */\r
     Irp->Tail.Overlay.OriginalFileObject = FileObject;\r
-    Irp->RequestorMode = PreviousMode;\r
-    Irp->UserIosb = IoStatusBlock;\r
-    Irp->UserEvent = (LocalEvent) ? &Event : NULL;\r
     Irp->Tail.Overlay.Thread = PsGetCurrentThread();\r
-    Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;\r
-    Irp->Flags |= (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;\r
+    Irp->RequestorMode = PreviousMode;\r
+    Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;\r
+    Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;\r
+    Irp->UserIosb = (LocalEvent) ? &KernelIosb : IoStatusBlock;\r
+    Irp->UserEvent = (LocalEvent) ? Event : NULL;\r
+    Irp->AssociatedIrp.SystemBuffer = NULL;\r
+    Irp->MdlAddress = NULL;\r
+    Irp->UserBuffer = FileInformation;\r
 \r
-    /* Set up Stack Data */\r
+    /* Set the Stack Data */\r
     StackPtr = IoGetNextIrpStackLocation(Irp);\r
     StackPtr->MajorFunction = IRP_MJ_SET_INFORMATION;\r
     StackPtr->FileObject = FileObject;\r
 \r
+    /* Allocate a buffer */\r
+    Irp->AssociatedIrp.SystemBuffer =\r
+        ExAllocatePoolWithTag(NonPagedPool,\r
+                              Length,\r
+                              TAG_SYSB);\r
+\r
+    /* Copy the data into it */\r
+    RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, FileInformation, Length);\r
+\r
+    /* Set the flags */\r
+    Irp->Flags = (IRP_BUFFERED_IO |\r
+                  IRP_DEALLOCATE_BUFFER |\r
+                  IRP_DEFER_IO_COMPLETION);\r
+\r
     /* Set the Parameters */\r
     StackPtr->Parameters.SetFile.FileInformationClass = FileInformationClass;\r
     StackPtr->Parameters.SetFile.Length = Length;\r
 \r
-    /* Call the Driver */\r
-    Status = IoCallDriver(DeviceObject, Irp);\r
+    /* Queue the IRP */\r
+    //IopQueueIrpToThread(Irp);\r
+\r
+    /* Update operation counts */\r
+    IopUpdateOperationCount(IopOtherTransfer);\r
+\r
+    /* FIXME: Later, we can implement a lot of stuff here and avoid a driver call */\r
+    /* Handle IO Completion Port quickly */\r
+    if (FileInformationClass == FileCompletionInformation)\r
+    {\r
+        /* Check if the file object already has a completion port */\r
+        if ((FileObject->Flags & FO_SYNCHRONOUS_IO) ||\r
+            (FileObject->CompletionContext))\r
+        {\r
+            /* Fail */\r
+            Status = STATUS_INVALID_PARAMETER;\r
+        }\r
+        else\r
+        {\r
+            /* Reference the Port */\r
+            CompletionInfo = Irp->AssociatedIrp.SystemBuffer;\r
+            Status = ObReferenceObjectByHandle(CompletionInfo->Port,\r
+                                               IO_COMPLETION_MODIFY_STATE,\r
+                                               IoCompletionType,\r
+                                               PreviousMode,\r
+                                               (PVOID*)&Queue,\r
+                                               NULL);\r
+            if (NT_SUCCESS(Status))\r
+            {\r
+                /* Allocate the Context */\r
+                Context = ExAllocatePoolWithTag(PagedPool,\r
+                                                sizeof(IO_COMPLETION_CONTEXT),\r
+                                                IOC_TAG);\r
+                if (Context)\r
+                {\r
+                    /* Set the Data */\r
+                    Context->Key = CompletionInfo->Key;\r
+                    Context->Port = Queue;\r
+                    if (InterlockedCompareExchangePointer(&FileObject->\r
+                                                          CompletionContext,\r
+                                                          Context,\r
+                                                          NULL))\r
+                    {\r
+                        /*\r
+                         * Someone else set the completion port in the\r
+                         * meanwhile, so dereference the port and fail.\r
+                         */\r
+                        ExFreePool(Context);\r
+                        ObDereferenceObject(Queue);\r
+                        Status = STATUS_INVALID_PARAMETER;\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    /* Dereference the Port now */\r
+                    ObDereferenceObject(Queue);\r
+                    Status = STATUS_INSUFFICIENT_RESOURCES;\r
+                }\r
+            }\r
+        }\r
+\r
+        /* Set the IRP Status */\r
+        Irp->IoStatus.Status = Status;\r
+        Irp->IoStatus.Information = 0;\r
+    }\r
+    else\r
+    {\r
+        /* Call the Driver */\r
+        Status = IoCallDriver(DeviceObject, Irp);\r
+    }\r
+\r
+    /* Check if we're waiting for the IRP to complete */\r
     if (Status == STATUS_PENDING)\r
     {\r
+        /* Check if this was async I/O */\r
         if (LocalEvent)\r
         {\r
-            KeWaitForSingleObject(&Event,\r
-                                  Executive,\r
-                                  PreviousMode,\r
-                                  FileObject->Flags & FO_ALERTABLE_IO,\r
-                                  NULL);\r
+            /* Then to a non-alertable wait */\r
+            Status = KeWaitForSingleObject(&Event,\r
+                                           Executive,\r
+                                           PreviousMode,\r
+                                           FALSE,\r
+                                           NULL);\r
+            if (Status == STATUS_USER_APC)\r
+            {\r
+                /* Abort the request */\r
+                IopAbortInterruptedIrp(Event, Irp);\r
+            }\r
+\r
+            /* Set the final status */\r
+            Status = KernelIosb.Status;\r
+\r
+            /* Enter SEH to write the IOSB back */\r
             _SEH_TRY\r
             {\r
-                Status = IoStatusBlock->Status;\r
+                /* Write it back to the caller */\r
+                *IoStatusBlock = KernelIosb;\r
             }\r
             _SEH_HANDLE\r
             {\r
+                /* Get the exception code */\r
                 Status = _SEH_GetExceptionCode();\r
             }\r
             _SEH_END;\r
+\r
+            /* Free the event */\r
+            ExFreePool(Event);\r
         }\r
         else\r
         {\r
-            KeWaitForSingleObject(&FileObject->Event,\r
-                                  Executive,\r
-                                  PreviousMode,\r
-                                  FileObject->Flags & FO_ALERTABLE_IO,\r
-                                  NULL);\r
-            _SEH_TRY\r
-            {\r
-                Status = FileObject->FinalStatus;\r
-            }\r
-            _SEH_HANDLE\r
+            /* Wait for the IRP */\r
+            Status = KeWaitForSingleObject(&FileObject->Event,\r
+                                           Executive,\r
+                                           PreviousMode,\r
+                                           FileObject->Flags & FO_ALERTABLE_IO,\r
+                                           NULL);\r
+            if ((Status == STATUS_USER_APC) || (Status == STATUS_ALERTED))\r
             {\r
-                Status = _SEH_GetExceptionCode();\r
+                /* Abort the request */\r
+                IopAbortInterruptedIrp(&FileObject->Event, Irp);\r
             }\r
-            _SEH_END;\r
+\r
+            /* Set the final status */\r
+            Status = FileObject->FinalStatus;\r
+\r
+            /* Release the file lock */\r
+            IopUnlockFileObject(FileObject);\r
         }\r
     }\r
+    else\r
+    {\r
+        /* Free the event if we had one */\r
+        if (LocalEvent)\r
+        {\r
+            /* Clear it in the IRP for completion */\r
+            Irp->UserEvent = NULL;\r
+            ExFreePool(Event);\r
+        }\r
+\r
+        /* Set the caller IOSB */\r
+        Irp->UserIosb = IoStatusBlock;\r
+\r
+        /* The IRP wasn't completed, complete it ourselves */\r
+        KeRaiseIrql(APC_LEVEL, &OldIrql);\r
+        IopCompleteRequest(&Irp->Tail.Apc,\r
+                           &NormalRoutine,\r
+                           &NormalContext,\r
+                           (PVOID*)&FileObject,\r
+                           &NormalContext);\r
+        KeLowerIrql(OldIrql);\r
+\r
+        /* Release the file object if we had locked it*/\r
+        if (!LocalEvent) IopUnlockFileObject(FileObject);\r
+    }\r
 \r
     /* Return the Status */\r
     return Status;\r