/*++ Copyright (c) 1989 Microsoft Corporation Module Name: WriteSup.c Abstract: This module implements the Write support routine. This is a common write function that is called by write, unbuffered write, and transceive. Author: Gary Kimura [GaryKi] 21-Sep-1990 Revision History: --*/ #include "NpProcs.h" // // The debug trace level // #define Dbg (DEBUG_TRACE_WRITESUP) #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, NpWriteDataQueue) #endif NTSTATUS NpWriteDataQueue ( IN PDATA_QUEUE WriteQueue, IN READ_MODE ReadMode, IN PUCHAR WriteBuffer, IN ULONG WriteLength, IN NAMED_PIPE_TYPE PipeType, OUT PULONG WriteRemaining, IN PCCB Ccb, IN NAMED_PIPE_END NamedPipeEnd, IN PETHREAD UserThread, IN PLIST_ENTRY DeferredList ) /*++ Routine Description: This procedure writes data from the write buffer into read entries in the write queue. It will also dequeue entries in the queue as necessary. Arguments: WriteQueue - Provides the write queue to process. ReadMode - Supplies the read mode of read entries in the write queue. WriteBuffer - Provides the buffer from which to read the data. WriteLength - Provides the length, in bytes, of WriteBuffer. PipeType - Indicates if type of pipe (i.e., message or byte stream). WriteRemaining - Receives the number of bytes remaining to be transfered that were not completed by this call. If the operation wrote everything then is value is set to zero. Ccb - Supplies the ccb for the operation NamedPipeEnd - Supplies the end of the pipe doing the write UserThread - Supplies the user thread DeferredList - List of IRPs to be completed after we drop the locks Return Value: BOOLEAN - TRUE if the operation wrote everything and FALSE otherwise. Note that a zero byte message that hasn't been written will return a function result of FALSE and WriteRemaining of zero. --*/ { NTSTATUS Result; BOOLEAN WriteZeroMessage; PDATA_ENTRY DataEntry; PUCHAR ReadBuffer; ULONG ReadLength; ULONG AmountToCopy; NTSTATUS Status; PSECURITY_CLIENT_CONTEXT SecurityContext; BOOLEAN DoneSecurity=FALSE; PIO_STACK_LOCATION IrpSp; BOOLEAN FreeBuffer; PIRP ReadIrp; PAGED_CODE(); DebugTrace(+1, Dbg, "NpWriteDataQueue\n", 0); DebugTrace( 0, Dbg, "WriteQueue = %08lx\n", WriteQueue); DebugTrace( 0, Dbg, "WriteBuffer = %08lx\n", WriteBuffer); DebugTrace( 0, Dbg, "WriteLength = %08lx\n", WriteLength); DebugTrace( 0, Dbg, "PipeType = %08lx\n", PipeType); DebugTrace( 0, Dbg, "Ccb = %08lx\n", Ccb); DebugTrace( 0, Dbg, "NamedPipeEnd = %08lx\n", NamedPipeEnd); DebugTrace( 0, Dbg, "UserThread = %08lx\n", UserThread); // // Determine if we are to write a zero byte message, and initialize // WriteRemaining // *WriteRemaining = WriteLength; if ((PipeType == FILE_PIPE_MESSAGE_TYPE) && (WriteLength == 0)) { WriteZeroMessage = TRUE; } else { WriteZeroMessage = FALSE; } // // Now while the write queue has some read entries in it and // there is some remaining write data or this is a write zero message // then we'll do the following main loop // for (DataEntry = NpGetNextRealDataQueueEntry( WriteQueue, DeferredList ); (NpIsDataQueueReaders(WriteQueue) && ((*WriteRemaining > 0) || WriteZeroMessage)); DataEntry = NpGetNextRealDataQueueEntry( WriteQueue, DeferredList )) { ReadLength = DataEntry->DataSize; DebugTrace(0, Dbg, "Top of main loop...\n", 0); DebugTrace(0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer); DebugTrace(0, Dbg, "ReadLength = %08lx\n", ReadLength); DebugTrace(0, Dbg, "*WriteRemaining = %08lx\n", *WriteRemaining); // // Check if this is a ReadOverflow Operation and if so then also check // that the read will succeed otherwise complete this read with // buffer overflow and continue on. // IrpSp = IoGetCurrentIrpStackLocation( DataEntry->Irp ); if (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_PIPE_INTERNAL_READ_OVFLOW) { if ((ReadLength < WriteLength) || WriteZeroMessage) { ReadIrp = NpRemoveDataQueueEntry( WriteQueue, TRUE, DeferredList ); if (ReadIrp != NULL) { NpDeferredCompleteRequest( ReadIrp, STATUS_BUFFER_OVERFLOW, DeferredList ); } continue; } } if (DataEntry->DataEntryType == Unbuffered) { DataEntry->Irp->Overlay.AllocationSize.QuadPart = WriteQueue->BytesInQueue - WriteQueue->BytesInQueue; } // // copy data from the write buffer at write offset to the // read buffer at read offset by the mininum of write remaining // or read remaining // AmountToCopy = (*WriteRemaining < ReadLength ? *WriteRemaining : ReadLength); if (DataEntry->DataEntryType != Unbuffered && AmountToCopy > 0) { ReadBuffer = NpAllocatePagedPool ( AmountToCopy, 'RFpN' ); if (ReadBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } FreeBuffer = TRUE; } else { ReadBuffer = DataEntry->Irp->AssociatedIrp.SystemBuffer; FreeBuffer = FALSE; } try { RtlCopyMemory( ReadBuffer, &WriteBuffer[ WriteLength - *WriteRemaining ], AmountToCopy ); } except (EXCEPTION_EXECUTE_HANDLER) { if (FreeBuffer) { NpFreePool (ReadBuffer); } return GetExceptionCode (); } // // Done update the security in the CCB multiple times. It won't change. // if (DoneSecurity == FALSE) { DoneSecurity = TRUE; // // Now update the security fields in the nonpaged ccb // Status = NpGetClientSecurityContext (NamedPipeEnd, Ccb, UserThread, &SecurityContext); if (!NT_SUCCESS(Status)) { if (FreeBuffer) { NpFreePool (ReadBuffer); } return Status; } if (SecurityContext != NULL) { NpFreeClientSecurityContext (Ccb->SecurityClientContext); Ccb->SecurityClientContext = SecurityContext; } } // // Now we've done with the read entry so remove it from the // write queue, get its irp, and fill in the information field // to be the bytes that we've transferred into the read buffer. // ReadIrp = NpRemoveDataQueueEntry( WriteQueue, TRUE, DeferredList ); if (ReadIrp == NULL) { if (FreeBuffer) { NpFreePool (ReadBuffer); } continue; } // // Update the Write remaining counts // *WriteRemaining -= AmountToCopy; ReadIrp->IoStatus.Information = AmountToCopy; if (FreeBuffer) { ReadIrp->AssociatedIrp.SystemBuffer = ReadBuffer; ReadIrp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION; } if (*WriteRemaining == 0) { DebugTrace(0, Dbg, "Finished up the write remaining\n", 0); //**** ASSERT( ReadIrp->IoStatus.Information != 0 ); NpDeferredCompleteRequest( ReadIrp, STATUS_SUCCESS, DeferredList ); WriteZeroMessage = FALSE; } else { // // There is still some space in the write buffer to be // written out, but before we can handle that (in the // following if statement) we need to finish the read. // If the read is message mode then we've overflowed the // buffer otherwise we completed successfully // if (ReadMode == FILE_PIPE_MESSAGE_MODE) { DebugTrace(0, Dbg, "Read buffer Overflow\n", 0); NpDeferredCompleteRequest( ReadIrp, STATUS_BUFFER_OVERFLOW, DeferredList ); } else { DebugTrace(0, Dbg, "Read buffer byte stream done\n", 0); //**** ASSERT( ReadIrp->IoStatus.Information != 0 ); NpDeferredCompleteRequest( ReadIrp, STATUS_SUCCESS, DeferredList ); } } } DebugTrace(0, Dbg, "Finished loop...\n", 0); DebugTrace(0, Dbg, "*WriteRemaining = %08lx\n", *WriteRemaining); DebugTrace(0, Dbg, "WriteZeroMessage = %08lx\n", WriteZeroMessage); // // At this point we've finished off all of the read entries in the // queue and we might still have something left to write. If that // is the case then we'll set our result to FALSE otherwise we're // done so we'll return TRUE. // if ((*WriteRemaining > 0) || (WriteZeroMessage)) { ASSERT( !NpIsDataQueueReaders( WriteQueue )); Result = STATUS_MORE_PROCESSING_REQUIRED; } else { Result = STATUS_SUCCESS; } DebugTrace(-1, Dbg, "NpWriteDataQueue -> %08lx\n", Result); return Result; }