/*++ Copyright (c) 1989 Microsoft Corporation Module Name: Write.c Abstract: This module implements the File Write routine for NPFS called by the dispatch driver. Author: Gary Kimura [GaryKi] 21-Aug-1990 Revision History: --*/ #include "NpProcs.h" // // The debug trace level // #define Dbg (DEBUG_TRACE_WRITE) #if DBG ULONG NpFastWriteTrue = 0; ULONG NpFastWriteFalse = 0; ULONG NpSlowWriteCalls = 0; #endif #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, NpCommonWrite) #pragma alloc_text(PAGE, NpFastWrite) #pragma alloc_text(PAGE, NpFsdWrite) #endif NTSTATUS NpFsdWrite ( IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of the NtWriteFile API calls. Arguments: NpfsDeviceObject - Supplies the device object to use. Irp - Supplies the Irp being processed Return Value: NTSTATUS - The Fsd status for the Irp --*/ { IO_STATUS_BLOCK Iosb; PIO_STACK_LOCATION IrpSp; LIST_ENTRY DeferredList; PAGED_CODE(); DebugTrace(+1, Dbg, "NpFsdWrite\n", 0); DbgDoit( NpSlowWriteCalls += 1 ); InitializeListHead (&DeferredList); IrpSp = IoGetCurrentIrpStackLocation( Irp ); FsRtlEnterFileSystem(); NpAcquireSharedVcb(); (VOID) NpCommonWrite( IrpSp->FileObject, Irp->UserBuffer, IrpSp->Parameters.Write.Length, Irp->Tail.Overlay.Thread, &Iosb, Irp, &DeferredList ); NpReleaseVcb(); // // Complete any deferred IRPs now we have dropped the locks // NpCompleteDeferredIrps (&DeferredList); FsRtlExitFileSystem(); if (Iosb.Status != STATUS_PENDING) { Irp->IoStatus.Information = Iosb.Information; NpCompleteRequest (Irp, Iosb.Status); } // // And return to our caller // DebugTrace(-1, Dbg, "NpFsdWrite -> %08lx\n", Iosb.Status ); return Iosb.Status; } BOOLEAN NpFastWrite ( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, IN PVOID Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine does a fast write bypassing the usual file system entry routine (i.e., without the Irp). Arguments: FileObject - Pointer to the file object being read. FileOffset - Byte offset in file for desired data. Length - Length of desired data in bytes. Wait - FALSE if caller may not block, TRUE otherwise LockKey - Supplies the Key used to use if the byte range being read is locked. Buffer - Pointer to output buffer to which data should be copied. IoStatus - Pointer to standard I/O status block to receive the status for the transfer. Return Value: BOOLEAN - TRUE if the operation completed successfully and FALSE if the caller needs to take the long IRP based route. --*/ { BOOLEAN Results = FALSE; LIST_ENTRY DeferredList; UNREFERENCED_PARAMETER( FileOffset ); UNREFERENCED_PARAMETER( Wait ); UNREFERENCED_PARAMETER( LockKey ); UNREFERENCED_PARAMETER( DeviceObject ); PAGED_CODE(); InitializeListHead (&DeferredList); FsRtlEnterFileSystem(); NpAcquireSharedVcb(); if (NpCommonWrite( FileObject, Buffer, Length, PsGetCurrentThread(), IoStatus, NULL, &DeferredList )) { DbgDoit( NpFastWriteTrue += 1 ); Results = TRUE; } else { DbgDoit( NpFastWriteFalse += 1 ); } NpReleaseVcb(); // // Complete any deferred IRPs now we have dropped the locks // NpCompleteDeferredIrps (&DeferredList); FsRtlExitFileSystem(); return Results; } // // Internal support routine // BOOLEAN NpCommonWrite ( IN PFILE_OBJECT FileObject, IN PVOID WriteBuffer, IN ULONG WriteLength, IN PETHREAD UserThread, OUT PIO_STATUS_BLOCK Iosb, IN PIRP Irp OPTIONAL, IN PLIST_ENTRY DeferredList ) /*++ Routine Description: This is the common routine for writing data to a named pipe both via the fast path and with an Irp. Arguments: FileObject - Supplies the file object used in this operation WriteBuffer - Supplies the buffer where data from which data is to be read WriteLength - Supplies the length of the write buffer in bytes UserThread - Supplies the thread id of the caller Iosb - Receives the final completion status of this operation Irp - Optionally supplies an Irp to be used in this operation Return Value: NTSTATUS - the return status for the operation --*/ { NODE_TYPE_CODE NodeTypeCode; PCCB Ccb; PNONPAGED_CCB NonpagedCcb; NAMED_PIPE_END NamedPipeEnd; NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; ULONG WriteRemaining; PDATA_QUEUE WriteQueue; PEVENT_TABLE_ENTRY Event; READ_MODE ReadMode; BOOLEAN Status; PDATA_ENTRY DataEntry; PAGED_CODE(); DebugTrace(+1, Dbg, "NpCommonWrite\n", 0); DebugTrace( 0, Dbg, "FileObject = %08lx\n", FileObject); DebugTrace( 0, Dbg, "WriteBuffer = %08lx\n", WriteBuffer); DebugTrace( 0, Dbg, "WriteLength = %08lx\n", WriteLength); DebugTrace( 0, Dbg, "UserThread = %08lx\n", UserThread); DebugTrace( 0, Dbg, "Iosb = %08lx\n", Iosb); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); Iosb->Information = 0; // // Get the Ccb and figure out who we are, and make sure we're not // disconnected // if ((NodeTypeCode = NpDecodeFileObject( FileObject, NULL, &Ccb, &NamedPipeEnd )) == NTC_UNDEFINED) { DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); Iosb->Status = STATUS_PIPE_DISCONNECTED; return TRUE; } // // Now we only will allow write operations on the pipe and not a directory // or the device // if (NodeTypeCode != NPFS_NTC_CCB) { DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); Iosb->Status = STATUS_INVALID_PARAMETER; return TRUE; } NpAcquireExclusiveCcb(Ccb); NonpagedCcb = Ccb->NonpagedCcb; try { // // Check if the pipe is not in the connected state. // if ((Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) || (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE) || (Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE)) { DebugTrace(0, Dbg, "Pipe in disconnected or listening or closing state\n", 0); if (Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) { Iosb->Status = STATUS_PIPE_DISCONNECTED; } else if (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE) { Iosb->Status = STATUS_PIPE_LISTENING; } else { Iosb->Status = STATUS_PIPE_CLOSING; } try_return(Status = TRUE); } ASSERT(Ccb->NamedPipeState == FILE_PIPE_CONNECTED_STATE); // // We only allow a write by the server on a non inbound only pipe // and by the client on a non outbound only pipe // NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration; if (((NamedPipeEnd == FILE_PIPE_SERVER_END) && (NamedPipeConfiguration == FILE_PIPE_INBOUND)) || ((NamedPipeEnd == FILE_PIPE_CLIENT_END) && (NamedPipeConfiguration == FILE_PIPE_OUTBOUND))) { DebugTrace(0, Dbg, "Trying to write to the wrong pipe configuration\n", 0); Iosb->Status = STATUS_INVALID_PARAMETER; try_return(Status = TRUE); } // // Set up the amount of data we will have written by the time this // operation gets completed and indicate success until we set it otherwise. // Iosb->Status = STATUS_SUCCESS; Iosb->Information = WriteLength; // // Now the data queue that we write into and the event that we signal // are based on the named pipe end. The server writes to the outbound // queue and signals the client event. The client does just the // opposite. We also need to figure out the read mode for the opposite // end of the pipe. // if (NamedPipeEnd == FILE_PIPE_SERVER_END) { WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; ReadMode = Ccb->ReadCompletionMode[ FILE_PIPE_CLIENT_END ].ReadMode; } else { WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; ReadMode = Ccb->ReadCompletionMode[ FILE_PIPE_SERVER_END ].ReadMode; } // // The next section checks if we should continue with the write operation. // The reasons why we will not continue are if we recongnize that the // pipe quota will not support this write and it is a message mode type // with complete operations. We will also bail out now if the quota will // not support the write and this is a fast I/O write request. // // If the pipe contains readers and amount to read plus pipe quota is less // than the write length then we need to do some additional checks. // Or if pipe does not contain reads and the amount of quota left is less // than the write length then we need to do some additional checks. // if ((NpIsDataQueueReaders( WriteQueue ) && (WriteQueue->BytesInQueue < WriteLength) && (WriteQueue->Quota < WriteLength - WriteQueue->BytesInQueue)) || (!NpIsDataQueueReaders( WriteQueue ) && ((WriteQueue->Quota - WriteQueue->QuotaUsed) < WriteLength))) { DebugTrace(0, Dbg, "Quota is not sufficient for the request\n", 0); // // If this is a message mode pipe with complete operations then we // complete without writing the message // if ((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) && (Ccb->ReadCompletionMode[NamedPipeEnd].CompletionMode == FILE_PIPE_COMPLETE_OPERATION)) { Iosb->Information = 0; Iosb->Status = STATUS_SUCCESS; try_return(Status = TRUE); } // // If this is a fast I/O pipe then we tell the call to take the long // Irp based route // if (!ARGUMENT_PRESENT(Irp)) { DebugTrace(0, Dbg, "Need to supply Irp\n", 0); try_return(Status = FALSE); } } // // Now we'll call our common write data queue routine to // transfer data out of our write buffer into the data queue. // If the result of the call is FALSE then we still have some // write data to put into the write queue. // Iosb->Status = NpWriteDataQueue( WriteQueue, ReadMode, WriteBuffer, WriteLength, Ccb->Fcb->Specific.Fcb.NamedPipeType, &WriteRemaining, Ccb, NamedPipeEnd, UserThread, DeferredList ); if (Iosb->Status == STATUS_MORE_PROCESSING_REQUIRED) { ASSERT( !NpIsDataQueueReaders( WriteQueue )); // // Check if the operation is not to block and if so then we // will complete the operation now with what we're written, if what is // left will not fit in the quota for the file // if (((Ccb->ReadCompletionMode[NamedPipeEnd].CompletionMode == FILE_PIPE_COMPLETE_OPERATION) || Irp == NULL) && ((WriteQueue->Quota - WriteQueue->QuotaUsed) < WriteRemaining)) { DebugTrace(0, Dbg, "Complete the byte stream write immediately\n", 0); Iosb->Information = WriteLength - WriteRemaining; Iosb->Status = STATUS_SUCCESS; } else { DebugTrace(0, Dbg, "Add write to data queue\n", 0); // // Add this write request to the write queue // ASSERT( !NpIsDataQueueReaders( WriteQueue )); Iosb->Status = NpAddDataQueueEntry( NamedPipeEnd, Ccb, WriteQueue, WriteEntries, Buffered, WriteLength, Irp, WriteBuffer, WriteLength - WriteRemaining ); } } else { DebugTrace(0, Dbg, "Complete the Write Irp\n", 0); } // // And because we've done something we need to signal the // other ends event // NpSignalEventTableEntry( Event ); Status = TRUE; try_exit: NOTHING; } finally { NpReleaseCcb(Ccb); } DebugTrace(-1, Dbg, "NpCommonWrite -> TRUE\n", 0); return Status; }