static NTSTATUS IopFileObjectGetCloseIrp( IN IO_FILE_HANDLE FileHandle, OUT PIRP* ppIrp ) { NTSTATUS status = STATUS_SUCCESS; int EE = 0; PIRP pIrp = NULL; // TODO-Use InterlockedExchangePointer() IopFileObjectLock(FileHandle); pIrp = FileHandle->pCloseIrp; FileHandle->pCloseIrp = NULL; IopFileObjectUnlock(FileHandle); if (!LWIO_ASSERT_VALUE_MSG(pIrp, "Cannot close already closed file")) { status = STATUS_FILE_CLOSED; GOTO_CLEANUP_ON_STATUS_EE(status, EE); } cleanup: IO_LOG_LEAVE_ON_STATUS_EE(status, EE); *ppIrp = pIrp; return status; }
NTSTATUS IopFileObjectAddDispatched( IN PIO_FILE_OBJECT pFileObject, IN IRP_TYPE Type ) { NTSTATUS status = STATUS_SUCCESS; IopFileObjectLock(pFileObject); if ((Type != IRP_TYPE_CLOSE) && (IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_CANCELLED) || IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN))) { status = STATUS_CANCELLED; } else { status = STATUS_SUCCESS; pFileObject->DispatchedIrpCount++; LWIO_ASSERT(pFileObject->DispatchedIrpCount >= 1); } IopFileObjectUnlock(pFileObject); return status; }
VOID IoFileSetZctSupportMask( IN IO_FILE_HANDLE FileHandle, IN LW_ZCT_ENTRY_MASK ZctReadMask, IN LW_ZCT_ENTRY_MASK ZctWriteMask ) { // It is up to the FSD to synchornize IopFileObjectLock(FileHandle); FileHandle->ZctReadMask = ZctReadMask; FileHandle->ZctWriteMask = ZctWriteMask; IopFileObjectUnlock(FileHandle); }
VOID IopFileObjectRemoveDispatched( IN PIO_FILE_OBJECT pFileObject, IN IRP_TYPE Type ) { BOOLEAN needContinueAsycClose = FALSE; IopFileObjectLock(pFileObject); pFileObject->DispatchedIrpCount--; LWIO_ASSERT(pFileObject->DispatchedIrpCount >= 0); if (IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN_WAIT) && (0 == pFileObject->DispatchedIrpCount)) { // TODO-Perhaps remove Type parameter since the use of // FILE_OBJECT_FLAG_RUNDOWN_WAIT negates the need for it. LWIO_ASSERT(Type != IRP_TYPE_CLOSE); if (pFileObject->Rundown.Callback) { needContinueAsycClose = TRUE; } else { LwRtlSignalConditionVariable(&pFileObject->Rundown.Condition); } ClearFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN_WAIT); } IopFileObjectUnlock(pFileObject); if (needContinueAsycClose) { // This will send the close to the FSD. Note that the callback // must be called from here on a synchronous completion because // there was no asynchronous IRP completion to take care of calling // the callback. NTSTATUS status = IopContinueAsyncCloseFile( pFileObject, pFileObject->Rundown.Callback, pFileObject->Rundown.CallbackContext, pFileObject->Rundown.pIoStatusBlock); if (STATUS_PENDING != status) { pFileObject->Rundown.Callback(pFileObject->Rundown.CallbackContext); } } }
NTSTATUS IopIrpAttach( IN OUT PIRP pIrp, IN IRP_TYPE Type, IN PIO_FILE_OBJECT pFileObject ) { NTSTATUS status = 0; int EE = 0; PIRP_INTERNAL irpInternal = IopIrpGetInternal(pIrp); LWIO_ASSERT(!pIrp->FileHandle); LWIO_ASSERT(pIrp->Type == IRP_TYPE_UNINITIALIZED); LWIO_ASSERT(Type != IRP_TYPE_UNINITIALIZED); IopFileObjectLock(pFileObject); // TODO-Add FILE_OBJECT_FLAG_CLOSED if ((Type != IRP_TYPE_CLOSE) && (IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_CANCELLED) || IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN))) { status = STATUS_CANCELLED; GOTO_CLEANUP_EE(EE); } else { LwListInsertTail(&pFileObject->IrpList, &irpInternal->FileObjectLinks); } // These are immutable from here until the IRP is freed. pIrp->Type = Type; pIrp->FileHandle = pFileObject; IopFileObjectReference(pFileObject); // The file object reference keeps an implicit reference to the // device and driver. pIrp->DeviceHandle = pFileObject->pDevice; pIrp->DriverHandle = pFileObject->pDevice->Driver; cleanup: IopFileObjectUnlock(pFileObject); IO_LOG_LEAVE_ON_STATUS_EE(status, EE); return status; }
PIRP IopIrpLoadZctIrp( IN OUT PIO_FILE_OBJECT pFileObject, IN PVOID pCompletionContext ) { PIRP pIrp = (PIRP) pCompletionContext; PIRP_INTERNAL irpInternal = IopIrpGetInternal(pIrp); LWIO_ASSERT(pIrp->Args.ReadWrite.ZctCompletionContext); LWIO_ASSERT(irpInternal->CancelLinks.Next && irpInternal->CancelLinks.Prev); IopFileObjectLock(pFileObject); LwListRemove(&irpInternal->CancelLinks); IopFileObjectUnlock(pFileObject); RtlZeroMemory(&irpInternal->CancelLinks, sizeof(irpInternal->CancelLinks)); return pIrp; }
VOID IopFileGetZctSupportMask( IN IO_FILE_HANDLE FileHandle, OUT OPTIONAL PLW_ZCT_ENTRY_MASK ZctReadMask, OUT OPTIONAL PLW_ZCT_ENTRY_MASK ZctWriteMask ) { if (ZctReadMask || ZctWriteMask) { IopFileObjectLock(FileHandle); if (ZctReadMask) { *ZctReadMask = FileHandle->ZctReadMask; } if (ZctWriteMask) { *ZctWriteMask = FileHandle->ZctWriteMask; } IopFileObjectUnlock(FileHandle); } }
PVOID IopIrpSaveZctIrp( IN OUT PIO_FILE_OBJECT pFileObject, IN PIRP pIrp, IN PVOID pCompletionContext ) { PIRP_INTERNAL irpInternal = IopIrpGetInternal(pIrp); LWIO_ASSERT(pCompletionContext); IopIrpReference(pIrp); pIrp->Args.ReadWrite.ZctCompletionContext = pCompletionContext; IopFileObjectLock(pFileObject); LwListInsertTail(&pFileObject->ZctCompletionIrpList, &irpInternal->CancelLinks); IopFileObjectUnlock(pFileObject); return pIrp; }
VOID IopIrpFreeZctIrpList( IN OUT PIO_FILE_OBJECT pFileObject ) { PLW_LIST_LINKS pLinks = NULL;; PIRP_INTERNAL irpInternal = NULL; PIRP pIrp = NULL; IopFileObjectLock(pFileObject); while (!LwListIsEmpty(&pFileObject->ZctCompletionIrpList)) { pLinks = LwListRemoveHead(&pFileObject->ZctCompletionIrpList); irpInternal = LW_STRUCT_FROM_FIELD(pLinks, IRP_INTERNAL, CancelLinks); pIrp = &irpInternal->Irp; LWIO_ASSERT(1 == irpInternal->ReferenceCount); LWIO_ASSERT(!pIrp->FileHandle); IopIrpDereference(&pIrp); } IopFileObjectUnlock(pFileObject); }
VOID IopIrpDereference( IN OUT PIRP* ppIrp ) { PIRP pIrp = *ppIrp; if (pIrp) { PIRP_INTERNAL irpInternal = IopIrpGetInternal(pIrp); LONG count = 0; PIO_FILE_OBJECT pFileObject = pIrp->FileHandle; if (pFileObject) { // Take a reference in case we free the IRP. IopFileObjectReference(pFileObject); // Lock since we may free and manipulate the FO IRP list. IopFileObjectLock(pFileObject); } count = InterlockedDecrement(&irpInternal->ReferenceCount); LWIO_ASSERT(count >= 0); if (0 == count) { IopIrpFree(ppIrp); } // Remove our reference. if (pFileObject) { IopFileObjectUnlock(pFileObject); IopFileObjectDereference(&pFileObject); } *ppIrp = NULL; } }
NTSTATUS IopFileObjectRundownEx( IN OUT PIO_FILE_OBJECT pFileObject, IN OPTIONAL PIO_ASYNC_COMPLETE_CALLBACK Callback, IN OPTIONAL PVOID CallbackContext, OUT PIO_STATUS_BLOCK IoStatusBlock ) { NTSTATUS status = STATUS_SUCCESS; int EE = 0; BOOLEAN isLocked = FALSE; IO_STATUS_BLOCK ioStatusBlock = { 0 }; IopFileObjectLock(pFileObject); isLocked = TRUE; if (IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_CLOSE_DONE)) { LWIO_ASSERT(IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN)); // Note that there can be IRP references for completed IRPs sitting // around where the caller has not yet gotten rid of the IRP by // calling IoDereferenceAsyncCancelContext(). So we cannot assert // that (1 == pFileObject->ReferenceCount). Therefore, we do the // next best thing and check DispatchedIrpCount. Technically, if // someone got far enough into a new call that they created an IRP, // they will also have a new IRP file objeject reference. However, // the new IRP will fail to dispatch. While that is a logic bug // in the caller, we cannot trap those sorts of bugs via asserts // in the I/O manager. LWIO_ASSERT(0 == pFileObject->DispatchedIrpCount); IopFileObjectUnlock(pFileObject); isLocked = FALSE; IopFileObjectDereference(&pFileObject); status = STATUS_SUCCESS; GOTO_CLEANUP_EE(EE); } if (IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN)) { LWIO_LOG_ERROR("Attempt to rundown multiple times"); status = STATUS_FILE_CLOSED; GOTO_CLEANUP_EE(EE); } SetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN); IopFileObjectUnlock(pFileObject); isLocked = FALSE; // Cancel everything now that rundown flag is set. IopIrpCancelFileObject(pFileObject, TRUE); // Now check whether we need to wait for rundown. IopFileObjectLock(pFileObject); isLocked = TRUE; if (0 != pFileObject->DispatchedIrpCount) { // Need to wait SetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_RUNDOWN_WAIT); if (!Callback) { // Wait inline for synchronous case LwRtlWaitConditionVariable( &pFileObject->Rundown.Condition, &pFileObject->Mutex, NULL); LWIO_ASSERT(0 == pFileObject->DispatchedIrpCount); } else { // Set up rundown callback for async case pFileObject->Rundown.Callback = Callback; pFileObject->Rundown.CallbackContext = CallbackContext; pFileObject->Rundown.pIoStatusBlock = IoStatusBlock; status = STATUS_PENDING; GOTO_CLEANUP_EE(EE); } } IopFileObjectUnlock(pFileObject); isLocked = FALSE; // We can now continue closing. status = IopContinueAsyncCloseFile( pFileObject, Callback, CallbackContext, IoStatusBlock); GOTO_CLEANUP_ON_STATUS_EE(status, EE); cleanup: if (isLocked) { IopFileObjectUnlock(pFileObject); } if (status && (STATUS_PENDING != status)) { ioStatusBlock.Status = status; } if ((STATUS_PENDING != status) && IoStatusBlock) { *IoStatusBlock = ioStatusBlock; } // TODO-Perhaps do not ASSERT here because LwRtlInitializeEvent() // could have failed if disaptching close IRP synchronously. LWIO_ASSERT((STATUS_SUCCESS == status) || (STATUS_PENDING == status) || (STATUS_FILE_CLOSED == status)); // TODO-Perhaps also remove object from device's file object // list such that it cannot be rundown multiple times. This // would avoid the STATUS_FILE_CLOSED above. IO_LOG_LEAVE_ON_STATUS_EE(status, EE); return status; }
static NTSTATUS IopContinueAsyncCloseFile( IN PIO_FILE_OBJECT FileHandle, IN OPTIONAL PIO_ASYNC_COMPLETE_CALLBACK Callback, IN OPTIONAL PVOID CallbackContext, OUT PIO_STATUS_BLOCK IoStatusBlock ) { NTSTATUS status = STATUS_SUCCESS; int EE = 0; PIRP pIrp = NULL; IO_ASYNC_CONTROL_BLOCK asyncControlBlock = { 0 }; PIO_ASYNC_CONTROL_BLOCK useAsyncControlBlock = NULL; BOOLEAN isOpen = FALSE; // // If the create never completed successfully, we do not want // to send a CLOSE IRP. // // TODO -- There is probably still a small race window // wrt create and device rundown. The fix may involve // changing when IopFileObjectRemoveDispatched() is called // from IopIrpCompleteInternal() so that it is called // after the switch on the IRP type. IopFileObjectLock(FileHandle); isOpen = IsSetFlag(FileHandle->Flags, FILE_OBJECT_FLAG_CREATE_DONE); IopFileObjectUnlock(FileHandle); if (!isOpen) { status = STATUS_SUCCESS; GOTO_CLEANUP_EE(EE); } // // The file was actually opened, so do rest of close cleanup. // status = IopFileObjectGetCloseIrp(FileHandle, &pIrp); GOTO_CLEANUP_ON_STATUS_EE(status, EE); status = IopIrpAttach(pIrp, IRP_TYPE_CLOSE, FileHandle); GOTO_CLEANUP_ON_STATUS_EE(status, EE); if (Callback) { asyncControlBlock.Callback = Callback; asyncControlBlock.CallbackContext = CallbackContext; useAsyncControlBlock = &asyncControlBlock; } status = IopIrpDispatch( pIrp, useAsyncControlBlock, IoStatusBlock); if (STATUS_PENDING == status) { IoDereferenceAsyncCancelContext(&asyncControlBlock.AsyncCancelContext); } GOTO_CLEANUP_ON_STATUS_EE(status, EE); cleanup: IopIrpDereference(&pIrp); if (!useAsyncControlBlock && IoStatusBlock && (STATUS_PENDING != status)) { IO_STATUS_BLOCK ioStatusBlock = { 0 }; ioStatusBlock.Status = status; *IoStatusBlock = ioStatusBlock; } IO_LOG_LEAVE_ON_STATUS_EE(status, EE); return status; }
VOID IopIrpCancelFileObject( IN PIO_FILE_OBJECT pFileObject, IN BOOLEAN IsForRundown ) { BOOLEAN isLocked = FALSE; PLW_LIST_LINKS pLinks = NULL; PIRP_INTERNAL irpInternal = NULL; LW_LIST_LINKS cancelList = { 0 }; PIRP pIrp = NULL; LwListInit(&cancelList); // Gather IRPs we want to cancel while holding FO lock. IopFileObjectLock(pFileObject); isLocked = TRUE; if (IsSetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_CANCELLED)) { GOTO_CLEANUP(); } if (IsForRundown) { SetFlag(pFileObject->Flags, FILE_OBJECT_FLAG_CANCELLED); } // gather list of IRPs for (pLinks = pFileObject->IrpList.Next; pLinks != &pFileObject->IrpList; pLinks = pLinks->Next) { irpInternal = LW_STRUCT_FROM_FIELD(pLinks, IRP_INTERNAL, FileObjectLinks); LWIO_ASSERT(irpInternal->Irp.FileHandle == pFileObject); // Verify that this IRP is not already being cancelled. if (!irpInternal->CancelLinks.Next) { IopIrpReference(&irpInternal->Irp); LwListInsertTail(&cancelList, &irpInternal->CancelLinks); } } IopFileObjectUnlock(pFileObject); isLocked = FALSE; // Iterate over list, calling IopIrpCancel as appropriate. while (!LwListIsEmpty(&cancelList)) { pLinks = LwListRemoveHead(&cancelList); irpInternal = LW_STRUCT_FROM_FIELD(pLinks, IRP_INTERNAL, CancelLinks); pIrp = &irpInternal->Irp; IopIrpCancel(pIrp); IopIrpDereference(&pIrp); } cleanup: if (isLocked) { IopFileObjectUnlock(pFileObject); } }
static VOID IopIrpCompleteInternal( IN OUT PIRP pIrp, IN BOOLEAN IsAsyncCompletion ) { PIRP_INTERNAL irpInternal = IopIrpGetInternal(pIrp); IopIrpAcquireCancelLock(pIrp); LWIO_ASSERT_MSG(IS_BOTH_OR_NEITHER(IsAsyncCompletion, IsSetFlag(irpInternal->Flags, IRP_FLAG_PENDING)), "IRP pending state is inconsistent."); LWIO_ASSERT_MSG(IsSetFlag(irpInternal->Flags, IRP_FLAG_DISPATCHED), "IRP cannot be completed unless it was properly dispatched."); LWIO_ASSERT_MSG(!IsSetFlag(irpInternal->Flags, IRP_FLAG_COMPLETE), "IRP cannot be completed more than once."); // // Note that the IRP may be CANCEL_PENDING or CANCELLED, but that // is ok. In fact, completion may have been called in response // to cancellation. // SetFlag(irpInternal->Flags, IRP_FLAG_COMPLETE); IopIrpReleaseCancelLock(pIrp); IopFileObjectRemoveDispatched(pIrp->FileHandle, pIrp->Type); LWIO_ASSERT(IsValidStatusForIrpType(pIrp->IoStatusBlock.Status, pIrp->Type)); switch (pIrp->Type) { case IRP_TYPE_CREATE: case IRP_TYPE_CREATE_NAMED_PIPE: if ((STATUS_SUCCESS == pIrp->IoStatusBlock.Status) || (STATUS_OPLOCK_BREAK_IN_PROGRESS == pIrp->IoStatusBlock.Status)) { // Handle special success processing having to do with file handle. // ISSUE-May not need lock since it should be only reference IopFileObjectLock(pIrp->FileHandle); SetFlag(pIrp->FileHandle->Flags, FILE_OBJECT_FLAG_CREATE_DONE); IopFileObjectUnlock(pIrp->FileHandle); IopFileObjectReference(pIrp->FileHandle); if (IsAsyncCompletion && irpInternal->Completion.IsAsyncCall) { *irpInternal->Completion.Async.OpOut.Create.pFileHandle = pIrp->FileHandle; } } break; case IRP_TYPE_CLOSE: if (STATUS_SUCCESS == pIrp->IoStatusBlock.Status) { PIO_FILE_OBJECT pFileObject = NULL; SetFlag(pIrp->FileHandle->Flags, FILE_OBJECT_FLAG_CLOSE_DONE); // Note that we must delete the reference from the create // w/o removing the file object value from the IRP (which // will be removed when the IRP is freed). pFileObject = pIrp->FileHandle; IopFileObjectDereference(&pFileObject); } else { LWIO_LOG_ERROR("Unable to close file object, status = 0x%08x", pIrp->IoStatusBlock.Status); } break; case IRP_TYPE_READ: case IRP_TYPE_WRITE: if (IRP_ZCT_OPERATION_PREPARE == pIrp->Args.ReadWrite.ZctOperation) { if (STATUS_SUCCESS == pIrp->IoStatusBlock.Status) { LWIO_ASSERT(pIrp->Args.ReadWrite.ZctCompletionContext); if (IsAsyncCompletion && irpInternal->Completion.IsAsyncCall) { PIRP pCompletionIrp = irpInternal->Completion.Async.OpOut.PrepareZctReadWrite.pCompletionIrp; PVOID pCompletionContext = IopIrpSaveZctIrp( pIrp->FileHandle, pCompletionIrp, pIrp->Args.ReadWrite.ZctCompletionContext); *irpInternal->Completion.Async.OpOut.PrepareZctReadWrite.pCompletionContext = pCompletionContext; } } if (irpInternal->Completion.IsAsyncCall) { IopIrpDereference(&irpInternal->Completion.Async.OpOut.PrepareZctReadWrite.pCompletionIrp); } } break; } if (IsAsyncCompletion) { if (irpInternal->Completion.IsAsyncCall) { *irpInternal->Completion.Async.pIoStatusBlock = pIrp->IoStatusBlock; irpInternal->Completion.Async.Callback( irpInternal->Completion.Async.CallbackContext); } else { LwRtlSetEvent(irpInternal->Completion.Sync.Event); } // // Release reference from IoIrpMarkPending(). // IopIrpDereference(&pIrp); } }