/*++ 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 &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 '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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 \\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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 Session = _Session; DrFile *pFile = (DrFile *)RxContext->pFobx->Context2; SmartPtr 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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; }