Windows2003-3790/termsrv/newclient/rdpdr/w32drive.cpp
2020-09-30 16:53:55 +02:00

2978 lines
92 KiB
C++

/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name:
w32drive
Abstract:
This module defines a child of the client-side RDP
device redirection, the "w32drive" W32Drive to provide
file system redirection on 32bit windows
Author:
Joy Chik 11/1/99
Revision History:
--*/
#include <precom.h>
#define TRC_FILE "w32drive"
#include "drdev.h"
#include "w32drdev.h"
#include "w32drive.h"
#include "proc.h"
#include "drconfig.h"
#include "w32utl.h"
#include "utl.h"
#include "drfsfile.h"
#ifdef OS_WINCE
#include "wceinc.h"
#include "axresrc.h"
#include <lss.h>
#include <ceconfig.h>
#include "filemgr.h"
#endif
///////////////////////////////////////////////////////////////
//
// W32Drive Methods
//
//
W32Drive::W32Drive(
ProcObj *processObject,
ULONG deviceID,
const TCHAR *deviceName,
const TCHAR *devicePath) : W32DrDeviceAsync(processObject, deviceID, devicePath)
/*++
Routine Description:
Constructor
Arguments:
processObject - Associated process object.
deviceName - Name of the drive.
id - Device ID for the drive.
devicePath - Path that can be opened by CreateFile
for drive.
Return Value:
NA
--*/
{
unsigned len;
DC_BEGIN_FN("W32Drive::W32Drive");
SetDeviceProperty();
_fFailedInConstructor = FALSE;
//
// Record the drive name.
//
TRC_ASSERT((deviceName != NULL), (TB, _T("deviceName is NULL")));
len = (STRLEN(deviceName) + 1);
_driveName = new TCHAR[len];
if (_driveName != NULL) {
StringCchCopy(_driveName, len, deviceName);
}
//
// Check and record our status,
//
if (_driveName == NULL) {
TRC_ERR((TB, _T("Memory allocation failed.")));
SetValid(FALSE);
_fFailedInConstructor = TRUE;
}
}
W32Drive::~W32Drive()
/*++
Routine Description:
Destructor
Arguments:
NA
Return Value:
NA
--*/
{
if (_driveName != NULL) {
delete _driveName;
}
}
DWORD
W32Drive::Enumerate(
IN ProcObj *procObj,
IN DrDeviceMgr *deviceMgr
)
{
//
// We enumerate all 26 drive letters
//
return W32Drive::EnumerateDrives(procObj, deviceMgr, 0x3FFFFFF);
}
DWORD
W32Drive::EnumerateDrives(
IN ProcObj *procObj,
IN DrDeviceMgr *deviceMgr,
IN UINT unitMask
)
/*++
Routine Description:
Enumerate devices of this type by adding appropriate device
instances to the device manager.
Arguments:
procObj - Corresponding process object.
deviceMgr - Device manager to add devices to.
Return Value:
ERROR_SUCCESS on success. Otherwise, an error code is returned.
--*/
{
TCHAR szBuff[LOGICAL_DRIVE_LEN * MAX_LOGICAL_DRIVES + 1];
LPTSTR lpszDrive = &szBuff[0];
TCHAR szDrive[3];
W32Drive *deviceObj;
RDPDR_VERSION serverVer;
DC_BEGIN_FN("W32Drive::Enumerate");
TRC_DBG((TB, _T("Enumerating drives")));
serverVer = procObj->serverVersion();
if(!procObj->GetVCMgr().GetInitData()->fEnableRedirectDrives)
{
TRC_DBG((TB,_T("Drive redirection disabled, bailing out")));
return ERROR_SUCCESS;
}
//
// If the server doesn't support drive redirection,
// then don't bother enumerate the drives, simply
// return success
//
if (COMPARE_VERSION(serverVer.Minor, serverVer.Major,
4, 1) < 0) {
TRC_NRM((TB, _T("Skipping drive enumeration")));
return ERROR_SUCCESS;
}
szDrive[2] = TEXT('\0');
#ifndef OS_WINCE
DWORD dwEnum;
//
// Enumerate all drive letters and find the valid drives
//
dwEnum = 0;
while (unitMask) {
if (unitMask & 0x1) {
//
// For each drive find whether it is a local sharable drive
//
lpszDrive = &(szBuff[LOGICAL_DRIVE_LEN * dwEnum]);
lpszDrive[0] = TEXT('A') + (TCHAR)dwEnum;
lpszDrive[1] = TEXT(':');
lpszDrive[2] = TEXT('\\');
lpszDrive[3] = TEXT('\0');
switch (GetDriveType(lpszDrive))
{
case DRIVE_REMOVABLE: // The disk can be removed from the drive.
case DRIVE_FIXED: // The disk cannot be removed from the drive.
case DRIVE_CDROM: // The drive is a CD-ROM drive.
case DRIVE_RAMDISK: // The drive is a RAM disk.
case DRIVE_REMOTE: // The drive is a remote (network) drive.
TRC_NRM((TB, _T("Redirecting drive %s"), lpszDrive));
// Copy <driveletter>: into drive device path
szDrive[0] = lpszDrive[0];
szDrive[1] = lpszDrive[1];
deviceObj = new W32Drive(procObj, deviceMgr->GetUniqueObjectID(),
&szDrive[0], &szDrive[0]);
//
// Add to the device manager if we got a valid object.
//
if (deviceObj != NULL) {
if (deviceObj->IfFailedInConstructor() != TRUE) {
deviceObj->Initialize();
if (!(deviceObj->IsValid() &&
(deviceMgr->AddObject(deviceObj) == STATUS_SUCCESS))) {
delete deviceObj;
}
}
else {
TRC_ERR((TB, _T("Failed in new W32Drive")));
delete deviceObj;
}
}
else {
TRC_ERR((TB, _T("Failed to allocate drive device.")));
}
break;
case DRIVE_UNKNOWN: // The drive type cannot be determined.
case DRIVE_NO_ROOT_DIR: // The root path is invalid. For example, no volume is mounted at the path.
default:
TRC_NRM((TB, _T("Skipping drive %s"), lpszDrive));
break;
}
}
unitMask = unitMask >> 0x1;
dwEnum++;
}
#else
//
// JOYC: Need to look into CE way of enumerate drives
// For now, we just use C drive e.g. \\tsclient\c on the server side
//
//CE doesnt have drive letters. So DevicePath='\\', DeviceName="Files". And this string should NOT be localized
deviceObj = new W32Drive(procObj, deviceMgr->GetUniqueObjectID(),
CEROOTDIRNAME, CEROOTDIR);
//
// Add to the device manager if we got a valid object.
//
if (deviceObj != NULL) {
deviceObj->Initialize();
if (!(deviceObj->IsValid() &&
(deviceMgr->AddObject(deviceObj) == STATUS_SUCCESS))) {
delete deviceObj;
}
}
else {
TRC_ERR((TB, _T("Failed to allocate drive device.")));
}
#endif
DC_END_FN();
return ERROR_SUCCESS;
}
DWORD
W32Drive::RemoveDrives(
IN ProcObj *procObj,
IN DrDeviceMgr *deviceMgr,
IN UINT unitMask
)
/*++
Routine Description:
Enumerate devices of this type by removing appropriate device
instances from the device manager.
Arguments:
procObj - Corresponding process object.
deviceMgr - Device manager to add devices to.
Return Value:
ERROR_SUCCESS on success. Otherwise, an error code is returned.
--*/
{
DWORD driveIndex = 0;
RDPDR_VERSION serverVer;
TCHAR szDrive[3];
DC_BEGIN_FN("W32Drive::RemoveDrives");
serverVer = procObj->serverVersion();
if(!procObj->GetVCMgr().GetInitData()->fEnableRedirectDrives)
{
TRC_DBG((TB,_T("Drive redirection disabled, bailing out")));
return ERROR_SUCCESS;
}
//
// If the server doesn't support drive removal,
// then don't bother enumerate the drives, simply
// return
//
if (!(procObj->GetServerCap().GeneralCap.extendedPDU & RDPDR_DEVICE_REMOVE_PDUS)) {
TRC_NRM((TB, _T("Skipping drive enumeration")));
return ERROR_SUCCESS;
}
szDrive[1] = TEXT(':');
szDrive[2] = TEXT('\0');
while (unitMask) {
if (unitMask & 0x1) {
DrDevice *deviceObj;
//
// Find if a device exists
//
szDrive[0] = TEXT('A') + (TCHAR)driveIndex;
deviceObj = (DrDevice*)deviceMgr->GetObject(szDrive, RDPDR_DTYP_FILESYSTEM);
if ( deviceObj != NULL ) {
deviceObj->_deviceChange = DEVICEREMOVE;
// Need to remove this device
TRC_NRM((TB, _T("Deleting drive %s"), szDrive));
}
}
unitMask = unitMask >> 0x1;
driveIndex++;
}
DC_END_FN();
return ERROR_SUCCESS;
}
ULONG
W32Drive::GetDevAnnounceDataSize()
/*++
Routine Description:
Return the size (in bytes) of a device announce packet for
this device.
Arguments:
NA
Return Value:
The size (in bytes) of a device announce packet for this device.
--*/
{
ULONG size = 0;
DC_BEGIN_FN("W32Drive::GetDevAnnounceDataSize");
TRC_ASSERT((IsValid()), (TB, _T("Invalid w32drive object")));
if (!IsValid()) { return 0; }
size = 0;
//
// Add the base announce size.
//
size += sizeof(RDPDR_DEVICE_ANNOUNCE);
DC_END_FN();
return size;
}
VOID W32Drive::GetDevAnnounceData(
IN PRDPDR_DEVICE_ANNOUNCE pDeviceAnnounce
)
/*++
Routine Description:
Add a device announce packet for this device to the input buffer.
Arguments:
pDeviceAnnounce - Device Announce Buf for this Device
Return Value:
NA
--*/
{
DC_BEGIN_FN("W32Drive::GetDevAnnounceData");
TRC_ASSERT((IsValid()), (TB, _T("Invalid w32drive object")));
if (!IsValid()) {
DC_END_FN();
return;
}
pDeviceAnnounce->DeviceId = GetID();
pDeviceAnnounce->DeviceType = GetDeviceType();
pDeviceAnnounce->DeviceDataLength = 0;
//
// Record the device name in ANSI.
//
#ifdef UNICODE
RDPConvertToAnsi(_driveName, (LPSTR)pDeviceAnnounce->PreferredDosName,
sizeof(pDeviceAnnounce->PreferredDosName)
);
#else
STRNCPY((char *)pDeviceAnnounce->PreferredDosName, _driveName, PREFERRED_DOS_NAME_SIZE);
pDeviceAnnounce->PreferredDosName[PREFERRED_DOS_NAME_SIZE - 1] = '\0';
#endif
DC_END_FN();
}
HANDLE
W32Drive::StartFSFunc(
IN W32DRDEV_ASYNCIO_PARAMS *params,
OUT DWORD *status
)
/*++
Routine Description:
Start a generic asynchronous File System IO operation.
Arguments:
params - Context for the IO request.
status - Return status for IO request in the form of a windows
error code.
Return Value:
Returns a handle to an object that will be signalled when the read
completes, if it is not completed in this function. Otherwise, NULL
is returned.
--*/
{
PRDPDR_DEVICE_IOREQUEST pIoRequest;
DrFile* pFile;
ULONG irpMajor;
DC_BEGIN_FN("W32Drive::StartFSFunc");
*status = ERROR_SUCCESS;
// Assert the integrity of the IO context
TRC_ASSERT((params->magicNo == GOODMEMMAGICNUMBER),
(TB, _T("bad params->magicNo: %x"), params->magicNo));
//
// Get the IO request and the IPR major.
//
pIoRequest = &params->pIoRequestPacket->IoRequest;
irpMajor = pIoRequest->MajorFunction;
//
// Hand the request off to the thread pool.
//
params->completionEvent = CreateEvent(
NULL, // no attribute.
TRUE, // manual reset.
FALSE, // initially not signalled.
NULL // no name.
);
if (params->completionEvent == NULL) {
*status = GetLastError();
TRC_ERR((TB, _T("Error in CreateEvent: %08X."), *status));
}
else {
switch (irpMajor)
{
case IRP_MJ_DIRECTORY_CONTROL:
params->thrPoolReq = _threadPool->SubmitRequest(
_AsyncDirCtrlFunc,
params, params->completionEvent
);
break;
}
if (params->thrPoolReq == INVALID_THREADPOOLREQUEST) {
*status = ERROR_SERVICE_NO_THREAD;
}
}
Cleanup:
//
// If IO is pending, return the handle to the pending IO.
//
if (params->thrPoolReq != INVALID_THREADPOOLREQUEST) {
*status = ERROR_IO_PENDING;
DC_END_FN();
return params->completionEvent;
}
//
// Otherwise, clean up the event handle and return NULL so that the
// CompleteIOFunc can be called to send the results to the server.
//
else {
//
// Get File Object
//
pFile = _FileMgr->GetObject(pIoRequest->FileId);
if (pFile) {
pFile->Release();
}
if (params->completionEvent != NULL) {
CloseHandle(params->completionEvent);
params->completionEvent = NULL;
}
DC_END_FN();
return NULL;
}
}
HANDLE
W32Drive::_StartFSFunc(
IN W32DRDEV_ASYNCIO_PARAMS *params,
OUT DWORD *status
)
{
return ((W32Drive*)params->pObject)->StartFSFunc(params, status);
}
VOID
W32Drive::MsgIrpQueryDirectory(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
Queries the directory information for this drive device.
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
DWORD result;
ULONG BufferLength = 0;
BYTE Buffer[RDP_FILE_DIRECTORY_INFO_MAXLENGTH + (MAX_PATH + 1) * sizeof(WCHAR)];
TCHAR *pFileName;
DrFile *pFile;
HANDLE FileHandle;
WIN32_FIND_DATA FileData;
unsigned DriveLen, PathLen;
BOOL rc;
HRESULT hr;
DC_BEGIN_FN("W32Drive::MsgIrpQueryDirectory");
ASSERT(_tcslen(_devicePath));
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
PathLen = DriveLen = 0;
pFileName = NULL;
memset(Buffer, 0, sizeof(Buffer));
//
// Map file Id to get the file object
//
pFile = _FileMgr->GetObject(pIoRequest->FileId);
//
// Query file or directory information
//
if (pFile) {
if (!pIoRequest->Parameters.QueryDir.InitialQuery) {
ASSERT(((DrFSFile *)pFile)->GetSearchHandle() != INVALID_TS_FILEHANDLE);
rc = FindNextFile(((DrFSFile *)pFile)->GetSearchHandle(), &FileData);
}
else {
TCHAR FileName[MAX_PATH];
//
// Setup the File Name
//
//
// Path is assumed string null terminated
//
if (packetLen < sizeof(RDPDR_IOREQUEST_PACKET) + pIoRequest->Parameters.QueryDir.PathLength) {
// call VirtualChannelClose
ProcessObject()->GetVCMgr().ChannelClose();
TRC_ASSERT(FALSE, (TB, _T("Packet Length Error")));
goto Cleanup;
}
PathLen = pIoRequest->Parameters.QueryDir.PathLength / sizeof(WCHAR) - 1;
if (PathLen) {
WCHAR *Path;
//
// Open a File
//
Path = (WCHAR *)(pIoRequestPacket + 1);
Path[PathLen] = L'\0';
#ifndef OS_WINCE
DriveLen = _tcslen(_devicePath);
#else
DriveLen = 0;
#endif
//
// Append device path and file path together
//
#ifndef OS_WINCE
if (DriveLen + PathLen < MAX_PATH) {
pFileName = &FileName[0];
//Length is pre-checked
hr = StringCchCopy(pFileName, MAX_PATH, _devicePath);
TRC_ASSERT(SUCCEEDED(hr),
(TB,_T("Str copy failed for pre-checked len: 0x%x"),hr));
} else {
UINT cchLen = DriveLen + PathLen + 5;
pFileName = new TCHAR[cchLen];
if (pFileName) {
//
// The file name needs to be in the \\?\ format.
//
// Note: we'll not get UNC path name
hr = StringCchPrintf(pFileName,
cchLen,
TEXT("\\\\?\\%s"),
_devicePath);
TRC_ASSERT(SUCCEEDED(hr),
(TB,_T("Str copy failed for pre-checked len: 0x%x"),hr));
DriveLen += 4;
} else {
TRC_ERR((TB, _T("Failed to alloc File Name")));
goto Cleanup;
}
}
#else
pFileName = &FileName[0];
#endif
#ifndef UNICODE
RDPConvertToAnsi(Path, pFileName + DriveLen, PathLen + 1);
#else
memcpy(pFileName + DriveLen, Path, PathLen * sizeof(WCHAR));
pFileName[DriveLen + PathLen] = _T('\0');
#endif
} else {
//
// Open the root drive
//
hr = StringCchPrintf(FileName, SIZE_TCHARS(FileName),
_T("%s\\"),
_devicePath);
TRC_ASSERT(SUCCEEDED(hr),
(TB,_T("Str printf failed for pre-checked len: 0x%x"),hr));
pFileName = &FileName[0];
}
FileHandle = FindFirstFile(pFileName, &FileData);
if (FileHandle != INVALID_HANDLE_VALUE) {
((DrFSFile *)pFile)->SetSearchHandle(FileHandle);
rc = TRUE;
} else {
rc = FALSE;
}
}
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query directory information failed, %ld."), ulRetCode));
goto SendPacket;
}
switch (pIoRequest->Parameters.QueryDir.FileInformationClass) {
case RdpFileDirectoryInformation:
if (rc)
{
PRDP_FILE_DIRECTORY_INFORMATION pFileDirInfo =
(PRDP_FILE_DIRECTORY_INFORMATION) Buffer;
pFileDirInfo->NextEntryOffset = 0;
pFileDirInfo->FileIndex = 0;
pFileDirInfo->CreationTime.LowPart = FileData.ftCreationTime.dwLowDateTime;
pFileDirInfo->CreationTime.HighPart = FileData.ftCreationTime.dwHighDateTime;
pFileDirInfo->LastAccessTime.LowPart = FileData.ftLastAccessTime.dwLowDateTime;
pFileDirInfo->LastAccessTime.HighPart = FileData.ftLastAccessTime.dwHighDateTime;
pFileDirInfo->LastWriteTime.LowPart = FileData.ftLastWriteTime.dwLowDateTime;
pFileDirInfo->LastWriteTime.HighPart = FileData.ftLastWriteTime.dwHighDateTime;
pFileDirInfo->ChangeTime.QuadPart = 0;
pFileDirInfo->EndOfFile.HighPart = FileData.nFileSizeHigh;
pFileDirInfo->EndOfFile.LowPart = FileData.nFileSizeLow;
// TODO Do we need to set the allocation size? and what should it be?
pFileDirInfo->AllocationSize.HighPart = FileData.nFileSizeHigh;
pFileDirInfo->AllocationSize.LowPart = FileData.nFileSizeLow;
pFileDirInfo->FileAttributes = FileData.dwFileAttributes;
pFileDirInfo->FileNameLength = _tcslen(FileData.cFileName) * sizeof(WCHAR);
#ifdef UNICODE
memcpy(&pFileDirInfo->FileName[0], FileData.cFileName, pFileDirInfo->FileNameLength);
#else
RDPConvertToUnicode(FileData.cFileName, (LPWSTR)FileNameBuffer, MAX_PATH);
memcpy(&pFileDirInfo->FileName[0], FileNameBuffer, pFileDirInfo->FileNameLength);
#endif
ulRetCode = ERROR_SUCCESS;
BufferLength = FIELD_OFFSET(RDP_FILE_DIRECTORY_INFORMATION, FileName) +
pFileDirInfo->FileNameLength;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query directory information failed, %ld."), ulRetCode));
}
break;
case RdpFileFullDirectoryInformation:
if (rc)
{
PRDP_FILE_FULL_DIR_INFORMATION pFileDirInfo =
(PRDP_FILE_FULL_DIR_INFORMATION) Buffer;
pFileDirInfo->NextEntryOffset = 0;
pFileDirInfo->FileIndex = 0;
pFileDirInfo->CreationTime.LowPart = FileData.ftCreationTime.dwLowDateTime;
pFileDirInfo->CreationTime.HighPart = FileData.ftCreationTime.dwHighDateTime;
pFileDirInfo->LastAccessTime.LowPart = FileData.ftLastAccessTime.dwLowDateTime;
pFileDirInfo->LastAccessTime.HighPart = FileData.ftLastAccessTime.dwHighDateTime;
pFileDirInfo->LastWriteTime.LowPart = FileData.ftLastWriteTime.dwLowDateTime;
pFileDirInfo->LastWriteTime.HighPart = FileData.ftLastWriteTime.dwHighDateTime;
pFileDirInfo->ChangeTime.QuadPart = 0;
pFileDirInfo->EndOfFile.HighPart = FileData.nFileSizeHigh;
pFileDirInfo->EndOfFile.LowPart = FileData.nFileSizeLow;
pFileDirInfo->AllocationSize.HighPart = FileData.nFileSizeHigh;
pFileDirInfo->AllocationSize.LowPart = FileData.nFileSizeLow;
pFileDirInfo->FileAttributes = FileData.dwFileAttributes;
pFileDirInfo->FileNameLength = _tcslen(FileData.cFileName) * sizeof(WCHAR);
#ifdef UNICODE
memcpy(&pFileDirInfo->FileName[0], FileData.cFileName, pFileDirInfo->FileNameLength);
#else
RDPConvertToUnicode(FileData.cFileName, (LPWSTR)FileNameBuffer, MAX_PATH);
memcpy(&pFileDirInfo->FileName[0], FileNameBuffer, pFileDirInfo->FileNameLength);
#endif
pFileDirInfo->EaSize = 0;
ulRetCode = ERROR_SUCCESS;
BufferLength = FIELD_OFFSET(RDP_FILE_FULL_DIR_INFORMATION, FileName) +
pFileDirInfo->FileNameLength;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query full directory information failed, %ld."), ulRetCode));
}
break;
case RdpFileBothDirectoryInformation:
if (rc)
{
PRDP_FILE_BOTH_DIR_INFORMATION pFileDirInfo =
(PRDP_FILE_BOTH_DIR_INFORMATION) Buffer;
pFileDirInfo->NextEntryOffset = 0;
pFileDirInfo->FileIndex = 0;
pFileDirInfo->CreationTime.LowPart = FileData.ftCreationTime.dwLowDateTime;
pFileDirInfo->CreationTime.HighPart = FileData.ftCreationTime.dwHighDateTime;
pFileDirInfo->LastAccessTime.LowPart = FileData.ftLastAccessTime.dwLowDateTime;
pFileDirInfo->LastAccessTime.HighPart = FileData.ftLastAccessTime.dwHighDateTime;
pFileDirInfo->LastWriteTime.LowPart = FileData.ftLastWriteTime.dwLowDateTime;
pFileDirInfo->LastWriteTime.HighPart = FileData.ftLastWriteTime.dwHighDateTime;
pFileDirInfo->ChangeTime.QuadPart = 0;
pFileDirInfo->EndOfFile.HighPart = FileData.nFileSizeHigh;
pFileDirInfo->EndOfFile.LowPart = FileData.nFileSizeLow;
pFileDirInfo->AllocationSize.HighPart = FileData.nFileSizeHigh;
pFileDirInfo->AllocationSize.LowPart = FileData.nFileSizeLow;
pFileDirInfo->FileAttributes = FileData.dwFileAttributes;
pFileDirInfo->EaSize = 0;
#ifndef OS_WINCE
pFileDirInfo->ShortNameLength = (CCHAR)(_tcslen(FileData.cAlternateFileName) * sizeof(WCHAR));
#else
pFileDirInfo->ShortNameLength = sizeof(WCHAR);
#endif
#ifndef OS_WINCE
#ifdef UNICODE
memcpy(&pFileDirInfo->ShortName[0], FileData.cAlternateFileName, pFileDirInfo->ShortNameLength);
#else
RDPConvertToUnicode(FileData.cAlternateFileName, (LPWSTR)ShortName,
sizeof(ShortName) / sizeof(WCHAR));
memcpy(&pFileDirInfo->ShortName[0], ShortName, pFileDirInfo->ShortNameLength);
#endif
#else
pFileDirInfo->ShortName[0] = L'\0';
#endif
pFileDirInfo->FileNameLength = _tcslen(FileData.cFileName) * sizeof(WCHAR);
#ifdef UNICODE
memcpy(&pFileDirInfo->FileName[0], FileData.cFileName, pFileDirInfo->FileNameLength);
#else
RDPConvertToUnicode(FileData.cFileName, FileNameBuffer, MAX_PATH);
memcpy(&pFileDirInfo->FileName[0], FileNameBuffer, pFileDirInfo->FileNameLength);
#endif
ulRetCode = ERROR_SUCCESS;
BufferLength = FIELD_OFFSET(RDP_FILE_BOTH_DIR_INFORMATION, FileName) +
pFileDirInfo->FileNameLength;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
} else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query both directory info failed, %ld."), ulRetCode));
}
break;
case RdpFileNamesInformation:
if (rc) {
PRDP_FILE_NAMES_INFORMATION pFileDirInfo =
(PRDP_FILE_NAMES_INFORMATION) Buffer;
pFileDirInfo->NextEntryOffset = 0;
pFileDirInfo->FileIndex = 0;
pFileDirInfo->FileNameLength = _tcslen(FileData.cFileName) * sizeof(WCHAR);
#ifdef UNICODE
memcpy(&pFileDirInfo->FileName[0], FileData.cFileName, pFileDirInfo->FileNameLength);
#else
RDPConvertToUnicode(FileData.cFileName, FileNameBuffer, MAX_PATH);
memcpy(&pFileDirInfo->FileName[0], FileNameBuffer, pFileDirInfo->FileNameLength);
#endif
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_NAMES_INFORMATION) +
pFileDirInfo->FileNameLength - sizeof(WCHAR);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
} else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query file names failed, %ld."), ulRetCode));
}
break;
default:
TRC_ERR((TB, _T("Unsupported QueryDirectory class %x"),
pIoRequest->Parameters.QueryDir.FileInformationClass));
ulRetCode = ERROR_INVALID_FUNCTION;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
break;
}
SendPacket:
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket, ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
pReplyPacket->IoCompletion.Parameters.QueryDir.Length = BufferLength;
if (BufferLength)
memcpy(pReplyPacket->IoCompletion.Parameters.QueryDir.Buffer,
Buffer, BufferLength);
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
if (pFileName && (DriveLen + PathLen >= MAX_PATH)) {
delete pFileName;
}
DC_END_FN();
}
VOID
W32Drive::MsgIrpDirectoryControl(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
Queries the directory information for this drive device.
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - length of the packet
Return Value:
NA
--*/
{
W32DRDEV_ASYNCIO_PARAMS *params;
DrFile* pFile;
DWORD status;
DC_BEGIN_FN("W32DrDeviceAsync::MsgIrpDirectoryControl");
TRC_NRM((TB, _T("Request Directory Control")));
//
// Leave the query directory in the forground thread as it is
//
if (pIoRequestPacket->IoRequest.MinorFunction == IRP_MN_QUERY_DIRECTORY ||
pIoRequestPacket->IoRequest.MinorFunction == 0) {
MsgIrpQueryDirectory(pIoRequestPacket, packetLen);
return;
}
//
// Get File Object and reference it
//
pFile = _FileMgr->GetObject(pIoRequestPacket->IoRequest.FileId);
if (pFile) {
pFile->AddRef();
}
else {
DefaultIORequestMsgHandle(pIoRequestPacket, STATUS_UNSUCCESSFUL);
return;
}
//
// Allocate and dispatch an asynchronous IO request.
//
params = new W32DRDEV_ASYNCIO_PARAMS(this, pIoRequestPacket);
if (params != NULL ) {
TRC_NRM((TB, _T("Async IO operation")));
status = ProcessObject()->DispatchAsyncIORequest(
(RDPAsyncFunc_StartIO)W32Drive::_StartFSFunc,
(RDPAsyncFunc_IOComplete)W32DrDeviceAsync::_CompleteIOFunc,
(RDPAsyncFunc_IOCancel)W32DrDeviceAsync::_CancelIOFunc,
params
);
}
else {
TRC_ERR((TB, _T("Memory alloc failed.")));
status = ERROR_NOT_ENOUGH_MEMORY;
}
//
// Clean up on error.
//
if (status != ERROR_SUCCESS) {
pFile->Release();
if (params != NULL) {
delete params;
}
delete pIoRequestPacket;
}
DC_END_FN();
}
DWORD
W32Drive::AsyncDirCtrlFunc(
IN W32DRDEV_ASYNCIO_PARAMS *params,
IN HANDLE cancelEvent
)
/*++
Routine Description:
Asynchrous Directory Control Function
Arguments:
params - Context for the IO request.
Return Value:
Always returns 0.
--*/
{
PRDPDR_DEVICE_IOREQUEST pIoRequest;
DC_BEGIN_FN("W32Drive::AsyncDirCtrlFunc");
//
// Get the IO request pointer
//
pIoRequest = &params->pIoRequestPacket->IoRequest;
if (pIoRequest->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY) {
#if (!defined (OS_WINCE)) || (!defined (WINCE_SDKBUILD))
return AsyncNotifyChangeDir(params, cancelEvent);
#else
DrFile* pFile;
pFile = _FileMgr->GetObject(pIoRequest->FileId);
if (pFile)
pFile->Release();
return STATUS_NOT_SUPPORTED;
#endif
}
else {
TRC_ASSERT((FALSE), (TB, _T("Invalid Minor Function: %x"), pIoRequest->MinorFunction));
return ERROR_INVALID_PARAMETER;
}
}
DWORD
W32Drive::_AsyncDirCtrlFunc(
IN PVOID params,
IN HANDLE cancelEvent
)
{
return ((W32Drive*)(((W32DRDEV_ASYNCIO_PARAMS *)params)->pObject))->AsyncDirCtrlFunc(
(W32DRDEV_ASYNCIO_PARAMS *)params, cancelEvent);
}
#if (!defined (OS_WINCE)) || (!defined (WINCE_SDKBUILD))
DWORD
W32Drive::AsyncNotifyChangeDir(
IN W32DRDEV_ASYNCIO_PARAMS *params,
IN HANDLE cancelEvent
)
/*++
Routine Description:
Directory change notification Function
Arguments:
params - Context for the IO request.
Return Value:
Always returns 0.
--*/
{
DWORD status;
ULONG replyPacketSize = 0;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket = NULL;
DrFile* pFile;
HANDLE FileHandle;
HANDLE NotifyHandle;
HANDLE waitableEvents[2];
HRESULT hr;
DC_BEGIN_FN("W32Drive::AsyncNotifyChangeDir");
//
// Get the IO request pointer
//
pIoRequest = &params->pIoRequestPacket->IoRequest;
//
// Get File Object
//
pFile = _FileMgr->GetObject(pIoRequest->FileId);
if (pFile) {
FileHandle = pFile->GetFileHandle();
NotifyHandle = ((DrFSFile*)pFile)->GetNotifyHandle();
}
else {
status = ERROR_CANCELLED;
TRC_ERR((TB, _T("File object already cancelled")));
goto Cleanup;
}
//
// Allocate reply buffer.
//
replyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
pReplyPacket = DrUTL_AllocIOCompletePacket(params->pIoRequestPacket,
replyPacketSize) ;
if (pReplyPacket == NULL) {
status = ERROR_NOT_ENOUGH_MEMORY;
TRC_ERR((TB, _T("Failed to alloc %ld bytes."), replyPacketSize));
goto Cleanup;
}
//
// Save the reply packet info to the context for this IO operation.
//
params->pIoReplyPacket = pReplyPacket;
params->IoReplyPacketSize = replyPacketSize;
//
// If we don't yet have a notification handle.
//
if (NotifyHandle == INVALID_HANDLE_VALUE) {
TRC_ASSERT((((DrFSFile*)pFile)->GetFileName() != NULL),
(TB, _T("FileName is empty")));
//
// Setup the notification handle
//
NotifyHandle = FindFirstChangeNotification(((DrFSFile*)pFile)->GetFileName(),
pIoRequest->Parameters.NotifyChangeDir.WatchTree,
pIoRequest->Parameters.NotifyChangeDir.CompletionFilter);
if (NotifyHandle == INVALID_HANDLE_VALUE) {
status = GetLastError();
TRC_ERR((TB, _T("FindFirstChangeNotification: %08X"), status));
goto Cleanup;
}
if (((DrFSFile*)pFile)->SetNotifyHandle(NotifyHandle) == FALSE) {
FindCloseChangeNotification(NotifyHandle);
NotifyHandle = INVALID_HANDLE_VALUE;
status = ERROR_CANCELLED;
goto Cleanup;
}
}
else {
//
// Notification handle already setup. Find next change notification.
//
if (!FindNextChangeNotification(NotifyHandle)) {
status = GetLastError();
TRC_ERR((TB, _T("FindNextChangeNotification: %08X"), status));
goto Cleanup;
}
}
//
// Wait for the cancel or notify event to be signaled.
//
waitableEvents[0] = NotifyHandle;
waitableEvents[1] = cancelEvent;
status = WaitForMultipleObjects(2, waitableEvents, FALSE, INFINITE);
if (status == WAIT_FAILED) {
status = GetLastError();
TRC_ERR((TB, _T("Error %ld."), status));
}
else if (status == WAIT_OBJECT_0){
status = ERROR_SUCCESS;
}
else if (status == WAIT_OBJECT_0 + 1) {
TRC_ERR((TB, _T("Client got disconnected/logoff")));
status = ERROR_CANCELLED;
}
Cleanup:
if (pFile)
pFile->Release();
DC_END_FN();
return status;
}
#endif
VOID
W32Drive::MsgIrpQueryVolumeInfo(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
This routine handles drive volume level information query
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
DWORD result;
TCHAR DeviceName[MAX_PATH + 2];
#ifndef OS_WINCE
TCHAR NameBuffer[MAX_PATH];
WCHAR TempBuffer[MAX_PATH + 1];
#endif
ULONG BufferLength = 0;
#ifndef OS_WINCE
DECLSPEC_ALIGN(8) BYTE Buffer[RDP_FILE_VOLUME_INFO_MAXLENGTH + MAX_PATH];
#else
BYTE Buffer[RDP_FILE_VOLUME_INFO_MAXLENGTH + MAX_PATH];
#endif
HRESULT hr;
DC_BEGIN_FN("W32Drive::MsgIrpQueryVolumeInfo");
TRC_ASSERT((_tcslen(_devicePath) != 0), (TB, _T("Empty devicePath")));
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
//
// DriveName needs to end with \
//
#ifndef OS_WINCE
hr = StringCchPrintf(DeviceName, SIZE_TCHARS(DeviceName),
TEXT("%s\\"), _devicePath);
#else
hr = StringCchPrintf(DeviceName, SIZE_TCHARS(DeviceName),
TEXT("\\"));
#endif
TRC_ASSERT(SUCCEEDED(hr),
(TB,_T("Str printf failed for pre-checked len: 0x%x"),hr));
memset(Buffer, 0, sizeof(Buffer));
switch (pIoRequest->Parameters.QueryVolume.FsInformationClass) {
case RdpFsVolumeInformation:
{
RDP_FILE_FS_VOLUME_INFORMATION *pVolumeInfo =
(RDP_FILE_FS_VOLUME_INFORMATION *) Buffer;
#ifndef OS_WINCE
if (GetVolumeInformation(DeviceName,
NameBuffer,
MAX_PATH,
&pVolumeInfo->VolumeSerialNumber,
NULL,
NULL,
NULL,
0
))
{
pVolumeInfo->VolumeCreationTime.QuadPart = 0;
pVolumeInfo->SupportsObjects = FALSE;
pVolumeInfo->VolumeLabelLength = _tcslen(NameBuffer) * sizeof(WCHAR);
#ifdef UNICODE
memcpy(&pVolumeInfo->VolumeLabel[0], NameBuffer, pVolumeInfo->VolumeLabelLength);
#else
RDPConvertToUnicode(NameBuffer, (LPWSTR)TempBuffer, MAX_PATH);
memcpy(&pVolumeInfo->VolumeLabel[0], TempBuffer, pVolumeInfo->VolumeLabelLength);
#endif
ulRetCode = ERROR_SUCCESS;
BufferLength = FIELD_OFFSET(RDP_FILE_FS_VOLUME_INFORMATION, VolumeLabel) +
pVolumeInfo->VolumeLabelLength;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query volume information failed, %ld."), ulRetCode));
}
#else
pVolumeInfo->VolumeCreationTime.QuadPart = 0;
pVolumeInfo->SupportsObjects = FALSE;
pVolumeInfo->VolumeLabelLength = sizeof(WCHAR);
pVolumeInfo->VolumeLabel[0] = L'\0';
pVolumeInfo->VolumeSerialNumber = 0;
ulRetCode = ERROR_SUCCESS;
BufferLength = FIELD_OFFSET(RDP_FILE_FS_VOLUME_INFORMATION, VolumeLabel) +
pVolumeInfo->VolumeLabelLength;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
#endif
break;
}
case RdpFsSizeInformation:
{
RDP_FILE_FS_SIZE_INFORMATION *pFsSizeInfo =
(RDP_FILE_FS_SIZE_INFORMATION *) Buffer;
#ifndef OS_WINCE
if (GetDiskFreeSpace(DeviceName,
&pFsSizeInfo->SectorsPerAllocationUnit,
&pFsSizeInfo->BytesPerSector,
&pFsSizeInfo->AvailableAllocationUnits.LowPart,
&pFsSizeInfo->TotalAllocationUnits.LowPart))
{
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_FS_SIZE_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query volume size failed, %ld."), ulRetCode));
}
#else
ULARGE_INTEGER FreeBytesAvailableToCaller;
if (GetDiskFreeSpaceEx(DeviceName, &FreeBytesAvailableToCaller,
(PULARGE_INTEGER)&pFsSizeInfo->AvailableAllocationUnits,
(PULARGE_INTEGER)&pFsSizeInfo->TotalAllocationUnits)) {
pFsSizeInfo->SectorsPerAllocationUnit = 1;
pFsSizeInfo->BytesPerSector = 1;
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_FS_SIZE_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query volume size failed, %ld."), ulRetCode));
}
#endif
break;
}
case RdpFsFullSizeInformation:
{
RDP_FILE_FS_FULL_SIZE_INFORMATION *pFsSizeInfo =
(RDP_FILE_FS_FULL_SIZE_INFORMATION *) Buffer;
#ifndef OS_WINCE
if (GetDiskFreeSpace(DeviceName,
&pFsSizeInfo->SectorsPerAllocationUnit,
&pFsSizeInfo->BytesPerSector,
&pFsSizeInfo->CallerAvailableAllocationUnits.LowPart,
&pFsSizeInfo->TotalAllocationUnits.LowPart))
{
//
// TODO what's the difference between actual and caller
// available allocation units?
//
pFsSizeInfo->ActualAvailableAllocationUnits.QuadPart =
pFsSizeInfo->CallerAvailableAllocationUnits.QuadPart;
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_FS_FULL_SIZE_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query volume full size failed, %ld."), ulRetCode));
}
#else
if (GetDiskFreeSpaceEx(DeviceName,
(PULARGE_INTEGER)&pFsSizeInfo->CallerAvailableAllocationUnits,
(PULARGE_INTEGER)&pFsSizeInfo->ActualAvailableAllocationUnits,
(PULARGE_INTEGER)&pFsSizeInfo->TotalAllocationUnits)) {
pFsSizeInfo->SectorsPerAllocationUnit = 1;
pFsSizeInfo->BytesPerSector = 1;
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_FS_FULL_SIZE_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query volume size failed, %ld."), ulRetCode));
}
#endif
break;
}
case RdpFsAttributeInformation:
{
RDP_FILE_FS_ATTRIBUTE_INFORMATION *pFsAttribInfo =
(RDP_FILE_FS_ATTRIBUTE_INFORMATION *) Buffer;
#ifndef OS_WINCE
if (GetVolumeInformation(DeviceName,
NULL,
NULL,
NULL,
(ULONG*)&pFsAttribInfo->MaximumComponentNameLength,
&pFsAttribInfo->FileSystemAttributes,
NameBuffer,
MAX_PATH
))
{
pFsAttribInfo->FileSystemNameLength =
_tcslen(NameBuffer) * sizeof(WCHAR);
#ifdef UNICODE
memcpy(&pFsAttribInfo->FileSystemName[0], NameBuffer,
pFsAttribInfo->FileSystemNameLength);
#else
RDPConvertToUnicode(NameBuffer, (LPWSTR)TempBuffer, MAX_PATH);
memcpy(&pFsAttribInfo->FileSystemName[0], TempBuffer,
pFsAttribInfo->FileSystemNameLength);
#endif
ulRetCode = ERROR_SUCCESS;
BufferLength = FIELD_OFFSET(RDP_FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) +
pFsAttribInfo->FileSystemNameLength;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query file system attributes failed, %ld."), ulRetCode));
}
#else
pFsAttribInfo->MaximumComponentNameLength = MAX_PATH;
pFsAttribInfo->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES |
FILE_UNICODE_ON_DISK;
wcscpy(&pFsAttribInfo->FileSystemName[0], L"FAT");
pFsAttribInfo->FileSystemNameLength =
_tcslen(pFsAttribInfo->FileSystemName) * sizeof(WCHAR);
ulRetCode = ERROR_SUCCESS;
BufferLength = FIELD_OFFSET(RDP_FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) +
pFsAttribInfo->FileSystemNameLength;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
#endif
break;
}
default:
TRC_ERR((TB, _T("Unsupported QueryVolume class %x"),
pIoRequest->Parameters.QueryVolume.FsInformationClass));
ulRetCode = ERROR_INVALID_FUNCTION;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
break;
}
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket,
ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
pReplyPacket->IoCompletion.Parameters.QueryVolume.Length = BufferLength;
if (BufferLength)
memcpy(pReplyPacket->IoCompletion.Parameters.QueryVolume.Buffer,
Buffer, BufferLength);
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
DC_END_FN();
}
VOID
W32Drive::MsgIrpSetVolumeInfo(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
This routine sets drive volume information.
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
DWORD result;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
TCHAR DeviceName[MAX_PATH + 2];
PBYTE pDataBuffer;
HRESULT hr;
DC_BEGIN_FN("W32Drive::MsgIrpSetVolumeInfo");
TRC_ASSERT((_tcslen(_devicePath) != 0), (TB, _T("Empty devicePath")));
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
//
// DriveName needs to end with \
//
hr = StringCchPrintf(DeviceName, SIZE_TCHARS(DeviceName),
TEXT("%s\\"), _devicePath);
TRC_ASSERT(SUCCEEDED(hr),
(TB,_T("Str printf failed for pre-checked len: 0x%x"),hr));
pDataBuffer = (PBYTE)(pIoRequest + 1);
switch (pIoRequest->Parameters.SetVolume.FsInformationClass) {
case RdpFsLabelInformation:
{
PRDP_FILE_FS_LABEL_INFORMATION pLabelInfo =
(PRDP_FILE_FS_LABEL_INFORMATION) pDataBuffer;
// To conform with the redirector, we will not allow
// user to change the volume label info
ulRetCode = ERROR_ACCESS_DENIED;
break;
#if 0
NameBuffer[0] = TEXT('\0');
RDPConvertToAnsi(pLabelInfo->VolumeLabel, NameBuffer,
MAX_PATH);
if (SetVolumeLabel(DeviceName, NameBuffer)) {
ulRetCode = ERROR_SUCCESS;
} else {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("Set Volume label failed, %ld."), ulRetCode));
}
#endif
}
break;
default:
TRC_ERR((TB, _T("Unsupported SetVolume class %x"),
pIoRequest->Parameters.SetVolume.FsInformationClass));
ulRetCode = ERROR_INVALID_FUNCTION;
break;
}
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket,
ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
pReplyPacket->IoCompletion.Parameters.SetVolume.Length =
pIoRequest->Parameters.SetVolume.Length;
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
DC_END_FN();
}
VOID
W32Drive::MsgIrpQueryFileInfo(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
This routine handles query file information
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
DWORD result;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
BOOL rc;
ULONG BufferLength = 0;
BYTE Buffer[RDP_FILE_INFORMATION_MAXLENGTH];
DrFile* pFile;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
BY_HANDLE_FILE_INFORMATION FileInformation;
DC_BEGIN_FN("W32Drive::MsgIrpQueryFileInfo");
TRC_ASSERT((_tcslen(_devicePath) != 0), (TB, _T("Empty device path")));
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
memset(Buffer, 0, sizeof(Buffer));
//
// Get file handle
//
pFile = _FileMgr->GetObject(pIoRequest->FileId);
if (pFile)
FileHandle = pFile->GetFileHandle();
if (pFile) {
if (!((DrFSFile *)pFile)->IsDirectory()) {
TRC_ASSERT((FileHandle != INVALID_TS_FILEHANDLE),
(TB, _T("Invalid FileHandle")));
#ifndef OS_WINCE
rc = GetFileInformationByHandle(FileHandle, &FileInformation);
#else
rc = CEGetFileInformationByHandle(FileHandle, &FileInformation);
#endif
}
else {
//
// This is a directory, we can only get attributes info
//
memset(&FileInformation, 0, sizeof(FileInformation));
FileInformation.dwFileAttributes =
GetFileAttributes(((DrFSFile*)pFile)->GetFileName());
if (FileInformation.dwFileAttributes != -1) {
rc = TRUE;
}
else {
rc = FALSE;
}
}
switch (pIoRequest->Parameters.QueryFile.FileInformationClass) {
case RdpFileBasicInformation:
if (rc)
{
PRDP_FILE_BASIC_INFORMATION pFileInfo =
(PRDP_FILE_BASIC_INFORMATION) Buffer;
pFileInfo->CreationTime.LowPart = FileInformation.ftCreationTime.dwLowDateTime;
pFileInfo->CreationTime.HighPart = FileInformation.ftCreationTime.dwHighDateTime;
pFileInfo->LastAccessTime.LowPart = FileInformation.ftLastAccessTime.dwLowDateTime;
pFileInfo->LastAccessTime.HighPart = FileInformation.ftLastAccessTime.dwHighDateTime;
pFileInfo->LastWriteTime.LowPart = FileInformation.ftLastWriteTime.dwLowDateTime;
pFileInfo->LastWriteTime.HighPart = FileInformation.ftLastWriteTime.dwHighDateTime;
pFileInfo->ChangeTime.QuadPart = 0;
pFileInfo->FileAttributes = FileInformation.dwFileAttributes;
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_BASIC_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
} else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query basic file information failed, %ld."), ulRetCode));
}
break;
case RdpFileStandardInformation:
if (rc)
{
PRDP_FILE_STANDARD_INFORMATION pFileInfo =
(PRDP_FILE_STANDARD_INFORMATION) Buffer;
// TODO: AllocationSize same as EndOfFile Size?
pFileInfo->AllocationSize.HighPart = FileInformation.nFileSizeHigh;
pFileInfo->AllocationSize.LowPart = FileInformation.nFileSizeLow;
pFileInfo->EndOfFile.HighPart = FileInformation.nFileSizeHigh;
pFileInfo->EndOfFile.LowPart = FileInformation.nFileSizeLow;
pFileInfo->NumberOfLinks = FileInformation.nNumberOfLinks;
pFileInfo->DeletePending = 0;
pFileInfo->Directory = !!(FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_STANDARD_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
} else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query standard file information failed, %ld."), ulRetCode));
}
break;
case RdpFileAttributeTagInformation:
if (rc)
{
PRDP_FILE_ATTRIBUTE_TAG_INFORMATION pFileInfo =
(PRDP_FILE_ATTRIBUTE_TAG_INFORMATION) Buffer;
// TODO: What's ReparseTag?
pFileInfo->FileAttributes = FileInformation.dwFileAttributes;
pFileInfo->ReparseTag = 0;
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_ATTRIBUTE_TAG_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
} else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Query attribute tag file information failed, %ld."), ulRetCode));
}
break;
case RdpFileInternalInformation:
if (rc)
{
PRDP_FILE_INTERNAL_INFORMATION pFileInfo =
(PRDP_FILE_INTERNAL_INFORMATION) Buffer;
// TODO: should we use this index number?
pFileInfo->IndexNumber.HighPart = FileInformation.nFileIndexHigh;
pFileInfo->IndexNumber.LowPart = FileInformation.nFileIndexLow;
ulRetCode = ERROR_SUCCESS;
BufferLength = sizeof(RDP_FILE_INTERNAL_INFORMATION);
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
} else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("CreateFile failed, %ld."), ulRetCode));
}
break;
default:
TRC_ERR((TB, _T("Unsupported QueryFile class %x"),
pIoRequest->Parameters.QueryFile.FileInformationClass));
ulRetCode = ERROR_INVALID_FUNCTION;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
break;
}
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
}
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket,
ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
pReplyPacket->IoCompletion.Parameters.QueryFile.Length = BufferLength;
if (BufferLength)
memcpy(pReplyPacket->IoCompletion.Parameters.QueryFile.Buffer,
Buffer, BufferLength);
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
DC_END_FN();
}
VOID
W32Drive::MsgIrpSetFileInfo(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
This routine handles set file information
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
DWORD result;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
PBYTE pDataBuffer;
DrFSFile* pFile;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
HRESULT hr;
DC_BEGIN_FN("W32Drive::MsgIrpSetFileInfo");
TRC_ASSERT((_tcslen(_devicePath) != 0), (TB, _T("Empty devicePath")));
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
//
// Make sure packetLen is right
//
if (packetLen < sizeof(RDPDR_IOREQUEST_PACKET) + pIoRequest->Parameters.SetFile.Length) {
// VirtualChannelClose
ProcessObject()->GetVCMgr().ChannelClose();
TRC_ASSERT(FALSE, (TB, _T("Packet Length Error")));
goto Cleanup;
}
//
// Get file handle
//
pFile = (DrFSFile *)_FileMgr->GetObject(pIoRequest->FileId);
if (pFile)
FileHandle = pFile->GetFileHandle();
pDataBuffer = (PBYTE)(pIoRequest + 1);
if (pFile) {
switch (pIoRequest->Parameters.SetFile.FileInformationClass) {
case RdpFileBasicInformation:
{
PRDP_FILE_BASIC_INFORMATION pFileInfo =
(PRDP_FILE_BASIC_INFORMATION) pDataBuffer;
FILETIME CreationTime, LastAccessTime, LastWriteTime;
FILETIME *pCreationTime, *pLastAccessTime, *pLastWriteTime;
//
// Set the file attributes if the attributes field is nonzero
//
if (pFileInfo->FileAttributes != 0) {
TRC_ASSERT((pFile->GetFileName() != NULL), (TB, _T("Empty FileName")));
if (SetFileAttributes(pFile->GetFileName(),
pFileInfo->FileAttributes)) {
ulRetCode = ERROR_SUCCESS;
} else {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("SetFileAttributes failed, %ld."), ulRetCode));
}
break;
}
//
// Set the file time
//
if (pFileInfo->CreationTime.QuadPart != 0) {
CreationTime.dwLowDateTime = pFileInfo->CreationTime.LowPart;
CreationTime.dwHighDateTime = pFileInfo->CreationTime.HighPart;
pCreationTime = &CreationTime;
}
else{
pCreationTime = NULL;
}
if (pFileInfo->LastAccessTime.QuadPart != 0) {
LastAccessTime.dwLowDateTime = pFileInfo->LastAccessTime.LowPart;
LastAccessTime.dwHighDateTime = pFileInfo->LastAccessTime.HighPart;
pLastAccessTime = &LastAccessTime;
}
else {
pLastAccessTime = NULL;
}
if (pFileInfo->LastWriteTime.QuadPart != 0) {
LastWriteTime.dwLowDateTime = pFileInfo->LastWriteTime.LowPart;
LastWriteTime.dwHighDateTime = pFileInfo->LastWriteTime.HighPart;
pLastWriteTime = &LastWriteTime;
}
else {
pLastWriteTime = NULL;
}
if (FileHandle != INVALID_HANDLE_VALUE) {
#ifndef OS_WINCE
if (SetFileTime(FileHandle, pCreationTime, pLastAccessTime, pLastWriteTime)) {
#else
if (CESetFileTime(FileHandle, pCreationTime, pLastAccessTime, pLastWriteTime)) {
#endif
ulRetCode = ERROR_SUCCESS;
} else {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("SetFileTime failed, %ld."), ulRetCode));
}
}
else {
ulRetCode = ERROR_SUCCESS;
TRC_NRM((TB, _T("Can't set filetime for directory")));
}
}
break;
case RdpFileEndOfFileInformation:
{
PRDP_FILE_END_OF_FILE_INFORMATION pFileInfo =
(PRDP_FILE_END_OF_FILE_INFORMATION) pDataBuffer;
LONG OffsetLow;
LONG OffsetHigh;
if (FileHandle != INVALID_HANDLE_VALUE) {
OffsetLow = pFileInfo->EndOfFile.LowPart;
OffsetHigh = pFileInfo->EndOfFile.HighPart;
#ifndef OS_WINCE
if (SetFilePointer(FileHandle,
#else
if (CESetFilePointer(FileHandle,
#endif
OffsetLow,
&OffsetHigh,
FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
ulRetCode = NO_ERROR;
}
else {
ulRetCode = GetLastError();
if (ulRetCode != NO_ERROR) {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("SetFilePointer failed, %ld."), ulRetCode));
}
}
if (ulRetCode == NO_ERROR) {
#ifndef OS_WINCE
if (SetEndOfFile(FileHandle)) {
#else
if (CESetEndOfFile(FileHandle)) {
#endif
ulRetCode = ERROR_SUCCESS;
} else {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("SetEndOfFile failed, %ld."), ulRetCode));
}
}
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
TRC_ERR((TB, _T("SetEndOfFile failed, %ld."), ulRetCode));
}
}
break;
case RdpFileDispositionInformation:
{
if (pFile->GetFileName()) {
if (!pFile->IsDirectory()) {
pFile->Close();
if (DeleteFile(pFile->GetFileName())) {
ulRetCode = ERROR_SUCCESS;
}
else {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("DeleteFile failed, %ld."), ulRetCode));
}
}
else {
if (RemoveDirectory(pFile->GetFileName())) {
ulRetCode = ERROR_SUCCESS;
} else {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("RemoveDirectory failed, %ld."), ulRetCode));
}
}
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
TRC_ERR((TB, _T("DeleteFile/RemoveDirectory failed, %ld."), ulRetCode));
}
}
break;
case RdpFileRenameInformation:
{
PRDP_FILE_RENAME_INFORMATION pFileInfo =
(PRDP_FILE_RENAME_INFORMATION) pDataBuffer;
TCHAR NewFileName[MAX_PATH];
if (pFile->GetFileName()) {
pFile->Close();
if (pFileInfo->RootDirectory == 0) {
//
// Copy the devicePath, the filename path
// below is relative to our device path
//
#ifndef OS_WINCE
hr = StringCchCopy(NewFileName, SIZE_TCHARS(NewFileName),
_devicePath);
TRC_ASSERT(SUCCEEDED(hr),
(TB,_T("Str copy failed for pre-checked len: 0x%x"),hr));
#else
NewFileName[0] = TEXT('\0');
#endif
}
else {
// The File name passed to us has already contained
// the root directory path
NewFileName[0] = TEXT('\0');
}
#ifdef UNICODE
UINT cchLenRemain = SIZE_TCHARS(NewFileName) - (_tcslen(NewFileName) + 1);
hr = StringCchCopy(NewFileName + _tcslen(NewFileName),
cchLenRemain,
pFileInfo->FileName);
if (FAILED(hr)) {
TRC_ERR((TB,_T("Fail to copy filename info: 0x%x"),hr));
ulRetCode = ERROR_INSUFFICIENT_BUFFER;
goto SendPacket;
}
#else
RDPConvertToAnsi(pFileInfo->FileName, NewFileName + _tcslen(NewFileName),
MAX_PATH - _tcslen(NewFileName));
#endif
if (pFileInfo->ReplaceIfExists) {
DeleteFile(NewFileName);
}
if (MoveFile(pFile->GetFileName(), NewFileName)) {
ulRetCode = ERROR_SUCCESS;
}
else {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("MoveFile failed, %ld."), ulRetCode));
}
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
TRC_ERR((TB, _T("MoveFile failed, %ld."), ulRetCode));
}
}
break;
case RdpFileAllocationInformation:
{
PRDP_FILE_ALLOCATION_INFORMATION pFileAllocationInfo =
(PRDP_FILE_ALLOCATION_INFORMATION) pDataBuffer;
ulRetCode = ERROR_SUCCESS;
TRC_NRM((TB, _T("Get RdpFileAllocationInformation")));
// If server side call CreateFile with TRUNCATE_EXISTING flag
// server will send FileAllocationInformation with
// AllocationSize.QuadPart is 0, we need to truncate the file
// Currently we don't support other QuadPart value
if (0 == pFileAllocationInfo->AllocationSize.QuadPart) {
if (FileHandle != INVALID_HANDLE_VALUE) {
if (INVALID_SET_FILE_POINTER != SetFilePointer(FileHandle, 0, 0, FILE_BEGIN))
{
if (!SetEndOfFile(FileHandle)) {
TRC_ERR((TB, _T("SetEndOfFile failed with %x"), GetLastError()));
ulRetCode = GetLastError();
}
}
else
{
TRC_ERR((TB, _T("SetFilePointer failed with %x"), GetLastError()));
ulRetCode = GetLastError();
}
}
else {
TRC_ERR((TB, _T("File handle invalid in setting FileAllocationInfo")));
ulRetCode = ERROR_FILE_NOT_FOUND;
}
}
else {
TRC_ASSERT(FALSE, (TB, _T("Get FileAllocationInformation with unsupported %d"),
pFileAllocationInfo->AllocationSize.QuadPart));
// Still return success here to avoid regression
}
}
break;
default:
TRC_ERR((TB, _T("Unsupported SetFile class %x"),
pIoRequest->Parameters.SetFile.FileInformationClass));
ulRetCode = ERROR_INVALID_FUNCTION;
break;
}
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
}
SendPacket:
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket,
ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
pReplyPacket->IoCompletion.Parameters.SetFile.Length =
pIoRequest->Parameters.SetFile.Length;
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
DC_END_FN();
}
VOID
W32Drive::MsgIrpDeviceControl(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
Handle a file system control request from the server.
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
DC_BEGIN_FN("W32Drive::MsgIrpDeviceControl");
DispatchIOCTLDirectlyToDriver(pIoRequestPacket);
DC_END_FN();
}
VOID
W32Drive::MsgIrpLockControl(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
Handle a file system lock control request from the server.
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
DWORD result;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
DrFSFile* pFile;
HANDLE FileHandle;
PRDP_LOCK_INFO LockInfo;
DC_BEGIN_FN("W32Drive::MsgIrpLockControl");
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
//
// Make sure the packetlength is right
//
if (packetLen < sizeof(RDPDR_IOREQUEST_PACKET) + sizeof(RDP_LOCK_INFO) * pIoRequest->Parameters.Locks.NumLocks) {
// Call VirtualChannelClose
ProcessObject()->GetVCMgr().ChannelClose();
TRC_ASSERT(FALSE, (TB, _T("Packet Length Error")));
goto Cleanup;
}
//
// Get file lock info
//
LockInfo = (PRDP_LOCK_INFO) (pIoRequest + 1);
//
// Get file handle
//
pFile = (DrFSFile *)_FileMgr->GetObject(pIoRequest->FileId);
if (pFile)
FileHandle = pFile->GetFileHandle();
else
FileHandle = INVALID_HANDLE_VALUE;
TRC_ASSERT((FileHandle != INVALID_HANDLE_VALUE), (TB, _T("Invalid FileHandle")));
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
if (FileHandle != INVALID_HANDLE_VALUE) {
switch (pIoRequest->Parameters.Locks.Operation) {
//
// Share lock request
//
case RDP_LOWIO_OP_SHAREDLOCK:
{
OVERLAPPED Overlapped;
Overlapped.hEvent = 0;
Overlapped.Internal = 0;
Overlapped.InternalHigh = 0;
Overlapped.Offset = LockInfo->OffsetLow;
Overlapped.OffsetHigh = LockInfo->OffsetHigh;
#ifndef OS_WINCE
if (!LockFileEx(FileHandle,
#else
if (!CELockFileEx(FileHandle,
#endif
(pIoRequest->Parameters.Locks.Flags & SL_FAIL_IMMEDIATELY) ?
LOCKFILE_FAIL_IMMEDIATELY : 0,
0,
LockInfo->LengthLow,
LockInfo->LengthHigh,
&Overlapped)) {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("Lock File failed, %ld."), ulRetCode));
}
break;
}
//
// Exclusive lock request
//
case RDP_LOWIO_OP_EXCLUSIVELOCK:
#ifndef OS_WINCE
if (!LockFile(FileHandle,
#else
if (!CELockFile(FileHandle,
#endif
LockInfo->OffsetLow,
LockInfo->OffsetHigh,
LockInfo->LengthLow,
LockInfo->LengthHigh)) {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("Lock File failed, %ld."), ulRetCode));
}
break;
//
// Unlock request
//
case RDP_LOWIO_OP_UNLOCK:
{
for (unsigned i = 0; i < pIoRequest->Parameters.Locks.NumLocks; i++) {
#ifndef OS_WINCE
if (!UnlockFile(FileHandle,
#else
if (!CEUnlockFile(FileHandle,
#endif
LockInfo->OffsetLow,
LockInfo->OffsetHigh,
LockInfo->LengthLow,
LockInfo->LengthHigh)) {
ulRetCode = GetLastError();
TRC_ERR((TB, _T("Unlock File failed, %ld."), ulRetCode));
break;
}
LockInfo++;
}
break;
}
default:
ulRetCode = ERROR_INVALID_FUNCTION;
TRC_ERR((TB, _T("Invalid lock operation %x"), pIoRequest->Parameters.Locks.Operation));
}
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
TRC_ERR((TB, _T("Lock File failed, %ld."), ulRetCode));
}
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket,
ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
DC_END_FN();
}
#ifndef OS_WINCE
BOOL SetPrivilege(
HANDLE hToken, // token handle
LPCTSTR Privilege, // Privilege to enable/disable
BOOL fEnablePrivilege // TRUE to enable. FALSE to disable
)
{
BOOL rc = TRUE;
TOKEN_PRIVILEGES tp;
LUID luid;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
HMODULE hModule = NULL;
typedef BOOL (FNLOOKUP_PRIVILEGE_VALUE)(LPCTSTR, LPCTSTR, PLUID);
FNLOOKUP_PRIVILEGE_VALUE *pfnLookupPrivilegeValue;
typedef BOOL (FNADJUST_TOKEN_PRIVILEGES)(HANDLE, BOOL, PTOKEN_PRIVILEGES,
DWORD, PTOKEN_PRIVILEGES, PDWORD);
FNADJUST_TOKEN_PRIVILEGES *pfnAdjustTokenPrivileges;
// get the handle to advapi32.dll library
hModule = LoadLibrary(TEXT("ADVAPI32.DLL"));
if (hModule != NULL) {
// get the proc address for LookupPrivilegeValue
#ifdef UNICODE
pfnLookupPrivilegeValue = (FNLOOKUP_PRIVILEGE_VALUE *)GetProcAddress(hModule, "LookupPrivilegeValueW");
#else
pfnLookupPrivilegeValue = (FNLOOKUP_PRIVILEGE_VALUE *)GetProcAddress(hModule, "LookupPrivilegeValueA");
#endif
if (pfnLookupPrivilegeValue) {
if (!pfnLookupPrivilegeValue(NULL, Privilege, &luid)) {
rc = FALSE;
goto EXIT;
}
}
else {
// Let it return true.
goto EXIT;
}
pfnAdjustTokenPrivileges = (FNADJUST_TOKEN_PRIVILEGES *)GetProcAddress(hModule, "AdjustTokenPrivileges");
if (pfnAdjustTokenPrivileges) {
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
pfnAdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious);
if (GetLastError() != ERROR_SUCCESS) {
rc = FALSE;
goto EXIT;
}
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if (fEnablePrivilege)
{
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
}
else
{
tpPrevious.Privileges[0].Attributes ^=
(SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes);
}
pfnAdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL);
if (GetLastError() != ERROR_SUCCESS) {
rc = FALSE;
goto EXIT;
}
}
}
EXIT:
if (hModule) {
FreeLibrary(hModule);
}
return rc;
}
#endif
VOID
W32Drive::MsgIrpQuerySdInfo(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
Handle a file system control request from the server.
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
DWORD result;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
DrFSFile* pFile;
HANDLE FileHandle;
BYTE* pSecurityDescriptor = NULL;
ULONG LengthNeeded = 0;
ULONG BufferLength = 0;
HANDLE hProcessToken = NULL;
HMODULE hModule = NULL;
DC_BEGIN_FN("W32Drive::MsgIrpQuerySd");
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
//
// Get file handle
//
pFile = (DrFSFile *)_FileMgr->GetObject(pIoRequest->FileId);
if (pFile)
FileHandle = pFile->GetFileHandle();
else
FileHandle = INVALID_HANDLE_VALUE;
if (pFile) {
#ifndef OS_WINCE
if (pIoRequest->Parameters.QuerySd.SecurityInformation & SACL_SECURITY_INFORMATION) {
typedef BOOL (FNOPEN_PROCESS_TOKEN)(HANDLE, DWORD, PHANDLE);
FNOPEN_PROCESS_TOKEN *pfnOpenProcessToken;
// get the handle to advapi32.dll library
hModule = LoadLibrary(TEXT("ADVAPI32.DLL"));
if (hModule != NULL) {
// get the proc address for OpenProcessToken
pfnOpenProcessToken = (FNOPEN_PROCESS_TOKEN *)GetProcAddress(hModule, "OpenProcessToken");
if (pfnOpenProcessToken) {
//
// Get the process token for this process. We'll
// need it in just a second ....
//
if (!pfnOpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hProcessToken))
{
ulRetCode = GetLastError();
TRC_ERR((TB, _T("OpenProcessToken failed, error %d."), ulRetCode));
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
goto SendPacket;
}
//
// Turn on SeSecurityPrivilege (by name that's SE_SECURITY_NAME). This allows
// us to read SACLs
//
if (!SetPrivilege(hProcessToken,
SE_SECURITY_NAME,
TRUE))
{
ulRetCode = GetLastError();
TRC_ERR((TB, _T("SetPrivilege failed. %d."), ulRetCode));
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
goto SendPacket;
}
}
}
}
GetFileSecurity(pFile->GetFileName(),
pIoRequest->Parameters.QuerySd.SecurityInformation,
NULL,
0,
&LengthNeeded);
if (LengthNeeded > 0) {
pSecurityDescriptor = new BYTE[LengthNeeded];
if (pSecurityDescriptor != NULL) {
if (GetFileSecurity(pFile->GetFileName(),
pIoRequest->Parameters.QuerySd.SecurityInformation,
pSecurityDescriptor,
LengthNeeded,
&LengthNeeded)) {
BufferLength = GetSecurityDescriptorLength(
(PSECURITY_DESCRIPTOR)pSecurityDescriptor);
ulRetCode = ERROR_SUCCESS;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) +
BufferLength - 1;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("GetFileSecurity failed, %ld."), ulRetCode));
}
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Failed to allocate memory for security descriptor")));
}
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("GetFileSecurity failed, %ld."), ulRetCode));
}
#else
BYTE bSecDescr[sizeof(SECURITY_DESCRIPTOR) + (2*sizeof(SID))];
BufferLength = sizeof(bSecDescr);
pSecurityDescriptor = bSecDescr;
SECURITY_DESCRIPTOR sd;
if (InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) &&
SetSecurityDescriptorGroup(&sd, NULL, FALSE) &&
SetSecurityDescriptorOwner(&sd, NULL, FALSE) &&
SetSecurityDescriptorDacl(&sd, (pIoRequest->Parameters.QuerySd.SecurityInformation & DACL_SECURITY_INFORMATION), NULL, FALSE) &&
SetSecurityDescriptorSacl(&sd, (pIoRequest->Parameters.QuerySd.SecurityInformation & SACL_SECURITY_INFORMATION), NULL, FALSE) &&
MakeSelfRelativeSD(&sd, pSecurityDescriptor, &BufferLength)) {
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) + BufferLength - 1;
ulRetCode = ERROR_SUCCESS;
}
else {
ulRetCode = ERROR_INVALID_PARAMETER;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Failed to construct a security descriptor, %ld."), ulRetCode));
}
#endif
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
}
SendPacket:
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket,
ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
pReplyPacket->IoCompletion.Parameters.QuerySd.Length = BufferLength;
if (BufferLength)
memcpy(pReplyPacket->IoCompletion.Parameters.QuerySd.Buffer,
pSecurityDescriptor, BufferLength);
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
#ifndef OS_WINCE
//
// Clean up the security descriptor buffer
//
if (pSecurityDescriptor != NULL) {
delete pSecurityDescriptor;
}
#endif
if (hModule) {
FreeLibrary(hModule);
}
if (hProcessToken) {
CloseHandle(hProcessToken);
}
DC_END_FN();
}
VOID
W32Drive::MsgIrpSetSdInfo(
IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket,
IN UINT32 packetLen
)
/*++
Routine Description:
Handle a file system set security request from the server.
Arguments:
pIoRequestPacket - Server IO request packet.
packetLen - Length of the packet
Return Value:
NA
--*/
{
ULONG ulRetCode = ERROR_SUCCESS;
DWORD result;
PRDPDR_DEVICE_IOREQUEST pIoRequest;
PRDPDR_IOCOMPLETION_PACKET pReplyPacket;
ULONG ulReplyPacketSize = 0;
DrFSFile* pFile;
HANDLE FileHandle;
#ifndef OS_WINCE
PSECURITY_DESCRIPTOR SecurityDescriptor;
#endif
DC_BEGIN_FN("W32Drive::MsgIrpQuerySd");
//
// Get IO request pointer.
//
pIoRequest = &pIoRequestPacket->IoRequest;
//
// Make sure the packetLen is right
//
if (packetLen < sizeof(RDPDR_IOREQUEST_PACKET) + pIoRequest->Parameters.SetSd.Length) {
// VirtualChannelClose
ProcessObject()->GetVCMgr().ChannelClose();
TRC_ASSERT(FALSE, (TB, _T("Packet Length Error")));
goto Cleanup;
}
//
// Get file handle
//
pFile = (DrFSFile *)_FileMgr->GetObject(pIoRequest->FileId);
if (pFile)
FileHandle = pFile->GetFileHandle();
else
FileHandle = INVALID_HANDLE_VALUE;
#ifndef OS_WINCE
//
// Set the file security
//
SecurityDescriptor = (PSECURITY_DESCRIPTOR)(pIoRequest + 1);
#endif
if (pFile) {
#ifndef OS_WINCE
if (SetFileSecurity(pFile->GetFileName(),
pIoRequest->Parameters.SetSd.SecurityInformation,
SecurityDescriptor)) {
ulRetCode = ERROR_SUCCESS;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET) ;
}
else {
ulRetCode = GetLastError();
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
TRC_ERR((TB, _T("Lock File failed, %ld."), ulRetCode));
}
#else
ulRetCode = ERROR_SUCCESS;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
#endif
}
else {
ulRetCode = ERROR_FILE_NOT_FOUND;
ulReplyPacketSize = sizeof(RDPDR_IOCOMPLETION_PACKET);
}
//
// Allocate reply buffer.
//
pReplyPacket = DrUTL_AllocIOCompletePacket(pIoRequestPacket,
ulReplyPacketSize) ;
if (pReplyPacket) {
//
// Send the result to the server.
//
result = TranslateWinError(ulRetCode);
pReplyPacket->IoCompletion.IoStatus = result;
ProcessObject()->GetVCMgr().ChannelWrite(
(PVOID)pReplyPacket, (UINT)ulReplyPacketSize);
}
else {
TRC_ERR((TB, _T("Failed to alloc %ld bytes."),ulReplyPacketSize));
}
Cleanup:
//
// Clean up the request packet.
//
delete pIoRequestPacket;
DC_END_FN();
}