525 lines
14 KiB
C
525 lines
14 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
|
|||
|
|
|||
|
#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;
|
|||
|
}
|