阅读:1781回复:1
困扰已久的问题,请教
我正做一个网络硬盘的类似产品。客户端在文件系统驱动这一层实现。也就是将IRP_MJ_CREATE,IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_CLOSE等对文件和目录的操作通过网络发送给服务器程序,服务器程序在操作服务器本地的文件和目录,并将结果和数据返回给客户端。
现在有一个问题是,在EXPLORE中从本地硬盘向网络硬盘拷贝文件时,进度对话框开始进展很快(而实际上写入并没有完成那么多),很快进度条指示拷贝将完成,这时进度条停滞不前,直到文件拷贝完成。一句话,EXPLORE的拷贝文件的进度对话框不能正常指示文件写入的进度。 现在整个系统已经差不多了,能读能写,能建立文件目录等,功能基本和本地硬盘差不多了。只是拷贝文件到网络硬盘时进度对话框不正常。 http://ud.35.com可以下载一个试用。 IRP_MJ_WRITE的实现机制时这样的: DWrite (IN PND_IRP_CONTEXT IrpContext) { NTSTATUS Status; PND_FCB Fcb; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; PND_VCB Vcb; __try { if (FlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE)) { Status = NDWriteComplete(IrpContext); } else { DeviceObject = IrpContext->DeviceObject; if (DeviceObject == NDGlobal->DeviceObject) { Status = NDCompleteIrpContext(IrpContext, STATUS_INVALID_DEVICE_REQUEST); __leave; } Vcb = (PND_VCB) DeviceObject->DeviceExtension; FileObject = IrpContext->FileObject; Fcb = (PND_FCB) FileObject->FsContext; if (Fcb->NodeType == NDFCB && !FlagOn(Fcb->FileAttr,FF_DIRECTORY) ) { Status = NDWriteFile(IrpContext); } else { Status = NDCompleteIrpContext(IrpContext, STATUS_INVALID_PARAMETER); } } } __finally { } return Status; } //#pragma code_seg("PAGE") NTSTATUS NDWriteFile(IN PND_IRP_CONTEXT IrpContext) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PND_VCB Vcb; PND_FCB Fcb; PND_CCB Ccb; PFILE_OBJECT FileObject; PFILE_OBJECT CacheObject; PDEVICE_OBJECT DeviceObject; PIRP Irp; PIO_STACK_LOCATION IoStackLocation; ULONG Length; ULONG ReturnedLength; LARGE_INTEGER ByteOffset; PWRITE_CONTEXT WriteContext = NULL; BOOLEAN PagingIo; BOOLEAN Nocache; BOOLEAN SynchronousIo; BOOLEAN MainResourceAcquired = FALSE; BOOLEAN PagingIoResourceAcquired = FALSE; BOOLEAN bNeedExtending = FALSE; BOOLEAN bAppendFile = FALSE; PUCHAR Buffer; __try { DeviceObject = IrpContext->DeviceObject; Vcb = (PND_VCB) DeviceObject->DeviceExtension; FileObject = IrpContext->FileObject; Fcb = (PND_FCB) FileObject->FsContext; Ccb = (PND_CCB) FileObject->FsContext2; Irp = IrpContext->Irp; IoStackLocation = IoGetCurrentIrpStackLocation(Irp); Length = IoStackLocation->Parameters.Write.Length; ByteOffset = IoStackLocation->Parameters.Write.ByteOffset; PagingIo = (Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE); Nocache = (Irp->Flags & IRP_NOCACHE ? TRUE : FALSE); SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO ? TRUE : FALSE); KdPrint(("NDWriteFile: Len=%xh Off=%I64x Paging=%xh Nocache=%xh\n", Length, ByteOffset.QuadPart, PagingIo, Nocache)); if (Length == 0) { Irp->IoStatus.Information = 0; Status = STATUS_SUCCESS; __leave; } //非缓冲读写,offset 和 Length 都必需是扇区大小的整数倍 if (Nocache && (ByteOffset.LowPart & (512 - 1) || Length & (512 - 1))) { Status = STATUS_INVALID_PARAMETER; __leave; } //如果是延迟操作,将请求POST if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC)) { ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC); Status = STATUS_PENDING; __leave; } //如果是在文件末尾追加,偏移置为文件的当前大小。 if (IsEndOfFile(ByteOffset)) { bAppendFile = TRUE; ByteOffset.QuadPart = Fcb->CommonFCBHeader.FileSize.QuadPart; } // // FLUSH缓冲区。对于非缓冲写(非页),为了一直性,必需FLUSH缓冲区 // if (Nocache && !PagingIo && (Fcb->SectionObject.DataSectionObject != NULL)) { ExAcquireResourceExclusive(&Fcb->MainResource, IsFlagOn(IrpContext->Flag, ICF_WAIT)); MainResourceAcquired = TRUE; ExAcquireSharedStarveExclusive( &Fcb->PagingIoResource, TRUE); ExReleaseResource(&Fcb->PagingIoResource); CcFlushCache( &(Fcb->SectionObject), &ByteOffset, Length, &(Irp->IoStatus)); ClearFlag(Fcb->Flag, FF_FILE_MODIFIED); if (!NT_SUCCESS(Irp->IoStatus.Status)) { Status = Irp->IoStatus.Status; __leave; } ExAcquireSharedStarveExclusive( &Fcb->PagingIoResource, TRUE); ExReleaseResource(&Fcb->PagingIoResource); CcPurgeCacheSection( &(Fcb->SectionObject), (PLARGE_INTEGER)&(ByteOffset), Length, FALSE ); ExReleaseResource(&Fcb->MainResource); MainResourceAcquired = FALSE; } //IF 不是页操作 THEN //加Fcb的MainResouce排他锁 //ELSE //加Fcb的MainResouce共享锁 //ENDIF if (!PagingIo) { if (!ExAcquireResourceSharedLite( &Fcb->MainResource, IrpContext->IsSynchronous )) { Status = STATUS_PENDING; __leave; } MainResourceAcquired = TRUE; } else { if (!ExAcquireResourceSharedLite( &Fcb->PagingIoResource, IrpContext->IsSynchronous )) { Status = STATUS_PENDING; __leave; } PagingIoResourceAcquired = TRUE; } //检查是否于加在文件上的字节锁冲突(对非也文件) if (!PagingIo) { if (!FsRtlCheckLockForWriteAccess( &Fcb->FileLockAnchor, Irp )) { Status = STATUS_FILE_LOCK_CONFLICT; __leave; } } //非缓冲写不能超出文件的现有分配空间。如果需要对offset和length进行调整。 if (Nocache) { if (ByteOffset.QuadPart + Length > Fcb->CommonFCBHeader.AllocationSize.QuadPart) { if (ByteOffset.QuadPart >= Fcb->CommonFCBHeader.AllocationSize.QuadPart) { Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; __leave; } else { if (Length > (ULONG)(Fcb->CommonFCBHeader.AllocationSize.QuadPart - ByteOffset.QuadPart)) Length = (ULONG)(Fcb->CommonFCBHeader.AllocationSize.QuadPart - ByteOffset.QuadPart); } } } //如果是缓冲写,且缓冲区未初使化,初使化缓冲区. if (!Nocache) { if (FileObject->PrivateCacheMap == NULL) { CcInitializeCacheMap( FileObject, (PCC_FILE_SIZES)(&Fcb->CommonFCBHeader.AllocationSize), FALSE, &NDGlobal->CacheManagerCallbacks, Fcb ); //CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY); //CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->CommonFCBHeader.AllocationSize))); } CacheObject = FileObject; } // // 扩展文件 // if (!Nocache) { if (bAppendFile || ((ULONG)(ByteOffset.QuadPart + Length) > (ULONG)(Fcb->CommonFCBHeader.FileSize.QuadPart))) { LARGE_INTEGER ExtendSize; LARGE_INTEGER FileSize; bNeedExtending = TRUE; FileSize = Fcb->CommonFCBHeader.FileSize; ExtendSize.QuadPart = (LONGLONG)(ByteOffset.QuadPart + Length); if (ExtendSize.QuadPart > Fcb->CommonFCBHeader.AllocationSize.QuadPart) { if (!NDExpandFileAllocation(IrpContext, Vcb, Fcb, &ExtendSize)) { Status = STATUS_INSUFFICIENT_RESOURCES; __leave; } } { Fcb->CommonFCBHeader.FileSize.QuadPart = ExtendSize.QuadPart; } //对缓冲区进行调整。以下的方法可能并不合适 if (FileObject->PrivateCacheMap) { //通知CMM,文件大小已经改变 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->CommonFCBHeader.AllocationSize))); //将文件空隙清除为0 if (ByteOffset.QuadPart > FileSize.QuadPart) { NDZeroHoles( IrpContext, Vcb, FileObject, FileSize.QuadPart, ByteOffset.QuadPart - FileSize.QuadPart); } //将文件空隙清除为0 if (Fcb->CommonFCBHeader.AllocationSize.QuadPart > ExtendSize.QuadPart) { NDZeroHoles( IrpContext, Vcb, FileObject, ExtendSize.QuadPart, Fcb->CommonFCBHeader.AllocationSize.QuadPart - ExtendSize.QuadPart); } } } } //真正的文件写 //缓冲写 if (!Nocache) { //MDL缓冲写 if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) { CcPrepareMdlWrite( CacheObject, (&ByteOffset), Length, &Irp->MdlAddress, &Irp->IoStatus ); Status = Irp->IoStatus.Status; } //非MDL缓冲文件写 else { Buffer = GetUserBuffer(Irp); if (Buffer == NULL) { Status = STATUS_INVALID_USER_BUFFER; __leave; } if (!CcCopyWrite( CacheObject, (PLARGE_INTEGER)&ByteOffset, Length, IrpContext->IsSynchronous, Buffer )) { Status = STATUS_PENDING; __leave; } Status = Irp->IoStatus.Status; } if (NT_SUCCESS(Status)) Irp->IoStatus.Information = Length; } //直接写 else { Status = LockUserBuffer( IrpContext->Irp, Length, IoReadAccess ); if (!NT_SUCCESS(Status)) { __leave; } Buffer = GetUserBuffer(Irp); if (Buffer == NULL) { Status = STATUS_INVALID_USER_BUFFER; __leave; } WriteContext = ExAllocatePool(PagedPool,sizeof(WRITE_CONTEXT)); if (WriteContext == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; __leave; } if ( ByteOffset.QuadPart + Length > Fcb->CommonFCBHeader.FileSize.QuadPart ) { if (ByteOffset.QuadPart > Fcb->CommonFCBHeader.FileSize.QuadPart) { Length = 0; ByteOffset.QuadPart = Fcb->CommonFCBHeader.FileSize.QuadPart; } else { Length = (ULONG)Fcb->CommonFCBHeader.FileSize.QuadPart - (ULONG)ByteOffset.QuadPart; } } ReturnedLength = Length; RtlZeroMemory(WriteContext,sizeof(WRITE_CONTEXT)); WriteContext->Offset = (ULONG)ByteOffset.QuadPart; WriteContext->Buffer = Buffer; WriteContext->Length = Length; WriteContext->Vcb = Vcb; WriteContext->Fcb = Fcb; Status = NDNetWrite( IrpContext, WriteContext); if (NT_SUCCESS(Status)) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = ReturnedLength; } else { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; } Irp = IrpContext->Irp; } } __finally { if (PagingIoResourceAcquired) { ExReleaseResourceForThreadLite( &Fcb->PagingIoResource, ExGetCurrentResourceThread()); } if (MainResourceAcquired) { ExReleaseResourceForThreadLite( &Fcb->MainResource, ExGetCurrentResourceThread()); } if (WriteContext) ExFreePool(WriteContext); if (!IrpContext->ExceptionInProgress) { if (Irp) { if (Status == STATUS_PENDING) { Status = LockUserBuffer( IrpContext->Irp, Length, IoReadAccess ); if (NT_SUCCESS(Status)) { Status = NDQueueRequest(IrpContext); } else { IrpContext->Irp->IoStatus.Status = Status; NDCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT); NDFreeIrpContext(IrpContext); } } else { IrpContext->Irp->IoStatus.Status = Status; //对同步写,需管理文件对象的FileObject->CurrentByteOffset if (SynchronousIo && !PagingIo && NT_SUCCESS(Status)) { FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + Irp->IoStatus.Information; } if (!PagingIo && NT_SUCCESS(Status)) { SetFlag(FileObject->Flags, FO_FILE_MODIFIED); SetFlag(Fcb->Flag, FF_FILE_MODIFIED); } NDCompleteRequest( IrpContext->Irp, (CCHAR) (NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT)); NDFreeIrpContext(IrpContext); } } else { NDFreeIrpContext(IrpContext); } } } return Status; } //#pragma code_seg("PAGE") NTSTATUS NDWriteComplete (IN PND_IRP_CONTEXT IrpContext) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PFILE_OBJECT FileObject; PIRP Irp; PIO_STACK_LOCATION IrpSp; __try { FileObject = IrpContext->FileObject; Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); CcMdlWriteComplete(FileObject, &(IrpSp->Parameters.Write.ByteOffset), Irp->MdlAddress); Irp->MdlAddress = NULL; Status = STATUS_SUCCESS; } __finally { if (!IrpContext->ExceptionInProgress) { if (IrpContext->Irp) { IrpContext->Irp->IoStatus.Status = Status; NDCompleteRequest( IrpContext->Irp, (CCHAR) (NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT) ); NDFreeIrpContext(IrpContext); } } } return Status; } static VOID WriteAckCallBack( PND_ACK_WAIT_CONTEXT AckWaitContext, PND_VCB Vcb) { ULONG GetByteLen; ND_FRAME_WRITE_ACK FrameWriteAck; PWRITE_CONTEXT WriteContext; ASSERT(AckWaitContext->AckContext); WriteContext = AckWaitContext->AckContext; if (AckWaitContext->FrameLen < FIELD_OFFSET(ND_FRAME_HEAD,Data) + sizeof(ND_FRAME_WRITE_ACK)) return; //读响应帧 GetByteLen = sizeof(ND_FRAME_WRITE_ACK); GetBytes( &FrameWriteAck, Vcb, AckWaitContext->FrameOffset + FIELD_OFFSET(ND_FRAME_HEAD,Data), &GetByteLen); ASSERT( GetByteLen == sizeof(ND_FRAME_WRITE_ACK)); WriteContext->Status = TransCreateErroToState( FrameWriteAck.Errno); } #define WRITE_BYTES_ONCE 1024 NTSTATUS NDNetWrite( IN PND_IRP_CONTEXT IrpContext, PWRITE_CONTEXT WriteContext) { NTSTATUS Status; PVOID PackBuffer = NULL; PVOID Buffer; ULONG PackSize; ULONG AnsiFileNameMaximumLength; __try { ASSERT(WriteContext); if (WriteContext->Length == 0) { Status = STATUS_SUCCESS; __leave; } AnsiFileNameMaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&WriteContext->Fcb->FileName); // // 计算报文缓冲区应该多大。由于报文多次重复使用,取其所需空间最大值 // ASSERT( (WriteContext->Offset & (ENCRYPT_PACK_SIZE - 1)) == 0); if ( WriteContext->Length > WRITE_BYTES_ONCE ) { PackSize = FIELD_OFFSET(ND_PACK_HEAD,Data) + FIELD_OFFSET(ND_FRAME_HEAD,Data) + FIELD_OFFSET(ND_FRAME_WRITE,PathName) + AnsiFileNameMaximumLength + WRITE_BYTES_ONCE; } else PackSize = FIELD_OFFSET(ND_PACK_HEAD,Data) + FIELD_OFFSET(ND_FRAME_HEAD,Data) + FIELD_OFFSET(ND_FRAME_WRITE,PathName) + AnsiFileNameMaximumLength + WriteContext->Length; //ASSERT ( PackSize <= MAX_PACK_SIZE); // // 分配报文 // PackBuffer = ExAllocatePool( PagedPool, PackSize ); if (PackBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; __leave; } WriteContext->PackBuffer = PackBuffer; //发送各报文。可能分多次 if (!IrpContext->IsSynchronous) { if (IrpContext->Irp) IoMarkIrpPending(IrpContext->Irp); WriteContext->Irp = IrpContext->Irp; IrpContext->Irp = NULL; Status = STATUS_PENDING; NDQueueSendWritePackets(WriteContext,IrpContext); } if (IrpContext->IsSynchronous) { NDSendWritePackets(WriteContext); Status = WriteContext->Status; } } __finally { } return Status; } VOID NDQueueSendWritePackets (IN PWRITE_CONTEXT WriteContext, PND_IRP_CONTEXT IrpContext) { // POST了以后,在工作者线程环境下可以同步执行 ExInitializeWorkItem( &WriteContext->WorkQueueItem, NDSendWritePackets, WriteContext); ExQueueWorkItem(&WriteContext->WorkQueueItem, CriticalWorkQueue); } VOID NDSendWritePackets (IN PVOID Context) { PWRITE_CONTEXT WriteContext = Context; KEVENT AckEvent; ULONG WriteLength; PVOID Buffer; ANSI_STRING AnsiFileName; ULONG PackSize; USHORT FrameId; BOOLEAN MultiFrameWrite = FALSE; PND_ACK_WAIT_CONTEXT AckWaitContxt; RtlZeroMemory(&AnsiFileName,sizeof(AnsiFileName)); AnsiFileName.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&WriteContext->Fcb->FileName); WriteContext->Status = STATUS_SUCCESS; // // 获得帧id // ExAcquireResourceExclusiveLite( &WriteContext->Vcb->SendResource, TRUE ); FrameId = WriteContext->Vcb->SendFrameId++; ExReleaseResourceForThreadLite( &WriteContext->Vcb->SendResource, ExGetCurrentResourceThread() ); if (WriteContext->Length > WRITE_BYTES_ONCE) MultiFrameWrite = TRUE; KeInitializeEvent( &AckEvent, SynchronizationEvent, FALSE ); WriteContext->Status = NDAddAckWaitQueue( WriteContext->Vcb, FT_WRITE | FT_ACK, FrameId, NULL, NULL, &AckEvent, FALSE, &AckWaitContxt); if (!NT_SUCCESS(WriteContext->Status)) goto Out; while(WriteContext->Length > 0) { WriteLength = WriteContext->Length; if (WriteLength > WRITE_BYTES_ONCE) WriteLength = WRITE_BYTES_ONCE; PackSize = FIELD_OFFSET(ND_PACK_HEAD,Data) + FIELD_OFFSET(ND_FRAME_HEAD,Data) + FIELD_OFFSET(ND_FRAME_WRITE,PathName) + AnsiFileName.MaximumLength + WriteLength; // 填充头 // Buffer = FillPackHead( WriteContext->PackBuffer, PackSize - FIELD_OFFSET(ND_PACK_HEAD,Data) ); Buffer = FillFrameHead( Buffer, FrameId, FT_WRITE, PackSize - FIELD_OFFSET(ND_PACK_HEAD,Data) - FIELD_OFFSET(ND_FRAME_HEAD,Data) ); // 填充写帧 // if (MultiFrameWrite) { if (WriteContext->Length > WRITE_BYTES_ONCE) ((PND_FRAME_WRITE)Buffer)->Flag = WF_MULTIWRITE_MORE; else ((PND_FRAME_WRITE)Buffer)->Flag = WF_MULTIWRITE_LAST; } else { ((PND_FRAME_WRITE)Buffer)->Flag = WF_ONECEWRITE; } ((PND_FRAME_WRITE)Buffer)->PathNameLen = AnsiFileName.MaximumLength; ((PND_FRAME_WRITE)Buffer)->Offset = WriteContext->Offset; ((PND_FRAME_WRITE)Buffer)->Length = WriteLength; ((PND_FRAME_WRITE)Buffer)->LogicDataLen = WriteLength; AnsiFileName.Buffer = ((PND_FRAME_WRITE)Buffer)->PathName; WriteContext->Status = RtlUnicodeStringToAnsiString( &AnsiFileName, &WriteContext->Fcb->FileName, FALSE ); ASSERT( NT_SUCCESS(WriteContext->Status)); Buffer = (PCHAR)AnsiFileName.Buffer + AnsiFileName.MaximumLength; RtlCopyMemory( Buffer, WriteContext->Buffer, WriteLength); // 发送报文 WriteContext->Status = NDSendPacket( WriteContext->Vcb, WriteContext->PackBuffer ); if (!NT_SUCCESS(WriteContext->Status)) break; // 准备下一次循环 // WriteContext->Buffer = (PCHAR)WriteContext->Buffer + WriteLength; WriteContext->Offset += WriteLength; WriteContext->Length -= WriteLength; } if (!NDWaitForAck(&AckEvent)) WriteContext->Status = STATUS_UNSUCCESSFUL; Out: ExFreePool(WriteContext->PackBuffer); if (WriteContext->Irp) { WriteContext->Irp->IoStatus.Status = WriteContext->Status; IoCompleteRequest(WriteContext->Irp,IO_DISK_INCREMENT); } // NDWaitForAck(&AckEvent); } |
|
沙发#
发布于:2004-07-13 11:24
太长了,没怎么看。
你每次写的时候,改成同步的看看,每次写完才发包回来表示写完。看行不行 |
|
|