NTSTATUS DokanDispatchSetInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { NTSTATUS status = STATUS_NOT_IMPLEMENTED; PIO_STACK_LOCATION irpSp; PVOID buffer; PFILE_OBJECT fileObject; PDokanCCB ccb; PDokanFCB fcb = NULL; PDokanVCB vcb; ULONG eventLength; PFILE_OBJECT targetFileObject; PEVENT_CONTEXT eventContext; BOOLEAN isPagingIo = FALSE; BOOLEAN fcbLocked = FALSE; PFILE_END_OF_FILE_INFORMATION pInfoEoF = NULL; vcb = DeviceObject->DeviceExtension; __try { DDbgPrint("==> DokanSetInformationn\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_INVALID_PARAMETER; __leave; } if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_INVALID_PARAMETER; __leave; } ccb = (PDokanCCB)fileObject->FsContext2; ASSERT(ccb != NULL); DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); buffer = Irp->AssociatedIrp.SystemBuffer; if (Irp->Flags & IRP_PAGING_IO) { isPagingIo = TRUE; } fcb = ccb->Fcb; ASSERT(fcb != NULL); switch (irpSp->Parameters.SetFile.FileInformationClass) { case FileAllocationInformation: DDbgPrint( " FileAllocationInformation %lld\n", ((PFILE_ALLOCATION_INFORMATION)buffer)->AllocationSize.QuadPart); break; case FileBasicInformation: DDbgPrint(" FileBasicInformation\n"); break; case FileDispositionInformation: DDbgPrint(" FileDispositionInformation\n"); break; case FileEndOfFileInformation: if ((fileObject->SectionObjectPointer != NULL) && (fileObject->SectionObjectPointer->DataSectionObject != NULL)) { pInfoEoF = (PFILE_END_OF_FILE_INFORMATION)buffer; if (!MmCanFileBeTruncated(fileObject->SectionObjectPointer, &pInfoEoF->EndOfFile)) { status = STATUS_USER_MAPPED_FILE; __leave; } if (!isPagingIo) { ExAcquireResourceExclusiveLite(&fcb->PagingIoResource, TRUE); CcFlushCache(&fcb->SectionObjectPointers, NULL, 0, NULL); CcPurgeCacheSection(&fcb->SectionObjectPointers, NULL, 0, FALSE); ExReleaseResourceLite(&fcb->PagingIoResource); } } DDbgPrint(" FileEndOfFileInformation %lld\n", ((PFILE_END_OF_FILE_INFORMATION)buffer)->EndOfFile.QuadPart); break; case FileLinkInformation: DDbgPrint(" FileLinkInformation\n"); break; case FilePositionInformation: { PFILE_POSITION_INFORMATION posInfo; posInfo = (PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ASSERT(posInfo != NULL); DDbgPrint(" FilePositionInformation %lld\n", posInfo->CurrentByteOffset.QuadPart); fileObject->CurrentByteOffset = posInfo->CurrentByteOffset; status = STATUS_SUCCESS; __leave; } break; case FileRenameInformation: DDbgPrint(" FileRenameInformation\n"); break; case FileValidDataLengthInformation: DDbgPrint(" FileValidDataLengthInformation\n"); break; default: DDbgPrint(" unknown type:%d\n", irpSp->Parameters.SetFile.FileInformationClass); break; } // // when this IRP is not handled in swich case // // calcurate the size of EVENT_CONTEXT // it is sum of file name length and size of FileInformation DokanFCBLockRW(fcb); fcbLocked = TRUE; eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length + irpSp->Parameters.SetFile.Length; targetFileObject = irpSp->Parameters.SetFile.FileObject; if (targetFileObject) { DDbgPrint(" FileObject Specified %wZ\n", &(targetFileObject->FileName)); eventLength += targetFileObject->FileName.Length; } eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); if (eventContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; } eventContext->Context = ccb->UserContext; eventContext->Operation.SetFile.FileInformationClass = irpSp->Parameters.SetFile.FileInformationClass; // the size of FileInformation eventContext->Operation.SetFile.BufferLength = irpSp->Parameters.SetFile.Length; // the offset from begining of structure to fill FileInfo eventContext->Operation.SetFile.BufferOffset = FIELD_OFFSET(EVENT_CONTEXT, Operation.SetFile.FileName[0]) + fcb->FileName.Length + sizeof(WCHAR); // the last null char BOOLEAN isRenameOrLink = irpSp->Parameters.SetFile.FileInformationClass == FileRenameInformation || irpSp->Parameters.SetFile.FileInformationClass == FileLinkInformation; if (!isRenameOrLink) { // copy FileInformation RtlCopyMemory( (PCHAR)eventContext + eventContext->Operation.SetFile.BufferOffset, Irp->AssociatedIrp.SystemBuffer, irpSp->Parameters.SetFile.Length); } if (isRenameOrLink) { // We need to hanle FileRenameInformation separetly because the structure // of FILE_RENAME_INFORMATION // has HANDLE type field, which size is different in 32 bit and 64 bit // environment. // This cases problems when driver is 64 bit and user mode library is 32 // bit. PFILE_RENAME_INFORMATION renameInfo = (PFILE_RENAME_INFORMATION)Irp->AssociatedIrp.SystemBuffer; PDOKAN_RENAME_INFORMATION renameContext = (PDOKAN_RENAME_INFORMATION)( (PCHAR)eventContext + eventContext->Operation.SetFile.BufferOffset); // This code assumes FILE_RENAME_INFORMATION and FILE_LINK_INFORMATION // have // the same typse and fields. ASSERT(sizeof(FILE_RENAME_INFORMATION) == sizeof(FILE_LINK_INFORMATION)); renameContext->ReplaceIfExists = renameInfo->ReplaceIfExists; renameContext->FileNameLength = renameInfo->FileNameLength; RtlCopyMemory(renameContext->FileName, renameInfo->FileName, renameInfo->FileNameLength); if (targetFileObject != NULL) { // if Parameters.SetFile.FileObject is specified, replase // FILE_RENAME_INFO's file name by // FileObject's file name. The buffer size is already adjusted. DDbgPrint(" renameContext->FileNameLength %d\n", renameContext->FileNameLength); DDbgPrint(" renameContext->FileName %ws\n", renameContext->FileName); RtlZeroMemory(renameContext->FileName, renameContext->FileNameLength); PFILE_OBJECT parentFileObject = targetFileObject->RelatedFileObject; if (parentFileObject != NULL) { RtlCopyMemory(renameContext->FileName, parentFileObject->FileName.Buffer, parentFileObject->FileName.Length); RtlStringCchCatW(renameContext->FileName, NTSTRSAFE_MAX_CCH, L"\\"); RtlStringCchCatW(renameContext->FileName, NTSTRSAFE_MAX_CCH, targetFileObject->FileName.Buffer); renameContext->FileNameLength = targetFileObject->FileName.Length + parentFileObject->FileName.Length + sizeof(WCHAR); } else { RtlCopyMemory(renameContext->FileName, targetFileObject->FileName.Buffer, targetFileObject->FileName.Length); renameContext->FileNameLength = targetFileObject->FileName.Length; } } if (irpSp->Parameters.SetFile.FileInformationClass == FileRenameInformation) { DDbgPrint(" rename: %wZ => %ls, FileCount = %u\n", fcb->FileName, renameContext->FileName, (ULONG)fcb->FileCount); } } // copy the file name eventContext->Operation.SetFile.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.SetFile.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // FsRtlCheckOpLock is called with non-NULL completion routine - not blocking. status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, eventContext, DokanOplockComplete, DokanPrePostIrp); // // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted // to service an oplock break and we need to leave now. // if (status != STATUS_SUCCESS) { if (status == STATUS_PENDING) { DDbgPrint(" FsRtlCheckOplock returned STATUS_PENDING\n"); } else { DokanFreeEventContext(eventContext); } __leave; } // register this IRP to waiting IRP list and make it pending status status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); } __finally { if(fcbLocked) DokanFCBUnlock(fcb); DokanCompleteIrpRequest(Irp, status, 0); DDbgPrint("<== DokanSetInformation\n"); } return status; }
VOID DokanCompleteSetInformation(__in PIRP_ENTRY IrpEntry, __in PEVENT_INFORMATION EventInfo) { PIRP irp; PIO_STACK_LOCATION irpSp; NTSTATUS status; ULONG info = 0; PDokanCCB ccb; PDokanFCB fcb = NULL; UNICODE_STRING oldFileName; FILE_INFORMATION_CLASS infoClass; irp = IrpEntry->Irp; status = EventInfo->Status; __try { DDbgPrint("==> DokanCompleteSetInformation\n"); irpSp = IrpEntry->IrpSp; ccb = IrpEntry->FileObject->FsContext2; ASSERT(ccb != NULL); KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&ccb->Resource, TRUE); fcb = ccb->Fcb; ASSERT(fcb != NULL); DokanFCBLockRW(fcb); ccb->UserContext = EventInfo->Context; info = EventInfo->BufferLength; infoClass = irpSp->Parameters.SetFile.FileInformationClass; RtlZeroMemory(&oldFileName, sizeof(UNICODE_STRING)); if (NT_SUCCESS(status)) { if (infoClass == FileDispositionInformation) { if (EventInfo->Operation.Delete.DeleteOnClose) { if (!MmFlushImageSection(&fcb->SectionObjectPointers, MmFlushForDelete)) { DDbgPrint(" Cannot delete user mapped image\n"); status = STATUS_CANNOT_DELETE; } else { DokanCCBFlagsSetBit(ccb, DOKAN_DELETE_ON_CLOSE); DokanFCBFlagsSetBit(fcb, DOKAN_DELETE_ON_CLOSE); DDbgPrint(" FileObject->DeletePending = TRUE\n"); IrpEntry->FileObject->DeletePending = TRUE; } } else { DokanCCBFlagsClearBit(ccb, DOKAN_DELETE_ON_CLOSE); DokanFCBFlagsClearBit(fcb, DOKAN_DELETE_ON_CLOSE); DDbgPrint(" FileObject->DeletePending = FALSE\n"); IrpEntry->FileObject->DeletePending = FALSE; } } // if rename is executed, reassign the file name if (infoClass == FileRenameInformation) { PVOID buffer = NULL; // this is used to inform rename in the bellow switch case oldFileName.Buffer = fcb->FileName.Buffer; oldFileName.Length = (USHORT)fcb->FileName.Length; oldFileName.MaximumLength = (USHORT)fcb->FileName.Length; // copy new file name buffer = ExAllocatePool(EventInfo->BufferLength + sizeof(WCHAR)); if (buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; ExReleaseResourceLite(&ccb->Resource); KeLeaveCriticalRegion(); __leave; } fcb->FileName.Buffer = buffer; ASSERT(fcb->FileName.Buffer != NULL); RtlZeroMemory(fcb->FileName.Buffer, EventInfo->BufferLength + sizeof(WCHAR)); RtlCopyMemory(fcb->FileName.Buffer, EventInfo->Buffer, EventInfo->BufferLength); fcb->FileName.Length = (USHORT)EventInfo->BufferLength; fcb->FileName.MaximumLength = (USHORT)EventInfo->BufferLength; } } ExReleaseResourceLite(&ccb->Resource); KeLeaveCriticalRegion(); if (NT_SUCCESS(status)) { switch (irpSp->Parameters.SetFile.FileInformationClass) { case FileAllocationInformation: DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); break; case FileBasicInformation: DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION, FILE_ACTION_MODIFIED); break; case FileDispositionInformation: if (IrpEntry->FileObject->DeletePending) { if (DokanFCBFlagsIsSet(fcb, DOKAN_FILE_DIRECTORY)) { DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_REMOVED); } else { DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); } } break; case FileEndOfFileInformation: DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); break; case FileLinkInformation: // TODO: should check whether this is a directory // TODO: should notify new link name // DokanNotifyReportChange(vcb, ccb, FILE_NOTIFY_CHANGE_FILE_NAME, // FILE_ACTION_ADDED); break; case FilePositionInformation: // this is never used break; case FileRenameInformation: { DokanNotifyReportChange0(fcb, &oldFileName, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME); // free old file name ExFreePool(oldFileName.Buffer); DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME); } break; case FileValidDataLengthInformation: DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); break; default: DDbgPrint(" unknown type:%d\n", irpSp->Parameters.SetFile.FileInformationClass); break; } } } __finally { if(fcb) DokanFCBUnlock(fcb); DokanCompleteIrpRequest(irp, status, info); DDbgPrint("<== DokanCompleteSetInformation\n"); } }
NTSTATUS DokanDispatchCleanup(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) /*++ Routine Description: This device control dispatcher handles Cleanup IRP. Arguments: DeviceObject - Context for the activity. Irp - The device control argument block. Return Value: NTSTATUS --*/ { PDokanVCB vcb; PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_INVALID_PARAMETER; PFILE_OBJECT fileObject; PDokanCCB ccb = NULL; PDokanFCB fcb = NULL; PEVENT_CONTEXT eventContext; ULONG eventLength; DOKAN_INIT_LOGGER(logger, DeviceObject->DriverObject, IRP_MJ_CLEANUP); __try { DDbgPrint("==> DokanCleanup\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); // Cleanup must be success in any case if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_SUCCESS; __leave; } vcb = DeviceObject->DeviceExtension; if (vcb == NULL) { DDbgPrint(" No device extension\n"); status = STATUS_SUCCESS; __leave; } if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_SUCCESS; __leave; } ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); if (fcb->IsKeepalive) { DokanFCBLockRW(fcb); BOOLEAN shouldUnmount = ccb->IsKeepaliveActive; if (shouldUnmount) { // Here we intentionally let the VCB-level flag stay set, because // there's no sense in having an opportunity for an "operation timeout // unmount" in this case. ccb->IsKeepaliveActive = FALSE; } DokanFCBUnlock(fcb); if (shouldUnmount) { if (IsUnmountPendingVcb(vcb)) { DokanLogInfo(&logger, L"Ignoring keepalive close because unmount is already in" L" progress."); } else { DokanLogInfo(&logger, L"Unmounting due to keepalive close."); DokanUnmount(vcb->Dcb); } } } if (fcb->BlockUserModeDispatch) { status = STATUS_SUCCESS; __leave; } FlushFcb(fcb, fileObject); DokanFCBLockRW(fcb); eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length; eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); if (eventContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; DokanFCBUnlock(fcb); __leave; } fileObject->Flags |= FO_CLEANUP_COMPLETE; eventContext->Context = ccb->UserContext; eventContext->FileFlags |= DokanCCBFlagsGet(ccb); // DDbgPrint(" get Context %X\n", (ULONG)ccb->UserContext); // copy the filename to EventContext from ccb eventContext->Operation.Cleanup.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.Cleanup.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // FsRtlCheckOpLock is called with non-NULL completion routine - not blocking. status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, eventContext, DokanOplockComplete, DokanPrePostIrp); DokanFCBUnlock(fcb); // // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted // to service an oplock break and we need to leave now. // if (status != STATUS_SUCCESS) { if (status == STATUS_PENDING) { DDbgPrint(" FsRtlCheckOplock returned STATUS_PENDING\n"); } else { DokanFreeEventContext(eventContext); } __leave; } // register this IRP to pending IRP list status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); } __finally { DokanCompleteIrpRequest(Irp, status, 0); DDbgPrint("<== DokanCleanup\n"); } return status; }
NTSTATUS DokanDispatchCleanup(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) /*++ Routine Description: This device control dispatcher handles Cleanup IRP. Arguments: DeviceObject - Context for the activity. Irp - The device control argument block. Return Value: NTSTATUS --*/ { PDokanVCB vcb; PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_INVALID_PARAMETER; PFILE_OBJECT fileObject; PDokanCCB ccb = NULL; PDokanFCB fcb = NULL; PEVENT_CONTEXT eventContext; ULONG eventLength; __try { DDbgPrint("==> DokanCleanup\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); // Cleanup must be success in any case if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_SUCCESS; __leave; } vcb = DeviceObject->DeviceExtension; if (vcb == NULL) { DDbgPrint(" No device extension\n"); status = STATUS_SUCCESS; __leave; } if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_SUCCESS; __leave; } ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); FlushFcb(fcb, fileObject); DokanFCBLockRW(fcb); eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length; eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); if (eventContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; DokanFCBUnlock(fcb); __leave; } fileObject->Flags |= FO_CLEANUP_COMPLETE; eventContext->Context = ccb->UserContext; eventContext->FileFlags |= DokanCCBFlagsGet(ccb); // DDbgPrint(" get Context %X\n", (ULONG)ccb->UserContext); // copy the filename to EventContext from ccb eventContext->Operation.Cleanup.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.Cleanup.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // FsRtlCheckOpLock is called with non-NULL completion routine - not blocking. status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, eventContext, DokanOplockComplete, DokanPrePostIrp); DokanFCBUnlock(fcb); // // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted // to service an oplock break and we need to leave now. // if (status != STATUS_SUCCESS) { if (status == STATUS_PENDING) { DDbgPrint(" FsRtlCheckOplock returned STATUS_PENDING\n"); } else { DokanFreeEventContext(eventContext); } __leave; } // register this IRP to pending IRP list status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); } __finally { DokanCompleteIrpRequest(Irp, status, 0); DDbgPrint("<== DokanCleanup\n"); } return status; }
NTSTATUS DokanCommonLockControl(__in PIRP Irp) { NTSTATUS Status = STATUS_SUCCESS; PDokanFCB Fcb; PDokanCCB Ccb; PFILE_OBJECT fileObject; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); DDbgPrint("==> DokanCommonLockControl\n"); PAGED_CODE(); fileObject = irpSp->FileObject; DokanPrintFileName(fileObject); Ccb = fileObject->FsContext2; if (Ccb == NULL || Ccb->Identifier.Type != CCB) { DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } Fcb = Ccb->Fcb; if (Fcb == NULL || Fcb->Identifier.Type != FCB) { DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } DokanFCBLockRW(Fcb); // // If the file is not a user file open then we reject the request // as an invalid parameter // if (FlagOn(Fcb->Flags, DOKAN_FILE_DIRECTORY)) { DDbgPrint(" DokanCommonLockControl -> STATUS_INVALID_PARAMETER\n", 0); DokanFCBUnlock(Fcb); return STATUS_INVALID_PARAMETER; } try { // // We check whether we can proceed // based on the state of the file oplocks. // #if (NTDDI_VERSION >= NTDDI_WIN8) if (((IRP_MN_LOCK == irpSp->MinorFunction) && ((ULONGLONG)irpSp->Parameters.LockControl.ByteOffset.QuadPart < (ULONGLONG)Fcb->AdvancedFCBHeader.AllocationSize.QuadPart)) || ((IRP_MN_LOCK != irpSp->MinorFunction) && FsRtlAreThereWaitingFileLocks(&Fcb->FileLock))) { // // Check whether we can proceed based on the state of file oplocks if doing // an operation that interferes with oplocks. Those operations are: // // 1. Lock a range within the file's AllocationSize. // 2. Unlock a range when there are waiting locks on the file. This one // is not guaranteed to interfere with oplocks, but it could, as // unlocking this range might cause a waiting lock to be granted // within AllocationSize! // #endif // Dokan DokanOplockComplete sends the operation to user mode, which isn't // what we want to do // so now wait for the oplock to be broken (pass in NULL for the callback) Status = FsRtlCheckOplock(DokanGetFcbOplock(Fcb), Irp, NULL /* EventContext */, NULL /*DokanOplockComplete*/, NULL); #if (NTDDI_VERSION >= NTDDI_WIN8) } #endif // If we were waiting for the callback, then STATUS_PENDING would be ok too if (Status != STATUS_SUCCESS) { __leave; } // // Now call the FsRtl routine to do the actual processing of the // Lock request // Status = FsRtlProcessFileLock(&Fcb->FileLock, Irp, NULL); } finally { DokanFCBUnlock(Fcb); } DDbgPrint("<== DokanCommonLockControl\n"); return Status; }
NTSTATUS DokanDispatchLock(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_INVALID_PARAMETER; PFILE_OBJECT fileObject; PDokanCCB ccb; PDokanFCB fcb = NULL; PDokanVCB vcb; PDokanDCB dcb; PEVENT_CONTEXT eventContext = NULL; ULONG eventLength; BOOLEAN completeIrp = TRUE; __try { DDbgPrint("==> DokanLock\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_INVALID_PARAMETER; __leave; } vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_INVALID_PARAMETER; __leave; } dcb = vcb->Dcb; DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); switch (irpSp->MinorFunction) { case IRP_MN_LOCK: DDbgPrint(" IRP_MN_LOCK\n"); break; case IRP_MN_UNLOCK_ALL: DDbgPrint(" IRP_MN_UNLOCK_ALL\n"); break; case IRP_MN_UNLOCK_ALL_BY_KEY: DDbgPrint(" IRP_MN_UNLOCK_ALL_BY_KEY\n"); break; case IRP_MN_UNLOCK_SINGLE: DDbgPrint(" IRP_MN_UNLOCK_SINGLE\n"); break; default: DDbgPrint(" unknown function : %d\n", irpSp->MinorFunction); break; } ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); DokanFCBLockRW(fcb); if (dcb->FileLockInUserMode) { eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length; eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); if (eventContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; } eventContext->Context = ccb->UserContext; DDbgPrint(" get Context %X\n", (ULONG)ccb->UserContext); // copy file name to be locked eventContext->Operation.Lock.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.Lock.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // parameters of Lock eventContext->Operation.Lock.ByteOffset = irpSp->Parameters.LockControl.ByteOffset; if (irpSp->Parameters.LockControl.Length != NULL) { eventContext->Operation.Lock.Length.QuadPart = irpSp->Parameters.LockControl.Length->QuadPart; } else { DDbgPrint(" LockControl.Length = NULL\n"); } eventContext->Operation.Lock.Key = irpSp->Parameters.LockControl.Key; // register this IRP to waiting IRP list and make it pending status status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); } else { status = DokanCommonLockControl(Irp); completeIrp = FALSE; } } __finally { if(fcb) DokanFCBUnlock(fcb); if (completeIrp) { DokanCompleteIrpRequest(Irp, status, 0); } DDbgPrint("<== DokanLock\n"); } return status; }
NTSTATUS DokanOplockRequest(__in PIRP *pIrp) { NTSTATUS Status = STATUS_SUCCESS; ULONG FsControlCode; PDokanDCB Dcb; PDokanVCB Vcb; PDokanFCB Fcb = NULL; PDokanCCB Ccb; PFILE_OBJECT fileObject; PIRP Irp = *pIrp; ULONG OplockCount = 0; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); BOOLEAN AcquiredVcb = FALSE; BOOLEAN AcquiredFcb = FALSE; #if (NTDDI_VERSION >= NTDDI_WIN7) PREQUEST_OPLOCK_INPUT_BUFFER InputBuffer = NULL; ULONG InputBufferLength; ULONG OutputBufferLength; #endif PAGED_CODE(); // // Save some references to make our life a little easier // FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; fileObject = IrpSp->FileObject; DokanPrintFileName(fileObject); Ccb = fileObject->FsContext2; if (Ccb == NULL || Ccb->Identifier.Type != CCB) { DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } Fcb = Ccb->Fcb; if (Fcb == NULL || Fcb->Identifier.Type != FCB) { DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } Vcb = Fcb->Vcb; if (Vcb == NULL || Vcb->Identifier.Type != VCB) { DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } Dcb = Vcb->Dcb; #if (NTDDI_VERSION >= NTDDI_WIN7) // // Get the input & output buffer lengths and pointers. // if (FsControlCode == FSCTL_REQUEST_OPLOCK) { InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; InputBuffer = (PREQUEST_OPLOCK_INPUT_BUFFER)Irp->AssociatedIrp.SystemBuffer; OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; // // Check for a minimum length on the input and ouput buffers. // if ((InputBufferLength < sizeof(REQUEST_OPLOCK_INPUT_BUFFER)) || (OutputBufferLength < sizeof(REQUEST_OPLOCK_OUTPUT_BUFFER))) { DDbgPrint(" DokanOplockRequest STATUS_BUFFER_TOO_SMALL\n"); return STATUS_BUFFER_TOO_SMALL; } } // // If the oplock request is on a directory it must be for a Read or // Read-Handle // oplock only. // // FIXME - should we synchronize access here on Fcb->Flags? if (FlagOn(Fcb->Flags, DOKAN_FILE_DIRECTORY) && ((FsControlCode != FSCTL_REQUEST_OPLOCK) || !FsRtlOplockIsSharedRequest(Irp))) { DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } #endif // // Use a try finally to free the Fcb/Vcb // try { // // We grab the Fcb exclusively for oplock requests, shared for oplock // break acknowledgement. // if ((FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_1) || (FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) || (FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) || (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) #if (NTDDI_VERSION >= NTDDI_WIN7) || ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn(InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_REQUEST)) #endif ) { AcquiredVcb = ExAcquireResourceSharedLite(&(Fcb->Vcb->Resource), TRUE); DokanFCBLockRW(Fcb); AcquiredFcb = TRUE; #if (NTDDI_VERSION >= NTDDI_WIN7) if (!Dcb->FileLockInUserMode && FsRtlOplockIsSharedRequest(Irp)) { #else if (!Dcb->FileLockInUserMode && FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { #endif // // Byte-range locks are only valid on files. // if (!FlagOn(Fcb->Flags, DOKAN_FILE_DIRECTORY)) { // // Set OplockCount to nonzero if FsRtl denies access // based on current byte-range lock state. // #if (NTDDI_VERSION >= NTDDI_WIN8) OplockCount = (ULONG)!FsRtlCheckLockForOplockRequest( &Fcb->FileLock, &Fcb->AdvancedFCBHeader.AllocationSize); #elif (NTDDI_VERSION >= NTDDI_WIN7) OplockCount = (ULONG)FsRtlAreThereCurrentOrInProgressFileLocks(&Fcb->FileLock); #else OplockCount = (ULONG)FsRtlAreThereCurrentFileLocks(&Fcb->FileLock); #endif } } else { // Shouldn't be something like UncleanCount counter and not FileCount // here? OplockCount = Fcb->FileCount; } } else if ((FsControlCode == FSCTL_OPLOCK_BREAK_ACKNOWLEDGE) || (FsControlCode == FSCTL_OPBATCH_ACK_CLOSE_PENDING) || (FsControlCode == FSCTL_OPLOCK_BREAK_NOTIFY) || (FsControlCode == FSCTL_OPLOCK_BREAK_ACK_NO_2) #if (NTDDI_VERSION >= NTDDI_WIN7) || ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn(InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_ACK)) #endif ) { DokanFCBLockRO(Fcb); AcquiredFcb = TRUE; #if (NTDDI_VERSION >= NTDDI_WIN7) } else if (FsControlCode == FSCTL_REQUEST_OPLOCK) { // // The caller didn't provide either REQUEST_OPLOCK_INPUT_FLAG_REQUEST or // REQUEST_OPLOCK_INPUT_FLAG_ACK on the input buffer. // DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } else { #else } else { #endif DDbgPrint(" DokanOplockRequest STATUS_INVALID_PARAMETER\n"); return STATUS_INVALID_PARAMETER; } // // Fail batch, filter, and handle oplock requests if the file is marked // for delete. // if (((FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) || (FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) #if (NTDDI_VERSION >= NTDDI_WIN7) || ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn(InputBuffer->RequestedOplockLevel, OPLOCK_LEVEL_CACHE_HANDLE)) #endif ) && FlagOn(Fcb->Flags, DOKAN_DELETE_ON_CLOSE)) { DDbgPrint(" DokanOplockRequest STATUS_DELETE_PENDING\n"); return STATUS_DELETE_PENDING; } // // Call the FsRtl routine to grant/acknowledge oplock. // Status = FsRtlOplockFsctrl(DokanGetFcbOplock(Fcb), Irp, OplockCount); // // Once we call FsRtlOplockFsctrl, we no longer own the IRP and we should // not complete it. // *pIrp = NULL; } finally {