[channels,drive] implement replaceable backends
Allow the drive channel to implement a backend just like the printer channel does. This way the existing file based backend can easily be replaced by something else (database, ...)
This commit is contained in:
parent
a17967f5eb
commit
52eefa838c
@ -27,3 +27,4 @@ set(${MODULE_PREFIX}_LIBS
|
||||
winpr freerdp
|
||||
)
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "file" "")
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,8 +27,8 @@
|
||||
#define FREERDP_CHANNEL_DRIVE_CLIENT_FILE_H
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/file.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/client/drive.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("drive.client")
|
||||
|
||||
@ -37,16 +37,17 @@ typedef struct S_DRIVE_FILE DRIVE_FILE;
|
||||
BOOL drive_file_free(DRIVE_FILE* file);
|
||||
|
||||
WINPR_ATTR_MALLOC(drive_file_free, 1)
|
||||
DRIVE_FILE* drive_file_new(const char* backend, const WCHAR* base_path, const WCHAR* path,
|
||||
UINT32 PathWCharLength, UINT32 id, UINT32 DesiredAccess,
|
||||
UINT32 CreateDisposition, UINT32 CreateOptions, UINT32 FileAttributes,
|
||||
UINT32 SharedAccess);
|
||||
DRIVE_FILE* drive_file_new(rdpContext* context, const rdpDriveDriver* backend,
|
||||
const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
|
||||
UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
|
||||
UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess);
|
||||
|
||||
const uintptr_t drive_file_get_id(DRIVE_FILE* drive);
|
||||
uintptr_t drive_file_get_id(DRIVE_FILE* drive);
|
||||
BOOL drive_file_is_dir_not_empty(DRIVE_FILE* drive);
|
||||
|
||||
char* drive_file_resolve_path(const char* backend, const char* what);
|
||||
char* drive_file_resolve_name(const char* backend, const char* path, const char* suggested);
|
||||
char* drive_file_resolve_path(const rdpDriveDriver* backend, const char* what);
|
||||
char* drive_file_resolve_name(const rdpDriveDriver* backend, const char* path,
|
||||
const char* suggested);
|
||||
|
||||
BOOL drive_file_open(DRIVE_FILE* file);
|
||||
BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset);
|
||||
|
@ -30,8 +30,6 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
@ -43,6 +41,7 @@
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/client/drive.h>
|
||||
|
||||
#include "drive_file.h"
|
||||
|
||||
@ -61,7 +60,7 @@ typedef struct
|
||||
DEVMAN* devman;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
char* backend;
|
||||
const rdpDriveDriver* backend;
|
||||
} DRIVE_DEVICE;
|
||||
|
||||
static DWORD drive_map_windows_err(DWORD fs_errno)
|
||||
@ -181,9 +180,9 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
||||
|
||||
path = Stream_ConstPointer(irp->input);
|
||||
FileId = irp->devman->id_sequence++;
|
||||
file = drive_file_new(drive->backend, drive->path, path, PathLength / sizeof(WCHAR), FileId,
|
||||
DesiredAccess, CreateDisposition, CreateOptions, FileAttributes,
|
||||
SharedAccess);
|
||||
file = drive_file_new(drive->rdpcontext, drive->backend, drive->path, path,
|
||||
PathLength / sizeof(WCHAR), FileId, DesiredAccess, CreateDisposition,
|
||||
CreateOptions, FileAttributes, SharedAccess);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@ -193,9 +192,9 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
|
||||
}
|
||||
else
|
||||
{
|
||||
void* key = (void*)drive_file_get_id(file);
|
||||
const uintptr_t key = drive_file_get_id(file);
|
||||
|
||||
if (!ListDictionary_Add(drive->files, key, file))
|
||||
if (!ListDictionary_Add(drive->files, (const void*)key, file))
|
||||
{
|
||||
WLog_ERR(TAG, "ListDictionary_Add failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
@ -268,19 +267,15 @@ static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp)
|
||||
*/
|
||||
static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
|
||||
{
|
||||
DRIVE_FILE* file = NULL;
|
||||
UINT32 Length = 0;
|
||||
UINT64 Offset = 0;
|
||||
|
||||
if (!drive || !irp || !irp->output || !irp->Complete)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(irp->input, Length);
|
||||
Stream_Read_UINT64(irp->input, Offset);
|
||||
file = drive_get_file_by_id(drive, irp->FileId);
|
||||
UINT32 Length = Stream_Get_UINT32(irp->input);
|
||||
const UINT64 Offset = Stream_Get_UINT64(irp->input);
|
||||
DRIVE_FILE* file = drive_get_file_by_id(drive, irp->FileId);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@ -607,7 +602,6 @@ static UINT drive_process_irp_silent_ignore(DRIVE_DEVICE* drive, IRP* irp)
|
||||
*/
|
||||
static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
||||
{
|
||||
const WCHAR* path = NULL;
|
||||
DRIVE_FILE* file = NULL;
|
||||
BYTE InitialQuery = 0;
|
||||
UINT32 PathLength = 0;
|
||||
@ -623,7 +617,7 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
|
||||
Stream_Read_UINT8(irp->input, InitialQuery);
|
||||
Stream_Read_UINT32(irp->input, PathLength);
|
||||
Stream_Seek(irp->input, 23); /* Padding */
|
||||
path = Stream_ConstPointer(irp->input);
|
||||
const WCHAR* path = Stream_PointerAs(irp->input, const WCHAR);
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
@ -835,7 +829,6 @@ static UINT drive_free_int(DRIVE_DEVICE* drive)
|
||||
MessageQueue_Free(drive->IrpQueue);
|
||||
Stream_Free(drive->device.data, TRUE);
|
||||
free(drive->path);
|
||||
free(drive->backend);
|
||||
free(drive);
|
||||
return error;
|
||||
}
|
||||
@ -886,23 +879,41 @@ static void drive_message_free(void* obj)
|
||||
irp->Discard(irp);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(drive_free_int, 1)
|
||||
static DRIVE_DEVICE* drive_new(rdpContext* rdpcontext, const char* backend, const char* path,
|
||||
const char* name, BOOL automount)
|
||||
static const rdpDriveDriver* drive_load_backend(const char* backend)
|
||||
{
|
||||
typedef UINT(VCAPITYPE * backend_load_t)(const rdpDriveDriver**);
|
||||
|
||||
static backend_load_t func = NULL;
|
||||
|
||||
if (!func)
|
||||
{
|
||||
PVIRTUALCHANNELENTRY entry = freerdp_load_channel_addin_entry("drive", backend, NULL, 0);
|
||||
func = WINPR_FUNC_PTR_CAST(entry, backend_load_t);
|
||||
if (!func)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const rdpDriveDriver* drive = NULL;
|
||||
const UINT rc = func(&drive);
|
||||
if (rc != CHANNEL_RC_OK)
|
||||
return NULL;
|
||||
|
||||
return drive;
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(drive_free_int, 1)
|
||||
static DRIVE_DEVICE* drive_new(rdpContext* rdpcontext, const rdpDriveDriver* backend,
|
||||
const char* path, const char* name, BOOL automount)
|
||||
{
|
||||
WINPR_ASSERT(backend);
|
||||
|
||||
size_t pathLength = strnlen(path, MAX_PATH);
|
||||
DRIVE_DEVICE* drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE));
|
||||
|
||||
if (!drive)
|
||||
return NULL;
|
||||
|
||||
if (backend)
|
||||
{
|
||||
drive->backend = _strdup(backend);
|
||||
if (!drive->backend)
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
drive->backend = backend;
|
||||
drive->device.type = RDPDR_DTYP_FILESYSTEM;
|
||||
drive->device.IRPRequest = drive_irp_request;
|
||||
drive->device.Free = drive_free;
|
||||
@ -990,8 +1001,8 @@ out_error:
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
const char* backend, const char* rawname, const char* rawpath,
|
||||
BOOL automount)
|
||||
const char* backendstr, const char* rawname,
|
||||
const char* rawpath, BOOL automount)
|
||||
{
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
DRIVE_DEVICE* drive = NULL;
|
||||
@ -1003,6 +1014,10 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const rdpDriveDriver* backend = drive_load_backend(backendstr);
|
||||
if (!backend)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
char* path = drive_file_resolve_path(backend, rawpath);
|
||||
if (!path)
|
||||
{
|
||||
|
30
channels/drive/client/file/CMakeLists.txt
Normal file
30
channels/drive/client/file/CMakeLists.txt
Normal file
@ -0,0 +1,30 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2024 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2024 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
define_channel_client_subsystem("drive" "file" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
drive_backend_file.c
|
||||
)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS
|
||||
winpr
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
579
channels/drive/client/file/drive_backend_file.c
Normal file
579
channels/drive/client/file/drive_backend_file.c
Normal file
@ -0,0 +1,579 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* File System Virtual Channel
|
||||
*
|
||||
* Copyright 2024 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2024 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <winpr/path.h>
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#include <freerdp/client/drive.h>
|
||||
#include <winpr/file.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("drive.client.backend.file")
|
||||
|
||||
struct rdp_drive_context
|
||||
{
|
||||
UINT32 dwDesiredAccess;
|
||||
UINT32 dwShareMode;
|
||||
UINT32 dwCreationDisposition;
|
||||
UINT32 dwFlagsAndAttributes;
|
||||
HANDLE file_handle;
|
||||
|
||||
HANDLE find_handle;
|
||||
WIN32_FIND_DATAW find_data;
|
||||
BY_HANDLE_FILE_INFORMATION file_by_handle;
|
||||
wLog* log;
|
||||
WCHAR* base_path;
|
||||
WCHAR* filename;
|
||||
size_t filename_len;
|
||||
WCHAR* fullpath;
|
||||
bool isDirectory;
|
||||
rdpContext* context;
|
||||
};
|
||||
|
||||
static BOOL drive_file_fix_path(WCHAR* path, size_t length)
|
||||
{
|
||||
if ((length == 0) || (length > UINT32_MAX))
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(path);
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
if (path[i] == L'\\')
|
||||
path[i] = L'/';
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
|
||||
return FALSE;
|
||||
|
||||
#else
|
||||
|
||||
if ((length == 1) && (path[0] == L'/'))
|
||||
return FALSE;
|
||||
|
||||
#endif
|
||||
|
||||
if ((length > 0) && (path[length - 1] == L'/'))
|
||||
path[length - 1] = L'\0';
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(free, 1)
|
||||
static char* drive_file_resolve_path(const char* what)
|
||||
{
|
||||
if (!what)
|
||||
return NULL;
|
||||
|
||||
/* Special case: path[0] == '*' -> export all drives
|
||||
* Special case: path[0] == '%' -> user home dir
|
||||
*/
|
||||
if (strcmp(what, "%") == 0)
|
||||
return GetKnownPath(KNOWN_PATH_HOME);
|
||||
#ifndef WIN32
|
||||
if (strcmp(what, "*") == 0)
|
||||
return _strdup("/");
|
||||
#else
|
||||
if (strcmp(what, "*") == 0)
|
||||
{
|
||||
const size_t len = GetLogicalDriveStringsA(0, NULL);
|
||||
char* devlist = calloc(len + 1, sizeof(char*));
|
||||
if (!devlist)
|
||||
return NULL;
|
||||
|
||||
/* Enumerate all devices: */
|
||||
DWORD rc = GetLogicalDriveStringsA(len, devlist);
|
||||
if (rc != len)
|
||||
{
|
||||
free(devlist);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* path = NULL;
|
||||
for (size_t i = 0;; i++)
|
||||
{
|
||||
char* dev = &devlist[i * sizeof(char*)];
|
||||
if (!*dev)
|
||||
break;
|
||||
if (*dev <= 'B')
|
||||
continue;
|
||||
|
||||
path = _strdup(dev);
|
||||
break;
|
||||
}
|
||||
free(devlist);
|
||||
return path;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return _strdup(what);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(free, 1)
|
||||
static char* drive_file_resolve_name(const char* path, const char* suggested)
|
||||
{
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
if (strcmp(path, "*") == 0)
|
||||
{
|
||||
if (suggested)
|
||||
{
|
||||
char* str = NULL;
|
||||
size_t len = 0;
|
||||
(void)winpr_asprintf(&str, &len, "[%s] %s", suggested, path);
|
||||
return str;
|
||||
}
|
||||
return _strdup(path);
|
||||
}
|
||||
if (strcmp(path, "%") == 0)
|
||||
return GetKnownPath(KNOWN_PATH_HOME);
|
||||
if (suggested)
|
||||
return _strdup(suggested);
|
||||
|
||||
return _strdup(path);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(free, 1)
|
||||
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
|
||||
size_t PathWCharLength)
|
||||
{
|
||||
BOOL ok = FALSE;
|
||||
WCHAR* fullpath = NULL;
|
||||
|
||||
if (!base_path || (!path && (PathWCharLength > 0)))
|
||||
goto fail;
|
||||
|
||||
const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
|
||||
const size_t length = base_path_length + PathWCharLength + 1;
|
||||
fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
|
||||
|
||||
if (!fullpath)
|
||||
goto fail;
|
||||
|
||||
CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR));
|
||||
if (path)
|
||||
CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR));
|
||||
|
||||
if (!drive_file_fix_path(fullpath, length))
|
||||
goto fail;
|
||||
|
||||
WCHAR* normalized = winpr_NormalizePathW(fullpath);
|
||||
free(fullpath);
|
||||
fullpath = normalized;
|
||||
|
||||
ok = winpr_PathIsRootOfW(base_path, fullpath);
|
||||
fail:
|
||||
if (!ok)
|
||||
{
|
||||
free(fullpath);
|
||||
fullpath = NULL;
|
||||
}
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
static void drive_free(rdpDriveContext* file)
|
||||
{
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
if (file->file_handle != INVALID_HANDLE_VALUE)
|
||||
(void)CloseHandle(file->file_handle);
|
||||
|
||||
if (file->find_handle != INVALID_HANDLE_VALUE)
|
||||
FindClose(file->find_handle);
|
||||
|
||||
free(file->base_path);
|
||||
free(file->filename);
|
||||
free(file->fullpath);
|
||||
free(file);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(drive_free, 1)
|
||||
static rdpDriveContext* drive_new(rdpContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
rdpDriveContext* file = calloc(1, sizeof(rdpDriveContext));
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
file->log = WLog_Get(TAG);
|
||||
file->context = context;
|
||||
file->file_handle = INVALID_HANDLE_VALUE;
|
||||
file->find_handle = INVALID_HANDLE_VALUE;
|
||||
return file;
|
||||
}
|
||||
|
||||
static bool drive_is_file(rdpDriveContext* context)
|
||||
{
|
||||
if (!context)
|
||||
return false;
|
||||
if (context->isDirectory)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool drive_exists(rdpDriveContext* context)
|
||||
{
|
||||
if (!context)
|
||||
return false;
|
||||
return PathFileExistsW(context->fullpath);
|
||||
}
|
||||
|
||||
static bool drive_empty(rdpDriveContext* context)
|
||||
{
|
||||
if (!context || !context->isDirectory)
|
||||
return false;
|
||||
return PathIsDirectoryEmptyW(context->fullpath);
|
||||
}
|
||||
|
||||
static SSIZE_T drive_seek(rdpDriveContext* context, SSIZE_T offset, int whence)
|
||||
{
|
||||
if (!drive_exists(context) || !drive_is_file(context))
|
||||
return -1;
|
||||
|
||||
LARGE_INTEGER li = { .QuadPart = offset };
|
||||
return SetFilePointerEx(context->file_handle, li, NULL, (DWORD)whence);
|
||||
}
|
||||
|
||||
static SSIZE_T drive_read(rdpDriveContext* context, void* buf, size_t nbyte)
|
||||
{
|
||||
if (!drive_exists(context) || !drive_is_file(context))
|
||||
return -1;
|
||||
|
||||
if (nbyte > UINT32_MAX)
|
||||
return -1;
|
||||
|
||||
DWORD read = 0;
|
||||
if (!ReadFile(context->file_handle, buf, (UINT32)nbyte, &read, NULL))
|
||||
return -1;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
static SSIZE_T drive_write(rdpDriveContext* context, const void* buf, size_t nbyte)
|
||||
{
|
||||
SSIZE_T rc = 0;
|
||||
if (!drive_exists(context) || !drive_is_file(context))
|
||||
return -1;
|
||||
|
||||
do
|
||||
{
|
||||
const DWORD towrite = (nbyte > UINT32_MAX) ? UINT32_MAX : (DWORD)nbyte;
|
||||
DWORD written = 0;
|
||||
|
||||
if (!WriteFile(context->file_handle, buf, towrite, &written, NULL))
|
||||
return -1;
|
||||
if (written > towrite)
|
||||
return -1;
|
||||
nbyte -= written;
|
||||
rc += written;
|
||||
} while (nbyte > 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool drive_remove(rdpDriveContext* context)
|
||||
{
|
||||
if (!context)
|
||||
return false;
|
||||
|
||||
if (!PathFileExistsW(context->fullpath))
|
||||
return false;
|
||||
|
||||
if (context->isDirectory)
|
||||
return winpr_RemoveDirectory_RecursiveW(context->fullpath);
|
||||
|
||||
return DeleteFileW(context->fullpath);
|
||||
}
|
||||
|
||||
static UINT32 drive_getFileAttributes(rdpDriveContext* context)
|
||||
{
|
||||
if (!drive_exists(context))
|
||||
return 0;
|
||||
return GetFileAttributesW(context->fullpath);
|
||||
}
|
||||
|
||||
static bool drive_setFileAttributesAndTimes(rdpDriveContext* context, UINT64 CreationTime,
|
||||
UINT64 LastAccessTime, UINT64 LastWriteTime,
|
||||
UINT64 ChangeTime, DWORD FileAttributes)
|
||||
{
|
||||
if (!drive_exists(context))
|
||||
return false;
|
||||
|
||||
if (!SetFileAttributesW(context->fullpath, FileAttributes))
|
||||
return false;
|
||||
|
||||
union
|
||||
{
|
||||
UINT64 u64;
|
||||
FILETIME ft;
|
||||
} c, a, w;
|
||||
c.u64 = CreationTime;
|
||||
a.u64 = LastAccessTime;
|
||||
w.u64 = LastWriteTime;
|
||||
|
||||
FILETIME* pc = (CreationTime > 0) ? &c.ft : NULL;
|
||||
FILETIME* pa = (LastAccessTime > 0) ? &a.ft : NULL;
|
||||
FILETIME* pw = (LastWriteTime > 0) ? &w.ft : NULL;
|
||||
|
||||
WINPR_UNUSED(ChangeTime);
|
||||
return SetFileTime(context->file_handle, pc, pa, pw);
|
||||
}
|
||||
|
||||
static bool drive_setSize(rdpDriveContext* context, INT64 size)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!drive_exists(context) || !drive_is_file(context))
|
||||
{
|
||||
WLog_Print(context->log, WLOG_ERROR, "Unable to truncate %s to %" PRId64 " (%" PRIu32 ")",
|
||||
context->fullpath, size, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
const LARGE_INTEGER liSize = { .QuadPart = size };
|
||||
|
||||
if (!SetFilePointerEx(context->file_handle, liSize, NULL, FILE_BEGIN))
|
||||
{
|
||||
WLog_Print(context->log, WLOG_ERROR, "Unable to truncate %s to %" PRId64 " (%" PRIu32 ")",
|
||||
context->fullpath, size, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SetEndOfFile(context->file_handle) == 0)
|
||||
{
|
||||
WLog_Print(context->log, WLOG_ERROR, "Unable to truncate %s to %" PRId64 " (%" PRIu32 ")",
|
||||
context->fullpath, size, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const WIN32_FIND_DATAW* drive_first(rdpDriveContext* context, const WCHAR* query,
|
||||
size_t numCharacters)
|
||||
{
|
||||
if (!context || !query)
|
||||
return NULL;
|
||||
|
||||
const size_t len = _wcsnlen(query, numCharacters + 1);
|
||||
if (len > numCharacters)
|
||||
return NULL;
|
||||
|
||||
if (context->find_handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(context->find_handle);
|
||||
WCHAR* ent_path = drive_file_combine_fullpath(context->base_path, query, numCharacters);
|
||||
if (!ent_path)
|
||||
return NULL;
|
||||
|
||||
context->find_handle = FindFirstFileW(ent_path, &context->find_data);
|
||||
free(ent_path);
|
||||
if (context->find_handle == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
return &context->find_data;
|
||||
}
|
||||
|
||||
static const WIN32_FIND_DATAW* drive_next(rdpDriveContext* context)
|
||||
{
|
||||
if (!context || (context->find_handle == INVALID_HANDLE_VALUE))
|
||||
return NULL;
|
||||
if (!FindNextFileW(context->find_handle, &context->find_data))
|
||||
return NULL;
|
||||
return &context->find_data;
|
||||
}
|
||||
|
||||
static bool drive_check_existing(rdpDriveContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
if (!context->fullpath)
|
||||
return false;
|
||||
|
||||
if (PathFileExistsW(context->fullpath))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool drive_createDirectory(rdpDriveContext* context)
|
||||
{
|
||||
if (!drive_check_existing(context))
|
||||
return false;
|
||||
|
||||
if (!CreateDirectoryW(context->fullpath, NULL))
|
||||
return false;
|
||||
context->isDirectory = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool drive_createFile(rdpDriveContext* context, UINT32 dwDesiredAccess, UINT32 dwShareMode,
|
||||
UINT32 dwCreationDisposition, UINT32 dwFlagsAndAttributes)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
if (!context->fullpath || context->isDirectory)
|
||||
return false;
|
||||
|
||||
context->file_handle = CreateFileW(context->fullpath, dwDesiredAccess, dwShareMode, NULL,
|
||||
dwCreationDisposition, dwFlagsAndAttributes, NULL);
|
||||
return context->file_handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
static bool drive_update_path(rdpDriveContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
free(context->fullpath);
|
||||
context->fullpath = NULL;
|
||||
if (!context->base_path || !context->filename)
|
||||
return false;
|
||||
|
||||
context->fullpath =
|
||||
drive_file_combine_fullpath(context->base_path, context->filename, context->filename_len);
|
||||
return context->fullpath != NULL;
|
||||
}
|
||||
|
||||
static bool drive_setPath(rdpDriveContext* context, const WCHAR* base_path, const WCHAR* filename,
|
||||
size_t nbFilenameChar)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WCHAR* cbase_path = NULL;
|
||||
WCHAR* cfilename = NULL;
|
||||
if (base_path)
|
||||
cbase_path = _wcsdup(base_path);
|
||||
if (filename)
|
||||
cfilename = wcsndup(filename, nbFilenameChar);
|
||||
|
||||
free(context->base_path);
|
||||
free(context->filename);
|
||||
context->base_path = cbase_path;
|
||||
context->filename = cfilename;
|
||||
context->filename_len = nbFilenameChar;
|
||||
|
||||
return drive_update_path(context);
|
||||
}
|
||||
|
||||
static const BY_HANDLE_FILE_INFORMATION* drive_getFileAttributeData(rdpDriveContext* context)
|
||||
{
|
||||
if (!drive_exists(context))
|
||||
return NULL;
|
||||
HANDLE hFile = CreateFileW(context->fullpath, 0, FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
const BOOL rc = GetFileInformationByHandle(hFile, &context->file_by_handle);
|
||||
CloseHandle(hFile);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA data = { 0 };
|
||||
if (!GetFileAttributesExW(context->fullpath, GetFileExInfoStandard, &data))
|
||||
return NULL;
|
||||
const BY_HANDLE_FILE_INFORMATION empty = { 0 };
|
||||
context->file_by_handle = empty;
|
||||
context->file_by_handle.dwFileAttributes = data.dwFileAttributes;
|
||||
context->file_by_handle.ftCreationTime = data.ftCreationTime;
|
||||
context->file_by_handle.ftLastAccessTime = data.ftLastAccessTime;
|
||||
context->file_by_handle.ftLastWriteTime = data.ftLastWriteTime;
|
||||
context->file_by_handle.nFileSizeHigh = data.nFileSizeHigh;
|
||||
context->file_by_handle.nFileSizeLow = data.nFileSizeLow;
|
||||
|
||||
out:
|
||||
return &context->file_by_handle;
|
||||
}
|
||||
|
||||
static bool drive_move(rdpDriveContext* context, const WCHAR* newName, size_t numCharacters,
|
||||
bool replaceIfExists)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
if (!newName || (numCharacters == 0))
|
||||
return false;
|
||||
|
||||
const size_t len = _wcsnlen(newName, numCharacters + 1);
|
||||
if (len > numCharacters)
|
||||
return false;
|
||||
|
||||
bool reopen = false;
|
||||
if (!context->isDirectory)
|
||||
{
|
||||
if (context->file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(context->file_handle);
|
||||
context->file_handle = INVALID_HANDLE_VALUE;
|
||||
reopen = true;
|
||||
}
|
||||
}
|
||||
|
||||
WCHAR* newpath = drive_file_combine_fullpath(context->base_path, newName, numCharacters);
|
||||
if (!newpath)
|
||||
return false;
|
||||
|
||||
const DWORD flags = replaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0;
|
||||
const BOOL res = MoveFileExW(context->fullpath, newpath, flags);
|
||||
free(newpath);
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
if (!drive_setPath(context, context->base_path, newName, numCharacters))
|
||||
return false;
|
||||
if (reopen)
|
||||
return drive_createFile(context, context->dwDesiredAccess, context->dwShareMode,
|
||||
context->dwCreationDisposition, context->dwFlagsAndAttributes);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const rdpDriveDriver s_driver = { .resolve_path = drive_file_resolve_path,
|
||||
.resolve_name = drive_file_resolve_name,
|
||||
.createDirectory = drive_createDirectory,
|
||||
.createFile = drive_createFile,
|
||||
.new = drive_new,
|
||||
.free = drive_free,
|
||||
.seek = drive_seek,
|
||||
.read = drive_read,
|
||||
.write = drive_write,
|
||||
.remove = drive_remove,
|
||||
.move = drive_move,
|
||||
.exists = drive_exists,
|
||||
.empty = drive_empty,
|
||||
.setSize = drive_setSize,
|
||||
.getFileAttributes = drive_getFileAttributes,
|
||||
.setFileAttributesAndTimes =
|
||||
drive_setFileAttributesAndTimes,
|
||||
.setPath = drive_setPath,
|
||||
.first = drive_first,
|
||||
.next = drive_next,
|
||||
.getFileAttributeData = drive_getFileAttributeData };
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE file_freerdp_drive_client_subsystem_entry(void* arg))
|
||||
{
|
||||
const rdpDriveDriver** ppDriver = (const rdpDriveDriver**)arg;
|
||||
if (!ppDriver)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
*ppDriver = &s_driver;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
154
include/freerdp/client/drive.h
Normal file
154
include/freerdp/client/drive.h
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Drive Virtual Channel
|
||||
*
|
||||
* Copyright 2024 Armin Novak <armin.novak@gmail.com>
|
||||
* Copyright 2024 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** @struct rdpDriveContext
|
||||
* @briefopaque type holding a drive context
|
||||
* @since version 3.10.0
|
||||
*/
|
||||
typedef struct rdp_drive_context rdpDriveContext;
|
||||
|
||||
/** @struct rdpDriveDriver
|
||||
* @brief This structure contains all function pointers required to implement a drive channel
|
||||
* backend.
|
||||
* @since version 3.10.0
|
||||
*
|
||||
* @var rdpDriveDriver::resolve_path
|
||||
* Member 'resolve_path' takes a path with wildcards '%' or '*' as input and resolves these to
|
||||
* an absolute local path. Returns this resolved path as allocated string. Returns \b NULL in
|
||||
* case of failure.
|
||||
*
|
||||
* @var rdpDriveDriver::resolve_name
|
||||
* Member 'resolve_name' takes the path to redirect and an optional name suggestion and converts
|
||||
* these to a usable name for the drive redirection. Preferred is the suggested name, but the
|
||||
* supplied path is used as fallback. In any case, forbidden symbols are replaced before a newly
|
||||
* allocated string is returned. Returns \b NULL in case of failure.
|
||||
*
|
||||
* @var rdpDriveDriver::new
|
||||
* Member 'new' allocates a new \b rdpDriveContext for a given \b rdpContext. Returns \b NULL in
|
||||
* case of failure.
|
||||
*
|
||||
* @var rdpDriveDriver::free
|
||||
* Member 'free' cleans up a previously allocated \b rdpDriveContext. Argument may be \b NULL
|
||||
*
|
||||
* @var rdpDriveDriver::setPath
|
||||
* Member 'setPath' initializes a \b rdpDriveContext. \b base_path argument is the (local)
|
||||
* absolute path to prefix, \b filename the path this context is for.
|
||||
*
|
||||
* @var rdpDriveDriver::createDirectory
|
||||
* Create a directory for a given context. Fails if the directory can not be created or the
|
||||
* context is not holding a directory
|
||||
*
|
||||
* @var rdpDriveDriver::createFile
|
||||
* Create or open a file for a given context. Fails if the context holds a directory or the file
|
||||
* creation failed.
|
||||
*
|
||||
* @var rdpDriveDriver::seek
|
||||
* Position the file pointer in an opened file. Fails if the file is not open, the seek can not
|
||||
* be done or the context is a directory.
|
||||
*
|
||||
* @var rdpDriveDriver::read
|
||||
* Read data from an opened file. Fails if the file can not be read, is not open or the context
|
||||
* holds a directory.
|
||||
*
|
||||
* @var rdpDriveDriver::write
|
||||
* Write data to an opened file. Fails if the file can not be written to, the file is not open
|
||||
* or the context holds a directory.
|
||||
*
|
||||
* @var rdpDriveDriver::remove
|
||||
* Delete a file or directory identified by context (recursively)
|
||||
*
|
||||
* @var rdpDriveDriver::move
|
||||
* Move a file or directory from the name the context holds to the new name supplied by \b
|
||||
* newName. Optionally overwrite existing entries.
|
||||
*
|
||||
* @var rdpDriveDriver::exists
|
||||
* Check a given context (file or directory) already exists
|
||||
*
|
||||
* @var rdpDriveDriver::empty
|
||||
* Check if a given context is a directory and if it is empty.
|
||||
*
|
||||
* @var rdpDriveDriver::setSize
|
||||
* Set the file size for a given context.
|
||||
*
|
||||
* @var rdpDriveDriver::getFileAttributes
|
||||
* Return the file attributes of a given context
|
||||
*
|
||||
* @var rdpDriveDriver::setFileAttributesAndTimes
|
||||
* Update file attributes and times for a given context
|
||||
*
|
||||
* @var rdpDriveDriver::first
|
||||
* Reset a directory iterator and return the first entry found or \b NULL in case of failure.
|
||||
*
|
||||
* @var rdpDriveDriver::next
|
||||
* Get the next directory iterator or \b NULL in case of no more elements.
|
||||
*
|
||||
* @var rdpDriveDriver::getFileAttributeData
|
||||
* Get file attribute data for a given context. Returns the attribute data or \b NULL in case of
|
||||
* failure.
|
||||
*/
|
||||
typedef struct rdp_drive_driver
|
||||
{
|
||||
char* (*resolve_path)(const char* what);
|
||||
|
||||
char* (*resolve_name)(const char* path, const char* suggested);
|
||||
|
||||
rdpDriveContext* (*new)(rdpContext* context);
|
||||
void (*free)(rdpDriveContext*);
|
||||
|
||||
bool (*setPath)(rdpDriveContext* context, const WCHAR* base_path, const WCHAR* filename,
|
||||
size_t nbFileNameChar);
|
||||
bool (*createDirectory)(rdpDriveContext* context);
|
||||
bool (*createFile)(rdpDriveContext* context, UINT32 dwDesiredAccess, UINT32 dwShareMode,
|
||||
UINT32 dwCreationDisposition, UINT32 dwFlagsAndAttributes);
|
||||
|
||||
SSIZE_T (*seek)(rdpDriveContext* context, SSIZE_T offset, int whence);
|
||||
SSIZE_T (*read)(rdpDriveContext* context, void* buf, size_t nbyte);
|
||||
SSIZE_T (*write)(rdpDriveContext* context, const void* buf, size_t nbyte);
|
||||
bool (*remove)(rdpDriveContext* context);
|
||||
bool (*move)(rdpDriveContext* context, const WCHAR* newName, size_t numCharacters,
|
||||
bool replaceIfExists);
|
||||
bool (*exists)(rdpDriveContext* context);
|
||||
bool (*empty)(rdpDriveContext* context);
|
||||
bool (*setSize)(rdpDriveContext* context, INT64 size);
|
||||
|
||||
UINT32 (*getFileAttributes)(rdpDriveContext* context);
|
||||
bool (*setFileAttributesAndTimes)(rdpDriveContext* context, UINT64 CreationTime,
|
||||
UINT64 LastAccessTime, UINT64 LastWriteTime,
|
||||
UINT64 ChangeTime, UINT32 dwFileAttributes);
|
||||
|
||||
const WIN32_FIND_DATAW* (*first)(rdpDriveContext* context, const WCHAR* query,
|
||||
size_t numCharacters);
|
||||
const WIN32_FIND_DATAW* (*next)(rdpDriveContext* context);
|
||||
|
||||
const BY_HANDLE_FILE_INFORMATION* (*getFileAttributeData)(rdpDriveContext* context);
|
||||
} rdpDriveDriver;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user