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.
2702 lines
74 KiB
2702 lines
74 KiB
|
|
|
|
/*++
|
|
|
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
rdpdyn.c
|
|
|
|
Abstract:
|
|
|
|
This module is the dynamic device management component for RDP device
|
|
redirection. It exposes an interface that can be opened by device management
|
|
user-mode components running in session context.
|
|
|
|
Need a check in IRP_MJ_CREATE to make sure that we are not being opened
|
|
2x by the same session. This shouldn't be allowed.
|
|
|
|
Where can I safely use PAGEDPOOL instead of NONPAGEDPOOL.
|
|
|
|
Author:
|
|
|
|
tadb
|
|
|
|
Revision History:
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#define TRC_FILE "rdpdyn"
|
|
#include "trc.h"
|
|
|
|
#define DRIVER
|
|
|
|
#include "cfg.h"
|
|
#include "pnp.h"
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
|
|
// Just shove the typedefs in for the Power Management functions now because I can't
|
|
// get the header conflicts resolved.
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif // __cplusplus
|
|
NTKERNELAPI VOID PoStartNextPowerIrp(IN PIRP Irp);
|
|
NTKERNELAPI NTSTATUS PoCallDriver(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif // __cplusplus
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Defines
|
|
//
|
|
// Calculate the size of a completed RDPDR_PRINTERDEVICE_SUB event.
|
|
#define CALCPRINTERDEVICE_SUB_SZ(rec) \
|
|
sizeof(RDPDR_PRINTERDEVICE_SUB) + \
|
|
(rec)->clientPrinterFields.PnPNameLen + \
|
|
(rec)->clientPrinterFields.DriverLen + \
|
|
(rec)->clientPrinterFields.PrinterNameLen + \
|
|
(rec)->clientPrinterFields.CachedFieldsLen
|
|
|
|
// Calculate the size of a completed RDPDR_REMOVEDEVICE event.
|
|
#define CALCREMOVEDEVICE_SUB_SZ(rec) \
|
|
sizeof(RDPDR_REMOVEDEVICE)
|
|
|
|
// Calculate the size of a completed RDPDR_PORTDEVICE_SUB event.
|
|
#define CALCPORTDEVICE_SUB_SZ(rec) \
|
|
sizeof(RDPDR_PORTDEVICE_SUB)
|
|
|
|
// Calculate the size of a completed RDPDR_DRIVEDEVICE_SUB event.
|
|
#define CALCDRIVEDEVICE_SUB_SZ(rec) \
|
|
sizeof(RDPDR_DRIVEDEVICE_SUB)
|
|
|
|
#if DBG
|
|
#define DEVMGRCONTEXTMAGICNO 0x55445544
|
|
|
|
// Test defines.
|
|
#define TESTDRIVERNAME L"HP LaserJet 4P"
|
|
//#define TESTDRIVERNAME L"This driver has no match"
|
|
#define TESTPNPNAME L""
|
|
#define TESTPRINTERNAME TESTDRIVERNAME
|
|
#define TESTDEVICEID 0xfafafafa
|
|
|
|
// Test port name.
|
|
#define TESTPORTNAME L"LPT1"
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Internal Typedefs
|
|
//
|
|
|
|
//
|
|
// Context for each open by a user-mode device manager component. This
|
|
// structure is stored in the FsContext field of the file object.
|
|
//
|
|
typedef struct tagDEVMGRCONTEXT
|
|
{
|
|
#if DBG
|
|
ULONG magicNo;
|
|
#endif
|
|
ULONG sessionID;
|
|
} DEVMGRCONTEXT, *PDEVMGRCONTEXT;
|
|
|
|
//
|
|
// Non-Opaque Version of Associated Data for a Device Managed by this Module
|
|
//
|
|
typedef struct tagRDPDYN_DEVICEDATAREC
|
|
{
|
|
ULONG PortNumber;
|
|
UNICODE_STRING SymbolicLinkName;
|
|
} RDPDYN_DEVICEDATAREC, *PRDPDYN_DEVICEDATAREC;
|
|
|
|
typedef struct tagCLIENTMESSAGECONTEXT {
|
|
RDPDR_ClientMessageCB *CB;
|
|
PVOID ClientData;
|
|
} CLIENTMESSAGECONTEXT, *PCLIENTMESSAGECONTEXT;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Internal Prototypes
|
|
//
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif // __cplusplus
|
|
|
|
// Return the next available printer port number.
|
|
NTSTATUS GetNextPrinterPortNumber(
|
|
OUT ULONG *portNumber
|
|
);
|
|
|
|
// Handle file object creation by a client of this driver.
|
|
NTSTATUS RDPDYN_Create(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
// Handle file object closure by a client of this driver.
|
|
NTSTATUS RDPDYN_Close(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
// This routine modifies the file object in preparation for returning STATUS_REPARSE.
|
|
NTSTATUS RDPDYN_PrepareForReparse(
|
|
PFILE_OBJECT fileObject
|
|
);
|
|
|
|
// Handle IOCTL IRP's.
|
|
NTSTATUS RDPDYN_DeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
// This routine modifies the file object in preparation for returning STATUS_REPARSE.
|
|
NTSTATUS RDPDYN_PrepareForDevMgmt(
|
|
PFILE_OBJECT fileObject,
|
|
PCWSTR sessionIDStr,
|
|
PIRP irp,
|
|
PIO_STACK_LOCATION irpStackLocation
|
|
);
|
|
|
|
// Generates a printer announce message for testing.
|
|
NTSTATUS RDPDYN_GenerateTestPrintAnnounceMsg(
|
|
IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN ULONG devAnnounceMsgSize,
|
|
OPTIONAL OUT ULONG *prnAnnounceMsgReqSize
|
|
);
|
|
|
|
// Completely handles IOCTL_RDPDR_GETNEXTDEVMGMTEVENT IRP's.
|
|
NTSTATUS RDPDYN_HandleGetNextDevMgmtEventIOCTL(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP irp
|
|
);
|
|
|
|
// Handle the cleanup IRP for a file object.
|
|
NTSTATUS RDPDYN_Cleanup(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
// Calculate the size of a device management event.
|
|
ULONG RDPDYN_DevMgmtEventSize(
|
|
IN PVOID devMgmtEvent,
|
|
IN ULONG type
|
|
);
|
|
|
|
// Completely handles IOCTL_RDPDR_CLIENTMSG IRP's.
|
|
NTSTATUS RDPDYN_HandleClientMsgIOCTL(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP pIrp
|
|
);
|
|
|
|
// Complete a pending IRP with a device management event.
|
|
NTSTATUS CompleteIRPWithDevMgmtEvent(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP pIrp,
|
|
IN ULONG eventSize,
|
|
IN ULONG eventType,
|
|
IN PVOID event,
|
|
IN DrDevice *drDevice
|
|
);
|
|
|
|
// Complete a pending IRP with a resize buffer event to the user-mode
|
|
// component.
|
|
NTSTATUS CompleteIRPWithResizeMsg(
|
|
IN PIRP pIrp,
|
|
IN ULONG requiredUserBufSize
|
|
);
|
|
|
|
// Format a port description.
|
|
void GeneratePortDescription(
|
|
IN PCSTR dosPortName,
|
|
IN PCWSTR clientName,
|
|
IN PWSTR description
|
|
);
|
|
|
|
NTSTATUS NTAPI DrSendMessageToClientCompletion(PVOID Context,
|
|
PIO_STATUS_BLOCK IoStatusBlock);
|
|
|
|
#if DBG
|
|
// This is for testing so we can create a new test printer on
|
|
// demand from user-mode.
|
|
NTSTATUS RDPDYN_HandleDbgAddNewPrnIOCTL(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP pIrp
|
|
);
|
|
|
|
// Generates a printer announce message for testing.
|
|
void RDPDYN_TracePrintAnnounceMsg(
|
|
IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN ULONG sessionID,
|
|
IN PCWSTR portName,
|
|
IN PCWSTR clientName
|
|
);
|
|
#endif
|
|
|
|
// Returns the next pending device management event request for the specified
|
|
// session, in the form of an IRP. Note that this function can not be called
|
|
// if a spinlock has been acquired.
|
|
PIRP GetNextEventRequest(
|
|
IN RDPEVNTLIST list,
|
|
IN ULONG sessionID
|
|
);
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif // __cplusplus
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
// Pointer to the device Object for the minirdr. This global is defined in rdpdr.c.
|
|
extern PRDBSS_DEVICE_OBJECT DrDeviceObject;
|
|
|
|
//
|
|
// Global Registry Path for RDPDR.SYS. This global is defined in rdpdr.c.
|
|
//
|
|
extern UNICODE_STRING DrRegistryPath;
|
|
|
|
// The Physical Device Object that terminates our DO stack.
|
|
PDEVICE_OBJECT RDPDYN_PDO = NULL;
|
|
|
|
// Manages user-mode component device management events and event requests.
|
|
RDPEVNTLIST UserModeEventListMgr = RDPEVNTLIST_INVALID_LIST;
|
|
|
|
// Remove this check, eventually.
|
|
#if DBG
|
|
BOOL RDPDYN_StopReceived = FALSE;
|
|
BOOL RDPDYN_QueryStopReceived = FALSE;
|
|
DWORD RDPDYN_StartCount = 0;
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function Definitions
|
|
//
|
|
|
|
NTSTATUS
|
|
RDPDYN_Initialize(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Init function for this module.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
BEGIN_FN("RDPDYN_Initialize");
|
|
|
|
//
|
|
// Create the user-mode device event manager.
|
|
//
|
|
TRC_ASSERT(UserModeEventListMgr == RDPEVNTLIST_INVALID_LIST,
|
|
(TB, "Initialize called more than 1 time"));
|
|
UserModeEventListMgr = RDPEVNTLIST_CreateNewList();
|
|
if (UserModeEventListMgr != RDPEVNTLIST_INVALID_LIST) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Initialize the dynamic port management module.
|
|
//
|
|
if (status == STATUS_SUCCESS) {
|
|
status = RDPDRPRT_Initialize();
|
|
}
|
|
|
|
TRC_NRM((TB, "return status %08X.", status));
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_Shutdown(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shutdown function for this module.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
ULONG sessionID;
|
|
void *devMgmtEvent;
|
|
PIRP pIrp;
|
|
ULONG type;
|
|
#if DBG
|
|
ULONG sz;
|
|
#endif
|
|
DrDevice *device;
|
|
KIRQL oldIrql;
|
|
PDRIVER_CANCEL setCancelResult;
|
|
|
|
BEGIN_FN("RDPDYN_Shutdown");
|
|
|
|
//
|
|
// Clean up any pending device management events and any pending IRP's.
|
|
//
|
|
TRC_ASSERT(UserModeEventListMgr != RDPEVNTLIST_INVALID_LIST,
|
|
(TB, "Invalid list mgr"));
|
|
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
while (RDPEVNTLLIST_GetFirstSessionID(UserModeEventListMgr, &sessionID)) {
|
|
//
|
|
// Remove pending IRP's
|
|
//
|
|
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(
|
|
UserModeEventListMgr,
|
|
sessionID
|
|
);
|
|
while (pIrp != NULL) {
|
|
//
|
|
// Set the cancel routine to NULL and record the current state.
|
|
//
|
|
setCancelResult = IoSetCancelRoutine(pIrp, NULL);
|
|
|
|
//
|
|
// Fail the IRP request.
|
|
//
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// If the IRP is not being canceled.
|
|
//
|
|
if (setCancelResult != NULL) {
|
|
//
|
|
// Fail the request.
|
|
//
|
|
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
//
|
|
// Remove the next IRP from the event/request queue.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(
|
|
UserModeEventListMgr,
|
|
sessionID
|
|
);
|
|
}
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Remove pending device management events.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
while (RDPEVNTLIST_DequeueEvent(
|
|
UserModeEventListMgr,
|
|
sessionID, &type,
|
|
&devMgmtEvent,
|
|
&device
|
|
)) {
|
|
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
#if DBG
|
|
// Zero the free'd event in checked builds.
|
|
sz = RDPDYN_DevMgmtEventSize(devMgmtEvent, type);
|
|
if (sz > 0) {
|
|
RtlZeroMemory(devMgmtEvent, sz);
|
|
}
|
|
#endif
|
|
if (devMgmtEvent != NULL) {
|
|
delete devMgmtEvent;
|
|
}
|
|
|
|
// Release the device, if appropriate.
|
|
if (device != NULL) {
|
|
device->Release();
|
|
}
|
|
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
}
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
}
|
|
|
|
//
|
|
// Shut down the dynamic port management module.
|
|
//
|
|
RDPDRPRT_Shutdown();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_Dispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle IRP's for DO's sitting on top of our physical device object.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object for the packet being processed.
|
|
|
|
Irp - Supplies the Irp being processed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The status for the Irp
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION ioStackLocation;
|
|
NTSTATUS status;
|
|
PRDPDYNDEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT stackDeviceObject;
|
|
BOOLEAN isPowerIRP;
|
|
|
|
BEGIN_FN("RDPDYN_Dispatch");
|
|
|
|
//
|
|
// Get our location in the IRP stack.
|
|
//
|
|
ioStackLocation = IoGetCurrentIrpStackLocation(Irp);
|
|
TRC_NRM((TB, "Major is %08X", ioStackLocation->MajorFunction));
|
|
|
|
//
|
|
// Get our device extension and stack device object.
|
|
//
|
|
deviceExtension = (PRDPDYNDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
TRC_ASSERT(deviceExtension != NULL, (TB, "Invalid device extension."));
|
|
if (deviceExtension == NULL) {
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
stackDeviceObject = deviceExtension->TopOfStackDeviceObject;
|
|
TRC_ASSERT(stackDeviceObject != NULL, (TB, "Invalid device object."));
|
|
|
|
//
|
|
// Function Dispatch Switch
|
|
//
|
|
isPowerIRP = FALSE;
|
|
switch (ioStackLocation->MajorFunction)
|
|
{
|
|
case IRP_MJ_CREATE:
|
|
|
|
TRC_NRM((TB, "IRP_MJ_CREATE"));
|
|
|
|
// RDPDYN_Create handles this completely.
|
|
return RDPDYN_Create(DeviceObject, Irp);
|
|
|
|
case IRP_MJ_CLOSE:
|
|
|
|
TRC_NRM((TB, "IRP_MJ_CLOSE"));
|
|
|
|
// RDPDYN_Close handles this completely.
|
|
return RDPDYN_Close(DeviceObject, Irp);
|
|
|
|
case IRP_MJ_CLEANUP:
|
|
|
|
TRC_NRM((TB, "IRP_MJ_CLEANUP"));
|
|
|
|
// RDPDYN_Cleanup handles this completely.
|
|
return RDPDYN_Cleanup(DeviceObject, Irp);
|
|
|
|
case IRP_MJ_READ:
|
|
|
|
// We shouldn't be receiving any read requests.
|
|
TRC_ASSERT(FALSE, (TB, "Read requests not supported."));
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
case IRP_MJ_WRITE:
|
|
|
|
// We shouldn't be receiving any write requests.
|
|
TRC_ASSERT(FALSE, (TB, "Write requests not supported."));
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
|
|
// RDPDYN_DeviceControl handles this completely.
|
|
return RDPDYN_DeviceControl(DeviceObject, Irp);
|
|
|
|
case IRP_MJ_POWER:
|
|
|
|
TRC_NRM((TB, "IRP_MJ_POWER"));
|
|
isPowerIRP = TRUE;
|
|
|
|
switch (ioStackLocation->MinorFunction)
|
|
{
|
|
case IRP_MN_SET_POWER:
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
default:
|
|
TRC_NRM((TB, "Unknown Power IRP"));
|
|
}
|
|
break;
|
|
|
|
case IRP_MJ_PNP: TRC_NRM((TB, "IRP_MJ_PNP"));
|
|
|
|
switch (ioStackLocation->MinorFunction)
|
|
{
|
|
case IRP_MN_START_DEVICE:
|
|
#if DBG
|
|
// Remove this debug code, eventually.
|
|
RDPDYN_StartCount++;
|
|
#endif
|
|
|
|
return(RDPDRPNP_HandleStartDeviceIRP(stackDeviceObject,
|
|
ioStackLocation, Irp));
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
#if DBG
|
|
// Remove this debug code, eventually.
|
|
RDPDYN_StopReceived = TRUE;
|
|
#endif
|
|
|
|
TRC_NRM((TB, "IRP_MN_STOP_DEVICE"));
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
return(RDPDRPNP_HandleRemoveDeviceIRP(DeviceObject,
|
|
stackDeviceObject, Irp));
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
|
|
TRC_NRM((TB, "IRP_MN_QUERY_CAPABILITIES"));
|
|
break;
|
|
|
|
case IRP_MN_QUERY_ID:
|
|
TRC_NRM((TB, "IRP_MN_QUERY_ID"));
|
|
break;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
|
|
TRC_NRM((TB, "IRP_MN_QUERY_DEVICE_RELATIONS"));
|
|
switch(ioStackLocation->Parameters.QueryDeviceRelations.Type)
|
|
{
|
|
case EjectionRelations:
|
|
TRC_NRM((TB, "Type==EjectionRelations"));
|
|
break;
|
|
|
|
case BusRelations:
|
|
// Note that we need to handle this if we end up kicking out any PDO's.
|
|
TRC_NRM((TB, "Type==BusRelations"));
|
|
break;
|
|
|
|
case PowerRelations:
|
|
TRC_NRM((TB, "Type==PowerRelations"));
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case RemovalRelations:
|
|
TRC_NRM((TB, "Type==RemovalRelations"));
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case TargetDeviceRelation:
|
|
TRC_NRM((TB, "Type==TargetDeviceRelation"));
|
|
break;
|
|
|
|
default:
|
|
TRC_NRM((TB, "Unknown IRP_MN_QUERY_DEVICE_RELATIONS minor type"));
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
|
|
#if DBG
|
|
// Remove this debug code, eventually.
|
|
RDPDYN_QueryStopReceived = TRUE;
|
|
#endif
|
|
|
|
// We will not allow a device to be stopped for load balancing.
|
|
TRC_NRM((TB, "IRP_MN_QUERY_STOP_DEVICE"));
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
|
|
// We will not allow our device to be removed.
|
|
TRC_NRM((TB, "IRP_MN_QUERY_REMOVE_DEVICE"));
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
TRC_NRM((TB, "IRP_MN_CANCEL_STOP_DEVICE"));
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
TRC_NRM((TB, "IRP_MN_CANCEL_REMOVE_DEVICE"));
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
|
|
TRC_NRM((TB, "IRP_MN_FILTER_RESOURCE_REQUIREMENTS"));
|
|
break;
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
TRC_NRM((TB, "IRP_MN_QUERY_PNP_DEVICE_STATE"));
|
|
break;
|
|
|
|
case IRP_MN_QUERY_BUS_INFORMATION:
|
|
TRC_NRM((TB, "IRP_MN_QUERY_BUS_INFORMATION"));
|
|
break;
|
|
|
|
default:
|
|
TRC_ALT((TB, "Unhandled PnP IRP with minor %08X",
|
|
ioStackLocation->MinorFunction));
|
|
}
|
|
}
|
|
|
|
//
|
|
// By default, pass the IRP down the stack.
|
|
//
|
|
if (isPowerIRP) {
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return PoCallDriver(stackDeviceObject, Irp);
|
|
}
|
|
else {
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(stackDeviceObject,Irp);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_Create(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Entry point for CreateFile calls.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to our device object.
|
|
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PFILE_OBJECT fileObject;
|
|
PIO_STACK_LOCATION nextStackLocation;
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
ULONG i;
|
|
BOOL matches;
|
|
WCHAR sessionIDString[]=RDPDYN_SESSIONIDSTRING;
|
|
ULONG idStrLen;
|
|
WCHAR *sessionIDPtr;
|
|
ULONG fnameLength;
|
|
|
|
BEGIN_FN("RDPDYN_Create");
|
|
|
|
// Get the current stack location.
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(Irp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
fileObject = currentStackLocation->FileObject;
|
|
|
|
// Return STATUS_REPARSE with the minirdr DO so it gets opened instead, if
|
|
// we have a file name.
|
|
if (fileObject->FileName.Length != 0)
|
|
{
|
|
//
|
|
// Find out if the client is trying to open us as the device manager from
|
|
// user-mode.
|
|
//
|
|
|
|
// Check for the session identifer string as the first few characters in
|
|
// the reference string.
|
|
idStrLen = wcslen(sessionIDString);
|
|
fnameLength = fileObject->FileName.Length/sizeof(WCHAR);
|
|
for (i=0; i<fnameLength && i<idStrLen; i++) {
|
|
if (fileObject->FileName.Buffer[i] != sessionIDString[i]) {
|
|
break;
|
|
}
|
|
}
|
|
matches = (i == idStrLen);
|
|
|
|
//
|
|
// If the client is trying to open us as the device manager from user-
|
|
// mode.
|
|
//
|
|
if (matches) {
|
|
|
|
// Prepare the file object for managing device management comms to
|
|
// the user-mode component that opened it.
|
|
ntStatus = RDPDYN_PrepareForDevMgmt(
|
|
fileObject,
|
|
&fileObject->FileName.Buffer[idStrLen],
|
|
Irp, currentStackLocation
|
|
);
|
|
}
|
|
// Otherwise, we can assume that this create is for a device that is being
|
|
// managed by RDPDR and the IFS kit.
|
|
else {
|
|
// Prepare the file object for reparse.
|
|
ntStatus = RDPDYN_PrepareForReparse(fileObject);
|
|
}
|
|
}
|
|
// Otherwise, fail. This should never happen.
|
|
else
|
|
{
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// Complete the IO request and return.
|
|
Irp->IoStatus.Status = ntStatus;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest (Irp,
|
|
IO_NO_INCREMENT
|
|
);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_PrepareForReparse(
|
|
PFILE_OBJECT fileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine modifies the file object in preparation for returning
|
|
STATUS_REPARSE
|
|
|
|
Arguments:
|
|
|
|
fileObject - the file object
|
|
|
|
Return Value:
|
|
|
|
STATUS_REPARSE if everything is successful
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
USHORT rootDeviceNameLength, reparsePathLength,
|
|
clientDevicePathLength;
|
|
PWSTR pFileNameBuffer = NULL;
|
|
ULONG i;
|
|
ULONG len;
|
|
BOOL clientDevPathMissingSlash;
|
|
HANDLE deviceInterfaceKey = INVALID_HANDLE_VALUE;
|
|
UNICODE_STRING unicodeStr;
|
|
ULONG requiredBytes;
|
|
PKEY_VALUE_PARTIAL_INFORMATION keyValueInfo = NULL;
|
|
WCHAR *clientDevicePath=L"";
|
|
GUID *pPrinterGuid;
|
|
UNICODE_STRING symbolicLinkName;
|
|
WCHAR *refString;
|
|
|
|
BEGIN_FN("RDPDYN_PrepareForReparse");
|
|
|
|
// We are not going to use these fields for storing any contextual
|
|
// information.
|
|
fileObject->FsContext = NULL;
|
|
fileObject->FsContext2 = NULL;
|
|
|
|
// Compute the number of bytes required to store the root of the device
|
|
// path, without the terminator.
|
|
rootDeviceNameLength = wcslen(RDPDR_DEVICE_NAME_U) *
|
|
sizeof(WCHAR);
|
|
|
|
//
|
|
// Get a pointer to the reference string for the reparse.
|
|
//
|
|
if (fileObject->FileName.Buffer[0] == L'\\') {
|
|
refString = &fileObject->FileName.Buffer[1];
|
|
}
|
|
else {
|
|
refString = &fileObject->FileName.Buffer[0];
|
|
}
|
|
|
|
//
|
|
// Resolve the reference name for the device into the symbolic link
|
|
// name for the device interface. We can optimize out this
|
|
// step and the next one by maintaining an internal table to convert
|
|
// from port names to symbolic link names.
|
|
//
|
|
pPrinterGuid = (GUID *)&DYNPRINT_GUID;
|
|
RtlInitUnicodeString(&unicodeStr, refString);
|
|
ntStatus=IoRegisterDeviceInterface(
|
|
RDPDYN_PDO, pPrinterGuid, &unicodeStr,
|
|
&symbolicLinkName
|
|
);
|
|
if (ntStatus == STATUS_SUCCESS) {
|
|
|
|
TRC_ERR((TB, "IoRegisterDeviceInterface succeeded."));
|
|
|
|
//
|
|
// Open the registry key for the device being opened.
|
|
//
|
|
ntStatus = IoOpenDeviceInterfaceRegistryKey(
|
|
&symbolicLinkName,
|
|
KEY_ALL_ACCESS,
|
|
&deviceInterfaceKey
|
|
);
|
|
|
|
RtlFreeUnicodeString(&symbolicLinkName);
|
|
}
|
|
|
|
//
|
|
// Get the size of the value info buffer required for the client device
|
|
// path for the device being opened.
|
|
//
|
|
if (ntStatus == STATUS_SUCCESS) {
|
|
TRC_NRM((TB, "IoOpenDeviceInterfaceRegistryKey succeeded."));
|
|
RtlInitUnicodeString(&unicodeStr, CLIENT_DEVICE_VALUE_NAME);
|
|
ntStatus = ZwQueryValueKey(
|
|
deviceInterfaceKey,
|
|
&unicodeStr,
|
|
KeyValuePartialInformation,
|
|
NULL, 0,
|
|
&requiredBytes
|
|
);
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "IoOpenDeviceInterfaceRegistryKey failed: %08X.", ntStatus));
|
|
|
|
deviceInterfaceKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Size the data buffer.
|
|
//
|
|
if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
|
|
keyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)
|
|
new(NonPagedPool) BYTE[requiredBytes];
|
|
if (keyValueInfo != NULL) {
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "failed to allocate client device path."));
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read the client device path.
|
|
//
|
|
if (ntStatus == STATUS_SUCCESS) {
|
|
ntStatus = ZwQueryValueKey(
|
|
deviceInterfaceKey,
|
|
&unicodeStr,
|
|
KeyValuePartialInformation,
|
|
keyValueInfo, requiredBytes,
|
|
&requiredBytes
|
|
);
|
|
}
|
|
|
|
//
|
|
// Allocate the reparsed filename.
|
|
//
|
|
if (ntStatus == STATUS_SUCCESS) {
|
|
TRC_NRM((TB, "ZwQueryValueKey succeeded."));
|
|
clientDevicePath = (WCHAR *)keyValueInfo->Data;
|
|
|
|
// Compute the number of bytes required to store the client device path,
|
|
// without the terminator.
|
|
clientDevicePathLength = wcslen(clientDevicePath) *
|
|
sizeof(WCHAR);
|
|
|
|
// See if the client device path is prefixed by a '\'
|
|
clientDevPathMissingSlash = clientDevicePath[0] != L'\\';
|
|
|
|
// Get the length (in bytes) of the entire reparsed device path, without the
|
|
// terminator.
|
|
reparsePathLength = rootDeviceNameLength +
|
|
clientDevicePathLength;
|
|
if (clientDevPathMissingSlash) {
|
|
reparsePathLength += sizeof(WCHAR);
|
|
}
|
|
|
|
pFileNameBuffer = (PWSTR)ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
reparsePathLength + (1 * sizeof(WCHAR)),
|
|
RDPDYN_POOLTAG);
|
|
if (pFileNameBuffer == NULL) {
|
|
TRC_NRM((TB, "failed to allocate reparse buffer."));
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Assign the reparse string to the IRP's file name for reparse.
|
|
//
|
|
if (ntStatus == STATUS_SUCCESS) {
|
|
// Copy the device name
|
|
RtlCopyMemory(
|
|
pFileNameBuffer,
|
|
RDPDR_DEVICE_NAME_U,
|
|
rootDeviceNameLength);
|
|
|
|
// Make sure we get a '\' between the root device name and
|
|
// the device path.
|
|
if (clientDevPathMissingSlash) {
|
|
pFileNameBuffer[rootDeviceNameLength/sizeof(WCHAR)] = L'\\';
|
|
rootDeviceNameLength += sizeof(WCHAR);
|
|
}
|
|
|
|
// Append the client device path to the end of the device name and
|
|
// include the client device path's terminator.
|
|
RtlCopyMemory(
|
|
((PBYTE)pFileNameBuffer + rootDeviceNameLength),
|
|
clientDevicePath, clientDevicePathLength + (1 * sizeof(WCHAR))
|
|
);
|
|
|
|
// Release the IRP's previous file name.
|
|
ExFreePool(fileObject->FileName.Buffer);
|
|
|
|
// Assign the reparse string to the IRP's file name.
|
|
fileObject->FileName.Buffer = pFileNameBuffer;
|
|
fileObject->FileName.Length = reparsePathLength;
|
|
fileObject->FileName.MaximumLength = fileObject->FileName.Length;
|
|
|
|
ntStatus = STATUS_REPARSE;
|
|
} else {
|
|
|
|
TRC_ERR((TB, "failed with status %08X.", ntStatus));
|
|
|
|
if (pFileNameBuffer != NULL) {
|
|
ExFreePool(pFileNameBuffer);
|
|
pFileNameBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
TRC_NRM((TB, "device file name after processing %wZ.",
|
|
&fileObject->FileName));
|
|
|
|
//
|
|
// Clean up and exit.
|
|
//
|
|
if (deviceInterfaceKey != INVALID_HANDLE_VALUE) {
|
|
ZwClose(deviceInterfaceKey);
|
|
}
|
|
if (keyValueInfo != NULL) {
|
|
delete keyValueInfo;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_PrepareForDevMgmt(
|
|
PFILE_OBJECT fileObject,
|
|
PCWSTR sessionIDStr,
|
|
PIRP irp,
|
|
PIO_STACK_LOCATION irpStackLocation
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine modifies the file object for managing device management comms
|
|
with the user-mode component that opened us.
|
|
|
|
Arguments:
|
|
|
|
fileObject - the file object.
|
|
sessionID - session identifier string.
|
|
irp - irp corresponding to the create for this file object.
|
|
irpStackLocation - current location in the IRP stack for the create.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if everything is successful
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PDEVMGRCONTEXT context;
|
|
ULONG sessionID;
|
|
ULONG i;
|
|
UNICODE_STRING uncSessionID;
|
|
NTSTATUS ntStatus;
|
|
ULONG irpSessionId;
|
|
|
|
BEGIN_FN("RDPDYN_PrepareForDevMgmt");
|
|
//
|
|
// Security check the IRP to make sure it comes from a thread
|
|
// with admin privilege
|
|
//
|
|
if (!DrIsAdminIoRequest(irp, irpStackLocation)) {
|
|
TRC_ALT((TB, "Access denied for non-Admin IRP."));
|
|
return STATUS_ACCESS_DENIED;
|
|
} else {
|
|
TRC_DBG((TB, "Admin IRP accepted."));
|
|
}
|
|
|
|
//
|
|
// Convert the session identifier string into a number.
|
|
//
|
|
RtlInitUnicodeString(&uncSessionID, sessionIDStr);
|
|
ntStatus = RtlUnicodeStringToInteger(&uncSessionID, 10, &sessionID);
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Allocate a context struct so we can remember information about
|
|
// which session we were opened from.
|
|
//
|
|
context = new(NonPagedPool) DEVMGRCONTEXT;
|
|
if (context == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
// Initialize this struct.
|
|
#if DBG
|
|
context->magicNo = DEVMGRCONTEXTMAGICNO;
|
|
#endif
|
|
context->sessionID = sessionID;
|
|
fileObject->FsContext = context;
|
|
|
|
// Success.
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_Close(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the closure of a file object.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS ntStatus;
|
|
PFILE_OBJECT fileObject;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVMGRCONTEXT context;
|
|
PIRP pIrp;
|
|
KIRQL oldIrql;
|
|
PDRIVER_CANCEL setCancelResult;
|
|
|
|
BEGIN_FN("RDPDYN_Close");
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
fileObject = irpStack->FileObject;
|
|
|
|
// Grab our "open" context for this instance of us from the current stack
|
|
// location's file object.
|
|
context = (PDEVMGRCONTEXT)irpStack->FileObject->FsContext;
|
|
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
|
|
|
|
//
|
|
// Make sure we got all the pending IRP's.
|
|
//
|
|
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL"));
|
|
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(
|
|
UserModeEventListMgr,
|
|
context->sessionID
|
|
);
|
|
while (pIrp != NULL) {
|
|
|
|
//
|
|
// Set the cancel routine to NULL and record the current state.
|
|
//
|
|
setCancelResult = IoSetCancelRoutine(pIrp, NULL);
|
|
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
TRC_NRM((TB, "canceling an IRP."));
|
|
|
|
//
|
|
// If the IRP is not being canceled.
|
|
//
|
|
if (setCancelResult != NULL) {
|
|
//
|
|
// Fail the request.
|
|
//
|
|
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
//
|
|
// Get the next one.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(
|
|
UserModeEventListMgr,
|
|
context->sessionID
|
|
);
|
|
}
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Release our context.
|
|
//
|
|
delete context;
|
|
irpStack->FileObject->FsContext = NULL;
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
ntStatus = Irp->IoStatus.Status;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_Cleanup(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the cleanup IRP for a file object.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PFILE_OBJECT fileObject;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVMGRCONTEXT context;
|
|
KIRQL oldIrql;
|
|
PIRP pIrp;
|
|
PDRIVER_CANCEL setCancelResult;
|
|
|
|
BEGIN_FN("RDPDYN_Cleanup");
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
fileObject = irpStack->FileObject;
|
|
|
|
// Grab our "open" context for this instance of us from the current stack
|
|
// location's file object.
|
|
context = (PDEVMGRCONTEXT)irpStack->FileObject->FsContext;
|
|
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
|
|
|
|
TRC_NRM((TB, "cancelling IRP's for session %ld.",
|
|
context->sessionID));
|
|
|
|
//
|
|
// Remove pending requests (IRP's)
|
|
// Nothing to do if event list is NULL
|
|
//
|
|
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL"));
|
|
|
|
if (UserModeEventListMgr == NULL) {
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(
|
|
UserModeEventListMgr,
|
|
context->sessionID
|
|
);
|
|
while (pIrp != NULL) {
|
|
|
|
//
|
|
// Set the cancel routine to NULL and record the current state.
|
|
//
|
|
setCancelResult = IoSetCancelRoutine(pIrp, NULL);
|
|
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
TRC_NRM((TB, "canceling an IRP."));
|
|
|
|
//
|
|
// If the IRP is not being canceled.
|
|
//
|
|
if (setCancelResult != NULL) {
|
|
//
|
|
// Fail the request.
|
|
//
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pIrp->IoStatus.Information = 0;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
//
|
|
// Get the next one.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(
|
|
UserModeEventListMgr,
|
|
context->sessionID
|
|
);
|
|
}
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
CleanupAndExit:
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
ntStatus = Irp->IoStatus.Status;
|
|
IoCompleteRequest (Irp,
|
|
IO_NO_INCREMENT
|
|
);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_DeviceControl(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle IOCTL IRP's.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object for this printer.
|
|
Irp - the IRP.
|
|
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
NTSTATUS ntStatus;
|
|
ULONG controlCode;
|
|
|
|
BEGIN_FN("RDPDYN_DeviceControl");
|
|
|
|
// Get the current stack location.
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(irp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
|
|
//
|
|
// Grab some info. out of the stack location.
|
|
//
|
|
controlCode = currentStackLocation->Parameters.DeviceIoControl.IoControlCode;
|
|
|
|
//
|
|
// Dispatch the IOCTL.
|
|
//
|
|
switch(controlCode)
|
|
{
|
|
case IOCTL_RDPDR_GETNEXTDEVMGMTEVENT :
|
|
|
|
ntStatus = RDPDYN_HandleGetNextDevMgmtEventIOCTL(deviceObject, irp);
|
|
break;
|
|
|
|
case IOCTL_RDPDR_CLIENTMSG :
|
|
|
|
ntStatus = RDPDYN_HandleClientMsgIOCTL(deviceObject, irp);
|
|
break;
|
|
|
|
#if DBG
|
|
case IOCTL_RDPDR_DBGADDNEWPRINTER :
|
|
|
|
// This is for testing so we can create a new test printer on
|
|
// demand from user-mode.
|
|
ntStatus = RDPDYN_HandleDbgAddNewPrnIOCTL(deviceObject, irp);
|
|
break;
|
|
|
|
#endif
|
|
|
|
default :
|
|
TRC_ASSERT(FALSE, (TB, "RPDR.SYS:Invalid IOCTL %08X.", controlCode));
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
irp->IoStatus.Status = ntStatus;
|
|
irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_HandleClientMsgIOCTL(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completely handles IOCTL_RDPDR_CLIENTMSG IRP's.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to our device object.
|
|
currentStackLocation - current location on the IRP stack.
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
PDEVMGRCONTEXT context;
|
|
NTSTATUS ntStatus;
|
|
ULONG inputLength;
|
|
|
|
BEGIN_FN("RDPDYN_HandleClientMsgIOCTL");
|
|
|
|
//
|
|
// Get the current stack location.
|
|
//
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
|
|
//
|
|
// Grab our "open" context for this instance of use from the current stack
|
|
// location's file object.
|
|
//
|
|
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext;
|
|
|
|
TRC_NRM((TB, "Requestor session ID %d.",
|
|
context->sessionID ));
|
|
|
|
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
|
|
|
|
//
|
|
// Grab some information about the user-mode's buffer off the IRP stack.
|
|
//
|
|
inputLength = currentStackLocation->Parameters.DeviceIoControl.InputBufferLength;
|
|
|
|
//
|
|
// Send the message to the client.
|
|
//
|
|
ntStatus = DrSendMessageToSession(
|
|
context->sessionID,
|
|
pIrp->AssociatedIrp.SystemBuffer,
|
|
inputLength,
|
|
NULL, NULL
|
|
);
|
|
if (ntStatus != STATUS_SUCCESS) {
|
|
TRC_ERR((TB, "msg failed."));
|
|
|
|
// Fail the IRP request.
|
|
pIrp->IoStatus.Status = ntStatus;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "msg succeeded."));
|
|
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
pIrp->IoStatus.Information = 0;
|
|
}
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
VOID DevMgmtEventRequestIRPCancel(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IRP cancel routine that is attached to device mgmt event request IRP's.
|
|
This routine is called with the cancel spinlock held.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to our device object.
|
|
pIrp - The IRP.
|
|
|
|
Return Value:
|
|
|
|
NA
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
KIRQL oldIrql;
|
|
ULONG sessionID;
|
|
PDEVMGRCONTEXT context;
|
|
|
|
BEGIN_FN("DevMgmtEventRequestIRPCancel");
|
|
|
|
//
|
|
// Get the current stack location.
|
|
//
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(Irp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
|
|
//
|
|
// Grab our "open" context for this instance of use from the current stack
|
|
// location's file object.
|
|
//
|
|
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext;
|
|
|
|
//
|
|
// Grab the session ID.
|
|
//
|
|
sessionID = context->sessionID;
|
|
TRC_NRM((TB, "session ID %d.", sessionID));
|
|
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
|
|
|
|
//
|
|
// Wax the current cancel routine pointer.
|
|
//
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
//
|
|
// Release the IRP cancellation spinlock.
|
|
//
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
//
|
|
// Remove the request from the device management list.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
RDPEVNTLIST_DequeueSpecificRequest(UserModeEventListMgr, sessionID, Irp);
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
TRC_NRM((TB, "DevMgmtEventRequestIRPCancel exiting."));
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_HandleGetNextDevMgmtEventIOCTL(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completely handles IOCTL_RDPDR_GETNEXTDEVMGMTEVENT IRP's.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to our device object.
|
|
pIrp - The IRP.
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
NTSTATUS status;
|
|
ULONG outputLength;
|
|
PDEVMGRCONTEXT context;
|
|
ULONG evType;
|
|
PVOID evt;
|
|
DrDevice *drDevice;
|
|
KIRQL oldIrql;
|
|
ULONG sessionID;
|
|
ULONG eventSize;
|
|
ULONG requiredUserBufSize;
|
|
|
|
BEGIN_FN("RDPDYN_HandleGetNextDevMgmtEventIOCTL");
|
|
|
|
//
|
|
// Get the current stack location.
|
|
//
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
|
|
//
|
|
// Grab our "open" context for this instance of use from the current stack
|
|
// location's file object.
|
|
//
|
|
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext;
|
|
|
|
//
|
|
// Grab the session ID.
|
|
//
|
|
sessionID = context->sessionID;
|
|
|
|
TRC_NRM((TB, "Requestor session ID %d.", context->sessionID ));
|
|
|
|
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
|
|
|
|
// Grab some information about the user-mode's buffer off the IRP stack.
|
|
outputLength = currentStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL"));
|
|
//
|
|
// Lock the device management event list.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
|
|
//
|
|
// See if we have a "device mgmt event pending."
|
|
//
|
|
if (RDPEVNTLIST_PeekNextEvent(
|
|
UserModeEventListMgr,
|
|
sessionID, &evt,
|
|
&evType, &drDevice
|
|
)) {
|
|
//
|
|
// If the pending IRP's pending buffer is large enough for the
|
|
// next event.
|
|
//
|
|
eventSize = RDPDYN_DevMgmtEventSize(evt, evType);
|
|
requiredUserBufSize = eventSize + sizeof(RDPDRDVMGR_EVENTHEADER);
|
|
if (outputLength >= requiredUserBufSize) {
|
|
//
|
|
// Dequeue the next pending event. This better be the one
|
|
// we just peeked at.
|
|
//
|
|
RDPEVNTLIST_DequeueEvent(
|
|
UserModeEventListMgr,
|
|
sessionID, &evType,
|
|
&evt, NULL
|
|
);
|
|
|
|
//
|
|
// It's safe to unlock the device management event list now.
|
|
//
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Complete the pending IRP.
|
|
//
|
|
status = CompleteIRPWithDevMgmtEvent(
|
|
deviceObject,
|
|
pIrp, eventSize,
|
|
evType, evt, drDevice
|
|
);
|
|
|
|
//
|
|
// Release the event.
|
|
//
|
|
if (evt != NULL) {
|
|
delete evt;
|
|
evt = NULL;
|
|
}
|
|
|
|
//
|
|
// Release our reference to the device, if we own one.
|
|
//
|
|
if (drDevice != NULL) {
|
|
drDevice->Release();
|
|
}
|
|
}
|
|
//
|
|
// Otherwise, need to send a resize buffer message to the
|
|
// user-mode copmonent.
|
|
//
|
|
else {
|
|
//
|
|
// It's safe to unlock the device management event list now.
|
|
//
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
status = CompleteIRPWithResizeMsg(pIrp, requiredUserBufSize);
|
|
}
|
|
}
|
|
//
|
|
// Otherwise, queue the IRP, mark the IRP pending and return.
|
|
//
|
|
else {
|
|
//
|
|
// Queue the request.
|
|
//
|
|
status = RDPEVNTLIST_EnqueueRequest(UserModeEventListMgr,
|
|
context->sessionID, pIrp);
|
|
//
|
|
// Set the cancel routine for the pending IRP.
|
|
//
|
|
if (status == STATUS_SUCCESS) {
|
|
IoMarkIrpPending(pIrp);
|
|
IoSetCancelRoutine(pIrp, DevMgmtEventRequestIRPCancel);
|
|
status = STATUS_PENDING;
|
|
}
|
|
else {
|
|
// Fail the IRP request.
|
|
pIrp->IoStatus.Status = status;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
//
|
|
// It's safe to unlock the device management event list now.
|
|
//
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
RDPDYN_SessionConnected(
|
|
IN ULONG sessionID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called when a new session is connected.
|
|
|
|
Arguments:
|
|
|
|
sessionID - Identifier for removed session.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
#if DBG
|
|
BOOL result;
|
|
PVOID evt;
|
|
DrDevice *drDevice;
|
|
KIRQL oldIrql;
|
|
ULONG evType;
|
|
#endif
|
|
|
|
BEGIN_FN("RDPDYN_SessionConnected");
|
|
TRC_NRM((TB, "Session %ld.", sessionID));
|
|
//
|
|
// Nothing to do if the event list is NULL
|
|
//
|
|
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL"));
|
|
|
|
if (UserModeEventListMgr == NULL) {
|
|
goto CleanupAndExit;
|
|
}
|
|
#if DBG
|
|
//
|
|
// See if there is still an event in the queue. Really, we should be checking
|
|
// to see if there is more than one event in the queue. This will catch most
|
|
// problems with events not gettin cleaned up on session disconnect.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
result = RDPEVNTLIST_PeekNextEvent(
|
|
UserModeEventListMgr,
|
|
sessionID, &evt, &evType,
|
|
&drDevice);
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// The only pending event allowed in the queue, at this point, is
|
|
// a remove client device event. RDPDYN_SessionDisconnected discards
|
|
// all other events.
|
|
//
|
|
if (result) {
|
|
TRC_ASSERT(evType == RDPDREVT_SESSIONDISCONNECT,
|
|
(TB, "Pending non-remove events %x on session connect.", evType));
|
|
}
|
|
#endif
|
|
CleanupAndExit:
|
|
return;
|
|
}
|
|
|
|
void
|
|
RDPDYN_SessionDisconnected(
|
|
IN ULONG sessionID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called when a session is disconnected from the system.
|
|
|
|
Arguments:
|
|
|
|
sessionID - Identifier for removed session.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
void *devMgmtEvent;
|
|
ULONG type;
|
|
BOOL queued;
|
|
KIRQL oldIrql;
|
|
DrDevice *device;
|
|
|
|
BEGIN_FN("RDPDYN_SessionDisconnected");
|
|
TRC_NRM((TB, "Session %ld.", sessionID));
|
|
|
|
//
|
|
// Remove all pending device management events for this session.
|
|
// Nothing to do if the event list is NULL
|
|
//
|
|
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL"));
|
|
|
|
if (UserModeEventListMgr == NULL) {
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
while (RDPEVNTLIST_DequeueEvent(
|
|
UserModeEventListMgr,
|
|
sessionID, &type, &devMgmtEvent,
|
|
&device
|
|
)) {
|
|
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
if (devMgmtEvent != NULL) {
|
|
delete devMgmtEvent;
|
|
}
|
|
if (device != NULL) {
|
|
device->Release();
|
|
}
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
}
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Dispatch a "session disconnect" event for the session to let user
|
|
// mode know about the event.
|
|
//
|
|
RDPDYN_DispatchNewDevMgmtEvent(
|
|
NULL, sessionID,
|
|
RDPDREVT_SESSIONDISCONNECT,
|
|
NULL
|
|
);
|
|
CleanupAndExit:
|
|
return;
|
|
}
|
|
|
|
PIRP
|
|
GetNextEventRequest(
|
|
IN RDPEVNTLIST list,
|
|
IN ULONG sessionID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the next pending device management event request for the specified
|
|
session, in the form of an IRP. Note that this function can not be called
|
|
if a spinlock has been acquired.
|
|
|
|
Arguments:
|
|
|
|
list - Device Management Event and Requeust List
|
|
sessionID - Destination session ID for event.
|
|
|
|
Return Value:
|
|
|
|
The next pending request (IRP) for the specified session or NULL if there are
|
|
not any IRP's pending.
|
|
|
|
--*/
|
|
{
|
|
PIRP pIrp;
|
|
KIRQL oldIrql;
|
|
BOOL done;
|
|
PDRIVER_CANCEL setCancelResult;
|
|
|
|
BEGIN_FN("GetNextEventRequest");
|
|
//
|
|
// Loop until we get an IRP that is not currently being cancelled.
|
|
//
|
|
done = FALSE;
|
|
setCancelResult = NULL;
|
|
while (!done) {
|
|
|
|
//
|
|
// Dequeue an IRP and take it out of a cancellable state.
|
|
//
|
|
RDPEVNTLIST_Lock(list, &oldIrql);
|
|
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(list, sessionID);
|
|
if (pIrp != NULL) {
|
|
setCancelResult = IoSetCancelRoutine(pIrp, NULL);
|
|
}
|
|
RDPEVNTLIST_Unlock(list, oldIrql);
|
|
|
|
done = (pIrp == NULL) || (setCancelResult != NULL);
|
|
}
|
|
|
|
return pIrp;
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_DispatchNewDevMgmtEvent(
|
|
IN PVOID devMgmtEvent,
|
|
IN ULONG sessionID,
|
|
IN ULONG eventType,
|
|
OPTIONAL IN DrDevice *devDevice
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch a device management event to the appropriate (session-wise) user-mode
|
|
device manager component. If there are not any event request IRP's pending
|
|
for the specified session, then the event is queued for future dispatch.
|
|
|
|
Arguments:
|
|
|
|
devMgmtEvent - The event.
|
|
sessionID - Destination session ID for event.
|
|
eventType - Type of event.
|
|
queued - TRUE if the event was queued for future dispatch.
|
|
devDevice - Device object associated with the event. NULL, if not
|
|
specified.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful, error status otherwise.
|
|
|
|
--*/
|
|
{
|
|
PIRP pIrp;
|
|
NTSTATUS status;
|
|
KIRQL oldIrql;
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
ULONG outputLength;
|
|
ULONG eventSize;
|
|
ULONG requiredUserBufSize;
|
|
DrDevice *drDevice = NULL;
|
|
PVOID evt;
|
|
ULONG evType;
|
|
|
|
BEGIN_FN("RDPDYN_DispatchNewDevMgmtEvent");
|
|
|
|
//
|
|
// Nothing to do if the event list is NULL
|
|
//
|
|
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL"));
|
|
|
|
if (UserModeEventListMgr == NULL) {
|
|
return STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
|
|
//
|
|
// Ref count the device, if provided.
|
|
//
|
|
if (devDevice != NULL) {
|
|
devDevice->AddRef();
|
|
}
|
|
//
|
|
// Enqueue the new event.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
status = RDPEVNTLIST_EnqueueEvent(
|
|
UserModeEventListMgr,
|
|
sessionID,
|
|
devMgmtEvent,
|
|
eventType,
|
|
devDevice
|
|
);
|
|
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// If we have an IRP pending for the specified session.
|
|
//
|
|
if (status == STATUS_SUCCESS) {
|
|
pIrp = GetNextEventRequest(UserModeEventListMgr, sessionID);
|
|
}
|
|
else {
|
|
if (devDevice != NULL) {
|
|
devDevice->Release();
|
|
}
|
|
}
|
|
|
|
if ((status == STATUS_SUCCESS) && (pIrp != NULL)) {
|
|
TRC_NRM((TB, "found an IRP pending for "
|
|
"session %ld", sessionID));
|
|
|
|
//
|
|
// Find out about the pending IRP.
|
|
//
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
outputLength =
|
|
currentStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// If we have a pending event.
|
|
//
|
|
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
|
|
if (RDPEVNTLIST_PeekNextEvent(
|
|
UserModeEventListMgr,
|
|
sessionID, &evt, &evType,
|
|
&drDevice
|
|
)) {
|
|
//
|
|
// If the pending IRP's pending buffer is large enough for the
|
|
// next event.
|
|
//
|
|
eventSize = RDPDYN_DevMgmtEventSize(evt, evType);
|
|
requiredUserBufSize = eventSize + sizeof(RDPDRDVMGR_EVENTHEADER);
|
|
if (outputLength >= requiredUserBufSize) {
|
|
//
|
|
// Dequeue the next pending event. This better be the one
|
|
// we just peeked at.
|
|
//
|
|
RDPEVNTLIST_DequeueEvent(
|
|
UserModeEventListMgr,
|
|
sessionID, &evType,
|
|
&evt, NULL
|
|
);
|
|
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Complete the pending IRP.
|
|
//
|
|
status = CompleteIRPWithDevMgmtEvent(
|
|
RDPDYN_PDO, pIrp, eventSize,
|
|
evType, evt,
|
|
drDevice
|
|
);
|
|
|
|
//
|
|
// Release the event.
|
|
//
|
|
if (evt != NULL) {
|
|
delete evt;
|
|
evt = NULL;
|
|
}
|
|
|
|
//
|
|
// Release our reference to the device, if we own one.
|
|
//
|
|
if (drDevice != NULL) {
|
|
drDevice->Release();
|
|
}
|
|
|
|
}
|
|
//
|
|
// Otherwise, need to send a resize buffer message to the
|
|
// user-mode component.
|
|
//
|
|
else {
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
status = CompleteIRPWithResizeMsg(pIrp, requiredUserBufSize);
|
|
}
|
|
}
|
|
//
|
|
// Otherwise, we need to requeue the IRP request.
|
|
//
|
|
else {
|
|
|
|
status = RDPEVNTLIST_EnqueueRequest(UserModeEventListMgr,
|
|
sessionID, pIrp);
|
|
|
|
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
|
|
|
|
//
|
|
// If we fail here, we need to fail the IRP.
|
|
//
|
|
if (status != STATUS_SUCCESS) {
|
|
pIrp->IoStatus.Status = status;
|
|
pIrp->IoStatus.Information = 0;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
}
|
|
|
|
TRC_NRM((TB, "exit RDPDYN_DispatchNewDevMgmtEvent"));
|
|
return status;
|
|
}
|
|
|
|
ULONG
|
|
RDPDYN_DevMgmtEventSize(
|
|
IN PVOID devMgmtEvent,
|
|
IN ULONG type
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculate the size of a device management event. This is more efficient than
|
|
storing the size with each event.
|
|
|
|
Arguments:
|
|
|
|
devMgmtEvent - Supplies the device object for the packet being processed.
|
|
|
|
type - Supplies the Irp being processed
|
|
|
|
Return Value:
|
|
|
|
The size, in bytes, of the event.
|
|
|
|
--*/
|
|
{
|
|
ULONG sz = 0;
|
|
|
|
BEGIN_FN("RDPDYN_DevMgmtEventSize");
|
|
switch(type) {
|
|
case RDPDREVT_PRINTERANNOUNCE :
|
|
sz = CALCPRINTERDEVICE_SUB_SZ((PRDPDR_PRINTERDEVICE_SUB)devMgmtEvent);
|
|
break;
|
|
case RDPDREVT_REMOVEDEVICE :
|
|
sz = CALCREMOVEDEVICE_SUB_SZ((PRDPDR_REMOVEDEVICE)devMgmtEvent);
|
|
break;
|
|
case RDPDREVT_PORTANNOUNCE :
|
|
sz = CALCPORTDEVICE_SUB_SZ((PRDPDR_PORTDEVICE_SUB)devMgmtEvent);
|
|
break;
|
|
case RDPDREVT_DRIVEANNOUNCE :
|
|
sz = CALCDRIVEDEVICE_SUB_SZ((PRDPDR_DRIVEDEVICE_SUB)devMgmtEvent);
|
|
break;
|
|
|
|
case RDPDREVT_SESSIONDISCONNECT :
|
|
// There is no associated event data.
|
|
sz = 0;
|
|
break;
|
|
default:
|
|
TRC_ASSERT(FALSE, (TB, "Invalid event type"));
|
|
}
|
|
return sz;
|
|
}
|
|
|
|
NTSTATUS CompleteIRPWithDevMgmtEvent(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP pIrp,
|
|
IN ULONG eventSize,
|
|
IN ULONG eventType,
|
|
IN PVOID event,
|
|
IN DrDevice *drDevice
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Complete a pending IRP with a device management event.
|
|
|
|
Arguments:
|
|
|
|
deviceObject- Associated Device Object. Must be non-NULL if
|
|
drDevice is non-NULL.
|
|
pIrp - Pending IRP.
|
|
eventSize - Size of event being returned.
|
|
eventType - Event type being returned.
|
|
event - The event being returned.
|
|
drDevice - Device object associated with the IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS on success.
|
|
|
|
--*/
|
|
{
|
|
PRDPDRDVMGR_EVENTHEADER msgHeader;
|
|
ULONG bytesReturned;
|
|
void *usrDevMgmtEvent;
|
|
NTSTATUS status;
|
|
|
|
BEGIN_FN("CompleteIRPWithDevMgmtEvent");
|
|
|
|
//
|
|
// Optional last-minute event completion.
|
|
//
|
|
if (drDevice != NULL) {
|
|
status = drDevice->OnDevMgmtEventCompletion(deviceObject, event);
|
|
}
|
|
else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Compute the size of the return buffer.
|
|
//
|
|
bytesReturned = eventSize + sizeof(RDPDRDVMGR_EVENTHEADER);
|
|
|
|
//
|
|
// Create the message header.
|
|
//
|
|
msgHeader = (PRDPDRDVMGR_EVENTHEADER)pIrp->AssociatedIrp.SystemBuffer;
|
|
msgHeader->EventType = eventType;
|
|
msgHeader->EventLength = eventSize;
|
|
|
|
//
|
|
// Copy the device mgmt event over to the user-mode buffer.
|
|
//
|
|
usrDevMgmtEvent = ((PBYTE)pIrp->AssociatedIrp.SystemBuffer +
|
|
sizeof(RDPDRDVMGR_EVENTHEADER));
|
|
if (event != NULL && eventSize > 0) {
|
|
RtlCopyMemory(usrDevMgmtEvent, event, eventSize);
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
|
|
pIrp->IoStatus.Status = status;
|
|
pIrp->IoStatus.Information = bytesReturned;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
TRC_NRM((TB, "exit CompleteIRPWithDevMgmtEvent"));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CompleteIRPWithResizeMsg(
|
|
IN PIRP pIrp,
|
|
IN ULONG requiredUserBufSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Complete a pending IRP with a resize buffer event to the user-mode
|
|
component.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS is returned on success.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
ULONG outputLength;
|
|
PRDPDR_BUFFERTOOSMALL bufTooSmallMsg;
|
|
PRDPDRDVMGR_EVENTHEADER msgHeader;
|
|
ULONG bytesReturned;
|
|
NTSTATUS status;
|
|
|
|
BEGIN_FN("CompleteIRPWithResizeMsg");
|
|
|
|
// Get the current stack location.
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
|
|
// Grab some stuff off the IRP stack.
|
|
outputLength = currentStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Fail the request if there isn't room for a buffer too small
|
|
// message.
|
|
//
|
|
if (outputLength < (sizeof(RDPDRDVMGR_EVENTHEADER) +
|
|
sizeof(RDPDR_BUFFERTOOSMALL))) {
|
|
|
|
TRC_NRM((TB, "CompleteIRPWithResizeMsg no room for header."));
|
|
|
|
bytesReturned = 0;
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
else {
|
|
// Create the header.
|
|
msgHeader = (PRDPDRDVMGR_EVENTHEADER)pIrp->AssociatedIrp.SystemBuffer;
|
|
msgHeader->EventType = RDPDREVT_BUFFERTOOSMALL;
|
|
msgHeader->EventLength = sizeof(RDPDR_BUFFERTOOSMALL);
|
|
|
|
// Create the buffer too small message.
|
|
bufTooSmallMsg = (PRDPDR_BUFFERTOOSMALL)
|
|
((PBYTE)pIrp->AssociatedIrp.SystemBuffer +
|
|
sizeof(RDPDRDVMGR_EVENTHEADER));
|
|
bufTooSmallMsg->RequiredSize = requiredUserBufSize;
|
|
|
|
// Calculate the number of bytes that we are returning.
|
|
bytesReturned = sizeof(RDPDRDVMGR_EVENTHEADER) +
|
|
sizeof(RDPDR_BUFFERTOOSMALL);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
pIrp->IoStatus.Status = status;
|
|
pIrp->IoStatus.Information = bytesReturned;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
TRC_NRM((TB, "exit CompleteIRPWithResizeMsg"));
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
DrSendMessageToSession(
|
|
IN ULONG SessionId,
|
|
IN PVOID Msg,
|
|
IN DWORD MsgSize,
|
|
OPTIONAL IN RDPDR_ClientMessageCB CB,
|
|
OPTIONAL IN PVOID ClientData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send a message to the client with the specified session ID.
|
|
|
|
Arguments:
|
|
|
|
SessionId - The session id.
|
|
Msg - The Message
|
|
MsgSize - Size (in bytes) of message.
|
|
CB - Optional callback to be called when the message is completely
|
|
sent.
|
|
ClientData - Optional client-data passed to callback when message is
|
|
completely sent.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Success/failure indication of the operation
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
SmartPtr<DrSession> Session;
|
|
PCLIENTMESSAGECONTEXT Context;
|
|
|
|
BEGIN_FN("DrSendMessageToSession");
|
|
|
|
//
|
|
// Find the client entry.
|
|
//
|
|
|
|
if (Sessions->FindSessionById(SessionId, Session)) {
|
|
//
|
|
// Allocate the context for the function call.
|
|
//
|
|
Context = new CLIENTMESSAGECONTEXT;
|
|
|
|
if (Context != NULL) {
|
|
|
|
TRC_NRM((TB, "sending %ld bytes to server", MsgSize));
|
|
|
|
//
|
|
// Set up the context.
|
|
//
|
|
Context->CB = CB;
|
|
Context->ClientData = ClientData;
|
|
Status = Session->SendToClient(Msg, MsgSize,
|
|
DrSendMessageToClientCompletion, FALSE, FALSE, Context);
|
|
} else {
|
|
TRC_ERR((TB, "unable to allocate memory."));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_NOT_FOUND;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI DrSendMessageToClientCompletion(PVOID Context,
|
|
PIO_STATUS_BLOCK IoStatusBlock)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IoCompletion APC routine for DrSendMessageToClient.
|
|
|
|
Arguments:
|
|
|
|
ApcContext - Contains a pointer to the client message context.
|
|
IoStatusBlock - Status information about the operation. The Information
|
|
indicates the actual number of bytes written
|
|
Reserved - Reserved
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PCLIENTMESSAGECONTEXT MsgContext = (PCLIENTMESSAGECONTEXT)Context;
|
|
|
|
BEGIN_FN("DrSendMessageToClientCompletion");
|
|
|
|
TRC_ASSERT(MsgContext != NULL, (TB, "Message context NULL."));
|
|
TRC_ASSERT(IoStatusBlock != NULL, (TB, "IoStatusBlock NULL."));
|
|
|
|
TRC_NRM((TB, "status %lx", IoStatusBlock->Status));
|
|
|
|
//
|
|
// Call the client callback if it is defined.
|
|
//
|
|
if (MsgContext->CB != NULL) {
|
|
MsgContext->CB(MsgContext->ClientData, IoStatusBlock->Status);
|
|
}
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
|
|
// delete IoStatusBlock; // I don't think so, not really
|
|
delete Context;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a printer announce message for testing.
|
|
|
|
Return Value:
|
|
|
|
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is
|
|
too small. STATUS_SUCCESS is returned on success.
|
|
|
|
--*/
|
|
|
|
#if DBG
|
|
void
|
|
RDPDYN_TracePrintAnnounceMsg(
|
|
IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN ULONG sessionID,
|
|
IN PCWSTR portName,
|
|
IN PCWSTR clientName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Trace a printer device announce message.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PWSTR driverName, printerName;
|
|
PWSTR pnpName;
|
|
PRDPDR_PRINTERDEVICE_ANNOUNCE clientPrinterFields;
|
|
PBYTE pClientPrinterData;
|
|
ULONG sz;
|
|
|
|
BEGIN_FN("RDPDYN_TracePrintAnnounceMsg");
|
|
|
|
// Check the type.
|
|
TRC_ASSERT(devAnnounceMsg->DeviceType == RDPDR_DTYP_PRINT,
|
|
(TB, "Invalid device type"));
|
|
|
|
// Get the address of all data following the base message.
|
|
pClientPrinterData = ((PBYTE)devAnnounceMsg) +
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE) +
|
|
sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
|
|
|
|
// Get the address of the client printer fields.
|
|
clientPrinterFields = (PRDPDR_PRINTERDEVICE_ANNOUNCE)(((PBYTE)devAnnounceMsg) +
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE));
|
|
|
|
sz = clientPrinterFields->PnPNameLen +
|
|
clientPrinterFields->DriverLen +
|
|
clientPrinterFields->PrinterNameLen +
|
|
clientPrinterFields->CachedFieldsLen +
|
|
sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
|
|
|
|
if (devAnnounceMsg->DeviceDataLength != sz) {
|
|
TRC_ASSERT(FALSE,(TB, "Size integrity questionable in dev announce buf."));
|
|
}
|
|
else {
|
|
|
|
// Get the specific fields.
|
|
pnpName = (PWSTR)((clientPrinterFields->PnPNameLen) ? pClientPrinterData : NULL);
|
|
driverName = (PWSTR)((clientPrinterFields->DriverLen) ?
|
|
(pClientPrinterData + clientPrinterFields->PnPNameLen) : NULL);
|
|
printerName = (PWSTR)((clientPrinterFields->PrinterNameLen) ? (pClientPrinterData +
|
|
clientPrinterFields->PnPNameLen +
|
|
clientPrinterFields->DriverLen) : NULL);
|
|
|
|
TRC_NRM((TB, "New printer received for session %ld.", sessionID));
|
|
TRC_NRM((TB, "-----------------------------------------"));
|
|
TRC_NRM((TB, "port:\t%ws", portName));
|
|
|
|
if (clientPrinterFields->Flags & RDPDR_PRINTER_ANNOUNCE_FLAG_ANSI) {
|
|
TRC_NRM((TB, "driver:\t%s", (PSTR)driverName));
|
|
TRC_NRM((TB, "pnp name:\t%s", (PSTR)pnpName));
|
|
TRC_NRM((TB, "printer name:\t%s", (PSTR)printerName));
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "driver:\t%ws", driverName));
|
|
TRC_NRM((TB, "pnp name:\t%ws", pnpName));
|
|
TRC_NRM((TB, "printer name:\t%ws", printerName));
|
|
}
|
|
|
|
TRC_NRM((TB, "client name:\t%ws", clientName));
|
|
TRC_NRM((TB, "-----------------------------------------"));
|
|
|
|
TRC_NRM((TB, "exit RDPDYN_TracePrintAnnounceMsg"));
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RDPDYN_GenerateTestPrintAnnounceMsg(
|
|
IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN ULONG devAnnounceMsgSize,
|
|
OPTIONAL OUT ULONG *prnAnnounceMsgReqSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a printer announce message for testing.
|
|
|
|
Return Value:
|
|
|
|
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceMsgSize size is
|
|
too small. STATUS_SUCCESS is returned on success.
|
|
|
|
--*/
|
|
{
|
|
ULONG requiredSize;
|
|
PBYTE pClientPrinterData;
|
|
PWSTR driverName, printerName;
|
|
PWSTR pnpName;
|
|
PRDPDR_PRINTERDEVICE_ANNOUNCE prnMsg;
|
|
PRDPDR_PRINTERDEVICE_ANNOUNCE clientPrinterFields;
|
|
PBYTE pCachedFields;
|
|
|
|
BEGIN_FN("RDPDYN_GenerateTestPrintAnnounceMsg");
|
|
requiredSize = (ULONG)(sizeof(RDPDR_DEVICE_ANNOUNCE) +
|
|
sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE) +
|
|
((wcslen(TESTDRIVERNAME) + 1) * sizeof(WCHAR)) +
|
|
((wcslen(TESTPNPNAME) + 1) * sizeof(WCHAR)) +
|
|
((wcslen(TESTPRINTERNAME) + 1) * sizeof(WCHAR)));
|
|
|
|
//
|
|
// Find out if there isn't room in the return buffer for our response.
|
|
//
|
|
if (devAnnounceMsgSize < requiredSize) {
|
|
if (prnAnnounceMsgReqSize != NULL) {
|
|
*prnAnnounceMsgReqSize = requiredSize;
|
|
}
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Type
|
|
devAnnounceMsg->DeviceType = RDPDR_DTYP_PRINT;
|
|
|
|
// ID
|
|
devAnnounceMsg->DeviceId = TESTDEVICEID;
|
|
|
|
// Get the address of the client printer fields in the device announce
|
|
// message.
|
|
clientPrinterFields = (PRDPDR_PRINTERDEVICE_ANNOUNCE)(((PBYTE)devAnnounceMsg) +
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE));
|
|
|
|
// Get the address of all data following the base message.
|
|
pClientPrinterData = ((PBYTE)devAnnounceMsg) +
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE) +
|
|
sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
|
|
|
|
//
|
|
// Add the PnP Name.
|
|
//
|
|
// The PnP name is the first field.
|
|
pnpName = (PWSTR)pClientPrinterData;
|
|
wcscpy(pnpName, TESTPNPNAME);
|
|
clientPrinterFields->PnPNameLen = ((wcslen(TESTPNPNAME) + 1) * sizeof(WCHAR));
|
|
|
|
//
|
|
// Add the Driver Name.
|
|
//
|
|
// The driver name is the second field.
|
|
driverName = (PWSTR)(pClientPrinterData + clientPrinterFields->PnPNameLen);
|
|
wcscpy(driverName, TESTDRIVERNAME);
|
|
clientPrinterFields->DriverLen = ((wcslen(TESTDRIVERNAME) + 1) * sizeof(WCHAR));
|
|
|
|
//
|
|
// Add the Printer Name.
|
|
//
|
|
// The driver name is the second field.
|
|
printerName = (PWSTR)(pClientPrinterData +
|
|
clientPrinterFields->PnPNameLen +
|
|
clientPrinterFields->DriverLen);
|
|
wcscpy(printerName, TESTPRINTERNAME);
|
|
clientPrinterFields->PrinterNameLen = ((wcslen(TESTPRINTERNAME) + 1) * sizeof(WCHAR));
|
|
|
|
//
|
|
// Add the Cached Fields Len.
|
|
//
|
|
// The cached fields follow everything else.
|
|
|
|
/* Don't need this for testing, yet.
|
|
pCachedFields = (PBYTE)(pClientPrinterData + clientPrinterFields->PnPNameLen +
|
|
clientPrinterFields->DriverLen +
|
|
clientPrinterFields->PrinterNameLen);
|
|
*/
|
|
clientPrinterFields->CachedFieldsLen = 0;
|
|
|
|
//
|
|
// Set to non-ansi for now.
|
|
//
|
|
clientPrinterFields->Flags = 0;
|
|
|
|
|
|
// Length of all data following deviceFields.
|
|
devAnnounceMsg->DeviceDataLength =
|
|
sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE) +
|
|
clientPrinterFields->PnPNameLen +
|
|
clientPrinterFields->DriverLen +
|
|
clientPrinterFields->PrinterNameLen +
|
|
clientPrinterFields->CachedFieldsLen;
|
|
|
|
if (prnAnnounceMsgReqSize != NULL) {
|
|
*prnAnnounceMsgReqSize = requiredSize;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if DBG
|
|
NTSTATUS
|
|
RDPDYN_HandleDbgAddNewPrnIOCTL(
|
|
IN PDEVICE_OBJECT deviceObject,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is for testing so we can create a new test printer on
|
|
demand from user-mode.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to our device object.
|
|
currentStackLocation - current location on the IRP stack.
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
PRDPDR_DEVICE_ANNOUNCE pDevAnnounceMsg;
|
|
ULONG bytesToAlloc;
|
|
PIO_STACK_LOCATION currentStackLocation;
|
|
ULONG requiredSize;
|
|
ULONG bytesReturned = 0;
|
|
PDEVMGRCONTEXT context;
|
|
NTSTATUS ntStatus;
|
|
WCHAR buffer[64]=L"Test Printer";
|
|
UNICODE_STRING referenceString;
|
|
PBYTE tmp;
|
|
|
|
BEGIN_FN("RDPDYN_HandleDbgAddNewPrnIOCTL");
|
|
|
|
// Get the current stack location.
|
|
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp);
|
|
TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
|
|
|
|
// Grab our "open" context for this instance of us from the current stack
|
|
// location's file object.
|
|
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext;
|
|
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO,
|
|
(TB, "invalid context"));
|
|
|
|
// Find out how much room we need for the test message.
|
|
RDPDYN_GenerateTestPrintAnnounceMsg(NULL, 0, &requiredSize);
|
|
|
|
// Generate the message.
|
|
pDevAnnounceMsg = (PRDPDR_DEVICE_ANNOUNCE)new(NonPagedPool) BYTE[requiredSize];
|
|
if (pDevAnnounceMsg != NULL) {
|
|
RDPDYN_GenerateTestPrintAnnounceMsg(pDevAnnounceMsg, requiredSize, &requiredSize);
|
|
|
|
//
|
|
// Announce the new port (just send to session 0 for now).
|
|
//
|
|
RtlInitUnicodeString(&referenceString, buffer);
|
|
|
|
//#pragma message(__LOC__"Unit test to add device disabled")
|
|
/*
|
|
//
|
|
// Initialize the client entry struct.
|
|
//
|
|
RtlZeroMemory(&clientEntry, sizeof(clientEntry));
|
|
wcscpy(clientEntry.ClientName, L"DBGTEST");
|
|
clientEntry.SessionId = 0;
|
|
|
|
// Note that I am ignoring the returned device data for this test.
|
|
// This is okay, since I never call RDPDYN_RemoveClientDevice(
|
|
ntStatus = RDPDYN_AddClientDevice(
|
|
&clientEntry,
|
|
pDevAnnounceMsg,
|
|
&referenceString,
|
|
&tmp
|
|
);
|
|
*/
|
|
// For a test, delete the device next.
|
|
// RDPDYN_RemoveClientDevice(TESTDEVICEID, 0, tmp);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pIrp->IoStatus.Status = ntStatus;
|
|
pIrp->IoStatus.Information = 0;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#endif
|
|
|
|
|