|
|
/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name :
device.cpp
Abstract:
Device object handles one redirected device
Revision History: --*/
#include "precomp.hxx"
#define TRC_FILE "device"
#include "trc.h"
#if DBG
extern UCHAR IrpNames[IRP_MJ_MAXIMUM_FUNCTION + 1][40]; #endif // DBG
DrDevice::DrDevice(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId, PUCHAR PreferredDosName) { unsigned len;
BEGIN_FN("DrDevice::DrDevice"); ASSERT(Session != NULL); ASSERT(PreferredDosName != NULL);
TRC_NRM((TB, "Create Device (%p, session: %p, type: %d, id: %d, dosname: %s", this, Session, DeviceType, DeviceId, PreferredDosName));
SetClassName("DrDevice"); _Session = Session; _DeviceType = DeviceType; _DeviceId = DeviceId; _DeviceStatus = dsAvailable;
#if DBG
_VNetRootFinalized = FALSE; _VNetRoot = NULL; #endif
RtlCopyMemory(_PreferredDosName, PreferredDosName, PREFERRED_DOS_NAME_SIZE);
//
// Review: We don't want to redirect any device name willy nilly,
// as I think that would be a security issue given a bad client
//
_PreferredDosName[PREFERRED_DOS_NAME_SIZE - 1] = 0;
//
// We don't want colon for end of DosName
//
len = strlen((CHAR*)_PreferredDosName); if (len && _PreferredDosName[len-1] == ':') { _PreferredDosName[len-1] = '\0'; } }
DrDevice::~DrDevice() { BEGIN_FN("DrDevice::~DrDevice");
#if DBG
if (_VNetRoot != NULL && _VNetRootFinalized != TRUE) { ASSERT(FALSE); } #endif
TRC_NRM((TB, "Delete Device %p for Session %p", this, _Session)); }
BOOL DrDevice::ShouldCreateDevice() { BEGIN_FN("DrDevice::ShouldCreateDevice");
//
// Default is to create the device
//
return TRUE; }
NTSTATUS DrDevice::Initialize(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg, ULONG Length) { BEGIN_FN("DrDevice::Initialize");
// Can't assume devAnnouceMsg is not NULL, need to check if uses it
// ASSERT(devAnnounceMsg != NULL);
return STATUS_SUCCESS; }
VOID DrDevice::CreateReferenceString( IN OUT PUNICODE_STRING refString) /*++
Routine Description:
Create the device reference string using information about the client device. This string is in a form suitable for reparse on IRP_MJ_CREATE and redirection to the minirdr DO.
The form of the resultant reference string is: \;<DriveLetter>:<sessionid>\clientname\preferredDosName Arguments:
DosDeviceName - Dos Device Name in UNICODE refString - UNICODE structure large enough to hold the entire resultant reference string. This works out to be DRMAXREFSTRINGLEN bytes.
Return Value:
NONE
--*/ { NTSTATUS status; STRING string; WCHAR numericBuf[RDPDRMAXULONGSTRING+1] = L"\0"; WCHAR ansiBuf[RDPDRMAXDOSNAMELEN+1] = L"\0"; UNICODE_STRING ansiUnc; UNICODE_STRING numericUnc; ULONG sessionID = _Session->GetSessionId(); PCSZ preferredDosName = (PCSZ)_PreferredDosName; BEGIN_FN("DrDevice::CreateReferenceString"); ASSERT(refString != NULL);
// Sanity check the preferred DOS name.
TRC_ASSERT(preferredDosName != NULL, (TB, "Invalid DOS device name."));
// Make sure the reference string buf is big enough.
TRC_ASSERT(refString->MaximumLength >= (RDPDRMAXREFSTRINGLEN * sizeof(WCHAR)), (TB, "Reference string buffer too small."));
// Zero it out.
refString->Length = 0; refString->Buffer[0] = L'\0';
// Add a '\;'
RtlAppendUnicodeToString(refString, L"\\;"); // Initialize the ansi conversion buf.
ansiUnc.Length = 0; ansiUnc.MaximumLength = RDPDRMAXDOSNAMELEN * sizeof(WCHAR); ansiUnc.Buffer = ansiBuf;
// Add the preferred dos name.
RtlInitAnsiString(&string, preferredDosName); RtlAnsiStringToUnicodeString(&ansiUnc, &string, FALSE); RtlAppendUnicodeStringToString(refString, &ansiUnc);
// Add a ':'
RtlAppendUnicodeToString(refString, L":");
// Initialize the numeric buf.
numericUnc.Length = 0; numericUnc.MaximumLength = RDPDRMAXULONGSTRING * sizeof(WCHAR); numericUnc.Buffer = numericBuf;
// Add the session ID in base 10.
RtlIntegerToUnicodeString(sessionID, 10, &numericUnc); RtlAppendUnicodeStringToString(refString, &numericUnc); // Add the '\'
RtlAppendUnicodeToString(refString, L"\\");
// Add Client name
#if 0
RtlAppendUnicodeToString(refString, _Session->GetClientName()); #endif
RtlAppendUnicodeToString(refString, DRUNCSERVERNAME_U);
// Add a '\'
RtlAppendUnicodeToString(refString, L"\\");
// Add the preferred dos name.
RtlAppendUnicodeStringToString(refString, &ansiUnc);
TRC_NRM((TB, "Reference string = %wZ", refString)); }
NTSTATUS DrDevice::CreateDevicePath(PUNICODE_STRING DevicePath) /*++
Create NT DeviceName compatible with RDBSS convention Format is: \device\rdpdr\;<DriveLetter>:<sessionid>\ClientName\DosDeviceName --*/ { NTSTATUS Status; UNICODE_STRING DevicePathTail; BEGIN_FN("DrDevice::CreateDevicePath"); ASSERT(DevicePath != NULL);
DevicePath->Length = 0; Status = RtlAppendUnicodeToString(DevicePath, RDPDR_DEVICE_NAME_U);
if (!NT_ERROR(Status)) { // Add the reference string to the end:
// Format is: \;<DriveLetter>:<sessionid>\clientName\share
DevicePathTail.Length = 0; DevicePathTail.MaximumLength = DevicePath->MaximumLength - DevicePath->Length; DevicePathTail.Buffer = DevicePath->Buffer + (DevicePath->Length / sizeof(WCHAR));
CreateReferenceString(&DevicePathTail);
DevicePath->Length += DevicePathTail.Length; }
TRC_NRM((TB, "DevicePath=%wZ", DevicePath));
return Status; }
NTSTATUS DrDevice::CreateDosDevicePath(PUNICODE_STRING DosDevicePath, PUNICODE_STRING DosDeviceName) { NTSTATUS Status; UNICODE_STRING linkNameTail;
BEGIN_FN("DrDevice::CreateDosDevicePath"); ASSERT(DosDevicePath != NULL); ASSERT(DosDeviceName != NULL); //
// Create the "\\Sessions\\<sessionId>\\DosDevices\\<DosDeviceName>" string
//
DosDevicePath->Length = 0; Status = RtlAppendUnicodeToString(DosDevicePath, L"\\Sessions\\");
if (!NT_ERROR(Status)) { //
// Append the Session Number
//
linkNameTail.Buffer = (PWSTR)(((PBYTE)DosDevicePath->Buffer) + DosDevicePath->Length); linkNameTail.Length = 0; linkNameTail.MaximumLength = DosDevicePath->MaximumLength - DosDevicePath->Length; Status = RtlIntegerToUnicodeString(_Session->GetSessionId(), 10, &linkNameTail); }
if (!NT_ERROR(Status)) { DosDevicePath->Length += linkNameTail.Length;
//
// Append DosDevices
//
Status = RtlAppendUnicodeToString(DosDevicePath, L"\\DosDevices\\"); }
if (!NT_ERROR(Status)) { Status = RtlAppendUnicodeStringToString(DosDevicePath, DosDeviceName); TRC_NRM((TB, "Created DosDevicePath: %wZ", DosDevicePath)); } TRC_NRM((TB, "DosDevicePath=%wZ", DosDevicePath)); return Status; }
NTSTATUS DrDevice::CreateDosSymbolicLink(PUNICODE_STRING DosDeviceName) { WCHAR NtDevicePathBuffer[RDPDRMAXNTDEVICENAMEGLEN]; UNICODE_STRING NtDevicePath; WCHAR DosDevicePathBuffer[MAX_PATH]; UNICODE_STRING DosDevicePath; NTSTATUS Status;
BEGIN_FN("DrDevice::CreateDosSymbolicLink"); ASSERT(DosDeviceName != NULL);
NtDevicePath.MaximumLength = sizeof(NtDevicePathBuffer); NtDevicePath.Length = 0; NtDevicePath.Buffer = &NtDevicePathBuffer[0];
DosDevicePath.MaximumLength = sizeof(DosDevicePathBuffer); DosDevicePath.Length = 0; DosDevicePath.Buffer = &DosDevicePathBuffer[0];
//
// Get the NT device path to this dr device
//
Status = CreateDevicePath(&NtDevicePath); TRC_NRM((TB, "Nt Device path: %wZ", &NtDevicePath));
if (!NT_ERROR(Status)) {
//
// Build the dos device path for this session
//
Status = CreateDosDevicePath(&DosDevicePath, DosDeviceName); TRC_NRM((TB, "Dos Device path: %wZ", &DosDevicePath)); } else { TRC_ERR((TB, "Can't create nt device path: 0x%08lx", Status)); return Status; }
if (!NT_ERROR(Status)) { //
// Actually create the symbolic link
//
IoDeleteSymbolicLink(&DosDevicePath); Status = IoCreateSymbolicLink(&DosDevicePath, &NtDevicePath);
if (NT_SUCCESS(Status)) { TRC_NRM((TB, "Successfully created Symbolic link")); } else { TRC_NRM((TB, "Failed to create Symbolic link %x", Status)); } } else { TRC_ERR((TB, "Can't create dos device path: 0x%08lx", Status)); return Status; }
return Status; }
NTSTATUS DrDevice::VerifyCreateSecurity(PRX_CONTEXT RxContext, ULONG CurrentSessionId) { NTSTATUS Status; ULONG irpSessionId;
BEGIN_FN("DrDevice::VerifyCreateSecurity"); ASSERT(RxContext != NULL); ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
Status = IoGetRequestorSessionId(RxContext->CurrentIrp, &irpSessionId); if (NT_SUCCESS(Status)) { if (irpSessionId == CurrentSessionId) { TRC_DBG((TB, "Access accepted in DrCreate.")); return STATUS_SUCCESS; } //
// If the request is from the console session, it needs to be from a system
// process.
//
else if (irpSessionId == CONSOLE_SESSIONID) { TRC_NRM((TB, "Create request from console process."));
if (!DrIsSystemProcessRequest(RxContext->CurrentIrp, RxContext->CurrentIrpSp)) { TRC_ALT((TB, "Create request not from system process.")); return STATUS_ACCESS_DENIED; } else { TRC_NRM((TB, "Create request from system. Access accepted.")); return STATUS_SUCCESS; } }
//
// If not from the console and doesn't match the client entry session
// ID then deny access.
//
else { TRC_ALT((TB, "Create request from %ld mismatch with session %ld.", irpSessionId, _Session->GetSessionId())); return STATUS_ACCESS_DENIED; } } else { TRC_ERR((TB, "IoGetRequestorSessionId failed with %08X.", Status)); return Status; } }
VOID DrDevice::FinishCreate(PRX_CONTEXT RxContext) { RxCaptureFcb; RX_FILE_TYPE StorageType;
BEGIN_FN("DrDevice::FinishCreate");
ULONG Attributes = 0; // in the fcb this is DirentRxFlags;
ULONG NumLinks = 0; // in the fcb this is NumberOfLinks;
LARGE_INTEGER CreationTime; // these fields are the same as for the Fcb
LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER LastChangeTime; LARGE_INTEGER AllocationSize; // common header fields
LARGE_INTEGER FileSize; LARGE_INTEGER ValidDataLength; FCB_INIT_PACKET InitPacket = { &Attributes, &NumLinks, &CreationTime, &LastAccessTime, &LastWriteTime, &LastChangeTime, &AllocationSize, &FileSize, &ValidDataLength };
ASSERT(RxContext != NULL); //
// Pretty sure this is Device specific, but maybe caching the information
// be generic? We might be able to fill in these values from member
// variables
//
CreationTime.QuadPart = 0; LastAccessTime.QuadPart = 0; LastWriteTime.QuadPart = 0; LastChangeTime.QuadPart = 0; AllocationSize.QuadPart = 0; FileSize.QuadPart = 0x7FFFFFFF; // These need to be non-zero for reads to occur
ValidDataLength.QuadPart = 0x7FFFFFFF; StorageType = RxInferFileType(RxContext);
if (StorageType == FileTypeNotYetKnown) { StorageType = FileTypeFile; } RxFinishFcbInitialization(capFcb, (RX_FILE_TYPE)RDBSS_STORAGE_NTC(StorageType), &InitPacket); }
NTSTATUS DrDevice::Create(IN OUT PRX_CONTEXT RxContext) /*++
Routine Description:
Opens a file (or device) across the network
Arguments:
RxContext - Context for the operation
Return Value:
Could return status success, cancelled, or pending.
--*/ { NTSTATUS Status; RxCaptureFcb; PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen; PMRX_SRV_CALL SrvCall = RxContext->Create.pSrvCall; PMRX_NET_ROOT NetRoot = RxContext->Create.pNetRoot; SmartPtr<DrSession> Session = _Session; PRDPDR_IOREQUEST_PACKET pIoPacket; ULONG cbPacketSize; PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME(SrvOpen, capFcb); LARGE_INTEGER TimeOut;
BEGIN_FN("DrDevice::Create");
ASSERT(RxContext != NULL); ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
if (!Session->IsConnected()) { TRC_ALT((TB, "Tried to open client device while not " "connected. State: %ld", Session->GetState())); return STATUS_DEVICE_NOT_CONNECTED; } //
// Security check the irp.
//
Status = VerifyCreateSecurity(RxContext, Session->GetSessionId());
if (NT_ERROR(Status)) { return Status; }
//
// We already have an exclusive lock on the fcb. Finish the create.
//
if (NT_SUCCESS(Status)) { //
// JC: Worry about this when do buffering
//
SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_WRITE_CACHING; SrvOpen->Flags |= SRVOPEN_FLAG_DONTUSE_READ_CACHING;
RxContext->pFobx = RxCreateNetFobx(RxContext, RxContext->pRelevantSrvOpen);
if (RxContext->pFobx != NULL) { // Fobx keeps a reference to the device so it won't go away
AddRef(); RxContext->pFobx->Context = (DrDevice *)this; Status = STATUS_SUCCESS; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } }
if (NT_SUCCESS(Status)) { //
// Get the file name
//
// If the file name only has back slash at the end and rdbss didn't record it
// we need to pass this to the client
//
if (GetDeviceType() == RDPDR_DTYP_FILESYSTEM && FlagOn(RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH) && FileName->Length == 0) { FileName->Buffer = L"\\"; FileName->Length = FileName->MaximumLength = sizeof(WCHAR); } TRC_DBG((TB, "Attempt to open = %wZ", FileName));
//
// Build the create packet and send it to the client
// We add the string null terminator to the filename
//
if (FileName->Length) { //
// FileName Length does not include string null terminator.
//
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + FileName->Length + sizeof(WCHAR); } else { cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET); }
pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
if (pIoPacket != NULL) { memset(pIoPacket, 0, cbPacketSize);
pIoPacket->Header.Component = RDPDR_CTYP_CORE; pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST; pIoPacket->IoRequest.DeviceId = _DeviceId; pIoPacket->IoRequest.MajorFunction = IRP_MJ_CREATE; pIoPacket->IoRequest.MinorFunction = 0;
pIoPacket->IoRequest.Parameters.Create.DesiredAccess = RxContext->Create.NtCreateParameters.DesiredAccess; pIoPacket->IoRequest.Parameters.Create.AllocationSize = RxContext->Create.NtCreateParameters.AllocationSize; pIoPacket->IoRequest.Parameters.Create.FileAttributes = RxContext->Create.NtCreateParameters.FileAttributes; pIoPacket->IoRequest.Parameters.Create.ShareAccess = RxContext->Create.NtCreateParameters.ShareAccess; pIoPacket->IoRequest.Parameters.Create.Disposition = RxContext->Create.NtCreateParameters.Disposition; pIoPacket->IoRequest.Parameters.Create.CreateOptions = RxContext->Create.NtCreateParameters.CreateOptions;
//
// File name path
//
if (FileName->Length) { pIoPacket->IoRequest.Parameters.Create.PathLength = FileName->Length + sizeof(WCHAR); RtlCopyMemory(pIoPacket + 1, FileName->Buffer, FileName->Length); //
// Packet is already zero'd, so no need to null terminate the string
//
} else { pIoPacket->IoRequest.Parameters.Create.PathLength = 0; }
TRC_NRM((TB, "Sending Create IoRequest")); TRC_NRM((TB, " DesiredAccess: %lx", pIoPacket->IoRequest.Parameters.Create.DesiredAccess)); TRC_NRM((TB, " AllocationSize: %lx", pIoPacket->IoRequest.Parameters.Create.AllocationSize)); TRC_NRM((TB, " FileAttributes: %lx", pIoPacket->IoRequest.Parameters.Create.FileAttributes)); TRC_NRM((TB, " ShareAccess: %lx", pIoPacket->IoRequest.Parameters.Create.ShareAccess)); TRC_NRM((TB, " Disposition: %lx", pIoPacket->IoRequest.Parameters.Create.Disposition)); TRC_NRM((TB, " CreateOptions: %lx", pIoPacket->IoRequest.Parameters.Create.CreateOptions));
//
// Always do create synchronously
// 30 seconds in hundreds of nano-seconds, in case client hangs,
// we don't want this create thread to wait infinitely.
//
TimeOut = RtlEnlargedIntegerMultiply( 300000, -1000 ); Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize, TRUE, &TimeOut);
delete pIoPacket; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } }
if (NT_SUCCESS(Status)) { FinishCreate(RxContext); } else { // Release the Device Reference
if (RxContext->pFobx != NULL) { ((DrDevice *)RxContext->pFobx->Context)->Release(); RxContext->pFobx->Context = NULL; } } return Status; } NTSTATUS DrDevice::Flush(IN OUT PRX_CONTEXT RxContext) { BEGIN_FN("DrDevice::Flush"); ASSERT(RxContext != NULL); return STATUS_SUCCESS; }
NTSTATUS DrDevice::Write(IN OUT PRX_CONTEXT RxContext, IN BOOL LowPrioSend) { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PMRX_NET_ROOT NetRoot = capFcb->pNetRoot; PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall; SmartPtr<DrSession> Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr<DrFile> FileObj = pFile; PRDPDR_IOREQUEST_PACKET pIoPacket; ULONG cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount; PVOID pv;
BEGIN_FN("DrDevice::Write");
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
ASSERT(RxContext != NULL); ASSERT(RxContext->MajorFunction == IRP_MJ_WRITE); if (!Session->IsConnected()) { return STATUS_DEVICE_NOT_CONNECTED; }
if (FileObj == NULL) { return STATUS_DEVICE_NOT_CONNECTED; } //
// Make sure the device is still enabled
//
if (_DeviceStatus != dsAvailable) { TRC_ALT((TB, "Tried to write to client device which is not " "available. State: %ld", _DeviceStatus)); return STATUS_DEVICE_NOT_CONNECTED; }
if (RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount == 0) { RxContext->IoStatusBlock.Information = 0; return STATUS_SUCCESS; }
pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
if (pIoPacket != NULL) {
memset(pIoPacket, 0, cbPacketSize);
pIoPacket->Header.Component = RDPDR_CTYP_CORE; pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST; pIoPacket->IoRequest.DeviceId = _DeviceId; pIoPacket->IoRequest.FileId = FileObj->GetFileId(); pIoPacket->IoRequest.MajorFunction = IRP_MJ_WRITE; pIoPacket->IoRequest.MinorFunction = 0; pIoPacket->IoRequest.Parameters.Write.Length = RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount; //
// Get the low dword byte offset of where to write
//
pIoPacket->IoRequest.Parameters.Write.OffsetLow = ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset) & 0xffffffff)); //
// Get the high dword by offset of where to write
//
pIoPacket->IoRequest.Parameters.Write.OffsetHigh = ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset) >> 32));
TRC_DBG((TB, "ByteOffset to write = %x", RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset));
pv = MmGetSystemAddressForMdlSafe(RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer, NormalPagePriority);
if (pv != NULL) { RtlCopyMemory(pIoPacket + 1, pv, // + RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset?,
pIoPacket->IoRequest.Parameters.Write.Length);
TRC_DBG((TB, "Write packet length: 0x%lx", pIoPacket->IoRequest.Parameters.Write.Length));
Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize, (BOOLEAN)!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION), NULL, LowPrioSend);
TRC_NRM((TB, "IoRequestWrite returned to DrWrite: %lx", Status)); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } delete pIoPacket;
} else { Status = STATUS_INSUFFICIENT_RESOURCES; }
return Status; }
NTSTATUS DrDevice::Read(IN OUT PRX_CONTEXT RxContext) { NTSTATUS Status = STATUS_NOT_IMPLEMENTED; RxCaptureFcb; RxCaptureFobx; PMRX_NET_ROOT NetRoot = capFcb->pNetRoot; PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall; SmartPtr<DrSession> Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr<DrFile> FileObj = pFile; RDPDR_IOREQUEST_PACKET IoPacket;
BEGIN_FN("DrDevice::Read");
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
ASSERT(Session != NULL); ASSERT(RxContext != NULL); ASSERT(RxContext->MajorFunction == IRP_MJ_READ); if (!Session->IsConnected()) { return STATUS_DEVICE_NOT_CONNECTED; }
if (FileObj == NULL) { return STATUS_DEVICE_NOT_CONNECTED; }
//
// Make sure the device is still enabled
//
if (_DeviceStatus != dsAvailable) { TRC_ALT((TB, "Tried to read from client device which is not " "available. State: %ld", _DeviceStatus)); return STATUS_DEVICE_NOT_CONNECTED; }
memset(&IoPacket, 0, sizeof(IoPacket));
IoPacket.Header.Component = RDPDR_CTYP_CORE; IoPacket.Header.PacketId = DR_CORE_DEVICE_IOREQUEST; IoPacket.IoRequest.DeviceId = _DeviceId; IoPacket.IoRequest.FileId = FileObj->GetFileId(); IoPacket.IoRequest.MajorFunction = IRP_MJ_READ; IoPacket.IoRequest.MinorFunction = 0; IoPacket.IoRequest.Parameters.Read.Length = RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount;
//
// Get low dword of read offset
//
IoPacket.IoRequest.Parameters.Read.OffsetLow = ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset) & 0xffffffff)); //
// Get high dword of read offset
//
IoPacket.IoRequest.Parameters.Read.OffsetHigh = ((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset) >> 32));
TRC_NRM((TB, "DrRead reading length: %ld, at offset: %x", IoPacket.IoRequest.Parameters.Read.Length, RxContext->LowIoContext.ParamsFor.ReadWrite.ByteOffset));
Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket), (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)); TRC_NRM((TB, "IoRequestWrite returned to DrRead: %lx", Status));
return Status; }
NTSTATUS DrDevice::IoControl(IN OUT PRX_CONTEXT RxContext) { NTSTATUS Status = STATUS_SUCCESS; RxCaptureFcb; RxCaptureFobx; PMRX_NET_ROOT NetRoot = capFcb->pNetRoot; PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall; SmartPtr<DrSession> Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr<DrFile> FileObj = pFile; PRDPDR_IOREQUEST_PACKET pIoPacket = NULL; PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; ULONG cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + LowIoContext->ParamsFor.IoCtl.InputBufferLength; ULONG IoControlCode = LowIoContext->ParamsFor.IoCtl.IoControlCode; ULONG InputBufferLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength; ULONG OutputBufferLength = LowIoContext->ParamsFor.IoCtl.OutputBufferLength; PVOID InputBuffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer; PVOID OutputBuffer = LowIoContext->ParamsFor.IoCtl.pOutputBuffer; BEGIN_FN("DrDevice::IoControl");
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
ASSERT(Session != NULL); ASSERT(RxContext != NULL); ASSERT(RxContext->MajorFunction == IRP_MJ_DEVICE_CONTROL || RxContext->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL || RxContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL); if (COMPARE_VERSION(Session->GetClientVersion().Minor, Session->GetClientVersion().Major, 4, 1) < 0) { TRC_ALT((TB, "Failing IoCtl for client that doesn't support it")); return STATUS_NOT_IMPLEMENTED; }
if (!Session->IsConnected()) { return STATUS_DEVICE_NOT_CONNECTED; }
if (FileObj == NULL) { return STATUS_DEVICE_NOT_CONNECTED; }
//
// Make sure the device is still enabled
//
if (_DeviceStatus != dsAvailable) { TRC_ALT((TB, "Tried to send IoControl to client device which is not " "available. State: %ld", _DeviceStatus)); return STATUS_DEVICE_NOT_CONNECTED; }
//
// Validate the buffer
//
__try { if (RxContext->CurrentIrp->RequestorMode != KernelMode) { // If the buffering method is METHOD_NEITHER or METHOD_IN_DIRECT
// then we need to probe the input buffer
if ((IoControlCode & 0x1) && InputBuffer != NULL && InputBufferLength != 0) { ProbeForRead(InputBuffer, InputBufferLength, sizeof(UCHAR)); } // If the buffering method is METHOD_NEITHER or METHOD_OUT_DIRECT
// then we need to probe the output buffer
if ((IoControlCode & 0x2) && OutputBuffer != NULL && OutputBufferLength != 0) { ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(UCHAR)); } }
pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
if (pIoPacket != NULL) { memset(pIoPacket, 0, cbPacketSize); //
// FS Control uses the same path as IO Control.
//
pIoPacket->Header.Component = RDPDR_CTYP_CORE; pIoPacket->Header.PacketId = DR_CORE_DEVICE_IOREQUEST; pIoPacket->IoRequest.DeviceId = _DeviceId; pIoPacket->IoRequest.FileId = FileObj->GetFileId(); pIoPacket->IoRequest.MajorFunction = IRP_MJ_DEVICE_CONTROL; pIoPacket->IoRequest.MinorFunction = LowIoContext->ParamsFor.IoCtl.MinorFunction; pIoPacket->IoRequest.Parameters.DeviceIoControl.OutputBufferLength = LowIoContext->ParamsFor.IoCtl.OutputBufferLength; pIoPacket->IoRequest.Parameters.DeviceIoControl.InputBufferLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength; pIoPacket->IoRequest.Parameters.DeviceIoControl.IoControlCode = LowIoContext->ParamsFor.IoCtl.IoControlCode; if (LowIoContext->ParamsFor.IoCtl.InputBufferLength != 0) { TRC_NRM((TB, "DrIoControl inputbufferlength: %lx", LowIoContext->ParamsFor.IoCtl.InputBufferLength)); RtlCopyMemory(pIoPacket + 1, LowIoContext->ParamsFor.IoCtl.pInputBuffer, LowIoContext->ParamsFor.IoCtl.InputBufferLength); } else { TRC_NRM((TB, "DrIoControl with no inputbuffer")); } Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize, (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)); TRC_NRM((TB, "IoRequestWrite returned to DrIoControl: %lx", Status)); delete pIoPacket; } else { TRC_ERR((TB, "DrIoControl unable to allocate packet: %lx", Status)); Status = STATUS_INSUFFICIENT_RESOURCES; } return Status; } __except (EXCEPTION_EXECUTE_HANDLER) { TRC_ERR((TB, "Invalid buffer parameter(s)"));
if (pIoPacket) { delete pIoPacket; }
return STATUS_INVALID_PARAMETER; } }
NTSTATUS DrDevice::Close(IN OUT PRX_CONTEXT RxContext) { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PMRX_NET_ROOT NetRoot = capFcb->pNetRoot; PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall; SmartPtr<DrSession> Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr<DrFile> FileObj = pFile; RDPDR_IOREQUEST_PACKET IoPacket;
BEGIN_FN("DrDevice::Close");
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
ASSERT(Session != NULL); ASSERT(RxContext != NULL); ASSERT(RxContext->MajorFunction == IRP_MJ_CLOSE); if (!Session->IsConnected()) { // Review: Since we're not connected, there shouldn't be any reason
// to say it isn't closed, right?
return STATUS_SUCCESS; }
if (FileObj == NULL) { return STATUS_DEVICE_NOT_CONNECTED; }
if (_DeviceStatus != dsAvailable) { TRC_ALT((TB, "Tried to close a client device which is not " "available. State: %ld", _DeviceStatus)); return STATUS_SUCCESS; }
memset(&IoPacket, 0, sizeof(IoPacket));
IoPacket.Header.Component = RDPDR_CTYP_CORE; IoPacket.Header.PacketId = DR_CORE_DEVICE_IOREQUEST; IoPacket.IoRequest.DeviceId = _DeviceId; IoPacket.IoRequest.FileId = FileObj->GetFileId(); IoPacket.IoRequest.MajorFunction = IRP_MJ_CLOSE; IoPacket.IoRequest.MinorFunction = 0;
Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket), (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
return Status; }
NTSTATUS DrDevice::Cleanup(IN OUT PRX_CONTEXT RxContext) { NTSTATUS Status; RxCaptureFcb; RxCaptureFobx; PMRX_NET_ROOT NetRoot = capFcb->pNetRoot; PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall; SmartPtr<DrSession> Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr<DrFile> FileObj = pFile; RDPDR_IOREQUEST_PACKET IoPacket;
BEGIN_FN("DrDevice::Cleanup");
//
// Make sure it's okay to access the Client at this time
// This is an optimization, we don't need to acquire the spin lock,
// because it is okay if we're not, we'll just catch it later
//
ASSERT(RxContext != NULL); ASSERT(RxContext->MajorFunction == IRP_MJ_CLEANUP); if (!Session->IsConnected()) { return STATUS_SUCCESS; }
if (FileObj == NULL) { return STATUS_DEVICE_NOT_CONNECTED; } //
// Make sure the device is still enabled
//
if (_DeviceStatus != dsAvailable) { TRC_ALT((TB, "Tried to cleanup a client device which is not " "available. State: %ld", _DeviceStatus)); return STATUS_SUCCESS; }
memset(&IoPacket, 0, sizeof(IoPacket));
IoPacket.Header.Component = RDPDR_CTYP_CORE; IoPacket.Header.PacketId = DR_CORE_DEVICE_IOREQUEST; IoPacket.IoRequest.DeviceId = _DeviceId; IoPacket.IoRequest.FileId = FileObj->GetFileId(); IoPacket.IoRequest.MajorFunction = IRP_MJ_CLEANUP; IoPacket.IoRequest.MinorFunction = 0;
Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket), (BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
return Status; }
NTSTATUS DrDevice::SendIoRequest(IN OUT PRX_CONTEXT RxContext, PRDPDR_IOREQUEST_PACKET IoRequest, ULONG Length, BOOLEAN Synchronous, PLARGE_INTEGER TimeOut, BOOL LowPrioSend) /*++
Routine Description:
Sends the request to the client, and manages the completion. This IO can only be completed once, by returning non-STATUS_PENDING or by calling RxLowIoCompletion.
Arguments:
RxContext - The IoRequest IoRequest - The IoRequest packet Length - size of IoRequest packet Synchronous - duh LowPrioSend - Packet should be sent to client at low priority.
Return Value:
None
--*/ { NTSTATUS Status = STATUS_SUCCESS; USHORT Mid = INVALID_MID; BOOL ExchangeCreated = FALSE; DrIoContext *Context = NULL; SmartPtr<DrExchange> Exchange; SmartPtr<DrDevice> Device(this);
BEGIN_FN("DrDevice::SendIoRequest");
ASSERT(RxContext != NULL); ASSERT(IoRequest != NULL); ASSERT(Length >= sizeof(RDPDR_IOREQUEST_PACKET));
Context = new DrIoContext(RxContext, Device);
if (Context != NULL) { Status = STATUS_SUCCESS; } else { Status = STATUS_INSUFFICIENT_RESOURCES; }
if (NT_SUCCESS(Status)) {
//
// Set up a mapping so the completion response handler can
// find this context
//
TRC_DBG((TB, "Create the context for this I/O")); KeClearEvent(&RxContext->SyncEvent);
ExchangeCreated = _Session->GetExchangeManager().CreateExchange(this, Context, Exchange);
if (ExchangeCreated) {
//
// No need to explicit Refcount for the RxContext
// The place it's been used is the cancel routine.
// Since CreateExchange holds the ref count. we are okay
//
//Exchange->AddRef();
RxContext->MRxContext[MRX_DR_CONTEXT] = (DrExchange *)Exchange;
Status = STATUS_SUCCESS; } else { delete Context; Status = STATUS_INSUFFICIENT_RESOURCES; } }
if (NT_SUCCESS(Status)) {
TRC_DBG((TB, "Writing IoRequest to the client channel"));
//
// Mark the IoRequest with the context mapper
//
IoRequest->IoRequest.CompletionId = Exchange->_Mid;
TRC_DBG((TB, "IO packet:")); TRC_DBG((TB, " Component %c%c", HIBYTE(IoRequest->Header.Component), LOBYTE(IoRequest->Header.Component))); TRC_DBG((TB, " PacketId %c%c", HIBYTE(IoRequest->Header.PacketId), LOBYTE(IoRequest->Header.PacketId))); TRC_DBG((TB, " DeviceId 0x%lx", IoRequest->IoRequest.DeviceId)); TRC_DBG((TB, " FileId 0x%lx", IoRequest->IoRequest.FileId)); TRC_DBG((TB, " MajorFunction 0x%lx", IoRequest->IoRequest.MajorFunction)); TRC_DBG((TB, " MinorFunction 0x%lx", IoRequest->IoRequest.MinorFunction));
Status = _Session->GetExchangeManager().StartExchange(Exchange, this, IoRequest, Length, LowPrioSend); }
if (NT_SUCCESS(Status)) {
TRC_DBG((TB, "Setting cancel routine for Io"));
//
// Set this after sending the IO to the client
// if cancel was requested already, we can just call the
// cancel routine ourselves
//
Status = RxSetMinirdrCancelRoutine(RxContext, MinirdrCancelRoutine);
if (Status == STATUS_CANCELLED) { TRC_NRM((TB, "Io was already cancelled"));
MinirdrCancelRoutine(RxContext); Status = STATUS_SUCCESS; } }
if (Synchronous) { //
// Some failure is going to prevent our completions routine from
// being called. Do that work now.
//
if (!ExchangeCreated) { //
// If we couldn't even create the exchange, we need to just
// complete the IO as failed
//
CompleteRxContext(RxContext, Status, 0); } else { TRC_DBG((TB, "Waiting for IoResult for synchronous request")); if (NT_SUCCESS(Status)) { Status = KeWaitForSingleObject(&RxContext->SyncEvent, UserRequest, KernelMode, FALSE, TimeOut); if (Status == STATUS_TIMEOUT) { RxContext->IoStatusBlock.Status = Status; TRC_DBG((TB, "Wait timed out")); MarkTimedOut(Exchange); } else { Status = RxContext->IoStatusBlock.Status; } } else {
//
// If we created the exchange and then got a transport failure
// we'll be disconnected, and the the I/O will be completed
// the same way all outstanding I/O is completed when we are
// disconnected.
//
if (MarkTimedOut(Exchange)) { CompleteRxContext(RxContext, Status, 0); } else { Status = KeWaitForSingleObject(&RxContext->SyncEvent, UserRequest, KernelMode, FALSE, NULL); Status = RxContext->IoStatusBlock.Status; } } } } else { TRC_DBG((TB, "Not waiting for IoResult for asynchronous request")); //
// Some failure is going to prevent our completions routine from
// being called. Do that work now.
//
if (!ExchangeCreated) { //
// If we couldn't even create the exchange, we need to just
// complete the IO as failed
//
CompleteRxContext(RxContext, Status, 0); } else { //
// If we created the exchange and then got a transport failure
// we'll be disconnected, and the the I/O will be completed
// the same way all outstanding I/O is completed when we are
// disconnected.
//
} Status = STATUS_PENDING; } return Status; }
VOID DrDevice::CompleteBusyExchange(SmartPtr<DrExchange> &Exchange, NTSTATUS Status, ULONG Information) /*++
Routine Description: Takes an exchange which is already busy and
Arguments: Mid - Id to find ExchangeFound - Pointer to a storage for the pointer to the context
Return Value: drexchBusy - Exchange provided, was marked busy drexchCancelled - Exchange provided, was already cancelled drexchUnavailable - Exchange not provided, disconnected
--*/ { DrIoContext *Context; PRX_CONTEXT RxContext;
BEGIN_FN("DrDevice::CompleteBusyExchange");
DrAcquireMutex(); Context = (DrIoContext *)Exchange->_Context; ASSERT(Context != NULL); ASSERT(Context->_Busy);
RxContext = Context->_RxContext; Context->_RxContext = NULL; Exchange->_Context = NULL; DrReleaseMutex();
//
// Note: We've left the Mutex, and the Exchange with no
// context still exists and can be looked up until we Discard it.
//
if (RxContext != NULL) { CompleteRxContext(RxContext, Status, Information); } _Session->GetExchangeManager().Discard(Exchange);
delete Context; }
VOID DrDevice::DiscardBusyExchange(SmartPtr<DrExchange> &Exchange) { DrIoContext *Context;
BEGIN_FN("DrDevice::DiscardBusyExchange");
DrAcquireMutex(); Context = (DrIoContext *)Exchange->_Context; ASSERT(Context != NULL); ASSERT(Context->_Busy); ASSERT(Context->_RxContext == NULL); Exchange->_Context = NULL; DrReleaseMutex();
//
// Note: We've left the Mutex, and the Exchange with no
// context still exists and can be looked up until we Discard it.
//
_Session->GetExchangeManager().Discard(Exchange);
delete Context; }
BOOL DrDevice::MarkBusy(SmartPtr<DrExchange> &Exchange) /*++
Routine Description: Marks an Exchange context as busy so it won't be cancelled while we're copying in to its buffer
Arguments: Exchange - Context
Return Value: TRUE - if Marked Busy FALSE - if Context was gone
--*/ { NTSTATUS Status; BOOL rc; DrIoContext *Context = NULL;
BEGIN_FN("DrDevice::MarkBusy"); ASSERT(Exchange != NULL);
DrAcquireMutex();
Context = (DrIoContext *)Exchange->_Context; if (Context != NULL) { ASSERT(!Context->_Busy); Context->_Busy = TRUE; rc = TRUE; } else { rc = FALSE; } DrReleaseMutex();
return rc; }
VOID DrDevice::MarkIdle(SmartPtr<DrExchange> &Exchange) /*++
Routine Description: Marks an Exchange context as idle. If it was cancelled while we're copying in to its buffer, do the cancel now
Arguments: The busy exchange
Return Value: None
--*/ { PRX_CONTEXT RxContext = NULL; DrIoContext *Context = NULL;
BEGIN_FN("DrDevice::MarkIdle");
ASSERT(Exchange != NULL); DrAcquireMutex(); Context = (DrIoContext *)Exchange->_Context; TRC_ASSERT(Context != NULL, (TB, "Not allowed to delete context while " "it is busy")); ASSERT(Context->_Busy);
Context->_Busy = FALSE;
if (Context->_Cancelled && Context->_RxContext != NULL) { TRC_DBG((TB, "Context was cancelled while busy, " "completing"));
//
// If we were cancelled while busy, we do the work now,
// swap out the RxContext safely while in the Mutex and
// actually cancel it right after. Also set the state to
// indicate the cancelling work has been done
//
RxContext = Context->_RxContext; TRC_ASSERT(RxContext != NULL, (TB, "Cancelled RxContext was NULL " "going from busy to Idle")); Context->_RxContext = NULL; RxContext->MRxContext[MRX_DR_CONTEXT] = NULL; }
if (Context->_Disconnected) {
//
// If the connection dropped while busy, clear that out
// in the Mutex for safety, and then delete it outside
//
Exchange->_Context = NULL; }
DrReleaseMutex();
if (RxContext != NULL) {
//
// We only remove the RxContext because marking Idle means
// we expect to come back and look for it again later after we
// receive more data
//
CompleteRxContext(RxContext, STATUS_CANCELLED, 0);
if (Context->_Disconnected) {
//
// We got disconnected while busy, and will get no further
// notifications from the Exachnge manager. The Context must
// be deleted now
//
delete Context; } } }
BOOL DrDevice::MarkTimedOut(SmartPtr<DrExchange> &Exchange) /*++
Routine Description: Marks an Exchange context as timed out so it won't be processd when the client later returns.
Arguments: Exchange - Context
Return Value: TRUE - if Marked TimedOut FALSE - if Context was gone
--*/ { NTSTATUS Status; BOOL rc; DrIoContext *Context = NULL; PRX_CONTEXT RxContext = NULL;
BEGIN_FN("DrDevice::MarkTimedOut"); ASSERT(Exchange != NULL);
DrAcquireMutex();
Context = (DrIoContext *)Exchange->_Context; if (Context != NULL) { ASSERT(!Context->_TimedOut); Context->_TimedOut = TRUE;
if (Context->_RxContext != NULL) { RxContext = Context->_RxContext; Context->_RxContext = NULL; RxContext->MRxContext[MRX_DR_CONTEXT] = NULL; rc = TRUE; } else { rc = FALSE; } } else { rc = FALSE; }
DrReleaseMutex();
return rc; }
VOID DrDevice::CompleteRxContext(PRX_CONTEXT RxContext, NTSTATUS Status, ULONG Information) /*++
Routine Description: Completes the Io from the RDBSS perspective with the supplied information
Arguments: RxContext - IFS kit context Status - Completion status Information - Completion information
Return Value: None
--*/ { BEGIN_FN_STATIC("DrDevice::CompleteRxContext"); ASSERT(RxContext != NULL);
RxContext->IoStatusBlock.Status = Status; RxContext->IoStatusBlock.Information = Information; if (((RxContext->LowIoContext.Flags & LOWIO_CONTEXT_FLAG_SYNCCALL) != 0) || (RxContext->MajorFunction == IRP_MJ_CREATE)) { TRC_DBG((TB, "Setting event for synchronous Io")); KeSetEvent(&RxContext->SyncEvent, 0, FALSE); } else { TRC_DBG((TB, "Calling RxLowIoCompletion for asynchronous Io")); RxLowIoCompletion(RxContext); } }
NTSTATUS DrDevice::OnDeviceIoCompletion( PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket, BOOL *DoDefaultRead, SmartPtr<DrExchange> &Exchange) /*++
Routine Description: Callback from the Exchange manager to process an Io
Arguments: CompletionPacket - The packet containing the completion cbPacket - count of bytes in the packet DoDefaultRead - Should be set to TRUE if read isn't explicitly called Exchange - Context for the Io
Return Value: NTSTATUS code. An error indicates a protocol error or need to disconnect the client
--*/ { DrIoContext *Context = NULL; NTSTATUS Status; PRX_CONTEXT RxContext;
BEGIN_FN("DrDevice::OnDeviceIoCompletion");
ASSERT(CompletionPacket != NULL); ASSERT(DoDefaultRead != NULL);
if (MarkBusy(Exchange)) { Context = (DrIoContext *)Exchange->_Context; ASSERT(Context != NULL);
TRC_NRM((TB, "Client completed %s irp, Completion Status: %lx", IrpNames[Context->_MajorFunction], CompletionPacket->IoCompletion.IoStatus));
//
// If the IRP was timed out, then we just discard this exchange
//
if (Context->_TimedOut) { TRC_NRM((TB, "Irp was timed out")); DiscardBusyExchange(Exchange); return STATUS_SUCCESS; }
switch (Context->_MajorFunction) { case IRP_MJ_CREATE: Status = OnCreateCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_WRITE: Status = OnWriteCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_READ: Status = OnReadCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_DEVICE_CONTROL: case IRP_MJ_FILE_SYSTEM_CONTROL: Status = OnDeviceControlCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break; case IRP_MJ_LOCK_CONTROL: Status = OnLocksCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_DIRECTORY_CONTROL: Status = OnDirectoryControlCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_QUERY_VOLUME_INFORMATION: Status = OnQueryVolumeInfoCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_SET_VOLUME_INFORMATION: Status = OnSetVolumeInfoCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_QUERY_INFORMATION: Status = OnQueryFileInfoCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_SET_INFORMATION: Status = OnSetFileInfoCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_QUERY_SECURITY: Status = OnQuerySdInfoCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_SET_SECURITY: Status = OnSetSdInfoCompletion(CompletionPacket, cbPacket, DoDefaultRead, Exchange); break;
case IRP_MJ_CLOSE: NotifyClose(); // no break;
default:
RxContext = Context->_RxContext; if (RxContext != NULL) { TRC_NRM((TB, "Irp: %s, Completion Status: %lx", IrpNames[RxContext->MajorFunction], RxContext->IoStatusBlock.Status));
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0); } else { TRC_NRM((TB, "Irp was cancelled")); DiscardBusyExchange(Exchange); } Status = STATUS_SUCCESS; } } else {
//
// We could have been disconnected between getting the callback and
// trying to mark it busy. So the only legitimate way for this to
// happen is if we were disconnected anyway.
//
TRC_ALT((TB, "Found no context in Io notification")); *DoDefaultRead = FALSE; Status = STATUS_UNSUCCESSFUL; }
return Status; }
NTSTATUS DrDevice::OnCreateCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket, BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange) { DrIoContext *Context = (DrIoContext *)Exchange->_Context; PRX_CONTEXT RxContext; SmartPtr<DrDevice> Device; SmartPtr<DrFile> FileObj; BEGIN_FN("DrDevice::OnCreateCompletion"); RxContext = Context->_RxContext;
if (cbPacket < (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.Create.Information)) {
//
// Bad packet. Bad. We've already claimed the RxContext in the
// atlas. Complete it as unsuccessful. Then shutdown the channel
// as this is a Bad Client.
//
TRC_ERR((TB, "Detected bad client CreateCompletion packet"));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); }
//
// No point in starting a default read or anything, what with the
// channel being shut down and all.
//
*DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
if (RxContext != NULL) { DrAcquireSpinLock(); Device = (DrDevice *)RxContext->Create.pVNetRoot->Context; DrReleaseSpinLock();
ASSERT(Device != NULL);
//
// We are using a file object to keep track of file open instance
// and any information stored in the mini-redir for this instance
//
FileObj = new(NonPagedPool) DrFile(Device, CompletionPacket->IoCompletion.Parameters.Create.FileId);
if (FileObj) { //
// Explicit reference the file object here
//
FileObj->AddRef(); RxContext->pFobx->Context2 = (VOID *)(FileObj);
TRC_NRM((TB, "CreateCompletion: status =%d, information=%d", CompletionPacket->IoCompletion.IoStatus, CompletionPacket->IoCompletion.Parameters.Create.Information));
if (cbPacket >= sizeof(RDPDR_IOCOMPLETION_PACKET)) { RxContext->Create.ReturnedCreateInformation = CompletionPacket->IoCompletion.Parameters.Create.Information;
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, CompletionPacket->IoCompletion.Parameters.Create.Information); } else { // For printer creat completion packet, the cbPacket is less than
// sizeof(RDPDR_IOCOMPLETION_PACKET). We don't want to access information beyond its length
RxContext->Create.ReturnedCreateInformation = 0;
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0); } } else { CompleteBusyExchange(Exchange, STATUS_INSUFFICIENT_RESOURCES, 0); return STATUS_INSUFFICIENT_RESOURCES; } } else {
//
// Was cancelled but Context wasn't cleaned up
//
DiscardBusyExchange(Exchange); } return STATUS_SUCCESS; }
NTSTATUS DrDevice::OnWriteCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket, BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange) { PRX_CONTEXT RxContext; DrIoContext *Context = (DrIoContext *)Exchange->_Context;
BEGIN_FN("DrDevice::OnWriteCompletion");
RxContext = Context->_RxContext;
if (cbPacket < sizeof(RDPDR_IOCOMPLETION_PACKET)) {
//
// Bad packet. Bad. We've already claimed the RxContext in the
// atlas. Complete it as unsuccessful. Then shutdown the channel
// as this is a Bad Client.
//
TRC_ERR((TB, "Detected bad client WriteCompletion packet"));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); }
//
// No point in starting a default read or anything, what with the
// channel being shut down and all.
//
*DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
if (RxContext != NULL) { ASSERT(RxContext->MajorFunction == IRP_MJ_WRITE);
TRC_NRM((TB, "Irp: %s, Completion Status: %lx", IrpNames[RxContext->MajorFunction], RxContext->IoStatusBlock.Status));
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, CompletionPacket->IoCompletion.Parameters.Write.Length); } else {
//
// Was cancelled but Context wasn't cleaned up
//
DiscardBusyExchange(Exchange); } return STATUS_SUCCESS; }
NTSTATUS DrDevice::OnReadCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket, BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange) { PRX_CONTEXT RxContext; PVOID pData = CompletionPacket->IoCompletion.Parameters.Read.Buffer; ULONG cbWantData; // Amount of actual Read data in this packet
ULONG cbHaveData; // Amount of data available so far
DrIoContext *Context = (DrIoContext *)Exchange->_Context; NTSTATUS Status; PVOID pv;
BEGIN_FN("DrDevice::OnReadCompletion");
//
// Even if the IO was cancelled we need to correctly parse
// this data.
//
// Check to make sure this is up to size before accessing
// further portions of the packet
//
RxContext = Context->_RxContext;
if (cbPacket < (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.Read.Buffer)) {
//
// Bad packet. Bad. We've already claimed the RxContext in the
// atlas. Complete it as unsuccessful. Then shutdown the channel
// as this is a Bad Client.
//
TRC_ERR((TB, "Detected bad client read packet"));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); }
//
// No point in starting a default read or anything, what with the
// channel being shut down and all.
//
*DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
//
// Calculate how much data is available immediately and how much data
// is coming
//
if (NT_SUCCESS(CompletionPacket->IoCompletion.IoStatus)) {
//
// Successful IO at the client end
//
TRC_DBG((TB, "Successful Read at the client end")); TRC_DBG((TB, "Read Length: 0x%d, DataCopied 0x%d", CompletionPacket->IoCompletion.Parameters.Read.Length, Context->_DataCopied)); cbWantData = CompletionPacket->IoCompletion.Parameters.Read.Length - Context->_DataCopied; cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.Read.Buffer);
if (cbHaveData > cbWantData) { //
// Sounds like a bad client to me
//
TRC_ERR((TB, "Read returned more data than " "advertised cbHaveData 0x%d cbWantData 0x%d", cbHaveData, cbWantData));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); }
*DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
if (RxContext != NULL) { // And not drexchCancelled
TRC_DBG((TB, "Copying data for Read")); ASSERT(RxContext != NULL);
if (cbWantData > RxContext->LowIoContext.ParamsFor.ReadWrite.ByteCount) {
TRC_ERR((TB, "Read returned more data than " "requested"));
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); *DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
//
// Copy the actual size of the read, and check to see if we have all
// the data. The information field tells us what to expect.
//
RxContext->IoStatusBlock.Information = CompletionPacket->IoCompletion.Parameters.Read.Length;
if (RxContext->IoStatusBlock.Information && cbHaveData) {
pv = MmGetSystemAddressForMdl(RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer);
RtlCopyMemory(((BYTE *)pv) + Context->_DataCopied, pData, cbHaveData);
//
// Keep track of how much data we've copied in case this is a
// multi chunk completion
//
Context->_DataCopied += cbHaveData; } }
if (cbHaveData == cbWantData) {
//
// There is exactly as much data as we need to satisfy the read,
// I like it.
//
if (RxContext != NULL) { CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, CompletionPacket->IoCompletion.Parameters.Read.Length); } else { DiscardBusyExchange(Exchange); }
//
// Go with a default channel read now
//
*DoDefaultRead = TRUE; return STATUS_SUCCESS; } else {
//
// We don't have all the data yet, release the DrExchange and
// read more data
//
MarkIdle(Exchange);
_Session->GetExchangeManager().ReadMore( (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.Read.Buffer));
*DoDefaultRead = FALSE; return STATUS_SUCCESS; } } else {
//
// Unsuccessful IO at the client end
//
TRC_DBG((TB, "Unsuccessful Read at the client end")); if (cbPacket >= FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.Read.Buffer)) { if (RxContext != NULL) { CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0); } else { DiscardBusyExchange(Exchange); } *DoDefaultRead = TRUE; return STATUS_SUCCESS; } else { TRC_ERR((TB, "Read returned invalid data"));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); }
*DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; } } }
NTSTATUS DrDevice::OnDeviceControlCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket, BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange) { PRX_CONTEXT RxContext; DrIoContext *Context = (DrIoContext *)Exchange->_Context; PVOID pData = CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBuffer; ULONG cbWantData; // Amount of actual Read data in this packet
ULONG cbHaveData; // Amount of data available so far
NTSTATUS Status; PVOID pv;
BEGIN_FN("DrDevice::OnDeviceControlCompletion");
//
// Even if the IO was cancelled we need to correctly parse
// this data.
//
// Check to make sure this is up to size before accessing
// further portions of the packet
//
RxContext = Context->_RxContext;
if (cbPacket < (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.DeviceIoControl.OutputBuffer)) {
//
// Bad packet. Bad. We've already claimed the RxContext in the
// atlas. Complete it as unsuccessful. Then shutdown the channel
// as this is a Bad Client.
//
TRC_ERR((TB, "Detected bad client DeviceControl packet"));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); }
//
// No point in starting a default read or anything, what with the
// channel being shut down and all.
//
*DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
//
// Calculate how much data is available immediately and how much data
// is coming
//
if (NT_SUCCESS(CompletionPacket->IoCompletion.IoStatus)) {
//
// Successful IO at the client end
//
TRC_DBG((TB, "Successful DeviceControl at the client end"));
cbWantData = CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBufferLength - Context->_DataCopied; cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.DeviceIoControl.OutputBuffer);
if (cbHaveData > cbWantData) { //
// Sounds like a bad client to me
//
TRC_ERR((TB, "DeviceControl returned more data than " "advertised, cbHaveData: %ld cbWantData: %ld", cbHaveData, cbWantData)); if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); } *DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
if (RxContext != NULL) { // And not drexchCancelled
TRC_DBG((TB, "Copying data for DeviceControl")); ASSERT(RxContext != NULL);
if (cbWantData > RxContext->LowIoContext.ParamsFor.IoCtl.OutputBufferLength) {
TRC_ERR((TB, "DeviceControl returned more data than " "requested"));
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); *DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; }
//
// Copy the actual size of the read, and check to see if we have all
// the data. The information field tells us what to expect.
//
RxContext->IoStatusBlock.Information = CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBufferLength;
__try { if (RxContext->IoStatusBlock.Information && cbHaveData) { RtlCopyMemory(((BYTE *)RxContext->LowIoContext.ParamsFor.IoCtl.pOutputBuffer) + Context->_DataCopied, pData, cbHaveData); //
// Keep track of how much data we've copied in case this is a
// multi chunk completion
//
Context->_DataCopied += cbHaveData; } } __except (EXCEPTION_EXECUTE_HANDLER) { TRC_ERR((TB, "Invalid buffer parameter(s)")); CompleteBusyExchange(Exchange, STATUS_INVALID_PARAMETER, 0); *DoDefaultRead = FALSE; // This is the status returned back to HandlePacket, not the status
// returned back to the caller of IoControl.
return STATUS_SUCCESS; } }
if (cbHaveData == cbWantData) {
//
// There is exactly as much data as we need to satisfy the io,
// I like it.
//
TRC_NRM((TB, "DeviceControl, read %d bytes", Context->_DataCopied));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, CompletionPacket->IoCompletion.Parameters.DeviceIoControl.OutputBufferLength); } else { DiscardBusyExchange(Exchange); }
//
// Go with a default channel read now
//
*DoDefaultRead = TRUE; return STATUS_SUCCESS; } else {
//
// We don't have all the data yet, release the DrExchange and
// read more data
//
MarkIdle(Exchange);
_Session->GetExchangeManager().ReadMore( (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.DeviceIoControl.OutputBuffer));
*DoDefaultRead = FALSE; return STATUS_SUCCESS; } } else {
//
// Unsuccessful IO at the client end
//
TRC_DBG((TB, "Unsuccessful DeviceControl at the client end"));
if (cbPacket >= FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET, IoCompletion.Parameters.DeviceIoControl.OutputBuffer)) { if (RxContext != NULL) { CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0); } else { DiscardBusyExchange(Exchange); }
*DoDefaultRead = TRUE; return STATUS_SUCCESS; } else { TRC_ERR((TB, "DeviceControl returned invalid data "));
if (RxContext != NULL) { CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0); } else { DiscardBusyExchange(Exchange); }
*DoDefaultRead = FALSE; return STATUS_DEVICE_PROTOCOL_ERROR; } } }
NTSTATUS NTAPI DrDevice::MinirdrCancelRoutine(PRX_CONTEXT RxContext) { SmartPtr<DrExchange> Exchange; DrIoContext *Context; BOOL bFound = FALSE;
BEGIN_FN_STATIC("DrDevice::MinirdrCancelRoutine"); DrAcquireMutex(); Exchange = (DrExchange *)RxContext->MRxContext[MRX_DR_CONTEXT];
if (Exchange == NULL) { DrReleaseMutex(); return STATUS_SUCCESS; }
ASSERT(Exchange->IsValid()); Context = (DrIoContext *)Exchange->_Context;
if (Context != NULL) { TRC_DBG((TB, "Marking Exchange cancelled"));
//
// Mark it as cancelled, if it is busy, it will be cancelled
// when it goes back to idle
//
Context->_Cancelled = TRUE;
if (!Context->_Busy) {
ASSERT(Context->_RxContext == RxContext);
//
// Wasn't busy, cancelling work should be done here
//
Context->_RxContext = NULL; TRC_DBG((TB, "Found context to cancel")); bFound = TRUE; } else { TRC_DBG((TB, "DrExchange was busy or RxContext " "not found")); } } else {
//
// This could happened if we destroyed the atlas
//
TRC_NRM((TB, "DrExchange was already cancelled")); }
DrReleaseMutex();
if (bFound) {
//
// Do the cancelling outside the mutex
//
CompleteRxContext(RxContext, STATUS_CANCELLED, 0); } return STATUS_SUCCESS; }
VOID DrDevice::OnIoDisconnected(SmartPtr<DrExchange> &Exchange) { DrIoContext *Context, *DeleteContext = NULL; PRX_CONTEXT RxContext = NULL; BOOL bFound = FALSE;
BEGIN_FN("DrDevice::OnIoDisconnected"); DrAcquireMutex(); ASSERT(Exchange->IsValid()); Context = (DrIoContext *)Exchange->_Context;
if (Context != NULL) { TRC_DBG((TB, "Marking Exchange cancelled"));
//
// Mark it as cancelled, if it is busy, it will be cancelled
// when it goes back to idle
//
// Also mark it disconnected, so we know to completely clean
// up the Context
//
Context->_Cancelled = TRUE; Context->_Disconnected = TRUE;
if (!Context->_Busy) {
RxContext = Context->_RxContext; Exchange->_Context = NULL;
// Need to delete the context when the exchange is already cancelled or
// about to be cancelled. Also deletion needs to happen outside the mutex
DeleteContext = Context; //
// Wasn't busy, cancelling work should be done here
//
if (RxContext) { RxContext->MRxContext[MRX_DR_CONTEXT] = NULL;
TRC_DBG((TB, "Found context to cancel")); bFound = TRUE; } else { TRC_DBG((TB, "RxContext was already cancelled ")); } } else { TRC_DBG((TB, "DrExchange was busy or RxContext " "not found")); } } else {
//
// This could happened if we destroyed the atlas right after
// the IO was completed, but before we discarded it
//
TRC_NRM((TB, "DrExchange was already cancelled")); }
DrReleaseMutex();
if (bFound) {
//
// Do the cancelling outside the mutex
//
CompleteRxContext(RxContext, STATUS_CANCELLED, 0); }
if (DeleteContext != NULL) { delete DeleteContext; } }
NTSTATUS DrDevice::OnStartExchangeCompletion(SmartPtr<DrExchange> &Exchange, PIO_STATUS_BLOCK IoStatusBlock) { BEGIN_FN("DrDevice::OnStartExchangeCompletion"); //
// if an error is returned, the connection should be dropped, and that
// is correct when an error comes in
//
return IoStatusBlock->Status; }
VOID DrDevice::Remove() { BEGIN_FN("DrDevice::Remove"); _DeviceStatus = dsDisabled; }
DrIoContext::DrIoContext(PRX_CONTEXT RxContext, SmartPtr<DrDevice> &Device) { BEGIN_FN("DrIoContext::DrIoContext"); SetClassName("DrIoContext"); _Device = Device; _MajorFunction = RxContext->MajorFunction; _MinorFunction = RxContext->MinorFunction; _Busy = FALSE; _Cancelled = FALSE; _Disconnected = FALSE; _TimedOut = FALSE; _DataCopied = 0; _RxContext = RxContext; }
VOID DrDevice::NotifyClose() { BEGIN_FN("DrDevice::NotifyClose");
// This was added for ports, which need to track exclusivity
}
|