///\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
*/\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