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.
2975 lines
116 KiB
2975 lines
116 KiB
/*++
|
|
|
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
drive.cpp
|
|
|
|
Author :
|
|
|
|
JoyC 11/1/1999
|
|
|
|
Abstract:
|
|
|
|
Drive Device object handles one redirected drive
|
|
|
|
Revision History:
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#define TRC_FILE "drive"
|
|
#include "trc.h"
|
|
|
|
#if DBG
|
|
extern UCHAR IrpNames[IRP_MJ_MAXIMUM_FUNCTION + 1][40];
|
|
#endif // DBG
|
|
|
|
DrDrive::DrDrive(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId,
|
|
PUCHAR PreferredDosName) : DrDevice(Session, DeviceType, DeviceId, PreferredDosName)
|
|
{
|
|
BEGIN_FN("DrDrive::DrDrive");
|
|
|
|
SetClassName("DrDrive");
|
|
|
|
TRC_NRM((TB, "Create drive object = %p", this));
|
|
}
|
|
|
|
BOOL DrDrive::ShouldCreateDevice()
|
|
{
|
|
BEGIN_FN("DrDrive::ShouldCreateDevice");
|
|
//
|
|
// Check if the device name is valid
|
|
//
|
|
if (!_Session->DisableDriveMapping()) {
|
|
return IsDeviceNameValid();
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL DrDrive::IsDeviceNameValid()
|
|
{
|
|
BEGIN_FN("DrDrive::IsDeviceNameValid");
|
|
BOOL fRet = TRUE;
|
|
int i, Len;
|
|
//
|
|
// Our device name is valid only if
|
|
// the first char contains a character between A-Z.
|
|
// and the 2nd char is NULL.
|
|
//
|
|
// For Mac client, drive name can have up to 7 characters and
|
|
// valid characters are: [a-z], [A-Z], [0-9], '-', '_' and ' '
|
|
//
|
|
Len = strlen((CHAR*)_PreferredDosName);
|
|
if ((Len <= 7) && (Len >= 1)) {
|
|
for (i=0; i<Len; i++) {
|
|
if(((_PreferredDosName[i] < 'A') || (_PreferredDosName[i] > 'Z')) &&
|
|
((_PreferredDosName[i] < 'a') || (_PreferredDosName[i] > 'z')) &&
|
|
((_PreferredDosName[i] < '0') || (_PreferredDosName[i] > '9')) &&
|
|
(_PreferredDosName[i] != '-') &&
|
|
(_PreferredDosName[i] != '_') &&
|
|
(_PreferredDosName[i] != ' ')) {
|
|
fRet = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fRet = FALSE;
|
|
}
|
|
|
|
//
|
|
// This assert should never fire for drive redirection
|
|
//
|
|
ASSERT(fRet);
|
|
return fRet;
|
|
}
|
|
|
|
NTSTATUS DrDrive::Initialize(PRDPDR_DEVICE_ANNOUNCE DeviceAnnounce, ULONG Length)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING DriveName;
|
|
WCHAR DriveNameBuff[PREFERRED_DOS_NAME_SIZE];
|
|
INT len;
|
|
|
|
BEGIN_FN("DrDrive::Initialize");
|
|
|
|
Status = DrDevice::Initialize(DeviceAnnounce, Length);
|
|
|
|
if (ShouldCreateDevice()) {
|
|
if (!NT_ERROR(Status)) {
|
|
DriveName.MaximumLength = sizeof(DriveNameBuff);
|
|
DriveName.Length = 0;
|
|
DriveName.Buffer = &DriveNameBuff[0];
|
|
memset(&DriveNameBuff, 0, sizeof(DriveNameBuff));
|
|
|
|
ASSERT(_PreferredDosName != NULL);
|
|
len = strlen((char *)_PreferredDosName);
|
|
len = ConvertToAndFromWideChar(0, DriveName.Buffer,
|
|
DriveName.MaximumLength, (char *)_PreferredDosName,
|
|
len, TRUE);
|
|
|
|
if (len != -1) {
|
|
|
|
//
|
|
// We need just the drive letter portion
|
|
//
|
|
DriveName.Length = (USHORT)len;
|
|
TRC_NRM((TB, "New drive: %wZ", &DriveName));
|
|
|
|
} else {
|
|
TRC_ERR((TB, "Error converting DriveName"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Request the user mode notify dll to create UNC connection
|
|
// for redirected client drives
|
|
//
|
|
Status = CreateDrive(DeviceAnnounce, DriveName.Buffer);
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::CreateDrive(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg, PWCHAR DriveName)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG driveAnnounceEventReqSize;
|
|
PRDPDR_DRIVEDEVICE_SUB driveAnnounceEvent;
|
|
|
|
BEGIN_FN("DrDrive::CreateDrive");
|
|
ASSERT(DriveName != NULL);
|
|
|
|
//
|
|
// Allocate the drive device announce buffer.
|
|
//
|
|
Status = CreateDriveAnnounceEvent(devAnnounceMsg, NULL, 0, L"",
|
|
&driveAnnounceEventReqSize);
|
|
|
|
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if( Status != STATUS_BUFFER_TOO_SMALL) {
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
driveAnnounceEvent = (PRDPDR_DRIVEDEVICE_SUB)new(NonPagedPool)
|
|
BYTE[driveAnnounceEventReqSize];
|
|
|
|
if (driveAnnounceEvent == NULL) {
|
|
TRC_ERR((TB, "Unable to allocate driveAnnounceEvent"));
|
|
Status = STATUS_NO_MEMORY;
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
//
|
|
// Create the drive anounce message.
|
|
//
|
|
Status = CreateDriveAnnounceEvent(devAnnounceMsg, driveAnnounceEvent,
|
|
driveAnnounceEventReqSize, DriveName, NULL);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
delete driveAnnounceEvent;
|
|
#if DBG
|
|
driveAnnounceEvent = NULL;
|
|
#endif
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
//
|
|
// Dispatch the event to the associated session.
|
|
//
|
|
Status = RDPDYN_DispatchNewDevMgmtEvent(
|
|
driveAnnounceEvent,
|
|
_Session->GetSessionId(),
|
|
RDPDREVT_DRIVEANNOUNCE,
|
|
NULL
|
|
);
|
|
|
|
CleanUpAndReturn:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::CreateDriveAnnounceEvent(
|
|
IN PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN OUT PRDPDR_DRIVEDEVICE_SUB driveAnnounceEvent,
|
|
IN ULONG driveAnnounceEventSize,
|
|
IN PCWSTR driveName,
|
|
OPTIONAL OUT ULONG *driveAnnounceEventReqSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a RDPDR_DRIVEDEVICE_SUB event from a client-sent
|
|
RDPDR_DEVICE_ANNOUNCE message.
|
|
|
|
Arguments:
|
|
|
|
devAnnounceMsg - Device announce message received from client.
|
|
driveAnnounceEvent - Buffer for receiving finished drive announce event.
|
|
driveAnnounceEventSize - Size of driveAnnounceEvent buffer.
|
|
driveName - Name of local drive to be associated with
|
|
client-side drive device.
|
|
driveAnnounceEventReqSize - Returned required size of driveAnnounceMsg buffer.
|
|
|
|
Return Value:
|
|
|
|
STATUS_INVALID_BUFFER_SIZE is returned if the driveAnnounceEventSize size is
|
|
too small. STATUS_SUCCESS is returned on success.
|
|
|
|
--*/
|
|
{
|
|
ULONG requiredSize;
|
|
ULONG sz;
|
|
|
|
BEGIN_FN("DrDrive::CreateDriveAnnounceEvent");
|
|
|
|
// Make sure the client-sent device announce message is a drive announce
|
|
// message.
|
|
TRC_ASSERT(devAnnounceMsg->DeviceType == RDPDR_DTYP_FILESYSTEM,
|
|
(TB, "file system device expected"));
|
|
|
|
//
|
|
// Make sure that the device datalengths we got from the client
|
|
// doesn't exceed what we expect
|
|
//
|
|
if (!DR_CHECK_DEVICEDATALEN(devAnnounceMsg, RDPDR_DRIVEDEVICE_SUB)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Calculate the number of bytes needed in the output buffer.
|
|
//
|
|
requiredSize = sizeof(RDPDR_DRIVEDEVICE_SUB) + devAnnounceMsg->DeviceDataLength;
|
|
|
|
if (driveAnnounceEventSize < requiredSize) {
|
|
if (driveAnnounceEventReqSize != NULL) {
|
|
*driveAnnounceEventReqSize = requiredSize;
|
|
}
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Add the data to the output buffer.
|
|
//
|
|
|
|
// Drive Name.
|
|
TRC_ASSERT(wcslen(driveName)+1 <= RDPDR_MAXPORTNAMELEN,
|
|
(TB, "drive name too long"));
|
|
wcscpy(driveAnnounceEvent->driveName, driveName);
|
|
|
|
// Client Name (UNC server name).
|
|
#if 0
|
|
TRC_ASSERT(wcslen(_Session->GetClientName())+1 <= RDPDR_MAX_COMPUTER_NAME_LENGTH,
|
|
(TB, "Client name too long"));
|
|
wcscpy(driveAnnounceEvent->clientName, _Session->GetClientName());
|
|
#endif
|
|
wcscpy(driveAnnounceEvent->clientName, DRUNCSERVERNAME_U);
|
|
|
|
// Client-received device announce message.
|
|
RtlCopyMemory(&driveAnnounceEvent->deviceFields, devAnnounceMsg,
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE) +
|
|
devAnnounceMsg->DeviceDataLength);
|
|
|
|
|
|
wcscpy(driveAnnounceEvent->clientDisplayName, _Session->GetClientDisplayName());
|
|
|
|
// Return the size.
|
|
if (driveAnnounceEventReqSize != NULL) {
|
|
*driveAnnounceEventReqSize = requiredSize;
|
|
}
|
|
|
|
TRC_NRM((TB, "exit CreateDriveAnnounceEvent."));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID DrDrive::Remove()
|
|
{
|
|
PRDPDR_REMOVEDEVICE deviceRemoveEventPtr = NULL;
|
|
|
|
BEGIN_FN("DrDrive::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 DrDrive::QueryDirectory(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;
|
|
ULONG cbPacketSize;
|
|
BOOL bTemplateEndsDOT = FALSE;
|
|
FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass;
|
|
PUNICODE_STRING DirectoryName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
|
|
PUNICODE_STRING QueryTemplate = &(capFobx->UnicodeQueryTemplate);
|
|
|
|
BEGIN_FN("DrDrive:QueryDirectory");
|
|
|
|
//
|
|
// 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_DIRECTORY_CONTROL);
|
|
ASSERT(Session != NULL);
|
|
|
|
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 query client directory information while not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
TRC_DBG((TB, "QueryDirectory information class = %x", FileInformationClass));
|
|
|
|
//
|
|
// Check what file information class it is requesting
|
|
//
|
|
switch (FileInformationClass) {
|
|
case FileDirectoryInformation:
|
|
case FileFullDirectoryInformation:
|
|
case FileBothDirectoryInformation:
|
|
case FileNamesInformation:
|
|
// let client handle these
|
|
break;
|
|
|
|
default:
|
|
TRC_DBG((TB, "Unhandled FileInformationClass=%x", FileInformationClass));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Build the querydir packet and send it to the client
|
|
//
|
|
if (RxContext->QueryDirectory.InitialQuery) {
|
|
LONG index;
|
|
|
|
ASSERT(DirectoryName->Length != 0);
|
|
ASSERT(QueryTemplate->Length != 0);
|
|
|
|
//
|
|
// Account for 3 extra characters
|
|
// 1) We append string null terminator to the end
|
|
// 2) add \ between directory name and query template
|
|
// 3) need to translate template ending < to *.
|
|
//
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + DirectoryName->Length +
|
|
capFobx->UnicodeQueryTemplate.Length + sizeof(WCHAR) * 3;
|
|
|
|
//
|
|
// Query template translation back into win32 format
|
|
// Look filefind.c from base\win32\client for the original translation
|
|
//
|
|
|
|
TRC_DBG((TB, "QueryTemplate before %wZ\n", QueryTemplate));
|
|
|
|
if (QueryTemplate->Buffer[QueryTemplate->Length/sizeof(WCHAR) - 1] == DOS_STAR) {
|
|
bTemplateEndsDOT = TRUE;
|
|
QueryTemplate->Buffer[QueryTemplate->Length/sizeof(WCHAR) - 1] = L'*';
|
|
}
|
|
|
|
for (index = QueryTemplate->Length/sizeof(WCHAR) - 1; index >= 0; index--) {
|
|
if (index && QueryTemplate->Buffer[index] == L'.' &&
|
|
QueryTemplate->Buffer[index - 1] == DOS_STAR) {
|
|
QueryTemplate->Buffer[index - 1] = L'*';
|
|
}
|
|
|
|
if (QueryTemplate->Buffer[index] == DOS_QM) {
|
|
QueryTemplate->Buffer[index] = L'?';
|
|
}
|
|
|
|
if (index && (QueryTemplate->Buffer[index] == L'?' ||
|
|
QueryTemplate->Buffer[index] == L'*') &&
|
|
QueryTemplate->Buffer[index - 1] == DOS_DOT) {
|
|
QueryTemplate->Buffer[index - 1] = L'.';
|
|
}
|
|
}
|
|
|
|
TRC_DBG((TB, "QueryTemplate after %wZ, bTemplateEndsDOT=%x\n", QueryTemplate,
|
|
bTemplateEndsDOT));
|
|
}
|
|
else {
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET);
|
|
}
|
|
|
|
pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
|
|
|
|
if (pIoPacket) {
|
|
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_DIRECTORY_CONTROL;
|
|
pIoPacket->IoRequest.MinorFunction = IRP_MN_QUERY_DIRECTORY;
|
|
|
|
pIoPacket->IoRequest.Parameters.QueryDir.FileInformationClass =
|
|
(RDP_FILE_INFORMATION_CLASS)FileInformationClass;
|
|
pIoPacket->IoRequest.Parameters.QueryDir.InitialQuery =
|
|
RxContext->QueryDirectory.InitialQuery;
|
|
|
|
if (RxContext->QueryDirectory.InitialQuery) {
|
|
//
|
|
// This is in the format of <DirectoryName>\<QueryTemplate>\0
|
|
//
|
|
|
|
RtlCopyMemory(pIoPacket + 1, DirectoryName->Buffer, DirectoryName->Length);
|
|
|
|
if (((PWCHAR)(pIoPacket + 1))[DirectoryName->Length / sizeof(WCHAR) - 1] != L'\\') {
|
|
((PWCHAR)(pIoPacket + 1))[DirectoryName->Length / sizeof(WCHAR)] = L'\\';
|
|
RtlCopyMemory((PBYTE)(pIoPacket + 1) + DirectoryName->Length + sizeof(WCHAR),
|
|
QueryTemplate->Buffer, QueryTemplate->Length);
|
|
pIoPacket->IoRequest.Parameters.QueryDir.PathLength = DirectoryName->Length +
|
|
QueryTemplate->Length + sizeof(WCHAR);
|
|
}
|
|
else {
|
|
RtlCopyMemory((PBYTE)(pIoPacket + 1) + DirectoryName->Length,
|
|
QueryTemplate->Buffer, QueryTemplate->Length);
|
|
pIoPacket->IoRequest.Parameters.QueryDir.PathLength = DirectoryName->Length +
|
|
QueryTemplate->Length;
|
|
cbPacketSize -= sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// Add . for the query template if it ends like *.
|
|
//
|
|
if (bTemplateEndsDOT) {
|
|
((PWCHAR)(pIoPacket + 1))[pIoPacket->IoRequest.Parameters.QueryDir.PathLength
|
|
/ sizeof(WCHAR)] = L'.';
|
|
pIoPacket->IoRequest.Parameters.QueryDir.PathLength += sizeof(WCHAR);
|
|
}
|
|
else {
|
|
cbPacketSize -= sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// Path length includes the null terminator
|
|
//
|
|
pIoPacket->IoRequest.Parameters.QueryDir.PathLength += sizeof(WCHAR);
|
|
|
|
// The pIoPacket is already zero'd. So, no need to null terminate it
|
|
} else {
|
|
//
|
|
// This is not the first query, so we should already have the file
|
|
// handle open
|
|
//
|
|
pIoPacket->IoRequest.Parameters.QueryDir.PathLength = 0;
|
|
}
|
|
|
|
Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
delete pIoPacket;
|
|
}
|
|
else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::NotifyChangeDirectory(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;
|
|
RDPDR_IOREQUEST_PACKET IoPacket;
|
|
ULONG cbPacketSize;
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
|
|
BEGIN_FN("DrDrive:NotifyChangeDirectory");
|
|
|
|
//
|
|
// 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_DIRECTORY_CONTROL);
|
|
ASSERT(Session != NULL);
|
|
|
|
|
|
if (COMPARE_VERSION(Session->GetClientVersion().Minor,
|
|
Session->GetClientVersion().Major, 4, 1) < 0) {
|
|
TRC_ALT((TB, "Failing NotifyChangeDirectory 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 query client directory change notify information while not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET);
|
|
|
|
memset(&IoPacket, 0, cbPacketSize);
|
|
|
|
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_DIRECTORY_CONTROL;
|
|
IoPacket.IoRequest.MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY;
|
|
|
|
IoPacket.IoRequest.Parameters.NotifyChangeDir.WatchTree =
|
|
pLowIoContext->ParamsFor.NotifyChangeDirectory.WatchTree;
|
|
IoPacket.IoRequest.Parameters.NotifyChangeDir.CompletionFilter =
|
|
pLowIoContext->ParamsFor.NotifyChangeDirectory.CompletionFilter;
|
|
|
|
Status = SendIoRequest(RxContext, &IoPacket, cbPacketSize,
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::QueryVolumeInfo(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;
|
|
RDPDR_IOREQUEST_PACKET IoPacket;
|
|
FS_INFORMATION_CLASS FsInformationClass = RxContext->Info.FsInformationClass;
|
|
|
|
BEGIN_FN("DrDrive: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()) {
|
|
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 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 FileFsVolumeInformation:
|
|
//case FileFsLabelInformation:
|
|
// Smb seems to handle query label information, but i think
|
|
// this is only for set label info. We'll see if we should
|
|
// actually handle query label information.
|
|
// query label can be achieved through volume information
|
|
case FileFsSizeInformation:
|
|
case FileFsAttributeInformation:
|
|
// let client handle these
|
|
break;
|
|
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 = FILE_DEVICE_DISK;
|
|
*pLengthRemaining -= (sizeof(FILE_FS_DEVICE_INFORMATION));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
FILE_FS_DEVICE_INFORMATION UsersBuffer;
|
|
|
|
UsersBuffer.Characteristics = FILE_REMOTE_DEVICE;
|
|
UsersBuffer.DeviceType = FILE_DEVICE_DISK;
|
|
RtlCopyMemory(RxContext->Info.Buffer, &UsersBuffer, *pLengthRemaining);
|
|
*pLengthRemaining = 0;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
case FileFsFullSizeInformation:
|
|
TRC_DBG((TB, "Unhandled FsInformationClass=%x", FsInformationClass));
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
default:
|
|
TRC_DBG((TB, "Unhandled FsInformationClass=%x", FsInformationClass));
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
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_QUERY_VOLUME_INFORMATION;
|
|
IoPacket.IoRequest.MinorFunction = 0;
|
|
IoPacket.IoRequest.Parameters.QueryVolume.FsInformationClass =
|
|
(RDP_FS_INFORMATION_CLASS)FsInformationClass;
|
|
|
|
Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket),
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::SetVolumeInfo(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;
|
|
ULONG cbPacketSize = 0;
|
|
FS_INFORMATION_CLASS FsInformationClass = RxContext->Info.FsInformationClass;
|
|
|
|
BEGIN_FN("DrDrive:SetVolumeInfo");
|
|
|
|
//
|
|
// 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_SET_VOLUME_INFORMATION);
|
|
ASSERT(Session != NULL);
|
|
|
|
if (!Session->IsConnected()) {
|
|
RxContext->IoStatusBlock.Information = 0;
|
|
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 set client device volume information while not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
//
|
|
// Check buffer length
|
|
//
|
|
if (RxContext->Info.Length == 0) {
|
|
RxContext->IoStatusBlock.Information = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
TRC_DBG((TB, "SetVolume Information class = %x", FsInformationClass));
|
|
|
|
switch (FsInformationClass) {
|
|
case FileFsLabelInformation:
|
|
{
|
|
PFILE_FS_LABEL_INFORMATION pRxBuffer =
|
|
(PFILE_FS_LABEL_INFORMATION) RxContext->Info.Buffer;
|
|
|
|
//
|
|
// REVIEW: Find out why Info.Length has the extra 2 bytes
|
|
// It doesn't seem to put string null terminator to it
|
|
//
|
|
if ((ULONG)RxContext->Info.Length == FIELD_OFFSET(FILE_FS_LABEL_INFORMATION,
|
|
VolumeLabel) + pRxBuffer->VolumeLabelLength + sizeof(WCHAR)) {
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
|
|
RxContext->Info.Length;
|
|
// Make sure that label is null terminiated
|
|
pRxBuffer->VolumeLabel[pRxBuffer->VolumeLabelLength/sizeof(WCHAR)] = L'\0';
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Invalid Volume label info"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
// Let client handle this
|
|
break;
|
|
}
|
|
default:
|
|
TRC_DBG((TB, "Unhandled FsInformationClass=%x", FsInformationClass));
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
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_SET_VOLUME_INFORMATION;
|
|
pIoPacket->IoRequest.MinorFunction = 0;
|
|
pIoPacket->IoRequest.Parameters.SetVolume.FsInformationClass =
|
|
(RDP_FS_INFORMATION_CLASS)FsInformationClass;
|
|
pIoPacket->IoRequest.Parameters.SetVolume.Length = RxContext->Info.Length;
|
|
RtlCopyMemory(pIoPacket + 1, RxContext->Info.Buffer, RxContext->Info.Length);
|
|
|
|
Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
delete pIoPacket;
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::QueryFileInfo(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;
|
|
RDPDR_IOREQUEST_PACKET IoPacket;
|
|
FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass;
|
|
|
|
BEGIN_FN("DrDrive:QueryFileInfo");
|
|
|
|
//
|
|
// 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_INFORMATION);
|
|
ASSERT(Session != NULL);
|
|
|
|
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 query client file information while not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
TRC_DBG((TB, "QueryFile information class = %x", FileInformationClass));
|
|
|
|
switch (FileInformationClass) {
|
|
case FileBasicInformation:
|
|
case FileStandardInformation:
|
|
case FileAttributeTagInformation:
|
|
// let client handle these
|
|
break;
|
|
|
|
case FileEaInformation:
|
|
{
|
|
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
|
|
// Should check buffer length
|
|
if (sizeof(FILE_EA_INFORMATION) <= *pLengthRemaining) {
|
|
((PFILE_EA_INFORMATION)(RxContext->Info.Buffer))->EaSize = 0;
|
|
*pLengthRemaining -= sizeof(FILE_EA_INFORMATION);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
case FileAllocationInformation:
|
|
case FileEndOfFileInformation:
|
|
case FileAlternateNameInformation:
|
|
case FileStreamInformation:
|
|
case FileCompressionInformation:
|
|
TRC_DBG((TB, "Unhandled FileInformationClass=%x", FileInformationClass));
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
case FileInternalInformation:
|
|
{
|
|
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
PFILE_INTERNAL_INFORMATION UsersBuffer =
|
|
(PFILE_INTERNAL_INFORMATION)RxContext->Info.Buffer;
|
|
|
|
if (sizeof(FILE_INTERNAL_INFORMATION) <= *pLengthRemaining) {
|
|
UsersBuffer->IndexNumber.QuadPart = (ULONG_PTR)capFcb;
|
|
*pLengthRemaining -= sizeof(FILE_INTERNAL_INFORMATION);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
default:
|
|
TRC_DBG((TB, "Unhandled FileInformationClass=%x", FileInformationClass));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
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_QUERY_INFORMATION;
|
|
IoPacket.IoRequest.MinorFunction = 0;
|
|
IoPacket.IoRequest.Parameters.QueryFile.FileInformationClass =
|
|
(RDP_FILE_INFORMATION_CLASS)FileInformationClass;
|
|
|
|
Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket),
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::SetFileInfo(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;
|
|
PRDPDR_IOREQUEST_PACKET pIoPacket;
|
|
ULONG cbPacketSize = 0;
|
|
FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass;
|
|
BOOLEAN bBufferRepackage = FALSE;
|
|
|
|
BEGIN_FN("DrDrive:SetFileInfo");
|
|
|
|
//
|
|
// 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_SET_INFORMATION);
|
|
ASSERT(Session != NULL);
|
|
|
|
if (!Session->IsConnected()) {
|
|
RxContext->IoStatusBlock.Information = 0;
|
|
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 set client device file information while not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
//
|
|
// Check buffer length
|
|
//
|
|
if (RxContext->Info.Length == 0) {
|
|
RxContext->IoStatusBlock.Information = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
TRC_DBG((TB, "SetFile information class=%x", FileInformationClass));
|
|
|
|
switch (FileInformationClass) {
|
|
case FileBasicInformation:
|
|
{
|
|
if (sizeof(FILE_BASIC_INFORMATION) == RxContext->Info.Length) {
|
|
if (RxContext->Info.Length == sizeof(RDP_FILE_BASIC_INFORMATION)) {
|
|
bBufferRepackage = FALSE;
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + RxContext->Info.Length;
|
|
}
|
|
else {
|
|
bBufferRepackage = TRUE;
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
|
|
sizeof(RDP_FILE_BASIC_INFORMATION);
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Invalid FileBasicInformation buffer"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
case FileEndOfFileInformation:
|
|
{
|
|
if (sizeof(FILE_END_OF_FILE_INFORMATION) == RxContext->Info.Length) {
|
|
bBufferRepackage = FALSE;
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + RxContext->Info.Length;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Invalid FileEndOfFileInformation buffer"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
case FileDispositionInformation:
|
|
{
|
|
if (sizeof(FILE_DISPOSITION_INFORMATION) == RxContext->Info.Length) {
|
|
if (((PFILE_DISPOSITION_INFORMATION)(RxContext->Info.Buffer))->DeleteFile) {
|
|
bBufferRepackage = FALSE;
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET);
|
|
}
|
|
else {
|
|
//
|
|
// We shouldn't get this if the DeleteFile flag is not on
|
|
//
|
|
ASSERT(FALSE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Invalid FileDispositionInformation buffer"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FileRenameInformation:
|
|
{
|
|
PFILE_RENAME_INFORMATION pRenameInformation =
|
|
(PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
|
|
|
|
if ((ULONG)(RxContext->Info.Length) == FIELD_OFFSET(FILE_RENAME_INFORMATION,
|
|
FileName) + pRenameInformation->FileNameLength) {
|
|
if ((ULONG)(RxContext->Info.Length) == FIELD_OFFSET(RDP_FILE_RENAME_INFORMATION,
|
|
FileName) + pRenameInformation->FileNameLength) {
|
|
bBufferRepackage = FALSE;
|
|
//
|
|
// Add string null terminator to the filename
|
|
//
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + RxContext->Info.Length + sizeof(WCHAR);
|
|
}
|
|
else {
|
|
bBufferRepackage = TRUE;
|
|
//
|
|
// Add string null terminator to the filename
|
|
//
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
|
|
FIELD_OFFSET(RDP_FILE_RENAME_INFORMATION,
|
|
FileName) + pRenameInformation->FileNameLength + sizeof(WCHAR);
|
|
}
|
|
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Bad buffer info for FileRenameInformation class, InfoBuffer length=%x, "
|
|
"FileName Length=%x", RxContext->Info.Length, pRenameInformation->FileNameLength));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FileAllocationInformation:
|
|
{
|
|
|
|
TRC_NRM((TB, "Get FileAllocationInfomation"));
|
|
|
|
if (sizeof(FILE_ALLOCATION_INFORMATION) == RxContext->Info.Length) {
|
|
bBufferRepackage = FALSE;
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + RxContext->Info.Length;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Invalid FileAllocationInformation buffer"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FileLinkInformation:
|
|
case FileAttributeTagInformation:
|
|
TRC_DBG((TB, "Unhandled FileInformationClass=%x", FileInformationClass));
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
default:
|
|
TRC_DBG((TB, "Unhandled FileInformationClass=%x", FileInformationClass));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
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_SET_INFORMATION;
|
|
pIoPacket->IoRequest.MinorFunction = 0;
|
|
pIoPacket->IoRequest.Parameters.SetFile.FileInformationClass =
|
|
(RDP_FILE_INFORMATION_CLASS)FileInformationClass;
|
|
|
|
if (cbPacketSize > sizeof(RDPDR_IOREQUEST_PACKET)) {
|
|
if (!bBufferRepackage) {
|
|
pIoPacket->IoRequest.Parameters.SetFile.Length = RxContext->Info.Length;
|
|
RtlCopyMemory(pIoPacket + 1, RxContext->Info.Buffer, RxContext->Info.Length);
|
|
}
|
|
else {
|
|
switch (FileInformationClass) {
|
|
case FileBasicInformation:
|
|
{
|
|
PFILE_BASIC_INFORMATION pRxFileInfo =
|
|
(PFILE_BASIC_INFORMATION) RxContext->Info.Buffer;
|
|
PRDP_FILE_BASIC_INFORMATION pRdpFileInfo =
|
|
(PRDP_FILE_BASIC_INFORMATION) (pIoPacket + 1);
|
|
|
|
pIoPacket->IoRequest.Parameters.SetFile.Length =
|
|
sizeof(RDP_FILE_BASIC_INFORMATION);
|
|
|
|
pRdpFileInfo->ChangeTime.QuadPart = pRxFileInfo->ChangeTime.QuadPart;
|
|
pRdpFileInfo->CreationTime.QuadPart = pRxFileInfo->CreationTime.QuadPart;
|
|
pRdpFileInfo->FileAttributes = pRxFileInfo->FileAttributes;
|
|
pRdpFileInfo->LastAccessTime.QuadPart = pRxFileInfo->LastAccessTime.QuadPart;
|
|
pRdpFileInfo->LastWriteTime.QuadPart = pRxFileInfo->LastWriteTime.QuadPart;
|
|
|
|
break;
|
|
}
|
|
case FileRenameInformation:
|
|
{
|
|
PFILE_RENAME_INFORMATION pRxFileInfo =
|
|
(PFILE_RENAME_INFORMATION) RxContext->Info.Buffer;
|
|
PRDP_FILE_RENAME_INFORMATION pRdpFileInfo =
|
|
(PRDP_FILE_RENAME_INFORMATION) (pIoPacket + 1);
|
|
|
|
pIoPacket->IoRequest.Parameters.SetFile.Length =
|
|
cbPacketSize - sizeof(RDPDR_IOREQUEST_PACKET);
|
|
|
|
pRdpFileInfo->ReplaceIfExists = pRxFileInfo->ReplaceIfExists;
|
|
|
|
// Always force the client to setup the root directory.
|
|
pRdpFileInfo->RootDirectory = 0;
|
|
|
|
pRdpFileInfo->FileNameLength = pRxFileInfo->FileNameLength + sizeof(WCHAR);
|
|
|
|
RtlCopyMemory(pRdpFileInfo->FileName, pRxFileInfo->FileName,
|
|
pRxFileInfo->FileNameLength);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
delete pIoPacket;
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::QuerySdInfo(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;
|
|
RDPDR_IOREQUEST_PACKET IoPacket;
|
|
|
|
BEGIN_FN("DrDrive:QuerySdInfo");
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
//
|
|
// 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_SECURITY);
|
|
ASSERT(Session != NULL);
|
|
|
|
//
|
|
// Return not supported if the client doesn't support query security
|
|
//
|
|
if (!(Session->GetClientCapabilitySet().GeneralCap.ioCode1 & RDPDR_IRP_MJ_QUERY_SECURITY)) {
|
|
TRC_DBG((TB, "QuerySdInfo not supported"));
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
return Status;
|
|
}
|
|
|
|
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 query client security information while 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_QUERY_SECURITY;
|
|
IoPacket.IoRequest.MinorFunction = 0;
|
|
IoPacket.IoRequest.Parameters.QuerySd.SecurityInformation =
|
|
RxContext->QuerySecurity.SecurityInformation;
|
|
|
|
Status = SendIoRequest(RxContext, &IoPacket, sizeof(IoPacket),
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::SetSdInfo(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 IoPacket;
|
|
ULONG SdLength = RtlLengthSecurityDescriptor(RxContext->SetSecurity.SecurityDescriptor);
|
|
ULONG cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) + SdLength;
|
|
|
|
BEGIN_FN("DrDrive:SetFileInfo");
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
//
|
|
// 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_SET_SECURITY);
|
|
ASSERT(Session != NULL);
|
|
|
|
//
|
|
// Return not supported if the client doesn't support query security
|
|
//
|
|
if (!(Session->GetClientCapabilitySet().GeneralCap.ioCode1 & RDPDR_IRP_MJ_SET_SECURITY)) {
|
|
TRC_DBG((TB, "SetSdInfo not supported"));
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
return Status;
|
|
}
|
|
|
|
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 set client device security information while not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
IoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
|
|
|
|
if (IoPacket != NULL) {
|
|
|
|
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_SET_SECURITY;
|
|
IoPacket->IoRequest.MinorFunction = 0;
|
|
IoPacket->IoRequest.Parameters.SetSd.SecurityInformation =
|
|
RxContext->SetSecurity.SecurityInformation;
|
|
IoPacket->IoRequest.Parameters.SetSd.Length = SdLength;
|
|
RtlCopyMemory(IoPacket + 1,RxContext->SetSecurity.SecurityDescriptor, SdLength);
|
|
|
|
Status = SendIoRequest(RxContext, IoPacket, cbPacketSize,
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
|
|
delete IoPacket;
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::Locks(IN OUT PRX_CONTEXT RxContext)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
RxCaptureRequestPacket;
|
|
RxCaptureParamBlock;
|
|
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 = 0;
|
|
ULONG NumLocks = 0;
|
|
|
|
BEGIN_FN("DrDrive::Locks");
|
|
|
|
//
|
|
// 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(Session != NULL);
|
|
|
|
// We can be called from Major function other than Lock Control
|
|
// For example, on Cleanup to unlock all the locks.
|
|
// ASSERT(RxContext->MajorFunction == IRP_MJ_LOCK_CONTROL);
|
|
|
|
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 lock client device file which is not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
switch (RxContext->LowIoContext.Operation) {
|
|
case LOWIO_OP_SHAREDLOCK:
|
|
case LOWIO_OP_EXCLUSIVELOCK:
|
|
case LOWIO_OP_UNLOCK:
|
|
NumLocks = 1;
|
|
break;
|
|
|
|
case LOWIO_OP_UNLOCK_MULTIPLE:
|
|
{
|
|
PLOWIO_LOCK_LIST LockList;
|
|
LockList = RxContext->LowIoContext.ParamsFor.Locks.LockList;
|
|
while (LockList) {
|
|
NumLocks++;
|
|
LockList = LockList->Next;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
cbPacketSize = sizeof(RDPDR_IOREQUEST_PACKET) +
|
|
NumLocks * sizeof(RDP_LOCK_INFO);
|
|
|
|
pIoPacket = (PRDPDR_IOREQUEST_PACKET)new(PagedPool) BYTE[cbPacketSize];
|
|
|
|
if (pIoPacket) {
|
|
unsigned i;
|
|
PRDP_LOCK_INFO pLockInfo = (PRDP_LOCK_INFO) (pIoPacket + 1);
|
|
|
|
|
|
memset(pIoPacket, 0, sizeof(pIoPacket));
|
|
|
|
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_LOCK_CONTROL;
|
|
pIoPacket->IoRequest.MinorFunction = 0;
|
|
pIoPacket->IoRequest.Parameters.Locks.Operation =
|
|
RxContext->LowIoContext.Operation;
|
|
pIoPacket->IoRequest.Parameters.Locks.Flags =
|
|
(capPARAMS->Flags & SL_FAIL_IMMEDIATELY) ? SL_FAIL_IMMEDIATELY: 0;
|
|
pIoPacket->IoRequest.Parameters.Locks.NumLocks = NumLocks;
|
|
|
|
if (NumLocks == 1) {
|
|
pLockInfo->LengthLow =
|
|
((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.Locks.Length)
|
|
& 0xffffffff));
|
|
pLockInfo->LengthHigh =
|
|
((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.Locks.Length)
|
|
>> 32));
|
|
pLockInfo->OffsetLow =
|
|
((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.Locks.ByteOffset)
|
|
& 0xffffffff));
|
|
pLockInfo->OffsetHigh =
|
|
((LONG)((LONGLONG)(RxContext->LowIoContext.ParamsFor.Locks.ByteOffset)
|
|
>> 32));
|
|
}
|
|
else {
|
|
PLOWIO_LOCK_LIST LockList;
|
|
LockList = RxContext->LowIoContext.ParamsFor.Locks.LockList;
|
|
|
|
for (i = 0; i < NumLocks; i++) {
|
|
pLockInfo->LengthLow =
|
|
((LONG)((LONGLONG)(LockList->Length) & 0xffffffff));
|
|
pLockInfo->LengthHigh =
|
|
((LONG)((LONGLONG)(LockList->Length) >> 32));
|
|
pLockInfo->OffsetLow =
|
|
((LONG)((LONGLONG)(LockList->ByteOffset) & 0xffffffff));
|
|
pLockInfo->OffsetHigh =
|
|
((LONG)((LONGLONG)(LockList->ByteOffset) >> 32));
|
|
pLockInfo++;
|
|
LockList = LockList->Next;
|
|
}
|
|
}
|
|
|
|
Status = SendIoRequest(RxContext, pIoPacket, cbPacketSize,
|
|
(BOOLEAN)!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
|
|
TRC_NRM((TB, "IoRequestWrite returned to DrRead: %lx", Status));
|
|
|
|
delete pIoPacket;
|
|
}
|
|
else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnDirectoryControlCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
DrIoContext *Context = (DrIoContext *)Exchange->_Context;
|
|
|
|
BEGIN_FN("DrDrive::OnDirectoryControlCompletion");
|
|
|
|
if (Context->_MinorFunction == IRP_MN_QUERY_DIRECTORY ||
|
|
Context->_MinorFunction == 0) {
|
|
return OnQueryDirectoryCompletion(CompletionPacket, cbPacket,
|
|
DoDefaultRead, Exchange);
|
|
}
|
|
else if (Context->_MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY) {
|
|
return OnNotifyChangeDirectoryCompletion(CompletionPacket, cbPacket,
|
|
DoDefaultRead, Exchange);
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
|
|
if (Context->_RxContext != NULL) {
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
} else {
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnQueryDirectoryCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
PVOID pData = CompletionPacket->IoCompletion.Parameters.QueryDir.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;
|
|
|
|
BEGIN_FN("DrDrive::OnQueryDirectoryCompletion");
|
|
|
|
//
|
|
// 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.QueryDir.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 query directory 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.QueryDir.Length,
|
|
Context->_DataCopied));
|
|
cbWantData = CompletionPacket->IoCompletion.Parameters.QueryDir.Length -
|
|
Context->_DataCopied;
|
|
cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
|
|
IoCompletion.Parameters.QueryDir.Buffer);
|
|
|
|
if (cbHaveData > cbWantData) {
|
|
//
|
|
// Sounds like a bad client to me
|
|
//
|
|
|
|
TRC_ERR((TB, "QueryDir 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
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
|
|
ASSERT(FileObj != NULL);
|
|
|
|
TRC_DBG((TB, "Copying data for Query Directory"));
|
|
|
|
if (cbHaveData < cbWantData || Context->_DataCopied) {
|
|
if (FileObj->GetBufferSize() < CompletionPacket->IoCompletion.Parameters.QueryDir.Length) {
|
|
if (!FileObj->AllocateBuffer(CompletionPacket->IoCompletion.Parameters.QueryDir.Length)) {
|
|
CompleteBusyExchange(Exchange, STATUS_INSUFFICIENT_RESOURCES, 0);
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
RtlCopyMemory(FileObj->GetBuffer() + 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) {
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass;
|
|
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
PBYTE pBuffer;
|
|
ULONG BufferLength;
|
|
|
|
if (!Context->_DataCopied) {
|
|
pBuffer = (PBYTE) pData;
|
|
}
|
|
else {
|
|
pBuffer = FileObj->GetBuffer();
|
|
}
|
|
|
|
BufferLength = CompletionPacket->IoCompletion.Parameters.QueryDir.Length;
|
|
|
|
switch (FileInformationClass) {
|
|
case FileDirectoryInformation:
|
|
{
|
|
PFILE_DIRECTORY_INFORMATION pRxBuffer = (PFILE_DIRECTORY_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_DIRECTORY_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_DIRECTORY_INFORMATION)pBuffer;
|
|
|
|
if (BufferLength >= FIELD_OFFSET(RDP_FILE_DIRECTORY_INFORMATION, FileName)) {
|
|
if (*pLengthRemaining >= FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName)) {
|
|
pRxBuffer->AllocationSize.QuadPart = pRetBuffer->AllocationSize.QuadPart;
|
|
pRxBuffer->ChangeTime.QuadPart = pRetBuffer->ChangeTime.QuadPart;
|
|
pRxBuffer->CreationTime.QuadPart = pRetBuffer->CreationTime.QuadPart;
|
|
pRxBuffer->EndOfFile.QuadPart = pRetBuffer->EndOfFile.QuadPart;
|
|
pRxBuffer->FileAttributes = pRetBuffer->FileAttributes;
|
|
pRxBuffer->FileIndex = pRetBuffer->FileIndex;
|
|
pRxBuffer->FileNameLength = pRetBuffer->FileNameLength;
|
|
pRxBuffer->LastAccessTime.QuadPart = pRetBuffer->LastAccessTime.QuadPart;
|
|
pRxBuffer->LastWriteTime.QuadPart = pRetBuffer->LastWriteTime.QuadPart;
|
|
pRxBuffer->NextEntryOffset = pRetBuffer->NextEntryOffset;
|
|
|
|
*pLengthRemaining -= (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
|
|
|
|
if ((ULONG)*pLengthRemaining >= pRxBuffer->FileNameLength) {
|
|
if (BufferLength ==
|
|
FIELD_OFFSET(RDP_FILE_DIRECTORY_INFORMATION, FileName) +
|
|
pRxBuffer->FileNameLength) {
|
|
RtlCopyMemory(pRxBuffer->FileName, pRetBuffer->FileName,
|
|
pRxBuffer->FileNameLength);
|
|
*pLengthRemaining -= pRxBuffer->FileNameLength;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Information is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Information, Bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FileFullDirectoryInformation:
|
|
{
|
|
PFILE_FULL_DIR_INFORMATION pRxBuffer = (PFILE_FULL_DIR_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_FULL_DIR_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_FULL_DIR_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength >= FIELD_OFFSET(RDP_FILE_FULL_DIR_INFORMATION, FileName)) {
|
|
if (*pLengthRemaining >= FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName)) {
|
|
pRxBuffer->AllocationSize.QuadPart = pRetBuffer->AllocationSize.QuadPart;
|
|
pRxBuffer->ChangeTime.QuadPart = pRetBuffer->ChangeTime.QuadPart;
|
|
pRxBuffer->CreationTime.QuadPart = pRetBuffer->CreationTime.QuadPart;
|
|
pRxBuffer->EaSize = pRetBuffer->EaSize;
|
|
pRxBuffer->EndOfFile.QuadPart = pRetBuffer->EndOfFile.QuadPart;
|
|
pRxBuffer->FileAttributes = pRetBuffer->FileAttributes;
|
|
pRxBuffer->FileIndex = pRetBuffer->FileIndex;
|
|
pRxBuffer->FileNameLength = pRetBuffer->FileNameLength;
|
|
pRxBuffer->LastAccessTime.QuadPart = pRetBuffer->LastAccessTime.QuadPart;
|
|
pRxBuffer->LastWriteTime.QuadPart = pRetBuffer->LastWriteTime.QuadPart;
|
|
pRxBuffer->NextEntryOffset = pRetBuffer->NextEntryOffset;
|
|
|
|
*pLengthRemaining -= (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
|
|
|
|
if ((ULONG)*pLengthRemaining >= pRxBuffer->FileNameLength) {
|
|
if (BufferLength ==
|
|
FIELD_OFFSET(RDP_FILE_FULL_DIR_INFORMATION, FileName) +
|
|
pRxBuffer->FileNameLength) {
|
|
RtlCopyMemory(pRxBuffer->FileName, pRetBuffer->FileName,
|
|
pRxBuffer->FileNameLength);
|
|
*pLengthRemaining -= pRxBuffer->FileNameLength;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Full Information is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Full Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Full Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Full Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FileBothDirectoryInformation:
|
|
{
|
|
PFILE_BOTH_DIR_INFORMATION pRxBuffer = (PFILE_BOTH_DIR_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_BOTH_DIR_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_BOTH_DIR_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength >= FIELD_OFFSET(RDP_FILE_BOTH_DIR_INFORMATION, FileName)) {
|
|
if (*pLengthRemaining >= FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName)) {
|
|
pRxBuffer->AllocationSize.QuadPart = pRetBuffer->AllocationSize.QuadPart;
|
|
pRxBuffer->ChangeTime.QuadPart = pRetBuffer->ChangeTime.QuadPart;
|
|
pRxBuffer->CreationTime.QuadPart = pRetBuffer->CreationTime.QuadPart;
|
|
pRxBuffer->EaSize = pRetBuffer->EaSize;
|
|
pRxBuffer->EndOfFile.QuadPart = pRetBuffer->EndOfFile.QuadPart;
|
|
pRxBuffer->FileAttributes = pRetBuffer->FileAttributes;
|
|
pRxBuffer->FileIndex = pRetBuffer->FileIndex;
|
|
pRxBuffer->FileNameLength = pRetBuffer->FileNameLength;
|
|
pRxBuffer->LastAccessTime.QuadPart = pRetBuffer->LastAccessTime.QuadPart;
|
|
pRxBuffer->LastWriteTime.QuadPart = pRetBuffer->LastWriteTime.QuadPart;
|
|
pRxBuffer->NextEntryOffset = pRetBuffer->NextEntryOffset;
|
|
pRxBuffer->ShortNameLength = pRetBuffer->ShortNameLength;
|
|
RtlCopyMemory(pRxBuffer->ShortName, pRetBuffer->ShortName,
|
|
sizeof(pRxBuffer->ShortName));
|
|
|
|
*pLengthRemaining -= (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
|
|
|
|
if ((ULONG)*pLengthRemaining >= pRxBuffer->FileNameLength) {
|
|
if ((BufferLength ==
|
|
FIELD_OFFSET(RDP_FILE_BOTH_DIR_INFORMATION, FileName) +
|
|
pRxBuffer->FileNameLength) &&
|
|
(pRxBuffer->ShortNameLength <= sizeof(pRxBuffer->ShortName))) {
|
|
RtlCopyMemory(pRxBuffer->FileName, pRetBuffer->FileName,
|
|
pRxBuffer->FileNameLength);
|
|
*pLengthRemaining -= pRxBuffer->FileNameLength;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Both Information is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Both Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Both Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Directory Both Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FileNamesInformation:
|
|
{
|
|
PFILE_NAMES_INFORMATION pRxBuffer = (PFILE_NAMES_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_NAMES_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_NAMES_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength >= FIELD_OFFSET(RDP_FILE_NAMES_INFORMATION, FileName)) {
|
|
if (*pLengthRemaining >= FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName)) {
|
|
pRxBuffer->FileIndex = pRetBuffer->FileIndex;
|
|
pRxBuffer->FileNameLength = pRetBuffer->FileNameLength;
|
|
pRxBuffer->NextEntryOffset = pRetBuffer->NextEntryOffset;
|
|
|
|
*pLengthRemaining -= (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
|
|
|
|
if ((ULONG)*pLengthRemaining >= pRxBuffer->FileNameLength) {
|
|
if (BufferLength ==
|
|
FIELD_OFFSET(RDP_FILE_NAMES_INFORMATION, FileName) +
|
|
pRxBuffer->FileNameLength) {
|
|
RtlCopyMemory(pRxBuffer->FileName, pRetBuffer->FileName,
|
|
pRxBuffer->FileNameLength);
|
|
*pLengthRemaining -= pRxBuffer->FileNameLength;
|
|
} else {
|
|
TRC_ERR((TB, "Directory Names Information is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Directory Names Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Directory Names Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Directory Names Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TRC_ERR((TB, "Directory Information Class is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
CompleteBusyExchange(Exchange,
|
|
CompletionPacket->IoCompletion.IoStatus,
|
|
CompletionPacket->IoCompletion.Parameters.QueryDir.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.QueryDir.Buffer));
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Unsuccessful IO at the client end
|
|
//
|
|
|
|
TRC_DBG((TB, "Unsuccessful Read at the client end"));
|
|
if (cbPacket >= sizeof(RDPDR_IOCOMPLETION_PACKET)) {
|
|
if (RxContext != NULL) {
|
|
CompleteBusyExchange(Exchange,
|
|
CompletionPacket->IoCompletion.IoStatus,
|
|
0);
|
|
}
|
|
else {
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
TRC_ERR((TB, "Query directory returned invalid data "));
|
|
|
|
if (RxContext != NULL) {
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
} else {
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnNotifyChangeDirectoryCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
DrIoContext *Context = (DrIoContext *)Exchange->_Context;
|
|
|
|
BEGIN_FN("DrDrive::OnNotifyChangeDirectoryCompletion");
|
|
|
|
RxContext = Context->_RxContext;
|
|
|
|
if (RxContext != NULL) {
|
|
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL);
|
|
|
|
TRC_NRM((TB, "Irp: %s, Completion Status: %lx",
|
|
IrpNames[RxContext->MajorFunction],
|
|
CompletionPacket->IoCompletion.IoStatus));
|
|
|
|
RxContext->InformationToReturn = 0;
|
|
RxContext->StoredStatus = CompletionPacket->IoCompletion.IoStatus;
|
|
|
|
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0);
|
|
|
|
} else {
|
|
//
|
|
// Was cancelled but Context wasn't cleaned up
|
|
//
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnQueryVolumeInfoCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
PVOID pData = CompletionPacket->IoCompletion.Parameters.QueryVolume.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;
|
|
|
|
BEGIN_FN("DrDrive::OnQueryVolumeInfoCompletion");
|
|
|
|
//
|
|
// 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.QueryVolume.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 query volume 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.QueryVolume.Length,
|
|
Context->_DataCopied));
|
|
cbWantData = CompletionPacket->IoCompletion.Parameters.QueryVolume.Length -
|
|
Context->_DataCopied;
|
|
cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
|
|
IoCompletion.Parameters.QueryVolume.Buffer);
|
|
|
|
if (cbHaveData > cbWantData) {
|
|
//
|
|
// Sounds like a bad client to me
|
|
//
|
|
|
|
TRC_ERR((TB, "Query volume 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
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
|
|
ASSERT(FileObj != NULL);
|
|
|
|
TRC_DBG((TB, "Copying data for Query Volume"));
|
|
|
|
if (cbHaveData < cbWantData || Context->_DataCopied) {
|
|
if (FileObj->GetBufferSize() < CompletionPacket->IoCompletion.Parameters.QueryVolume.Length) {
|
|
if (!FileObj->AllocateBuffer(CompletionPacket->IoCompletion.Parameters.QueryVolume.Length)) {
|
|
CompleteBusyExchange(Exchange, STATUS_INSUFFICIENT_RESOURCES, 0);
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
RtlCopyMemory(FileObj->GetBuffer() + 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) {
|
|
if (RxContext != NULL) {
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
FS_INFORMATION_CLASS FsInformationClass = RxContext->Info.FsInformationClass;
|
|
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
PBYTE pBuffer;
|
|
ULONG BufferLength;
|
|
|
|
//
|
|
// There is exactly as much data as we need to satisfy the read,
|
|
// I like it.
|
|
//
|
|
if (!Context->_DataCopied) {
|
|
pBuffer = (PBYTE) pData;
|
|
} else {
|
|
pBuffer = FileObj->GetBuffer();
|
|
}
|
|
|
|
BufferLength = CompletionPacket->IoCompletion.Parameters.QueryVolume.Length;
|
|
|
|
switch (FsInformationClass) {
|
|
case FileFsVolumeInformation:
|
|
{
|
|
PFILE_FS_VOLUME_INFORMATION pRxBuffer = (PFILE_FS_VOLUME_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_FS_VOLUME_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_FS_VOLUME_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength >= FIELD_OFFSET(RDP_FILE_FS_VOLUME_INFORMATION, VolumeLabel)) {
|
|
if (*pLengthRemaining >= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel)) {
|
|
pRxBuffer->SupportsObjects = pRetBuffer->SupportsObjects;
|
|
pRxBuffer->VolumeCreationTime.QuadPart = pRetBuffer->VolumeCreationTime.QuadPart;
|
|
pRxBuffer->VolumeSerialNumber = pRetBuffer->VolumeSerialNumber;
|
|
pRxBuffer->VolumeLabelLength = pRetBuffer->VolumeLabelLength;
|
|
|
|
*pLengthRemaining -= (FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel));
|
|
|
|
if ((ULONG)*pLengthRemaining >= pRxBuffer->VolumeLabelLength) {
|
|
if (BufferLength ==
|
|
FIELD_OFFSET(RDP_FILE_FS_VOLUME_INFORMATION, VolumeLabel) +
|
|
pRxBuffer->VolumeLabelLength) {
|
|
RtlCopyMemory(pRxBuffer->VolumeLabel, pRetBuffer->VolumeLabel,
|
|
pRxBuffer->VolumeLabelLength);
|
|
*pLengthRemaining -= pRxBuffer->VolumeLabelLength;
|
|
} else {
|
|
TRC_ERR((TB, "Volume Information is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
} else {
|
|
TRC_NRM((TB, "Volume Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Volume Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Volume Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FileFsSizeInformation:
|
|
{
|
|
PFILE_FS_SIZE_INFORMATION pRxBuffer = (PFILE_FS_SIZE_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_FS_SIZE_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_FS_SIZE_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength == sizeof(RDP_FILE_FS_SIZE_INFORMATION)) {
|
|
if (*pLengthRemaining >= sizeof(FILE_FS_SIZE_INFORMATION)) {
|
|
|
|
pRxBuffer->AvailableAllocationUnits.QuadPart =
|
|
pRetBuffer->AvailableAllocationUnits.QuadPart;
|
|
pRxBuffer->BytesPerSector = pRetBuffer->BytesPerSector;
|
|
pRxBuffer->SectorsPerAllocationUnit = pRetBuffer->SectorsPerAllocationUnit;
|
|
pRxBuffer->TotalAllocationUnits.QuadPart =
|
|
pRetBuffer->TotalAllocationUnits.QuadPart;
|
|
|
|
*pLengthRemaining -= (sizeof(FILE_FS_SIZE_INFORMATION));
|
|
|
|
} else {
|
|
TRC_ERR((TB, "Volume Size Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Volume Size Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case FileFsFullSizeInformation:
|
|
{
|
|
PFILE_FS_FULL_SIZE_INFORMATION pRxBuffer = (PFILE_FS_FULL_SIZE_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_FS_FULL_SIZE_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_FS_FULL_SIZE_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength == sizeof(RDP_FILE_FS_FULL_SIZE_INFORMATION)) {
|
|
if (*pLengthRemaining >= sizeof(FILE_FS_FULL_SIZE_INFORMATION)) {
|
|
|
|
pRxBuffer->ActualAvailableAllocationUnits.QuadPart =
|
|
pRetBuffer->ActualAvailableAllocationUnits.QuadPart;
|
|
pRxBuffer->BytesPerSector = pRetBuffer->BytesPerSector;
|
|
pRxBuffer->SectorsPerAllocationUnit = pRetBuffer->SectorsPerAllocationUnit;
|
|
pRxBuffer->TotalAllocationUnits.QuadPart =
|
|
pRetBuffer->TotalAllocationUnits.QuadPart;
|
|
pRxBuffer->CallerAvailableAllocationUnits.QuadPart =
|
|
pRetBuffer->CallerAvailableAllocationUnits.QuadPart;
|
|
|
|
*pLengthRemaining -= (sizeof(FILE_FS_FULL_SIZE_INFORMATION));
|
|
|
|
} else {
|
|
TRC_ERR((TB, "Volume Size Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Volume Size Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FileFsAttributeInformation:
|
|
{
|
|
PFILE_FS_ATTRIBUTE_INFORMATION pRxBuffer = (PFILE_FS_ATTRIBUTE_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_FS_ATTRIBUTE_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_FS_ATTRIBUTE_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength >= FIELD_OFFSET(RDP_FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName)) {
|
|
if (*pLengthRemaining >= FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName)) {
|
|
pRxBuffer->FileSystemAttributes = pRetBuffer->FileSystemAttributes;
|
|
pRxBuffer->MaximumComponentNameLength = pRetBuffer->MaximumComponentNameLength;
|
|
pRxBuffer->FileSystemNameLength = pRetBuffer->FileSystemNameLength;
|
|
|
|
*pLengthRemaining -= (FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName));
|
|
|
|
if ((ULONG)*pLengthRemaining >= pRxBuffer->FileSystemNameLength) {
|
|
if (BufferLength ==
|
|
FIELD_OFFSET(RDP_FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) +
|
|
pRxBuffer->FileSystemNameLength) {
|
|
RtlCopyMemory(pRxBuffer->FileSystemName, pRetBuffer->FileSystemName,
|
|
pRxBuffer->FileSystemNameLength);
|
|
*pLengthRemaining -= pRxBuffer->FileSystemNameLength;
|
|
} else {
|
|
TRC_ERR((TB, "Volume Attributes Information is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Volume Attributes Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Volume Attributes Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "Volume Attributes Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TRC_ERR((TB, "Volume Information Class is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
CompleteBusyExchange(Exchange,
|
|
CompletionPacket->IoCompletion.IoStatus,
|
|
CompletionPacket->IoCompletion.Parameters.QueryVolume.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.QueryVolume.Buffer));
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Unsuccessful IO at the client end
|
|
//
|
|
|
|
TRC_DBG((TB, "Unsuccessful Read at the client end"));
|
|
if (cbPacket >= sizeof(RDPDR_IOCOMPLETION_PACKET)) {
|
|
if (RxContext != NULL) {
|
|
CompleteBusyExchange(Exchange,
|
|
CompletionPacket->IoCompletion.IoStatus, 0);
|
|
}
|
|
else {
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
TRC_ERR((TB, "Query volume returned invalid data "));
|
|
|
|
if (RxContext != NULL) {
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
} else {
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnSetVolumeInfoCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
DrIoContext *Context = (DrIoContext *)Exchange->_Context;
|
|
|
|
BEGIN_FN("DrDrive::OnSetVolumeInfoCompletion");
|
|
|
|
RxContext = Context->_RxContext;
|
|
if (RxContext != NULL) {
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION);
|
|
|
|
TRC_NRM((TB, "Irp: %s, Completion Status: %lx",
|
|
IrpNames[RxContext->MajorFunction],
|
|
RxContext->IoStatusBlock.Status));
|
|
|
|
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0);
|
|
} else {
|
|
//
|
|
// Was cancelled but Context wasn't cleaned up
|
|
//
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnQueryFileInfoCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
PVOID pData = CompletionPacket->IoCompletion.Parameters.QueryFile.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;
|
|
|
|
BEGIN_FN("DrDrive::OnQueryFileInfoCompletion");
|
|
|
|
//
|
|
// 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.QueryFile.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.QueryFile.Length,
|
|
Context->_DataCopied));
|
|
cbWantData = CompletionPacket->IoCompletion.Parameters.QueryFile.Length -
|
|
Context->_DataCopied;
|
|
cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
|
|
IoCompletion.Parameters.QueryFile.Buffer);
|
|
|
|
if (cbHaveData > cbWantData) {
|
|
//
|
|
// Sounds like a bad client to me
|
|
//
|
|
|
|
TRC_ERR((TB, "Query file 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
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
|
|
ASSERT(FileObj != NULL);
|
|
|
|
TRC_DBG((TB, "Copying data for Query File"));
|
|
|
|
if (cbHaveData < cbWantData || Context->_DataCopied) {
|
|
if (FileObj->GetBufferSize() < CompletionPacket->IoCompletion.Parameters.QueryFile.Length) {
|
|
if (!FileObj->AllocateBuffer(CompletionPacket->IoCompletion.Parameters.QueryFile.Length)) {
|
|
CompleteBusyExchange(Exchange, STATUS_INSUFFICIENT_RESOURCES, 0);
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
RtlCopyMemory(FileObj->GetBuffer() + 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) {
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass;
|
|
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
PBYTE pBuffer;
|
|
ULONG BufferLength;
|
|
|
|
if (!Context->_DataCopied) {
|
|
pBuffer = (PBYTE) pData;
|
|
}
|
|
else {
|
|
pBuffer = FileObj->GetBuffer();
|
|
}
|
|
|
|
BufferLength = CompletionPacket->IoCompletion.Parameters.QueryDir.Length;
|
|
|
|
switch (FileInformationClass) {
|
|
case FileBasicInformation:
|
|
{
|
|
PFILE_BASIC_INFORMATION pRxBuffer = (PFILE_BASIC_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_BASIC_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_BASIC_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength == sizeof(RDP_FILE_BASIC_INFORMATION)) {
|
|
if (*pLengthRemaining >= sizeof(FILE_BASIC_INFORMATION)) {
|
|
|
|
pRxBuffer->ChangeTime.QuadPart = pRetBuffer->ChangeTime.QuadPart;
|
|
pRxBuffer->CreationTime.QuadPart = pRetBuffer->CreationTime.QuadPart;
|
|
pRxBuffer->FileAttributes = pRetBuffer->FileAttributes;
|
|
pRxBuffer->LastAccessTime.QuadPart =
|
|
pRetBuffer->LastAccessTime.QuadPart;
|
|
pRxBuffer->LastWriteTime.QuadPart =
|
|
pRetBuffer->LastWriteTime.QuadPart;
|
|
|
|
*pLengthRemaining -= (sizeof(FILE_BASIC_INFORMATION));
|
|
|
|
} else {
|
|
TRC_ERR((TB, "File Basic Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "File Basic Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FileStandardInformation:
|
|
{
|
|
PFILE_STANDARD_INFORMATION pRxBuffer = (PFILE_STANDARD_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_STANDARD_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_STANDARD_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength == sizeof(RDP_FILE_STANDARD_INFORMATION)) {
|
|
if (*pLengthRemaining >= sizeof(FILE_STANDARD_INFORMATION)) {
|
|
|
|
pRxBuffer->AllocationSize.QuadPart = pRetBuffer->AllocationSize.QuadPart;
|
|
pRxBuffer->DeletePending = pRetBuffer->DeletePending;
|
|
pRxBuffer->Directory = pRetBuffer->Directory;
|
|
pRxBuffer->EndOfFile.QuadPart =
|
|
pRetBuffer->EndOfFile.QuadPart;
|
|
pRxBuffer->NumberOfLinks = pRetBuffer->NumberOfLinks;
|
|
|
|
*pLengthRemaining -= (sizeof(FILE_STANDARD_INFORMATION));
|
|
|
|
} else {
|
|
TRC_ERR((TB, "File Standard Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "File Standard Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FileAttributeTagInformation:
|
|
{
|
|
PFILE_ATTRIBUTE_TAG_INFORMATION pRxBuffer = (PFILE_ATTRIBUTE_TAG_INFORMATION)
|
|
(RxContext->Info.Buffer);
|
|
PRDP_FILE_ATTRIBUTE_TAG_INFORMATION pRetBuffer =
|
|
(PRDP_FILE_ATTRIBUTE_TAG_INFORMATION) pBuffer;
|
|
|
|
if (BufferLength == sizeof(RDP_FILE_ATTRIBUTE_TAG_INFORMATION)) {
|
|
if (*pLengthRemaining >= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION)) {
|
|
pRxBuffer->FileAttributes = pRetBuffer->FileAttributes;
|
|
pRxBuffer->ReparseTag = pRetBuffer->ReparseTag;
|
|
|
|
*pLengthRemaining -= (sizeof(FILE_ATTRIBUTE_TAG_INFORMATION));
|
|
|
|
} else {
|
|
TRC_ERR((TB, "File Attribute Tag Information, RxBuffer overflows"));
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
TRC_ERR((TB, "File Attribute Tag Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TRC_ERR((TB, "File Information Class is invalid"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
CompleteBusyExchange(Exchange,
|
|
CompletionPacket->IoCompletion.IoStatus,
|
|
CompletionPacket->IoCompletion.Parameters.QueryFile.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.QueryFile.Buffer));
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Unsuccessful IO at the client end
|
|
//
|
|
|
|
TRC_DBG((TB, "Unsuccessful Read at the client end"));
|
|
if (cbPacket >= sizeof(RDPDR_IOCOMPLETION_PACKET)) {
|
|
if (RxContext != NULL) {
|
|
CompleteBusyExchange(Exchange,
|
|
CompletionPacket->IoCompletion.IoStatus, 0);
|
|
}
|
|
else {
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
TRC_ERR((TB, "Query file returned invalid data "));
|
|
|
|
if (RxContext != NULL) {
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
} else {
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnSetFileInfoCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
DrIoContext *Context = (DrIoContext *)Exchange->_Context;
|
|
|
|
BEGIN_FN("DrDrive::OnSetFileInfoCompletion");
|
|
|
|
RxContext = Context->_RxContext;
|
|
if (RxContext != NULL) {
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_SET_INFORMATION);
|
|
|
|
TRC_NRM((TB, "Irp: %s, Completion Status: %lx",
|
|
IrpNames[RxContext->MajorFunction],
|
|
RxContext->IoStatusBlock.Status));
|
|
|
|
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0);
|
|
} else {
|
|
|
|
//
|
|
// Was cancelled but Context wasn't cleaned up
|
|
//
|
|
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnQuerySdInfoCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
PVOID pData = CompletionPacket->IoCompletion.Parameters.QuerySd.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("DrDrive::OnQuerySdInfoCompletion");
|
|
|
|
//
|
|
// 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.QuerySd.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.QuerySd.Length,
|
|
Context->_DataCopied));
|
|
cbWantData = CompletionPacket->IoCompletion.Parameters.QuerySd.Length -
|
|
Context->_DataCopied;
|
|
cbHaveData = cbPacket - (ULONG)FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
|
|
IoCompletion.Parameters.QuerySd.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
|
|
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
|
|
ASSERT(FileObj != NULL);
|
|
|
|
TRC_DBG((TB, "Copying data for Query File"));
|
|
|
|
if (cbHaveData < cbWantData || Context->_DataCopied) {
|
|
if (FileObj->GetBufferSize() < CompletionPacket->IoCompletion.Parameters.QueryFile.Length) {
|
|
if (!FileObj->AllocateBuffer(CompletionPacket->IoCompletion.Parameters.QueryFile.Length)) {
|
|
CompleteBusyExchange(Exchange, STATUS_INSUFFICIENT_RESOURCES, 0);
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
RtlCopyMemory(FileObj->GetBuffer() + 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) {
|
|
DrFile *pFile = (DrFile *)RxContext->pFobx->Context2;
|
|
SmartPtr<DrFile> FileObj = pFile;
|
|
PBYTE pBuffer;
|
|
ULONG BufferLength = CompletionPacket->IoCompletion.Parameters.QuerySd.Length;
|
|
PBYTE RxBuffer = (PBYTE)(RxContext->Info.Buffer);
|
|
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
|
|
if (!Context->_DataCopied) {
|
|
pBuffer = (PBYTE) pData;
|
|
}
|
|
else {
|
|
pBuffer = FileObj->GetBuffer();
|
|
}
|
|
|
|
PSECURITY_DESCRIPTOR SelfRelativeSd = (PSECURITY_DESCRIPTOR)pBuffer;
|
|
ULONG SdLength = RtlLengthSecurityDescriptor(SelfRelativeSd);
|
|
|
|
if (BufferLength == SdLength) {
|
|
if (*pLengthRemaining >= (LONG)SdLength) {
|
|
RtlCopyMemory(RxBuffer, SelfRelativeSd, SdLength);
|
|
*pLengthRemaining -= SdLength;
|
|
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "File Security Information, RxBuffer overflows"));
|
|
RxContext->InformationToReturn = SdLength;
|
|
RxContext->StoredStatus = STATUS_BUFFER_OVERFLOW;
|
|
CompleteBusyExchange(Exchange, STATUS_BUFFER_OVERFLOW, SdLength);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "File Security Information, bad data length"));
|
|
CompleteBusyExchange(Exchange, STATUS_DEVICE_PROTOCOL_ERROR, 0);
|
|
*DoDefaultRead = TRUE;
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
CompleteBusyExchange(Exchange,
|
|
CompletionPacket->IoCompletion.IoStatus,
|
|
CompletionPacket->IoCompletion.Parameters.QuerySd.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.QuerySd.Buffer));
|
|
|
|
*DoDefaultRead = FALSE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Unsuccessful IO at the client end
|
|
//
|
|
|
|
TRC_DBG((TB, "Unsuccessful Read at the client end"));
|
|
if (cbPacket >= sizeof(RDPDR_IOCOMPLETION_PACKET)) {
|
|
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 DrDrive::OnSetSdInfoCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
DrIoContext *Context = (DrIoContext *)Exchange->_Context;
|
|
|
|
BEGIN_FN("DrDrive::OnSetSdInfoCompletion");
|
|
|
|
RxContext = Context->_RxContext;
|
|
if (RxContext != NULL) {
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_SET_SECURITY);
|
|
|
|
TRC_NRM((TB, "Irp: %s, Completion Status: %lx",
|
|
IrpNames[RxContext->MajorFunction],
|
|
RxContext->IoStatusBlock.Status));
|
|
|
|
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0);
|
|
} else {
|
|
|
|
//
|
|
// Was cancelled but Context wasn't cleaned up
|
|
//
|
|
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS DrDrive::OnLocksCompletion(PRDPDR_IOCOMPLETION_PACKET CompletionPacket, ULONG cbPacket,
|
|
BOOL *DoDefaultRead, SmartPtr<DrExchange> Exchange)
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
DrIoContext *Context = (DrIoContext *)Exchange->_Context;
|
|
|
|
BEGIN_FN("DrDrive::OnLocksCompletion");
|
|
|
|
RxContext = Context->_RxContext;
|
|
if (RxContext != NULL) {
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_LOCK_CONTROL);
|
|
|
|
TRC_NRM((TB, "Irp: %s, Completion Status: %lx",
|
|
IrpNames[RxContext->MajorFunction],
|
|
RxContext->IoStatusBlock.Status));
|
|
|
|
CompleteBusyExchange(Exchange, CompletionPacket->IoCompletion.IoStatus, 0);
|
|
} else {
|
|
|
|
//
|
|
// Was cancelled but Context wasn't cleaned up
|
|
//
|
|
|
|
DiscardBusyExchange(Exchange);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|