Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

991 lines
29 KiB

/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name :
prnport.cpp
Abstract:
Printer port Device object handles one redirected printer port
Revision History:
--*/
#include "precomp.hxx"
#define TRC_FILE "prnport"
#include "trc.h"
#include "TSQPublic.h"
extern "C" void RDPDYN_TracePrintAnnounceMsg(
IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
IN ULONG sessionID,
IN PCWSTR portName,
IN PCWSTR clientName
);
//
// RDPDR.CPP: Configure Devices to send IO packets to client at
// low priority.
//
extern ULONG DeviceLowPrioSendFlags;
//
// RDPDR.CPP: RDPDR.SYS Device Object
//
extern PRDBSS_DEVICE_OBJECT DrDeviceObject;
//
// RDPDr.cpp : The TS Worker Queue pointer
//
extern PVOID RDPDR_TsQueue;
#define LPTNAME "LPT"
#define COMNAME "COM"
#define PRNNAME "PRN"
/////////////////////////////////////////////////////////////////
//
// DrPrinterPort Methods
//
DrPrinterPort::DrPrinterPort(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId,
PUCHAR PreferredDosName) : DrDevice(Session, DeviceType, DeviceId, PreferredDosName)
{
BEGIN_FN("DrPrinterPort::DrPrinterPort");
SetClassName("DrPrinterPort");
_PortNumber = 0;
_SymbolicLinkName.Length = 0;
_SymbolicLinkName.MaximumLength = 0;
_SymbolicLinkName.Buffer = NULL;
_IsOpen = FALSE;
_PortType = FILE_DEVICE_PRINTER;
}
DrPrinterPort::~DrPrinterPort()
{
//
// If the device has a port registered, then unregister the port.
//
if ((_PortNumber != 0) && (_SymbolicLinkName.Buffer != NULL)) {
RDPDRPRT_UnregisterPrinterPortInterface(_PortNumber,
&_SymbolicLinkName);
}
}
NTSTATUS DrPrinterPort::Initialize(PRDPDR_DEVICE_ANNOUNCE DeviceAnnounce, ULONG Length)
{
NTSTATUS status = STATUS_SUCCESS;
DrPrinterPortWorkItem *pItem;
BEGIN_FN("DrPrinterPort::Initialize");
ASSERT(DeviceAnnounce != NULL);
//
// Create a new context for the work item.
//
pItem = new DrPrinterPortWorkItem;
if (pItem == NULL) {
status = STATUS_NO_MEMORY;
goto CLEANUPANDEXIT;
}
pItem->pObj = this;
//
// Copy the device announce message.
//
pItem->deviceAnnounce = (PRDPDR_DEVICE_ANNOUNCE)new(NonPagedPool)
BYTE[sizeof(RDPDR_DEVICE_ANNOUNCE) + Length];
if (pItem->deviceAnnounce == NULL) {
TRC_ERR((TB, "Failed to allocate device announce message."));
status = STATUS_NO_MEMORY;
goto CLEANUPANDEXIT;
}
RtlCopyMemory(pItem->deviceAnnounce, DeviceAnnounce,
sizeof(RDPDR_DEVICE_ANNOUNCE) + Length);
//
// AddRef ourselves so we don't go away while the work item is trying to complete.
//
AddRef();
//
// Use our TS queue worker to queue the workitem
//
status = TSAddWorkItemToQueue( RDPDR_TsQueue, pItem, ProcessWorkItem );
if ( status != STATUS_SUCCESS ) {
TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", status));
}
CLEANUPANDEXIT:
if (status != STATUS_SUCCESS) {
if (pItem != NULL) {
if (pItem->deviceAnnounce != NULL) {
delete pItem->deviceAnnounce;
}
delete pItem;
}
}
TRC_NRM((TB, "exit PrnPort::Initialize"));
return status;
}
NTSTATUS
DrPrinterPort::FinishDeferredInitialization(
DrPrinterPortWorkItem *pItem
)
/*++
Routine Description:
FinishDeferredInitialization
Handles deferred initialization of this object in a work item.
Arguments:
pItem - Printer port work item.
Return Value:
STATUS_SUCCESS on success. Otherwise, an error code is returned.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BEGIN_FN("DrPrinterPort::FinishDeferredInitialization");
TRC_ASSERT(pItem->deviceAnnounce != NULL,
(TB, "pItem->deviceAnnounce != NULL"));
//
// If printer redirection is enabled at all and the subclass okays it
// then create the announce message.
//
if (ShouldCreatePrinter()) {
TRC_NRM((TB, "Creating printer."));
#if DBG
// Trace information about the printer.
RDPDYN_TracePrintAnnounceMsg(pItem->deviceAnnounce,
_Session->GetSessionId(), L"",
_Session->GetClientName());
#endif
Status = AnnouncePrinter(pItem->deviceAnnounce);
}
//
// Otherwise, check to see if we should only announce a port device.
//
else if (ShouldAnnouncePrintPort()) {
TRC_NRM((TB, "Announcing printer port."));
Status = AnnouncePrintPort(pItem->deviceAnnounce);
} else {
TRC_NRM((TB, "Skipping printing device."));
Status = STATUS_SUCCESS;
}
//
// Release the work item.
//
if (pItem != NULL) {
delete pItem->deviceAnnounce;
delete pItem;
}
//
// Release the ref count on ourselves that was added in the main initialization
// routine.
//
Release();
return Status;
}
NTSTATUS DrPrinterPort::CreateDevicePath(PUNICODE_STRING DevicePath)
/*++
Create NT DeviceName compatible with RDBSS convention
Format is:
\device\rdpdrport\;<DriveLetter>:<sessionid>\ClientName\DosDeviceName
--*/
{
NTSTATUS Status;
UNICODE_STRING DevicePathTail;
BEGIN_FN("DrPrinterPort::CreateDevicePath");
ASSERT(DevicePath != NULL);
DevicePath->Length = 0;
Status = RtlAppendUnicodeToString(DevicePath, RDPDR_PORT_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;
}
BOOL DrPrinterPort::ShouldAnnouncePrintPort()
{
BEGIN_FN("DrPrinterPort::ShouldAnnouncePrintPort");
return IsDeviceNameValid();
}
BOOL DrPrinterPort::ShouldCreatePrinter()
{
BEGIN_FN("DrPrinterPort::ShouldCreatePrinter");
if(!_Session->DisablePrinterMapping()) {
return IsDeviceNameValid();
}
return FALSE;
}
BOOL DrPrinterPort::ShouldCreatePort()
{
BEGIN_FN("DrPrinterPort::ShouldCreatePort");
if (!_Session->DisablePrinterMapping()) {
return IsDeviceNameValid();
}
return FALSE;
}
BOOL DrPrinterPort::IsDeviceNameValid()
{
BEGIN_FN("DrPrinterPort::IsDeviceNameValid");
BOOL fRet = FALSE;
PUCHAR PreferredDosName = _PreferredDosName;
char* portName = NULL;
//
// Our device name is valid only if
// the first 3 chars contain "LPT or "COM" or PRN"
// and the rest are digits.
// We will do case-sensitive compare.
//
switch(_DeviceType) {
case RDPDR_DTYP_SERIAL:
portName = COMNAME;
break;
case RDPDR_DTYP_PARALLEL:
portName = LPTNAME;
break;
case RDPDR_DTYP_PRINT:
portName = PRNNAME;
break;
default:
break;
}
if (portName != NULL) {
DWORD numChars = strlen(portName);
//
// ASSERT that we got atleast 3 chars for devicename
//
ASSERT(strlen((char*)PreferredDosName) >= numChars);
if(!strncmp((char*)PreferredDosName, portName, numChars)) {
fRet = TRUE;
//
// portname matches, check for digits.
//
PreferredDosName += numChars;
while(PreferredDosName && *PreferredDosName) {
if(!isdigit(*PreferredDosName)) {
fRet = FALSE;
break;
}
PreferredDosName++;
}
}
}
//
// This assert should never fire for port redirection
//
ASSERT(fRet);
return fRet;
}
NTSTATUS
DrPrinterPort::Write(
IN OUT PRX_CONTEXT RxContext,
IN BOOL LowPrioSend
)
/*++
Routine Description:
Override the 'Write' method. This needs to go to the client at low priority
to prevent us from filling the entire pipe on a slow link with print data.
Arguments:
Return Value:
STATUS_SUCCESS on success. Otherwise, an error code is returned.
--*/
{
return DrDevice::Write(
RxContext,
DeviceLowPrioSendFlags & DEVICE_LOWPRIOSEND_PRINTERS
);
}
VOID
DrPrinterPort::ProcessWorkItem(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID context
)
/*++
Routine Description:
ProcessWorkItem
Arguments:
deviceObject - Associated device object.
context - Work item context.
Return Value:
STATUS_SUCCESS on success. Otherwise, an error code is returned.
--*/
{
DrPrinterPortWorkItem* pItem = (DrPrinterPortWorkItem*)context;
pItem->pObj->FinishDeferredInitialization(pItem);
}
NTSTATUS DrPrinterPort::CreatePrinterPort(PWCHAR portName)
{
NTSTATUS status;
WCHAR ntDevicePathBuffer[RDPDRMAXREFSTRINGLEN];
UNICODE_STRING ntDevicePath = {0, sizeof(ntDevicePathBuffer),
ntDevicePathBuffer};
BEGIN_FN("DrPrinterPort::CreatePrinterPort");
CreateReferenceString(&ntDevicePath);
status = RDPDRPRT_RegisterPrinterPortInterface(_Session->GetClientName(),
(LPSTR)_PreferredDosName, &ntDevicePath, portName, &_SymbolicLinkName,
&_PortNumber);
if (status != STATUS_SUCCESS) {
_PortNumber = 0;
}
return status;
}
NTSTATUS DrPrinterPort::AnnouncePrintPort(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg)
{
NTSTATUS Status;
ULONG portAnnounceEventReqSize;
PRDPDR_PORTDEVICE_SUB portAnnounceEvent;
BEGIN_FN("DrPrinterPort::AnnouncePrintPort");
WCHAR portName[RDPDR_MAXPORTNAMELEN];
Status = CreatePrinterPort(portName);
if (Status != STATUS_SUCCESS) {
goto CleanUpAndReturn;
}
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
(TB, "Port name too long"));
//
// Allocate the port device announce buffer.
//
Status = CreatePortAnnounceEvent(
devAnnounceMsg,
NULL,
0,
//L"",
portName,
&portAnnounceEventReqSize
);
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
if( Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_SUCCESS ) {
portAnnounceEvent = (PRDPDR_PORTDEVICE_SUB)new(NonPagedPool)
BYTE[portAnnounceEventReqSize];
if (portAnnounceEvent == NULL) {
TRC_ERR((TB, "Unable to allocate portAnnounceEvent"));
Status = STATUS_NO_MEMORY;
goto CleanUpAndReturn;
}
//
// Create the port anounce message.
//
Status = CreatePortAnnounceEvent(
devAnnounceMsg,
portAnnounceEvent,
portAnnounceEventReqSize,
//L"",
portName,
&portAnnounceEventReqSize
);
if (Status != STATUS_SUCCESS) {
delete portAnnounceEvent;
#if DBG
portAnnounceEvent = NULL;
#endif
goto CleanUpAndReturn;
}
// device is a printer port.
portAnnounceEvent->deviceFields.DeviceType = RDPDR_DRYP_PRINTPORT;
//
// This happens in a work item so we need to avoid a race in terms of having us
// get disconnected previous to announcing the device to the user-mode component.
//
_Session->LockRDPDYNConnectStateChange();
if (_Session->IsConnected()) {
//
// Dispatch the event to the associated session.
//
Status = RDPDYN_DispatchNewDevMgmtEvent(
portAnnounceEvent,
_Session->GetSessionId(),
RDPDREVT_PORTANNOUNCE,
this
);
}
else {
delete portAnnounceEvent;
portAnnounceEvent = NULL;
}
_Session->UnlockRDPDYNConnectStateChange();
}
CleanUpAndReturn:
return Status;
}
NTSTATUS DrPrinterPort::AnnouncePrinter(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg)
{
NTSTATUS Status;
ULONG prnAnnounceEventReqSize;
PRDPDR_PRINTERDEVICE_SUB prnAnnounceEvent;
BEGIN_FN("DrPrinterPort::AnnouncePrinter");
WCHAR portName[RDPDR_MAXPORTNAMELEN];
Status = CreatePrinterPort(portName);
if (Status != STATUS_SUCCESS) {
goto CleanUpAndReturn;
}
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
(TB, "Port name too long"));
//
// Allocate the printer device announce buffer.
//
Status = CreatePrinterAnnounceEvent(devAnnounceMsg, NULL, 0,
//L"",
portName,
&prnAnnounceEventReqSize);
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
if (Status != STATUS_BUFFER_TOO_SMALL) {
goto CleanUpAndReturn;
}
prnAnnounceEvent = (PRDPDR_PRINTERDEVICE_SUB)new(NonPagedPool)
BYTE[prnAnnounceEventReqSize];
if (prnAnnounceEvent == NULL) {
TRC_ERR((TB, "Unable to allocate prnAnnounceEvent"));
Status = STATUS_NO_MEMORY;
goto CleanUpAndReturn;
}
//
// Create the printer anounce message, but defer assigning a
// port name until just before we return the announce event
// back to user mode.
//
Status = CreatePrinterAnnounceEvent(devAnnounceMsg, prnAnnounceEvent,
prnAnnounceEventReqSize,
//L"",
portName,
NULL);
if (Status != STATUS_SUCCESS) {
delete prnAnnounceEvent;
#if DBG
prnAnnounceEvent = NULL;
#endif
goto CleanUpAndReturn;
}
//
// This happens in a work item so we need to avoid a race in terms of having us
// get disconnected previous to announcing the device to the user-mode component.
//
_Session->LockRDPDYNConnectStateChange();
if (_Session->IsConnected()) {
//
// Dispatch the event to the associated session.
//
Status = RDPDYN_DispatchNewDevMgmtEvent(
prnAnnounceEvent,
_Session->GetSessionId(),
RDPDREVT_PRINTERANNOUNCE,
this
);
}
else {
delete prnAnnounceEvent;
prnAnnounceEvent = NULL;
}
_Session->UnlockRDPDYNConnectStateChange();
CleanUpAndReturn:
return Status;
}
NTSTATUS DrPrinterPort::CreatePrinterAnnounceEvent(
IN PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
IN OUT PRDPDR_PRINTERDEVICE_SUB prnAnnounceEvent,
IN ULONG prnAnnounceEventSize,
IN PCWSTR portName,
OPTIONAL OUT ULONG *prnAnnounceEventReqSize
)
/*++
Routine Description:
Generate a RDPDR_PRINTERDEVICE_SUB event from a client-sent
RDPDR_DEVICE_ANNOUNCE message.
Arguments:
devAnnounceMsg - Device announce message received from client.
prnAnnounceEvent - Buffer for receiving finished printer announce event.
prnAnnounceEventSize - Size of prnAnnounceEvent buffer.
portName - Name of local printer port to be associated with
client-side printing device.
prnAnnounceEventReqSize - Returned required size of prnAnnounceMsg buffer.
Return Value:
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is
too small. STATUS_SUCCESS is returned on success.
--*/
{
ULONG requiredSize;
PRDPDR_PRINTERDEVICE_ANNOUNCE pClientPrinterFields;
ULONG sz;
BEGIN_FN("DrPrinterPort::CreatePrinterAnnounceEvent");
// Make sure the client-sent device announce message is a printer announce
// message.
TRC_ASSERT(devAnnounceMsg->DeviceType == RDPDR_DTYP_PRINT,
(TB, "Printing device expected"));
//
// Validate device datalengths for some minimum lengths.
// Maximum lengths are verified by the device manager.
//
if (devAnnounceMsg->DeviceDataLength < sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE)) {
TRC_ASSERT(FALSE,
(TB, "Innvalid device announce buf."));
TRC_ERR((TB, "Invalid device datalength %ld", devAnnounceMsg->DeviceDataLength));
return STATUS_INVALID_PARAMETER;
}
// Get access to the printer-specific fields for the device announce message.
pClientPrinterFields = (PRDPDR_PRINTERDEVICE_ANNOUNCE)(((PBYTE)devAnnounceMsg) +
sizeof(RDPDR_DEVICE_ANNOUNCE));
//
// Calculate the number of bytes needed in the output buffer.
//
requiredSize = sizeof(RDPDR_PRINTERDEVICE_SUB) +
pClientPrinterFields->PnPNameLen +
pClientPrinterFields->DriverLen +
pClientPrinterFields->PrinterNameLen +
pClientPrinterFields->CachedFieldsLen;
if (prnAnnounceEventSize < requiredSize) {
if (prnAnnounceEventReqSize != NULL) {
*prnAnnounceEventReqSize = requiredSize;
}
return STATUS_BUFFER_TOO_SMALL;
}
// Check the integrity of the input buffer using known sizes.
sz = pClientPrinterFields->PnPNameLen +
pClientPrinterFields->DriverLen +
pClientPrinterFields->PrinterNameLen +
pClientPrinterFields->CachedFieldsLen +
sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
//
// Sanity Check
//
if (devAnnounceMsg->DeviceDataLength != sz) {
TRC_ASSERT(devAnnounceMsg->DeviceDataLength == sz,
(TB, "Size integrity questionable in dev announce buf."));
return STATUS_INVALID_PARAMETER;
}
//
// The above check alone is not enough.
// Someone can do an overflow attack
// Overflow means for example:
// PnpNameLen : 1,
// DriverLen: 2,
// PrinterNameLen:0xfffffffd,
// CachedFieldsLen:2
// Combined these will be good, but individually, one of them will cause havoc
//
if (pClientPrinterFields->PnPNameLen > devAnnounceMsg->DeviceDataLength ||
pClientPrinterFields->DriverLen > devAnnounceMsg->DeviceDataLength ||
pClientPrinterFields->PrinterNameLen > devAnnounceMsg->DeviceDataLength ||
pClientPrinterFields->CachedFieldsLen > devAnnounceMsg->DeviceDataLength) {
TRC_ASSERT(FALSE,
(TB, "Field lengths and device datalengths mismatched in dev announce buf."));
return STATUS_INVALID_PARAMETER;
}
//
// Add the data to the output buffer.
//
// Port Name.
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
(TB, "Port name too long"));
wcscpy(prnAnnounceEvent->portName, portName);
// Client Name (computer name).
TRC_ASSERT(wcslen(_Session->GetClientName())+1 <= RDPDR_MAX_COMPUTER_NAME_LENGTH,
(TB, "Client name too long"));
wcscpy(prnAnnounceEvent->clientName, _Session->GetClientName());
// Client-received device announce message.
RtlCopyMemory(&prnAnnounceEvent->deviceFields, devAnnounceMsg,
sizeof(RDPDR_DEVICE_ANNOUNCE) +
devAnnounceMsg->DeviceDataLength);
// Return the size.
if (prnAnnounceEventReqSize != NULL) {
*prnAnnounceEventReqSize = requiredSize;
}
TRC_NRM((TB, "exit CreatePrinterAnnounceEvent."));
return STATUS_SUCCESS;
}
NTSTATUS DrPrinterPort::CreatePortAnnounceEvent(
IN PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
IN OUT PRDPDR_PORTDEVICE_SUB portAnnounceEvent,
IN ULONG portAnnounceEventSize,
IN PCWSTR portName,
OPTIONAL OUT ULONG *portAnnounceEventReqSize
)
/*++
Routine Description:
Generate a PRDPDR_PORTDEVICE_SUB event from a client-sent
RDPDR_DEVICE_ANNOUNCE message.
Arguments:
devAnnounceMsg - Device announce message received from
client.
portAnnounceEvent - Buffer for receiving finished printer
announce event.
portAnnounceEventSize - Size of prnAnnounceEvent buffer.
portName - Name of local printer port to be associated
with client-side printing device.
portAnnounceEventReqSize - Returned required size of prnAnnounceMsg
buffer.
Return Value:
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is
too small. STATUS_SUCCESS is returned on success.
--*/
{
ULONG requiredSize;
PRDPDR_PRINTERDEVICE_ANNOUNCE pClientPrinterFields;
#if DBG
ULONG sz;
#endif
WCHAR NtDevicePathBuffer[RDPDRMAXNTDEVICENAMEGLEN + 1];
UNICODE_STRING NtDevicePath;
NTSTATUS Status;
NtDevicePath.MaximumLength = sizeof(NtDevicePathBuffer);
NtDevicePath.Length = 0;
NtDevicePath.Buffer = &NtDevicePathBuffer[0];
BEGIN_FN("CreatePortAnnounceEvent");
//
// Get the NT device path to this dr device
//
Status = CreateDevicePath(&NtDevicePath);
TRC_NRM((TB, "Nt Device path: %wZ", &NtDevicePath));
if (!NT_ERROR(Status)) {
// Make sure the client-sent device announce message is a printer announce
// message.
TRC_ASSERT((devAnnounceMsg->DeviceType == RDPDR_DTYP_SERIAL) ||
(devAnnounceMsg->DeviceType == RDPDR_DTYP_PARALLEL),
(TB, "Port device expected"));
//
// Make sure device data length is what we expect from the client
//
if(!DR_CHECK_DEVICEDATALEN(devAnnounceMsg, RDPDR_PORTDEVICE_SUB)) {
TRC_ASSERT(FALSE,
(TB, "Invalid Device DataLength"));
TRC_ERR((TB,"Invalid Device DataLength %d", devAnnounceMsg->DeviceDataLength));
return STATUS_INVALID_PARAMETER;
}
//
// Calculate the number of bytes needed in the output buffer.
//
requiredSize = sizeof(RDPDR_PORTDEVICE_SUB) + devAnnounceMsg->DeviceDataLength;
if (portAnnounceEventSize < requiredSize) {
if (portAnnounceEventReqSize != NULL) {
*portAnnounceEventReqSize = requiredSize;
}
return STATUS_BUFFER_TOO_SMALL;
}
// We shouldn't have any "additional" device-specific data from the client.
TRC_ASSERT(devAnnounceMsg->DeviceDataLength == 0,
(TB, "Size integrity questionable in dev announce buf."));
//
// Add the data to the output buffer.
//
// Port Name.
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
(TB, "Port name too long"));
wcscpy(portAnnounceEvent->portName, portName);
// Device Path.
NtDevicePath.Buffer[NtDevicePath.Length/sizeof(WCHAR)] = L'\0';
TRC_ASSERT(wcslen(NtDevicePath.Buffer)+1 <= RDPDRMAXNTDEVICENAMEGLEN,
(TB, "Device path too long"));
wcscpy(portAnnounceEvent->devicePath, NtDevicePath.Buffer);
// Client-received device announce message.
RtlCopyMemory(&portAnnounceEvent->deviceFields, devAnnounceMsg,
sizeof(RDPDR_DEVICE_ANNOUNCE) +
devAnnounceMsg->DeviceDataLength);
// Return the size.
if (portAnnounceEventReqSize != NULL) {
*portAnnounceEventReqSize = requiredSize;
}
TRC_NRM((TB, "exit CreatePortAnnounceEvent."));
return STATUS_SUCCESS;
}
else {
return Status;
}
}
VOID DrPrinterPort::Remove()
{
PUNICODE_STRING symbolicLinkName;
PRDPDR_REMOVEDEVICE deviceRemoveEventPtr = NULL;
BEGIN_FN("DrPrinterPort::Remove");
//
// Create and dispatch the remove device event.
//
deviceRemoveEventPtr = new(NonPagedPool) RDPDR_REMOVEDEVICE;
if (deviceRemoveEventPtr != NULL) {
//
// Dispatch it.
//
deviceRemoveEventPtr->deviceID = _DeviceId;
RDPDYN_DispatchNewDevMgmtEvent(
deviceRemoveEventPtr,
_Session->GetSessionId(),
RDPDREVT_REMOVEDEVICE,
NULL
);
}
else {
TRC_ERR((TB, "Unable to allocate %ld bytes for remove event",
sizeof(RDPDR_REMOVEDEVICE)));
}
}
NTSTATUS DrPrinterPort::Create(IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS Status;
BEGIN_FN("DrPrinterPort::Create");
//
// Fail Creates when we're already open once
//
DrAcquireSpinLock();
if (_IsOpen) {
DrReleaseSpinLock();
TRC_ALT((TB, "Failing create while already open"));
return STATUS_SHARING_VIOLATION;
} else {
_IsOpen = TRUE;
DrReleaseSpinLock();
}
Status = DrDevice::Create(RxContext);
if (!NT_SUCCESS(Status)) {
DrAcquireSpinLock();
ASSERT(_IsOpen);
TRC_NRM((TB, "Marking creatable for failed open"));
_IsOpen = FALSE;
DrReleaseSpinLock();
}
return Status;
}
NTSTATUS DrPrinterPort::QueryVolumeInfo(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;
FS_INFORMATION_CLASS FsInformationClass = RxContext->Info.FsInformationClass;
BEGIN_FN("DrPrinterPort:QueryVolumeInfo");
//
// 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_QUERY_VOLUME_INFORMATION);
ASSERT(Session != NULL);
if (!Session->IsConnected()) {
TRC_ALT((TB, "Tried to query client device volume information while not "
"connected. State: %ld", _Session->GetState()));
return STATUS_DEVICE_NOT_CONNECTED;
}
//
// Make sure the device is still enabled
//
if (_DeviceStatus != dsAvailable) {
TRC_ALT((TB, "Tried to query client device volume information while not "
"available. State: %ld", _DeviceStatus));
return STATUS_DEVICE_NOT_CONNECTED;
}
TRC_DBG((TB, "QueryVolume information class = %x", FsInformationClass));
switch (FsInformationClass) {
case FileFsDeviceInformation:
{
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
if (sizeof(FILE_FS_DEVICE_INFORMATION) <= *pLengthRemaining) {
PFILE_FS_DEVICE_INFORMATION UsersBuffer =
(PFILE_FS_DEVICE_INFORMATION) RxContext->Info.Buffer;
UsersBuffer->Characteristics = FILE_REMOTE_DEVICE;
UsersBuffer->DeviceType = _PortType;
*pLengthRemaining -= (sizeof(FILE_FS_DEVICE_INFORMATION));
return STATUS_SUCCESS;
}
else {
FILE_FS_DEVICE_INFORMATION UsersBuffer;
UsersBuffer.Characteristics = FILE_REMOTE_DEVICE;
UsersBuffer.DeviceType = _PortType;
RtlCopyMemory(RxContext->Info.Buffer, &UsersBuffer, *pLengthRemaining);
*pLengthRemaining = 0;
return STATUS_BUFFER_OVERFLOW;
}
}
default:
TRC_DBG((TB, "Unhandled FsInformationClass=%x", FsInformationClass));
return STATUS_NOT_IMPLEMENTED;
}
return Status;
}
VOID DrPrinterPort::NotifyClose()
{
BEGIN_FN("DrPrinterPort::NotifyClose");
DrDevice::NotifyClose();
DrAcquireSpinLock();
ASSERT(_IsOpen);
TRC_NRM((TB, "Marking creatable once closed"));
_IsOpen = FALSE;
DrReleaseSpinLock();
}