609 lines
17 KiB
C
609 lines
17 KiB
C
/*++
|
||
|
||
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
|
||
|
||
//
|
||
// local procedure prototypes
|
||
//
|
||
|
||
BOOLEAN
|
||
NpCommonWrite (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PVOID WriteBuffer,
|
||
IN ULONG WriteLength,
|
||
IN PETHREAD UserThread,
|
||
OUT PIO_STATUS_BLOCK Iosb,
|
||
IN PIRP Irp OPTIONAL
|
||
);
|
||
|
||
#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;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "NpFsdWrite\n", 0);
|
||
DbgDoit( NpSlowWriteCalls += 1 );
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
NpAcquireSharedVcb();
|
||
|
||
try {
|
||
|
||
(VOID) NpCommonWrite( IrpSp->FileObject,
|
||
Irp->UserBuffer,
|
||
IrpSp->Parameters.Write.Length,
|
||
Irp->Tail.Overlay.Thread,
|
||
&Iosb,
|
||
Irp );
|
||
|
||
} except(NpExceptionFilter( GetExceptionCode() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Iosb.Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
NpReleaseVcb();
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// 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;
|
||
UNREFERENCED_PARAMETER( FileOffset );
|
||
UNREFERENCED_PARAMETER( Wait );
|
||
UNREFERENCED_PARAMETER( LockKey );
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
PAGED_CODE();
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
NpAcquireSharedVcb();
|
||
|
||
try {
|
||
|
||
if (NpCommonWrite( FileObject,
|
||
Buffer,
|
||
Length,
|
||
PsGetCurrentThread(),
|
||
IoStatus,
|
||
NULL )) {
|
||
|
||
DbgDoit( NpFastWriteTrue += 1 );
|
||
|
||
if (IoStatus->Status == STATUS_PENDING) {
|
||
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
Results = TRUE;
|
||
|
||
} else {
|
||
|
||
DbgDoit( NpFastWriteFalse += 1 );
|
||
}
|
||
|
||
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
|
||
? EXCEPTION_EXECUTE_HANDLER
|
||
: EXCEPTION_CONTINUE_SEARCH ) {
|
||
|
||
NOTHING;
|
||
}
|
||
|
||
NpReleaseVcb();
|
||
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
|
||
)
|
||
|
||
/*++
|
||
|
||
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;
|
||
if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus.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;
|
||
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, 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;
|
||
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, 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;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, Iosb->Status ); }
|
||
|
||
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;
|
||
if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, 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;
|
||
if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus.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->ReadMode[ FILE_PIPE_CLIENT_END ];
|
||
|
||
} else {
|
||
|
||
WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ];
|
||
|
||
Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ];
|
||
ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ];
|
||
}
|
||
|
||
//
|
||
// 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 + sizeof(DATA_ENTRY))))
|
||
|
||
||
|
||
|
||
(!NpIsDataQueueReaders( WriteQueue ) &&
|
||
((WriteQueue->Quota - WriteQueue->QuotaUsed) < (WriteLength + sizeof(DATA_ENTRY))))) {
|
||
|
||
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->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION)) {
|
||
|
||
Iosb->Information = 0;
|
||
if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, 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.
|
||
//
|
||
|
||
if (!NpWriteDataQueue( WriteQueue,
|
||
ReadMode,
|
||
WriteBuffer,
|
||
WriteLength,
|
||
Ccb->Fcb->Specific.Fcb.NamedPipeType,
|
||
&WriteRemaining,
|
||
Ccb,
|
||
NamedPipeEnd,
|
||
UserThread )) {
|
||
|
||
ASSERT( !NpIsDataQueueReaders( WriteQueue ));
|
||
|
||
ASSERT((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_BYTE_STREAM_TYPE) ||
|
||
(Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_QUEUE_OPERATION) ||
|
||
(WriteRemaining <= (WriteQueue->Quota - WriteQueue->QuotaUsed)));
|
||
|
||
//
|
||
// 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->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION) &&
|
||
((WriteQueue->Quota - WriteQueue->QuotaUsed) < (sizeof(DATA_ENTRY) + WriteLength))) {
|
||
|
||
DebugTrace(0, Dbg, "Complete the byte stream write immediately\n", 0);
|
||
|
||
Iosb->Information = WriteLength - WriteRemaining;
|
||
if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, STATUS_SUCCESS ); }
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Add write to data queue\n", 0);
|
||
|
||
//
|
||
// Add this write request to the write queue
|
||
//
|
||
|
||
ASSERT( !NpIsDataQueueReaders( WriteQueue ));
|
||
|
||
if (ARGUMENT_PRESENT(Irp)) {
|
||
|
||
IoMarkIrpPending( Irp );
|
||
|
||
try {
|
||
|
||
DataEntry = NpAddDataQueueEntry( WriteQueue,
|
||
WriteEntries,
|
||
Buffered,
|
||
WriteLength,
|
||
Irp,
|
||
WriteBuffer );
|
||
|
||
} finally {
|
||
|
||
if (AbnormalTermination()) {
|
||
|
||
IoGetCurrentIrpStackLocation((Irp))->Control &= ~SL_PENDING_RETURNED;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
DataEntry = NpAddDataQueueEntry( WriteQueue,
|
||
WriteEntries,
|
||
Buffered,
|
||
WriteLength,
|
||
Irp,
|
||
WriteBuffer );
|
||
}
|
||
|
||
//
|
||
// And set the security part of the data entry
|
||
//
|
||
|
||
NpSetDataEntryClientContext( NamedPipeEnd,
|
||
Ccb,
|
||
DataEntry,
|
||
UserThread );
|
||
|
||
//
|
||
// Now if the remaining length is not equal to the original
|
||
// write length then this must have been the first write entry
|
||
// into the data queue and we need to set the Next Byte
|
||
// field
|
||
//
|
||
|
||
if (WriteLength > WriteRemaining) {
|
||
|
||
WriteQueue->NextByteOffset = WriteLength - WriteRemaining;
|
||
}
|
||
|
||
//
|
||
// Set our status for the write irp to pending
|
||
//
|
||
|
||
Iosb->Status = STATUS_PENDING;
|
||
}
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Complete the Write Irp\n", 0);
|
||
|
||
//
|
||
// The write irp is finished so we can complete it now
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, STATUS_SUCCESS ); }
|
||
}
|
||
|
||
//
|
||
// Now we need to advance the write queue to the next read irp to
|
||
// skip over flushes and closes
|
||
//
|
||
|
||
//****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue );
|
||
|
||
//
|
||
// 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;
|
||
}
|