Windows-Server-2003/base/fs/npfs/write.c

525 lines
14 KiB
C
Raw Normal View History

2024-08-04 01:28:15 +02:00
/*++
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;
}