VOID DokanCompleteCleanup(__in PIRP_ENTRY IrpEntry, __in PEVENT_INFORMATION EventInfo) { PIRP irp; PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_SUCCESS; PDokanCCB ccb; PDokanFCB fcb; PDokanVCB vcb; PFILE_OBJECT fileObject; DDbgPrint("==> DokanCompleteCleanup\n"); irp = IrpEntry->Irp; irpSp = IrpEntry->IrpSp; fileObject = IrpEntry->FileObject; ASSERT(fileObject != NULL); ccb = fileObject->FsContext2; ASSERT(ccb != NULL); ccb->UserContext = EventInfo->Context; // DDbgPrint(" set Context %X\n", (ULONG)ccb->UserContext); fcb = ccb->Fcb; ASSERT(fcb != NULL); vcb = fcb->Vcb; status = EventInfo->Status; DokanFCBLockRO(fcb); if (DokanFCBFlagsIsSet(fcb, DOKAN_DELETE_ON_CLOSE)) { 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); } } DokanFCBUnlock(fcb); // // Unlock all outstanding file locks. // (VOID) FsRtlFastUnlockAll(&fcb->FileLock, fileObject, IoGetRequestorProcess(irp), NULL); if (DokanFCBFlagsIsSet(fcb, DOKAN_FILE_DIRECTORY)) { FsRtlNotifyCleanup(vcb->NotifySync, &vcb->DirNotifyList, ccb); } IoRemoveShareAccess(irpSp->FileObject, &fcb->ShareAccess); DokanCompleteIrpRequest(irp, status, 0); DDbgPrint("<== DokanCompleteCleanup\n"); }
NTSTATUS DokanNotifyChangeDirectory(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { PDokanCCB ccb; PDokanFCB fcb; PFILE_OBJECT fileObject; PIO_STACK_LOCATION irpSp; PDokanVCB vcb; DDbgPrint("\tNotifyChangeDirectory\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB) { return STATUS_INVALID_PARAMETER; } ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); if (!DokanFCBFlagsIsSet(fcb, DOKAN_FILE_DIRECTORY)) { return STATUS_INVALID_PARAMETER; } DDbgPrint("\tFsRtlNotifyFullChangeDirectory FileName %wZ\n", &fcb->FileName); DokanFCBLockRO(fcb); FsRtlNotifyFullChangeDirectory( vcb->NotifySync, &vcb->DirNotifyList, ccb, (PSTRING)&fcb->FileName, (irpSp->Flags & SL_WATCH_TREE) ? TRUE : FALSE, FALSE, irpSp->Parameters.NotifyDirectory.CompletionFilter, Irp, NULL, NULL); DokanFCBUnlock(fcb); return STATUS_PENDING; }
NTSTATUS DokanDispatchWrite(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { PIO_STACK_LOCATION irpSp; PFILE_OBJECT fileObject; NTSTATUS status = STATUS_INVALID_PARAMETER; PEVENT_CONTEXT eventContext; ULONG eventLength; PDokanCCB ccb; PDokanFCB fcb = NULL; PDokanVCB vcb; PVOID buffer; BOOLEAN writeToEoF = FALSE; BOOLEAN isPagingIo = FALSE; BOOLEAN isNonCached = FALSE; BOOLEAN isSynchronousIo = FALSE; BOOLEAN fcbLocked = FALSE; __try { DDbgPrint("==> DokanWrite\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; // // If this is a zero length write then return SUCCESS immediately. // if (irpSp->Parameters.Write.Length == 0) { DDbgPrint(" Parameters.Write.Length == 0\n"); Irp->IoStatus.Information = 0; status = STATUS_SUCCESS; __leave; } if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_INVALID_DEVICE_REQUEST; __leave; } vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_INVALID_PARAMETER; __leave; } DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); if (DokanFCBFlagsIsSet(fcb, DOKAN_FILE_DIRECTORY)) { status = STATUS_INVALID_PARAMETER; __leave; } if (Irp->MdlAddress) { DDbgPrint(" use MdlAddress\n"); buffer = MmGetSystemAddressForMdlNormalSafe(Irp->MdlAddress); } else { DDbgPrint(" use UserBuffer\n"); buffer = Irp->UserBuffer; } if (buffer == NULL) { DDbgPrint(" buffer == NULL\n"); status = STATUS_INVALID_PARAMETER; __leave; } if (irpSp->Parameters.Write.ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE && irpSp->Parameters.Write.ByteOffset.HighPart == -1) { writeToEoF = TRUE; } if (Irp->Flags & IRP_PAGING_IO) { isPagingIo = TRUE; } if (Irp->Flags & IRP_NOCACHE) { isNonCached = TRUE; } if (fileObject->Flags & FO_SYNCHRONOUS_IO) { isSynchronousIo = TRUE; } if (!isPagingIo && (fileObject->SectionObjectPointer != NULL) && (fileObject->SectionObjectPointer->DataSectionObject != NULL)) { ExAcquireResourceExclusiveLite(&fcb->PagingIoResource, TRUE); CcFlushCache(&fcb->SectionObjectPointers, writeToEoF ? NULL : &irpSp->Parameters.Write.ByteOffset, irpSp->Parameters.Write.Length, NULL); CcPurgeCacheSection(&fcb->SectionObjectPointers, writeToEoF ? NULL : &irpSp->Parameters.Write.ByteOffset, irpSp->Parameters.Write.Length, FALSE); ExReleaseResourceLite(&fcb->PagingIoResource); } // Cannot write at end of the file when using paging IO if (writeToEoF && isPagingIo) { DDbgPrint(" writeToEoF & isPagingIo\n"); Irp->IoStatus.Information = 0; status = STATUS_SUCCESS; __leave; } // the length of EventContext is sum of length to write and length of file // name DokanFCBLockRO(fcb); fcbLocked = TRUE; eventLength = sizeof(EVENT_CONTEXT) + irpSp->Parameters.Write.Length + fcb->FileName.Length; eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); // no more memory! if (eventContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; } eventContext->Context = ccb->UserContext; // DDbgPrint(" get Context %X\n", (ULONG)ccb->UserContext); // When the length is bigger than usual event notitfication buffer, // saves pointer in DiverContext to copy EventContext after allocating // more bigger memory. Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = eventContext; if (isPagingIo) { DDbgPrint(" Paging IO\n"); eventContext->FileFlags |= DOKAN_PAGING_IO; } if (isSynchronousIo) { DDbgPrint(" Synchronous IO\n"); eventContext->FileFlags |= DOKAN_SYNCHRONOUS_IO; } if (isNonCached) { DDbgPrint(" Nocache\n"); eventContext->FileFlags |= DOKAN_NOCACHE; } // offset of file to write eventContext->Operation.Write.ByteOffset = irpSp->Parameters.Write.ByteOffset; if (writeToEoF) { eventContext->FileFlags |= DOKAN_WRITE_TO_END_OF_FILE; DDbgPrint(" WriteOffset = end of file\n"); } if (isSynchronousIo && ((irpSp->Parameters.Write.ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) && (irpSp->Parameters.Write.ByteOffset.HighPart == -1))) { // NOTE: // http://msdn.microsoft.com/en-us/library/ms795960.aspx // Do not check IrpSp->Parameters.Write.ByteOffset.QuadPart == 0 // Probably the document is wrong. eventContext->Operation.Write.ByteOffset.QuadPart = fileObject->CurrentByteOffset.QuadPart; } // the size of buffer to write eventContext->Operation.Write.BufferLength = irpSp->Parameters.Write.Length; // the offset from the begining of structure // the contents to write will be copyed to this offset eventContext->Operation.Write.BufferOffset = FIELD_OFFSET(EVENT_CONTEXT, Operation.Write.FileName[0]) + fcb->FileName.Length + sizeof(WCHAR); // adds last null char // copies the content to write to EventContext RtlCopyMemory((PCHAR)eventContext + eventContext->Operation.Write.BufferOffset, buffer, irpSp->Parameters.Write.Length); // copies file name eventContext->Operation.Write.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.Write.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // When eventlength is less than event notification buffer, // returns it to user-mode using pending event. if (eventLength <= EVENT_CONTEXT_MAX_SIZE) { DDbgPrint(" Offset %d:%d, Length %d\n", irpSp->Parameters.Write.ByteOffset.HighPart, irpSp->Parameters.Write.ByteOffset.LowPart, irpSp->Parameters.Write.Length); // EventContext is no longer needed, clear it Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; // // We now check whether we can proceed based on the state of // the file oplocks. // // FsRtlCheckOpLock is called with non-NULL completion routine - not blocking. if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { 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 IRP waiting list and make it pending status status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); // Resuests bigger memory // eventContext will be freed later using // Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] } else { // the length at lest file name can be stored ULONG requestContextLength = max( sizeof(EVENT_CONTEXT), eventContext->Operation.Write.BufferOffset); PEVENT_CONTEXT requestContext = AllocateEventContext(vcb->Dcb, Irp, requestContextLength, ccb); // no more memory! if (requestContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; DokanFreeEventContext(eventContext); __leave; } DDbgPrint(" Offset %d:%d, Length %d (request)\n", irpSp->Parameters.Write.ByteOffset.HighPart, irpSp->Parameters.Write.ByteOffset.LowPart, irpSp->Parameters.Write.Length); // copies from begining of EventContext to the end of file name RtlCopyMemory(requestContext, eventContext, eventContext->Operation.Write.BufferOffset); // puts actual size of RequestContext requestContext->Length = requestContextLength; // requsts enough size to copy EventContext requestContext->Operation.Write.RequestLength = eventLength; // // We now check whether we can proceed based on the state of // the file oplocks. // // FsRtlCheckOpLock is called with non-NULL completion routine - not blocking. if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, requestContext, 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(requestContext); Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; DokanFreeEventContext(eventContext); } __leave; } } // regiters this IRP to IRP wainting list and make it pending status status = DokanRegisterPendingIrp(DeviceObject, Irp, requestContext, 0); } } __finally { if(fcbLocked) DokanFCBUnlock(fcb); DokanCompleteIrpRequest(Irp, status, 0); DDbgPrint("<== DokanWrite\n"); } return status; }
NTSTATUS DokanDispatchQueryInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { NTSTATUS status = STATUS_NOT_IMPLEMENTED; PIO_STACK_LOCATION irpSp; PFILE_OBJECT fileObject; PDokanCCB ccb; PDokanFCB fcb = NULL; PDokanVCB vcb; ULONG info = 0; ULONG eventLength; PEVENT_CONTEXT eventContext; BOOLEAN isNormalized = FALSE; // PAGED_CODE(); __try { DDbgPrint("==> DokanQueryInformation\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; DDbgPrint(" FileInfoClass %d\n", irpSp->Parameters.QueryFile.FileInformationClass); DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_INVALID_PARAMETER; __leave; } DokanPrintFileName(fileObject); /* if (fileObject->FsContext2 == NULL && fileObject->FileName.Length == 0) { // volume open? status = STATUS_SUCCESS; __leave; }*/ vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_INVALID_PARAMETER; __leave; } ccb = (PDokanCCB)fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); DokanFCBLockRO(fcb); switch (irpSp->Parameters.QueryFile.FileInformationClass) { case FileBasicInformation: DDbgPrint(" FileBasicInformation\n"); break; case FileInternalInformation: DDbgPrint(" FileInternalInformation\n"); break; case FileEaInformation: DDbgPrint(" FileEaInformation\n"); break; case FileStandardInformation: DDbgPrint(" FileStandardInformation\n"); break; case FileAllInformation: DDbgPrint(" FileAllInformation\n"); break; case FileAlternateNameInformation: DDbgPrint(" FileAlternateNameInformation\n"); break; case FileAttributeTagInformation: DDbgPrint(" FileAttributeTagInformation\n"); break; case FileCompressionInformation: DDbgPrint(" FileCompressionInformation\n"); break; case FileNormalizedNameInformation: DDbgPrint(" FileNormalizedNameInformation\n"); isNormalized = TRUE; case FileNameInformation: { PFILE_NAME_INFORMATION nameInfo; DDbgPrint(" FileNameInformation\n"); nameInfo = (PFILE_NAME_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ASSERT(nameInfo != NULL); BOOLEAN isNetworkFileSystem = (vcb->Dcb->VolumeDeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM); PUNICODE_STRING fileName = &fcb->FileName; USHORT length = fcb->FileName.Length; BOOLEAN doConcat = FALSE; if (isNetworkFileSystem) { if (fcb->FileName.Length == 0 || fcb->FileName.Buffer[0] != L'\\') { DDbgPrint(" NetworkFileSystem has no root folder. So return the full device name \n"); fileName = vcb->Dcb->DiskDeviceName; length = fileName->Length; } else { if (isNormalized) { DDbgPrint(" FullFileName should be returned \n"); fileName = vcb->Dcb->DiskDeviceName; length = fileName->Length + vcb->Dcb->DiskDeviceName->Length; doConcat = TRUE; } } } if (irpSp->Parameters.QueryFile.Length < sizeof(FILE_NAME_INFORMATION) + length) { info = irpSp->Parameters.QueryFile.Length; status = STATUS_BUFFER_OVERFLOW; } else { RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, irpSp->Parameters.QueryFile.Length); nameInfo->FileNameLength = fileName->Length; RtlCopyMemory(&nameInfo->FileName[0], fileName->Buffer, fileName->Length); if (doConcat) { DDbgPrint(" Concat the devicename with the filename to get the fullname of the file \n"); RtlStringCchCatW(nameInfo->FileName, NTSTRSAFE_MAX_CCH, fcb->FileName.Buffer); } info = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + length; status = STATUS_SUCCESS; } __leave; } break; case FileNetworkOpenInformation: DDbgPrint(" FileNetworkOpenInformation\n"); break; case FilePositionInformation: { PFILE_POSITION_INFORMATION posInfo; DDbgPrint(" FilePositionInformation\n"); if (irpSp->Parameters.QueryFile.Length < sizeof(FILE_POSITION_INFORMATION)) { status = STATUS_INFO_LENGTH_MISMATCH; } else { posInfo = (PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ASSERT(posInfo != NULL); RtlZeroMemory(posInfo, sizeof(FILE_POSITION_INFORMATION)); if (fileObject->CurrentByteOffset.QuadPart < 0) { status = STATUS_INVALID_PARAMETER; } else { // set the current file offset posInfo->CurrentByteOffset = fileObject->CurrentByteOffset; info = sizeof(FILE_POSITION_INFORMATION); status = STATUS_SUCCESS; } } __leave; } break; case FileStreamInformation: DDbgPrint(" FileStreamInformation\n"); break; case FileStandardLinkInformation: DDbgPrint(" FileStandardLinkInformation\n"); break; case FileNetworkPhysicalNameInformation: DDbgPrint(" FileNetworkPhysicalNameInformation\n"); break; case FileRemoteProtocolInformation: DDbgPrint(" FileRemoteProtocolInformation\n"); break; default: DDbgPrint(" unknown type:%d\n", irpSp->Parameters.QueryFile.FileInformationClass); break; } // if it is not treadted in swich case // calculate the length of EVENT_CONTEXT // sum of it's size and file name length 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); eventContext->Operation.File.FileInformationClass = irpSp->Parameters.QueryFile.FileInformationClass; // bytes length which is able to be returned eventContext->Operation.File.BufferLength = irpSp->Parameters.QueryFile.Length; // copy file name to EventContext from FCB eventContext->Operation.File.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.File.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // register this IRP to pending IPR list status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); } __finally { if(fcb) DokanFCBUnlock(fcb); DokanCompleteIrpRequest(Irp, status, info); DDbgPrint("<== DokanQueryInformation\n"); } return status; }
NTSTATUS DokanDispatchRead(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) /*++ Routine Description: This device control dispatcher handles read IRPs. Arguments: DeviceObject - Context for the activity. Irp - The device control argument block. Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpSp; PFILE_OBJECT fileObject; ULONG bufferLength; LARGE_INTEGER byteOffset; NTSTATUS status = STATUS_INVALID_PARAMETER; ULONG readLength = 0; PDokanCCB ccb; PDokanFCB fcb = NULL; PDokanVCB vcb; PVOID currentAddress = NULL; PEVENT_CONTEXT eventContext; ULONG eventLength; BOOLEAN fcbLocked = FALSE; BOOLEAN isPagingIo = FALSE; BOOLEAN isSynchronousIo = FALSE; BOOLEAN noCache = FALSE; __try { DDbgPrint("==> DokanRead\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; // // If this is a zero length read then return SUCCESS immediately. // if (irpSp->Parameters.Read.Length == 0) { DDbgPrint(" Parameters.Read.Length == 0 \n"); Irp->IoStatus.Information = 0; status = STATUS_SUCCESS; __leave; } if (irpSp->MinorFunction == IRP_MN_COMPLETE) { Irp->MdlAddress = NULL; status = STATUS_SUCCESS; __leave; } if (fileObject == NULL && Irp->MdlAddress != NULL) { DDbgPrint(" Reads by File System Recognizers\n"); currentAddress = MmGetSystemAddressForMdlNormalSafe(Irp->MdlAddress); if (currentAddress == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; } // here we could return the bootsector. If we don't have one // the requested read lenght must be returned as requested readLength = irpSp->Parameters.Read.Length; status = STATUS_SUCCESS; __leave; } if (fileObject == NULL) { DDbgPrint(" fileObject == NULL\n"); status = STATUS_INVALID_DEVICE_REQUEST; __leave; } vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB || !DokanCheckCCB(vcb->Dcb, fileObject->FsContext2)) { status = STATUS_INVALID_DEVICE_REQUEST; __leave; } bufferLength = irpSp->Parameters.Read.Length; if (irpSp->Parameters.Read.ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION && irpSp->Parameters.Read.ByteOffset.HighPart == -1) { // irpSp->Parameters.Read.ByteOffset == NULL don't need check? DDbgPrint("use FileObject ByteOffset\n"); byteOffset = fileObject->CurrentByteOffset; } else { byteOffset = irpSp->Parameters.Read.ByteOffset; } DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); DDbgPrint(" ByteCount:%lu ByteOffset:%I64d\n", bufferLength, byteOffset.QuadPart); if (bufferLength == 0) { status = STATUS_SUCCESS; readLength = 0; __leave; } // make a MDL for UserBuffer that can be used later on another thread // context if (Irp->MdlAddress == NULL) { status = DokanAllocateMdl(Irp, irpSp->Parameters.Read.Length); if (!NT_SUCCESS(status)) { __leave; } } ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); if (DokanFCBFlagsIsSet(fcb, DOKAN_FILE_DIRECTORY)) { DDbgPrint(" DOKAN_FILE_DIRECTORY %p\n", fcb); status = STATUS_INVALID_PARAMETER; __leave; } if (Irp->Flags & IRP_PAGING_IO) { isPagingIo = TRUE; } if (fileObject->Flags & FO_SYNCHRONOUS_IO) { isSynchronousIo = TRUE; } if (Irp->Flags & IRP_NOCACHE) { noCache = TRUE; } if (!isPagingIo && (fileObject->SectionObjectPointer != NULL) && (fileObject->SectionObjectPointer->DataSectionObject != NULL)) { ExAcquireResourceExclusiveLite(&fcb->PagingIoResource, TRUE); CcFlushCache(&fcb->SectionObjectPointers, &irpSp->Parameters.Read.ByteOffset, irpSp->Parameters.Read.Length, NULL); ExReleaseResourceLite(&fcb->PagingIoResource); } DokanFCBLockRO(fcb); fcbLocked = TRUE; // length of EventContext is sum of file name length and itself 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); if (isPagingIo) { DDbgPrint(" Paging IO\n"); eventContext->FileFlags |= DOKAN_PAGING_IO; } if (isSynchronousIo) { DDbgPrint(" Synchronous IO\n"); eventContext->FileFlags |= DOKAN_SYNCHRONOUS_IO; } if (noCache) { DDbgPrint(" Nocache\n"); eventContext->FileFlags |= DOKAN_NOCACHE; } // offset of file to read eventContext->Operation.Read.ByteOffset = byteOffset; // buffer size for read // user-mode file system application can return this size eventContext->Operation.Read.BufferLength = irpSp->Parameters.Read.Length; // copy the accessed file name eventContext->Operation.Read.FileNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.Read.FileName, fcb->FileName.Buffer, fcb->FileName.Length); // // We now check whether we can proceed based on the state of // the file oplocks. // if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { // 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; } // // We have to check for read access according to the current // state of the file locks, and set FileSize from the Fcb. // // FsRtlCheckLockForReadAccess does not block. if (!FsRtlCheckLockForReadAccess(&fcb->FileLock, Irp)) { status = STATUS_FILE_LOCK_CONFLICT; __leave; } } // register this IRP to pending IPR list and make it pending status status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); } __finally { if(fcbLocked) DokanFCBUnlock(fcb); DokanCompleteIrpRequest(Irp, status, readLength); DDbgPrint("<== DokanRead\n"); } return status; }
VOID DokanCompleteWrite(__in PIRP_ENTRY IrpEntry, __in PEVENT_INFORMATION EventInfo) { PIRP irp; PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_SUCCESS; PDokanCCB ccb; PDokanFCB fcb; PFILE_OBJECT fileObject; fileObject = IrpEntry->FileObject; ASSERT(fileObject != NULL); DDbgPrint("==> DokanCompleteWrite %wZ\n", &fileObject->FileName); irp = IrpEntry->Irp; irpSp = IrpEntry->IrpSp; ccb = fileObject->FsContext2; ASSERT(ccb != NULL); fcb = ccb->Fcb; ASSERT(fcb != NULL); ccb->UserContext = EventInfo->Context; // DDbgPrint(" set Context %X\n", (ULONG)ccb->UserContext); status = EventInfo->Status; irp->IoStatus.Status = status; irp->IoStatus.Information = EventInfo->BufferLength; if (NT_SUCCESS(status)) { //Check if file size changed if (fcb->AdvancedFCBHeader.FileSize.QuadPart < EventInfo->Operation.Write.CurrentByteOffset.QuadPart) { if (!(irp->Flags & IRP_PAGING_IO)) { DokanFCBLockRO(fcb); } DokanNotifyReportChange(fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); if (!(irp->Flags & IRP_PAGING_IO)) { DokanFCBUnlock(fcb); } //Update size with new offset InterlockedExchange64( &fcb->AdvancedFCBHeader.FileSize.QuadPart, EventInfo->Operation.Write.CurrentByteOffset.QuadPart); } DokanFCBFlagsSetBit(fcb, DOKAN_FILE_CHANGE_LAST_WRITE); if (EventInfo->BufferLength != 0 && fileObject->Flags & FO_SYNCHRONOUS_IO && !(irp->Flags & IRP_PAGING_IO)) { // update current byte offset only when synchronous IO and not paging IO fileObject->CurrentByteOffset.QuadPart = EventInfo->Operation.Write.CurrentByteOffset.QuadPart; DDbgPrint(" Updated CurrentByteOffset %I64d\n", fileObject->CurrentByteOffset.QuadPart); } } DokanCompleteIrpRequest(irp, irp->IoStatus.Status, irp->IoStatus.Information); DDbgPrint("<== DokanCompleteWrite\n"); }
NTSTATUS DokanQueryDirectory(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { PFILE_OBJECT fileObject; PIO_STACK_LOCATION irpSp; PDokanVCB vcb; PDokanCCB ccb; PDokanFCB fcb; NTSTATUS status; ULONG eventLength; PEVENT_CONTEXT eventContext; ULONG index; BOOLEAN initial; ULONG flags = 0; irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB) { return STATUS_INVALID_PARAMETER; } ccb = fileObject->FsContext2; if (ccb == NULL) { return STATUS_INVALID_PARAMETER; } ASSERT(ccb != NULL); if (irpSp->Flags & SL_INDEX_SPECIFIED) { DDbgPrint(" index specified %d\n", irpSp->Parameters.QueryDirectory.FileIndex); } if (irpSp->Flags & SL_RETURN_SINGLE_ENTRY) { DDbgPrint(" return single entry\n"); } if (irpSp->Flags & SL_RESTART_SCAN) { DDbgPrint(" restart scan\n"); } if (irpSp->Parameters.QueryDirectory.FileName) { DDbgPrint(" pattern:%wZ\n", irpSp->Parameters.QueryDirectory.FileName); } switch (irpSp->Parameters.QueryDirectory.FileInformationClass) { case FileDirectoryInformation: DDbgPrint(" FileDirectoryInformation\n"); break; case FileFullDirectoryInformation: DDbgPrint(" FileFullDirectoryInformation\n"); break; case FileNamesInformation: DDbgPrint(" FileNamesInformation\n"); break; case FileBothDirectoryInformation: DDbgPrint(" FileBothDirectoryInformation\n"); break; case FileIdBothDirectoryInformation: DDbgPrint(" FileIdBothDirectoryInformation\n"); break; default: DDbgPrint(" unknown FileInfoClass %d\n", irpSp->Parameters.QueryDirectory.FileInformationClass); break; } // make a MDL for UserBuffer that can be used later on another thread context if (Irp->MdlAddress == NULL) { status = DokanAllocateMdl(Irp, irpSp->Parameters.QueryDirectory.Length); if (!NT_SUCCESS(status)) { return status; } flags = DOKAN_MDL_ALLOCATED; } fcb = ccb->Fcb; ASSERT(fcb != NULL); DokanFCBLockRO(fcb); // size of EVENT_CONTEXT is sum of its length and file name length eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length; initial = (BOOLEAN)(ccb->SearchPattern == NULL && !(ccb->Flags & DOKAN_DIR_MATCH_ALL)); // this is an initial query if (initial) { DDbgPrint(" initial query\n"); // and search pattern is provided if (irpSp->Parameters.QueryDirectory.FileName) { // free current search pattern stored in CCB if (ccb->SearchPattern) ExFreePool(ccb->SearchPattern); // the size of search pattern ccb->SearchPatternLength = irpSp->Parameters.QueryDirectory.FileName->Length; ccb->SearchPattern = ExAllocatePool(ccb->SearchPatternLength + sizeof(WCHAR)); if (ccb->SearchPattern == NULL) { DokanFCBUnlock(fcb); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(ccb->SearchPattern, ccb->SearchPatternLength + sizeof(WCHAR)); // copy provided search pattern to CCB RtlCopyMemory(ccb->SearchPattern, irpSp->Parameters.QueryDirectory.FileName->Buffer, ccb->SearchPatternLength); } else { ccb->Flags |= DOKAN_DIR_MATCH_ALL; } } // if search pattern is provided, add the length of it to store pattern if (ccb->SearchPattern) { eventLength += ccb->SearchPatternLength; } eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); if (eventContext == NULL) { DokanFCBUnlock(fcb); return STATUS_INSUFFICIENT_RESOURCES; } eventContext->Context = ccb->UserContext; // DDbgPrint(" get Context %X\n", (ULONG)ccb->UserContext); // index which specified index-1 th directory entry has been returned // this time, 'index'th entry should be returned index = 0; if (irpSp->Flags & SL_INDEX_SPECIFIED) { index = irpSp->Parameters.QueryDirectory.FileIndex; DDbgPrint(" using FileIndex %d\n", index); } else if (FlagOn(irpSp->Flags, SL_RESTART_SCAN)) { DDbgPrint(" SL_RESTART_SCAN\n"); index = 0; } else { index = (ULONG)ccb->Context; DDbgPrint(" ccb->Context %d\n", index); } eventContext->Operation.Directory.FileInformationClass = irpSp->Parameters.QueryDirectory.FileInformationClass; eventContext->Operation.Directory.BufferLength = irpSp->Parameters.QueryDirectory.Length; // length of buffer eventContext->Operation.Directory.FileIndex = index; // directory index which should be returned this time // copying file name(directory name) eventContext->Operation.Directory.DirectoryNameLength = fcb->FileName.Length; RtlCopyMemory(eventContext->Operation.Directory.DirectoryName, fcb->FileName.Buffer, fcb->FileName.Length); DokanFCBUnlock(fcb); // if search pattern is specified, copy it to EventContext if (ccb->SearchPatternLength && ccb->SearchPattern) { PVOID searchBuffer; eventContext->Operation.Directory.SearchPatternLength = ccb->SearchPatternLength; eventContext->Operation.Directory.SearchPatternOffset = eventContext->Operation.Directory.DirectoryNameLength; searchBuffer = (PVOID)( (SIZE_T)&eventContext->Operation.Directory.SearchPatternBase[0] + (SIZE_T)eventContext->Operation.Directory.SearchPatternOffset); RtlCopyMemory(searchBuffer, ccb->SearchPattern, ccb->SearchPatternLength); DDbgPrint(" ccb->SearchPattern %ws\n", ccb->SearchPattern); } status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, flags); 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 {