NT4/private/ntos/npfs/write.c
2020-09-30 17:12:29 +02:00

609 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}