From 17d0c0e4a7ca8ee57e86e1bc6191b64df33c3f27 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 4 Nov 2024 12:03:48 +0100 Subject: [PATCH 1/4] [channels,client] make DRIVE_FILE opaque --- channels/drive/client/drive_file.c | 34 ++++++++++++++++++++++++++++++ channels/drive/client/drive_file.h | 24 ++++++--------------- channels/drive/client/drive_main.c | 7 +++--- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c index a594449c48b0..7580de7055c5 100644 --- a/channels/drive/client/drive_file.c +++ b/channels/drive/client/drive_file.c @@ -44,6 +44,23 @@ #include "drive_file.h" +struct S_DRIVE_FILE +{ + UINT32 id; + BOOL is_dir; + HANDLE file_handle; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + const WCHAR* basepath; + WCHAR* fullpath; + BOOL delete_pending; + UINT32 FileAttributes; + UINT32 SharedAccess; + UINT32 DesiredAccess; + UINT32 CreateDisposition; + UINT32 CreateOptions; +}; + #ifdef WITH_DEBUG_RDPDR #define DEBUG_WSTR(msg, wstr) \ do \ @@ -996,3 +1013,20 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT8(output, 0); /* Padding */ return FALSE; } + +const uintptr_t drive_file_get_id(DRIVE_FILE* drive) +{ + WINPR_ASSERT(drive); + return drive->id; +} + +BOOL drive_file_is_dir_not_empty(DRIVE_FILE* drive) +{ + if (!drive) + return FALSE; + + if (!drive->is_dir) + return FALSE; + + return !PathIsDirectoryEmptyW(drive->fullpath); +} diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h index 761295b6576b..a8b09b1600a6 100644 --- a/channels/drive/client/drive_file.h +++ b/channels/drive/client/drive_file.h @@ -32,27 +32,17 @@ #define TAG CHANNELS_TAG("drive.client") -typedef struct -{ - UINT32 id; - BOOL is_dir; - HANDLE file_handle; - HANDLE find_handle; - WIN32_FIND_DATAW find_data; - const WCHAR* basepath; - WCHAR* fullpath; - BOOL delete_pending; - UINT32 FileAttributes; - UINT32 SharedAccess; - UINT32 DesiredAccess; - UINT32 CreateDisposition; - UINT32 CreateOptions; -} DRIVE_FILE; +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 WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength, UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess); -BOOL drive_file_free(DRIVE_FILE* file); + +const uintptr_t drive_file_get_id(DRIVE_FILE* drive); +BOOL drive_file_is_dir_not_empty(DRIVE_FILE* drive); BOOL drive_file_open(DRIVE_FILE* file); BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset); diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c index 7cd0b7f7860d..f35acbe28337 100644 --- a/channels/drive/client/drive_main.c +++ b/channels/drive/client/drive_main.c @@ -191,7 +191,7 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp) } else { - void* key = (void*)(size_t)file->id; + void* key = (void*)drive_file_get_id(file); if (!ListDictionary_Add(drive->files, key, file)) { @@ -401,7 +401,6 @@ static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp) */ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp) { - DRIVE_FILE* file = NULL; UINT32 FsInformationClass = 0; UINT32 Length = 0; @@ -414,8 +413,8 @@ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp) Stream_Read_UINT32(irp->input, FsInformationClass); Stream_Read_UINT32(irp->input, Length); Stream_Seek(irp->input, 24); /* Padding */ - file = drive_get_file_by_id(drive, irp->FileId); + DRIVE_FILE* file = drive_get_file_by_id(drive, irp->FileId); if (!file) { irp->IoStatus = STATUS_UNSUCCESSFUL; @@ -425,7 +424,7 @@ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp) irp->IoStatus = drive_map_windows_err(GetLastError()); } - if (file && file->is_dir && !PathIsDirectoryEmptyW(file->fullpath)) + if (drive_file_is_dir_not_empty(file)) irp->IoStatus = STATUS_DIRECTORY_NOT_EMPTY; Stream_Write_UINT32(irp->output, Length); From c815b14c3855484985346324c0fedcd9bcd135c8 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 4 Nov 2024 12:53:03 +0100 Subject: [PATCH 2/4] [channels,drive] clean out drive_main.c move all platform dependent code to drive_file.c --- channels/drive/client/drive_file.c | 74 ++++++++++++++++ channels/drive/client/drive_file.h | 3 + channels/drive/client/drive_main.c | 133 ++++++----------------------- 3 files changed, 103 insertions(+), 107 deletions(-) diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c index 7580de7055c5..a96803b569ea 100644 --- a/channels/drive/client/drive_file.c +++ b/channels/drive/client/drive_file.c @@ -1030,3 +1030,77 @@ BOOL drive_file_is_dir_not_empty(DRIVE_FILE* drive) return !PathIsDirectoryEmptyW(drive->fullpath); } + +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); +} + +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); +} diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h index a8b09b1600a6..fdcbaa99396e 100644 --- a/channels/drive/client/drive_file.h +++ b/channels/drive/client/drive_file.h @@ -44,6 +44,9 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat const 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* what); +char* drive_file_resolve_name(const char* path, const char* suggested); + BOOL drive_file_open(DRIVE_FILE* file); BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset); BOOL drive_file_read(DRIVE_FILE* file, BYTE* buffer, UINT32* Length); diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c index f35acbe28337..dabd72f1b54d 100644 --- a/channels/drive/client/drive_main.c +++ b/channels/drive/client/drive_main.c @@ -889,20 +889,34 @@ static void drive_message_free(void* obj) * * @return 0 on success, otherwise a Win32 error code */ -static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, const char* name, - const char* path, BOOL automount) +static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, + const char* rawname, const char* rawpath, BOOL automount) { size_t length = 0; DRIVE_DEVICE* drive = NULL; UINT error = ERROR_INTERNAL_ERROR; - if (!pEntryPoints || !name || !path) + if (!pEntryPoints || !rawname || !rawpath) { - WLog_ERR(TAG, "[%s] Invalid parameters: pEntryPoints=%p, name=%p, path=%p", pEntryPoints, - name, path); + WLog_ERR(TAG, "Invalid parameters: pEntryPoints=%p, name=%p, path=%p", pEntryPoints, + rawname, rawpath); return ERROR_INVALID_PARAMETER; } + char* path = drive_file_resolve_path(rawpath); + if (!path) + { + WLog_ERR(TAG, "failed to resolve path '%s'", rawpath); + return ERROR_INVALID_PARAMETER; + } + + char* name = drive_file_resolve_name(path, rawname); + if (!name) + { + WLog_ERR(TAG, "failed to resolve name ['%s'|'%s']", rawpath, rawname); + free(path); + return ERROR_INVALID_PARAMETER; + } if (name[0] && path[0]) { size_t pathLength = strnlen(path, MAX_PATH); @@ -911,6 +925,8 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, if (!drive) { WLog_ERR(TAG, "calloc failed!"); + free(path); + free(name); return CHANNEL_RC_NO_MEMORY; } @@ -1007,6 +1023,8 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, return CHANNEL_RC_OK; out_error: drive_free_int(drive); + free(path); + free(name); return error; } @@ -1018,110 +1036,11 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, FREERDP_ENTRY_POINT( UINT VCAPITYPE drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)) { - RDPDR_DRIVE* drive = NULL; - UINT error = 0; -#ifdef WIN32 - int len; - char devlist[512], buf[512]; - char* bufdup; - char* devdup; -#endif - WINPR_ASSERT(pEntryPoints); - drive = (RDPDR_DRIVE*)pEntryPoints->device; + RDPDR_DRIVE* drive = (RDPDR_DRIVE*)pEntryPoints->device; WINPR_ASSERT(drive); -#ifndef WIN32 - if (strcmp(drive->Path, "*") == 0) - { - /* all drives */ - free(drive->Path); - drive->Path = _strdup("/"); - - if (!drive->Path) - { - WLog_ERR(TAG, "_strdup failed!"); - return CHANNEL_RC_NO_MEMORY; - } - } - else if (strcmp(drive->Path, "%") == 0) - { - free(drive->Path); - drive->Path = GetKnownPath(KNOWN_PATH_HOME); - - if (!drive->Path) - { - WLog_ERR(TAG, "_strdup failed!"); - return CHANNEL_RC_NO_MEMORY; - } - } - - error = - drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path, drive->automount); -#else - /* Special case: path[0] == '*' -> export all drives */ - /* Special case: path[0] == '%' -> user home dir */ - if (strcmp(drive->Path, "%") == 0) - { - GetEnvironmentVariableA("USERPROFILE", buf, sizeof(buf)); - PathCchAddBackslashA(buf, sizeof(buf)); - free(drive->Path); - drive->Path = _strdup(buf); - - if (!drive->Path) - { - WLog_ERR(TAG, "_strdup failed!"); - return CHANNEL_RC_NO_MEMORY; - } - - error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path, - drive->automount); - } - else if (strcmp(drive->Path, "*") == 0) - { - /* Enumerate all devices: */ - GetLogicalDriveStringsA(sizeof(devlist) - 1, devlist); - - for (size_t i = 0;; i++) - { - char* dev = &devlist[i * 4]; - if (!*dev) - break; - if (*dev > 'B') - { - /* Suppress disk drives A and B to avoid pesty messages */ - len = sprintf_s(buf, sizeof(buf) - 4, "%s", drive->device.Name); - buf[len] = '_'; - buf[len + 1] = dev[0]; - buf[len + 2] = 0; - buf[len + 3] = 0; - - if (!(bufdup = _strdup(buf))) - { - WLog_ERR(TAG, "_strdup failed!"); - return CHANNEL_RC_NO_MEMORY; - } - - if (!(devdup = _strdup(dev))) - { - WLog_ERR(TAG, "_strdup failed!"); - return CHANNEL_RC_NO_MEMORY; - } - - if ((error = drive_register_drive_path(pEntryPoints, bufdup, devdup, TRUE))) - { - break; - } - } - } - } - else - { - error = drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path, - drive->automount); - } - -#endif - return error; + return drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path, + drive->automount); } From 47510ab6dad3ec06a895ee781172b7d300d60393 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 4 Nov 2024 13:02:52 +0100 Subject: [PATCH 3/4] [channels,drive] prepare switchable backends --- channels/drive/client/drive_file.c | 11 +- channels/drive/client/drive_file.h | 11 +- channels/drive/client/drive_main.c | 212 +++++++++++++++-------------- 3 files changed, 125 insertions(+), 109 deletions(-) diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c index a96803b569ea..c58b67ab962c 100644 --- a/channels/drive/client/drive_file.c +++ b/channels/drive/client/drive_file.c @@ -330,9 +330,10 @@ static BOOL drive_file_init(DRIVE_FILE* file) return file->file_handle != INVALID_HANDLE_VALUE; } -DRIVE_FILE* drive_file_new(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(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* file = NULL; @@ -1031,7 +1032,7 @@ BOOL drive_file_is_dir_not_empty(DRIVE_FILE* drive) return !PathIsDirectoryEmptyW(drive->fullpath); } -char* drive_file_resolve_path(const char* what) +char* drive_file_resolve_path(const char* backend, const char* what) { if (!what) return NULL; @@ -1081,7 +1082,7 @@ char* drive_file_resolve_path(const char* what) return _strdup(what); } -char* drive_file_resolve_name(const char* path, const char* suggested) +char* drive_file_resolve_name(const char* backend, const char* path, const char* suggested) { if (!path) return NULL; diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h index fdcbaa99396e..da0a35a4e231 100644 --- a/channels/drive/client/drive_file.h +++ b/channels/drive/client/drive_file.h @@ -37,15 +37,16 @@ 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 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(const char* 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); BOOL drive_file_is_dir_not_empty(DRIVE_FILE* drive); -char* drive_file_resolve_path(const char* what); -char* drive_file_resolve_name(const char* path, const char* suggested); +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); BOOL drive_file_open(DRIVE_FILE* file); BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset); diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c index dabd72f1b54d..e62563547de6 100644 --- a/channels/drive/client/drive_main.c +++ b/channels/drive/client/drive_main.c @@ -61,6 +61,7 @@ typedef struct DEVMAN* devman; rdpContext* rdpcontext; + char* backend; } DRIVE_DEVICE; static DWORD drive_map_windows_err(DWORD fs_errno) @@ -180,8 +181,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->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess, - CreateDisposition, CreateOptions, FileAttributes, SharedAccess); + file = drive_file_new(drive->backend, drive->path, path, PathLength / sizeof(WCHAR), FileId, + DesiredAccess, CreateDisposition, CreateOptions, FileAttributes, + SharedAccess); if (!file) { @@ -833,6 +835,7 @@ 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; } @@ -855,7 +858,6 @@ static UINT drive_free(DEVICE* device) { error = GetLastError(); WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); - return error; } return drive_free_int(drive); @@ -884,17 +886,115 @@ 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) +{ + 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->device.type = RDPDR_DTYP_FILESYSTEM; + drive->device.IRPRequest = drive_irp_request; + drive->device.Free = drive_free; + drive->rdpcontext = rdpcontext; + drive->automount = automount; + const size_t length = strlen(name); + drive->device.data = Stream_New(NULL, length + 1); + + if (!drive->device.data) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto out_error; + } + + for (size_t i = 0; i < length; i++) + { + /* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */ + switch (name[i]) + { + case ':': + case '<': + case '>': + case '\"': + case '/': + case '\\': + case '|': + case ' ': + Stream_Write_UINT8(drive->device.data, '_'); + break; + default: + Stream_Write_UINT8(drive->device.data, (BYTE)name[i]); + break; + } + } + Stream_Write_UINT8(drive->device.data, '\0'); + + drive->device.name = Stream_BufferAs(drive->device.data, char); + if (!drive->device.name) + goto out_error; + + if ((pathLength > 1) && (path[pathLength - 1] == '/')) + pathLength--; + + drive->path = ConvertUtf8NToWCharAlloc(path, pathLength, NULL); + if (!drive->path) + goto out_error; + + drive->files = ListDictionary_New(TRUE); + + if (!drive->files) + { + WLog_ERR(TAG, "ListDictionary_New failed!"); + goto out_error; + } + + ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree; + drive->IrpQueue = MessageQueue_New(NULL); + + if (!drive->IrpQueue) + { + WLog_ERR(TAG, "ListDictionary_New failed!"); + goto out_error; + } + + wObject* obj = MessageQueue_Object(drive->IrpQueue); + WINPR_ASSERT(obj); + obj->fnObjectFree = drive_message_free; + + drive->thread = CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL); + if (!drive->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + goto out_error; + } + + return drive; +out_error: + drive_free_int(drive); + return NULL; +} + /** * Function description * * @return 0 on success, otherwise a Win32 error code */ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, - const char* rawname, const char* rawpath, BOOL automount) + const char* backend, const char* rawname, const char* rawpath, + BOOL automount) { - size_t length = 0; - DRIVE_DEVICE* drive = NULL; UINT error = ERROR_INTERNAL_ERROR; + DRIVE_DEVICE* drive = NULL; if (!pEntryPoints || !rawname || !rawpath) { @@ -903,14 +1003,14 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, return ERROR_INVALID_PARAMETER; } - char* path = drive_file_resolve_path(rawpath); + char* path = drive_file_resolve_path(backend, rawpath); if (!path) { WLog_ERR(TAG, "failed to resolve path '%s'", rawpath); return ERROR_INVALID_PARAMETER; } - char* name = drive_file_resolve_name(path, rawname); + char* name = drive_file_resolve_name(backend, path, rawname); if (!name) { WLog_ERR(TAG, "failed to resolve name ['%s'|'%s']", rawpath, rawname); @@ -919,110 +1019,24 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, } if (name[0] && path[0]) { - size_t pathLength = strnlen(path, MAX_PATH); - drive = (DRIVE_DEVICE*)calloc(1, sizeof(DRIVE_DEVICE)); + drive = drive_new(pEntryPoints->rdpcontext, backend, path, name, automount); if (!drive) - { - WLog_ERR(TAG, "calloc failed!"); - free(path); - free(name); - return CHANNEL_RC_NO_MEMORY; - } - - drive->device.type = RDPDR_DTYP_FILESYSTEM; - drive->device.IRPRequest = drive_irp_request; - drive->device.Free = drive_free; - drive->rdpcontext = pEntryPoints->rdpcontext; - drive->automount = automount; - length = strlen(name); - drive->device.data = Stream_New(NULL, length + 1); - - if (!drive->device.data) - { - WLog_ERR(TAG, "Stream_New failed!"); - error = CHANNEL_RC_NO_MEMORY; - goto out_error; - } - - for (size_t i = 0; i < length; i++) - { - /* Filter 2.2.1.3 Device Announce Header (DEVICE_ANNOUNCE) forbidden symbols */ - switch (name[i]) - { - case ':': - case '<': - case '>': - case '\"': - case '/': - case '\\': - case '|': - case ' ': - Stream_Write_UINT8(drive->device.data, '_'); - break; - default: - Stream_Write_UINT8(drive->device.data, (BYTE)name[i]); - break; - } - } - Stream_Write_UINT8(drive->device.data, '\0'); - - drive->device.name = Stream_BufferAs(drive->device.data, char); - if (!drive->device.name) - goto out_error; - - if ((pathLength > 1) && (path[pathLength - 1] == '/')) - pathLength--; - - drive->path = ConvertUtf8NToWCharAlloc(path, pathLength, NULL); - if (!drive->path) - { - error = CHANNEL_RC_NO_MEMORY; - goto out_error; - } - - drive->files = ListDictionary_New(TRUE); - - if (!drive->files) - { - WLog_ERR(TAG, "ListDictionary_New failed!"); - error = CHANNEL_RC_NO_MEMORY; goto out_error; - } - ListDictionary_ValueObject(drive->files)->fnObjectFree = drive_file_objfree; - drive->IrpQueue = MessageQueue_New(NULL); - - if (!drive->IrpQueue) - { - WLog_ERR(TAG, "ListDictionary_New failed!"); - error = CHANNEL_RC_NO_MEMORY; - goto out_error; - } - - wObject* obj = MessageQueue_Object(drive->IrpQueue); - WINPR_ASSERT(obj); - obj->fnObjectFree = drive_message_free; - - if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)drive))) + error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &drive->device); + if (error != 0) { WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error); goto out_error; } - if (!(drive->thread = - CreateThread(NULL, 0, drive_thread_func, drive, CREATE_SUSPENDED, NULL))) - { - WLog_ERR(TAG, "CreateThread failed!"); - goto out_error; - } - ResumeThread(drive->thread); } return CHANNEL_RC_OK; out_error: - drive_free_int(drive); + (void)drive_free_int(drive); free(path); free(name); return error; @@ -1041,6 +1055,6 @@ FREERDP_ENTRY_POINT( RDPDR_DRIVE* drive = (RDPDR_DRIVE*)pEntryPoints->device; WINPR_ASSERT(drive); - return drive_register_drive_path(pEntryPoints, drive->device.Name, drive->Path, + return drive_register_drive_path(pEntryPoints, "file", drive->device.Name, drive->Path, drive->automount); } From c322fd800943f79d8ab178bacf778da7796d1a54 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Wed, 6 Nov 2024 14:51:56 +0100 Subject: [PATCH 4/4] [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, ...) --- channels/drive/client/CMakeLists.txt | 1 + channels/drive/client/drive_file.c | 763 ++++-------------- channels/drive/client/drive_file.h | 17 +- channels/drive/client/drive_main.c | 73 +- channels/drive/client/file/CMakeLists.txt | 30 + .../drive/client/file/drive_backend_file.c | 579 +++++++++++++ include/freerdp/client/drive.h | 154 ++++ 7 files changed, 996 insertions(+), 621 deletions(-) create mode 100644 channels/drive/client/file/CMakeLists.txt create mode 100644 channels/drive/client/file/drive_backend_file.c create mode 100644 include/freerdp/client/drive.h diff --git a/channels/drive/client/CMakeLists.txt b/channels/drive/client/CMakeLists.txt index a236f7634637..45972cac841b 100644 --- a/channels/drive/client/CMakeLists.txt +++ b/channels/drive/client/CMakeLists.txt @@ -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" "") diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c index c58b67ab962c..80946d8e6aff 100644 --- a/channels/drive/client/drive_file.c +++ b/channels/drive/client/drive_file.c @@ -36,11 +36,11 @@ #include #include #include -#include -#include #include #include +#include +#include #include "drive_file.h" @@ -48,17 +48,14 @@ struct S_DRIVE_FILE { UINT32 id; BOOL is_dir; - HANDLE file_handle; - HANDLE find_handle; - WIN32_FIND_DATAW find_data; - const WCHAR* basepath; - WCHAR* fullpath; BOOL delete_pending; UINT32 FileAttributes; UINT32 SharedAccess; UINT32 DesiredAccess; UINT32 CreateDisposition; UINT32 CreateOptions; + const rdpDriveDriver* backend; + rdpDriveContext* context; }; #ifdef WITH_DEBUG_RDPDR @@ -76,137 +73,15 @@ struct S_DRIVE_FILE } while (0) #endif -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; -} - -static BOOL contains_dotdot(const WCHAR* path, size_t base_length, size_t path_length) -{ - WCHAR dotdotbuffer[6] = { 0 }; - const WCHAR* dotdot = InitializeConstWCharFromUtf8("..", dotdotbuffer, ARRAYSIZE(dotdotbuffer)); - const WCHAR* tst = path; - - if (path_length < 2) - return FALSE; - - do - { - tst = _wcsstr(tst, dotdot); - if (!tst) - return FALSE; - - /* Filter .. sequences in file or directory names */ - if ((base_length == 0) || (*(tst - 1) == L'/') || (*(tst - 1) == L'\\')) - { - if (tst + 2 < path + path_length) - { - if ((tst[2] == '/') || (tst[2] == '\\')) - return TRUE; - } - } - tst += 2; - } while (TRUE); - - return FALSE; -} - -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; - - /* Ensure the path does not contain sequences like '..' */ - if (contains_dotdot(&fullpath[base_path_length], base_path_length, PathWCharLength)) - { - char abuffer[MAX_PATH] = { 0 }; - (void)ConvertWCharToUtf8(&fullpath[base_path_length], abuffer, ARRAYSIZE(abuffer)); - - WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!", - &abuffer[base_path_length]); - goto fail; - } - - ok = TRUE; -fail: - if (!ok) - { - free(fullpath); - fullpath = NULL; - } - return fullpath; -} - -static BOOL drive_file_set_fullpath(DRIVE_FILE* file, WCHAR* fullpath) -{ - if (!file || !fullpath) - return FALSE; - - const size_t len = _wcslen(fullpath); - free(file->fullpath); - file->fullpath = NULL; - - if (len == 0) - return TRUE; - - file->fullpath = fullpath; - - const WCHAR sep[] = { PathGetSeparatorW(PATH_STYLE_NATIVE), '\0' }; - WCHAR* filename = _wcsrchr(file->fullpath, *sep); - if (filename && _wcsncmp(filename, sep, ARRAYSIZE(sep)) == 0) - *filename = '\0'; - - return TRUE; -} - static BOOL drive_file_init(DRIVE_FILE* file) { UINT CreateDisposition = 0; - DWORD dwAttr = GetFileAttributesW(file->fullpath); + + WINPR_ASSERT(file); + WINPR_ASSERT(file->context); + WINPR_ASSERT(file->backend); + WINPR_ASSERT(file->backend->getFileAttributes); + const DWORD dwAttr = file->backend->getFileAttributes(file->context); if (dwAttr != INVALID_FILE_ATTRIBUTES) { @@ -248,10 +123,9 @@ static BOOL drive_file_init(DRIVE_FILE* file) if ((file->CreateDisposition == FILE_OPEN_IF) || (file->CreateDisposition == FILE_CREATE)) { - if (CreateDirectoryW(file->fullpath, NULL) != 0) - { + WINPR_ASSERT(file->backend->createDirectory); + if (file->backend->createDirectory(file->context) != 0) return TRUE; - } } SetLastError(ERROR_FILE_NOT_FOUND); @@ -259,7 +133,8 @@ static BOOL drive_file_init(DRIVE_FILE* file) } } - if (file->file_handle == INVALID_HANDLE_VALUE) + WINPR_ASSERT(file->backend->exists); + if (!file->backend->exists(file->context)) { switch (file->CreateDisposition) { @@ -297,77 +172,57 @@ static BOOL drive_file_init(DRIVE_FILE* file) break; } -#ifndef WIN32 - file->SharedAccess = 0; -#endif - file->file_handle = CreateFileW(file->fullpath, file->DesiredAccess, file->SharedAccess, - NULL, CreateDisposition, file->FileAttributes, NULL); - } - -#ifdef WIN32 - if (file->file_handle == INVALID_HANDLE_VALUE) - { - /* Get the error message, if any. */ - DWORD errorMessageID = GetLastError(); - - if (errorMessageID != 0) - { - LPSTR messageBuffer = NULL; - size_t size = - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&messageBuffer, 0, NULL); - WLog_ERR(TAG, "Error in drive_file_init: %s %s", messageBuffer, file->fullpath); - /* Free the buffer. */ - LocalFree(messageBuffer); - /* restore original error code */ - SetLastError(errorMessageID); - } + if (!file->backend->createFile(file->context, file->DesiredAccess, file->SharedAccess, + CreateDisposition, file->FileAttributes)) + return FALSE; } -#endif - return file->file_handle != INVALID_HANDLE_VALUE; + WINPR_ASSERT(file->backend->exists); + return file->backend->exists(file->context); } -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) { - DRIVE_FILE* file = NULL; - + WINPR_ASSERT(backend); if (!base_path || (!path && (PathWCharLength > 0))) return NULL; - file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE)); + DRIVE_FILE* file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE)); if (!file) - { - WLog_ERR(TAG, "calloc failed!"); - return NULL; - } + goto fail; + + file->backend = backend; + + WINPR_ASSERT(backend->new); + file->context = backend->new(context); + if (!file->context) + goto fail; - file->file_handle = INVALID_HANDLE_VALUE; - file->find_handle = INVALID_HANDLE_VALUE; file->id = id; - file->basepath = base_path; file->FileAttributes = FileAttributes; file->DesiredAccess = DesiredAccess; file->CreateDisposition = CreateDisposition; file->CreateOptions = CreateOptions; file->SharedAccess = SharedAccess; - drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathWCharLength)); + + WINPR_ASSERT(file->backend); + WINPR_ASSERT(file->context); + WINPR_ASSERT(file->backend->setPath); + if (!file->backend->setPath(file->context, base_path, path, PathWCharLength)) + goto fail; if (!drive_file_init(file)) - { - DWORD lastError = GetLastError(); - drive_file_free(file); - SetLastError(lastError); - return NULL; - } + goto fail; return file; + +fail: + drive_file_free(file); + return NULL; } BOOL drive_file_free(DRIVE_FILE* file) @@ -377,85 +232,74 @@ BOOL drive_file_free(DRIVE_FILE* file) if (!file) return FALSE; - if (file->file_handle != INVALID_HANDLE_VALUE) - { - (void)CloseHandle(file->file_handle); - file->file_handle = INVALID_HANDLE_VALUE; - } - - if (file->find_handle != INVALID_HANDLE_VALUE) - { - FindClose(file->find_handle); - file->find_handle = INVALID_HANDLE_VALUE; - } - + WINPR_ASSERT(file->backend); if (file->delete_pending) { - if (file->is_dir) - { - if (!winpr_RemoveDirectory_RecursiveW(file->fullpath)) - goto fail; - } - else if (!DeleteFileW(file->fullpath)) + WINPR_ASSERT(file->backend->remove); + if (!file->backend->remove(file->context)) goto fail; } rc = TRUE; fail: - DEBUG_WSTR("Free %s", file->fullpath); - free(file->fullpath); + WINPR_ASSERT(file->backend->free); + file->backend->free(file->context); + free(file); return rc; } BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset) { - LARGE_INTEGER loffset = { 0 }; - if (!file) return FALSE; if (Offset > INT64_MAX) return FALSE; - loffset.QuadPart = (LONGLONG)Offset; - return SetFilePointerEx(file->file_handle, loffset, NULL, FILE_BEGIN); + WINPR_ASSERT(file->context); + WINPR_ASSERT(file->backend); + WINPR_ASSERT(file->backend->seek); + + return file->backend->seek(file->context, (SSIZE_T)Offset, SEEK_SET) >= 0; } BOOL drive_file_read(DRIVE_FILE* file, BYTE* buffer, UINT32* Length) { - DWORD read = 0; - if (!file || !buffer || !Length) return FALSE; - DEBUG_WSTR("Read file %s", file->fullpath); + const size_t size = *Length; + *Length = 0; - if (ReadFile(file->file_handle, buffer, *Length, &read, NULL)) - { - *Length = read; - return TRUE; - } + WINPR_ASSERT(file->context); + WINPR_ASSERT(file->backend); + WINPR_ASSERT(file->backend->read); + const SSIZE_T rc = file->backend->read(file->context, buffer, size); + if (rc < 0) + return FALSE; - return FALSE; + WINPR_ASSERT((size_t)rc <= size); + *Length = (UINT32)rc; + return TRUE; } BOOL drive_file_write(DRIVE_FILE* file, const BYTE* buffer, UINT32 Length) { - DWORD written = 0; - if (!file || !buffer) return FALSE; - DEBUG_WSTR("Write file %s", file->fullpath); - + WINPR_ASSERT(file->context); + WINPR_ASSERT(file->backend); + WINPR_ASSERT(file->backend->write); while (Length > 0) { - if (!WriteFile(file->file_handle, buffer, Length, &written, NULL)) + const SSIZE_T rc = file->backend->write(file->context, buffer, Length); + if (rc < 0) return FALSE; - Length -= written; - buffer += written; + Length -= (UINT32)rc; + buffer += (size_t)rc; } return TRUE; @@ -524,106 +368,21 @@ static BOOL drive_file_query_from_handle_information(const DRIVE_FILE* file, return TRUE; } -static BOOL drive_file_query_from_attributes(const DRIVE_FILE* file, - const WIN32_FILE_ATTRIBUTE_DATA* attrib, - UINT32 FsInformationClass, wStream* output) -{ - switch (FsInformationClass) - { - case FileBasicInformation: - - /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ - if (!Stream_EnsureRemainingCapacity(output, 4 + 36)) - return FALSE; - - Stream_Write_UINT32(output, 36); /* Length */ - Stream_Write_UINT32(output, attrib->ftCreationTime.dwLowDateTime); /* CreationTime */ - Stream_Write_UINT32(output, attrib->ftCreationTime.dwHighDateTime); /* CreationTime */ - Stream_Write_UINT32(output, - attrib->ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ - Stream_Write_UINT32(output, - attrib->ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ - Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ - Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ - Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwLowDateTime); /* ChangeTime */ - Stream_Write_UINT32(output, attrib->ftLastWriteTime.dwHighDateTime); /* ChangeTime */ - Stream_Write_UINT32(output, attrib->dwFileAttributes); /* FileAttributes */ - /* Reserved(4), MUST NOT be added! */ - break; - - case FileStandardInformation: - - /* http://msdn.microsoft.com/en-us/library/cc232088.aspx */ - if (!Stream_EnsureRemainingCapacity(output, 4 + 22)) - return FALSE; - - Stream_Write_UINT32(output, 22); /* Length */ - Stream_Write_UINT32(output, attrib->nFileSizeLow); /* AllocationSize */ - Stream_Write_UINT32(output, attrib->nFileSizeHigh); /* AllocationSize */ - Stream_Write_UINT32(output, attrib->nFileSizeLow); /* EndOfFile */ - Stream_Write_UINT32(output, attrib->nFileSizeHigh); /* EndOfFile */ - Stream_Write_UINT32(output, 0); /* NumberOfLinks */ - Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */ - Stream_Write_UINT8(output, attrib->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY - ? TRUE - : FALSE); /* Directory */ - /* Reserved(2), MUST NOT be added! */ - break; - - case FileAttributeTagInformation: - - /* http://msdn.microsoft.com/en-us/library/cc232093.aspx */ - if (!Stream_EnsureRemainingCapacity(output, 4 + 8)) - return FALSE; - - Stream_Write_UINT32(output, 8); /* Length */ - Stream_Write_UINT32(output, attrib->dwFileAttributes); /* FileAttributes */ - Stream_Write_UINT32(output, 0); /* ReparseTag */ - break; - - default: - /* Unhandled FsInformationClass */ - return FALSE; - } - - return TRUE; -} - BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, wStream* output) { - BY_HANDLE_FILE_INFORMATION fileInformation = { 0 }; - BOOL status = 0; - HANDLE hFile = NULL; - if (!file || !output) return FALSE; - hFile = CreateFileW(file->fullpath, 0, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) - { - status = GetFileInformationByHandle(hFile, &fileInformation); - (void)CloseHandle(hFile); - if (!status) - goto out_fail; + WINPR_ASSERT(file->backend); + WINPR_ASSERT(file->context); - if (!drive_file_query_from_handle_information(file, &fileInformation, FsInformationClass, - output)) - goto out_fail; - - return TRUE; - } - - /* If we failed before (i.e. if information for a drive is queried) fall back to - * GetFileAttributesExW */ - WIN32_FILE_ATTRIBUTE_DATA fileAttributes = { 0 }; - if (!GetFileAttributesExW(file->fullpath, GetFileExInfoStandard, &fileAttributes)) + WINPR_ASSERT(file->backend->getFileAttributeData); + const BY_HANDLE_FILE_INFORMATION* info = file->backend->getFileAttributeData(file->context); + if (!info) goto out_fail; - if (!drive_file_query_from_attributes(file, &fileAttributes, FsInformationClass, output)) - goto out_fail; + return drive_file_query_from_handle_information(file, info, FsInformationClass, output); - return TRUE; out_fail: Stream_Write_UINT32(output, 0); /* Length */ return FALSE; @@ -633,89 +392,40 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN wStream* input) { INT64 size = 0; - WCHAR* fullpath = NULL; - ULARGE_INTEGER liCreationTime = { 0 }; - ULARGE_INTEGER liLastAccessTime = { 0 }; - ULARGE_INTEGER liLastWriteTime = { 0 }; - ULARGE_INTEGER liChangeTime = { 0 }; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - FILETIME* pftCreationTime = NULL; - FILETIME* pftLastAccessTime = NULL; - FILETIME* pftLastWriteTime = NULL; - UINT32 FileAttributes = 0; UINT32 FileNameLength = 0; - LARGE_INTEGER liSize = { 0 }; UINT8 delete_pending = 0; UINT8 ReplaceIfExists = 0; - DWORD attr = 0; if (!file || !input) return FALSE; + WINPR_ASSERT(file->backend); + WINPR_ASSERT(file->context); switch (FsInformationClass) { case FileBasicInformation: + { if (!Stream_CheckAndLogRequiredLength(TAG, input, 36)) return FALSE; /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ - Stream_Read_UINT64(input, liCreationTime.QuadPart); - Stream_Read_UINT64(input, liLastAccessTime.QuadPart); - Stream_Read_UINT64(input, liLastWriteTime.QuadPart); - Stream_Read_UINT64(input, liChangeTime.QuadPart); - Stream_Read_UINT32(input, FileAttributes); - - if (!PathFileExistsW(file->fullpath)) + const UINT64 CreationTime = Stream_Get_UINT64(input); + const UINT64 LastAccessTime = Stream_Get_UINT64(input); + const UINT64 LastWriteTime = Stream_Get_UINT64(input); + const UINT64 ChangeTime = Stream_Get_UINT64(input); + const UINT32 FileAttributes = Stream_Get_UINT32(input); + + WINPR_ASSERT(file->backend->exists); + if (!file->backend->exists(file->context)) return FALSE; - if (file->file_handle == INVALID_HANDLE_VALUE) - { - WLog_ERR(TAG, "Unable to set file time %s (%" PRId32 ")", file->fullpath, - GetLastError()); - return FALSE; - } - - if (liCreationTime.QuadPart != 0) - { - ftCreationTime.dwHighDateTime = liCreationTime.u.HighPart; - ftCreationTime.dwLowDateTime = liCreationTime.u.LowPart; - pftCreationTime = &ftCreationTime; - } - - if (liLastAccessTime.QuadPart != 0) - { - ftLastAccessTime.dwHighDateTime = liLastAccessTime.u.HighPart; - ftLastAccessTime.dwLowDateTime = liLastAccessTime.u.LowPart; - pftLastAccessTime = &ftLastAccessTime; - } - - if (liLastWriteTime.QuadPart != 0) - { - ftLastWriteTime.dwHighDateTime = liLastWriteTime.u.HighPart; - ftLastWriteTime.dwLowDateTime = liLastWriteTime.u.LowPart; - pftLastWriteTime = &ftLastWriteTime; - } - - if (liChangeTime.QuadPart != 0 && liChangeTime.QuadPart > liLastWriteTime.QuadPart) - { - ftLastWriteTime.dwHighDateTime = liChangeTime.u.HighPart; - ftLastWriteTime.dwLowDateTime = liChangeTime.u.LowPart; - pftLastWriteTime = &ftLastWriteTime; - } - - DEBUG_WSTR("SetFileTime %s", file->fullpath); - - SetFileAttributesW(file->fullpath, FileAttributes); - if (!SetFileTime(file->file_handle, pftCreationTime, pftLastAccessTime, - pftLastWriteTime)) - { - WLog_ERR(TAG, "Unable to set file time to %s", file->fullpath); + WINPR_ASSERT(file->backend->setFileAttributesAndTimes); + if (!file->backend->setFileAttributesAndTimes(file->context, CreationTime, + LastAccessTime, LastWriteTime, ChangeTime, + FileAttributes)) return FALSE; - } - - break; + } + break; case FileEndOfFileInformation: @@ -727,30 +437,9 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */ Stream_Read_INT64(input, size); - if (file->file_handle == INVALID_HANDLE_VALUE) - { - WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", file->fullpath, - size, GetLastError()); + WINPR_ASSERT(file->backend->setSize); + if (!file->backend->setSize(file->context, size)) return FALSE; - } - - liSize.QuadPart = size; - - if (!SetFilePointerEx(file->file_handle, liSize, NULL, FILE_BEGIN)) - { - WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", file->fullpath, - size, GetLastError()); - return FALSE; - } - - DEBUG_WSTR("Truncate %s", file->fullpath); - - if (SetEndOfFile(file->file_handle) == 0) - { - WLog_ERR(TAG, "Unable to truncate %s to %" PRId64 " (%" PRId32 ")", file->fullpath, - size, GetLastError()); - return FALSE; - } break; @@ -758,7 +447,8 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */ /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */ - if (file->is_dir && !PathIsDirectoryEmptyW(file->fullpath)) + WINPR_ASSERT(file->backend->empty); + if (file->is_dir && !file->backend->empty(file->context)) break; /* TODO: SetLastError ??? */ if (Length) @@ -773,8 +463,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN if (delete_pending) { - DEBUG_WSTR("SetDeletePending %s", file->fullpath); - attr = GetFileAttributesW(file->fullpath); + const UINT32 attr = file->backend->getFileAttributes(file->context); if (attr & FILE_ATTRIBUTE_READONLY) { @@ -798,39 +487,11 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN if (!Stream_CheckAndLogRequiredLength(TAG, input, FileNameLength)) return FALSE; - fullpath = drive_file_combine_fullpath(file->basepath, Stream_ConstPointer(input), - FileNameLength / sizeof(WCHAR)); - - if (!fullpath) - return FALSE; - -#ifdef _WIN32 - - if (file->file_handle != INVALID_HANDLE_VALUE) - { - (void)CloseHandle(file->file_handle); - file->file_handle = INVALID_HANDLE_VALUE; - } - -#endif - DEBUG_WSTR("MoveFileExW %s", file->fullpath); - - if (MoveFileExW(file->fullpath, fullpath, - MOVEFILE_COPY_ALLOWED | - (ReplaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0))) - { - if (!drive_file_set_fullpath(file, fullpath)) - return FALSE; - } - else - { - free(fullpath); + const WCHAR* str = Stream_ConstPointer(input); + const size_t len = FileNameLength / sizeof(WCHAR); + WINPR_ASSERT(file->backend->move); + if (!file->backend->move(file->context, str, len, ReplaceIfExists)) return FALSE; - } - -#ifdef _WIN32 - drive_file_init(file); -#endif break; default: @@ -843,30 +504,30 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery, const WCHAR* path, UINT32 PathWCharLength, wStream* output) { - size_t length = 0; - WCHAR* ent_path = NULL; - if (!file || !path || !output) return FALSE; + WINPR_ASSERT(file->context); + WINPR_ASSERT(file->backend); + + const WIN32_FIND_DATAW* find_data = NULL; if (InitialQuery != 0) { - /* release search handle */ - if (file->find_handle != INVALID_HANDLE_VALUE) - FindClose(file->find_handle); - - ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength); /* open new search handle and retrieve the first entry */ - file->find_handle = FindFirstFileW(ent_path, &file->find_data); - free(ent_path); - - if (file->find_handle == INVALID_HANDLE_VALUE) - goto out_fail; + WINPR_ASSERT(file->backend->first); + find_data = file->backend->first(file->context, path, PathWCharLength); } - else if (!FindNextFileW(file->find_handle, &file->find_data)) + else + { + WINPR_ASSERT(file->backend->next); + find_data = file->backend->next(file->context); + } + + if (!find_data) goto out_fail; - length = _wcslen(file->find_data.cFileName) * 2; + const size_t length = + _wcsnlen(find_data->cFileName, ARRAYSIZE(find_data->cFileName)) * sizeof(WCHAR); switch (FsInformationClass) { @@ -882,29 +543,26 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, (UINT32)(64 + length)); /* Length */ Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ + Stream_Write_UINT32(output, find_data->ftCreationTime.dwLowDateTime); /* CreationTime */ Stream_Write_UINT32(output, - file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */ + find_data->ftCreationTime.dwHighDateTime); /* CreationTime */ Stream_Write_UINT32(output, - file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */ - Stream_Write_UINT32( - output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ - Stream_Write_UINT32( - output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ + find_data->ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ + find_data->ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + find_data->ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */ - Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */ - Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */ - Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */ - Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */ - Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */ - Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */ + find_data->ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, find_data->ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, find_data->ftLastWriteTime.dwHighDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, find_data->nFileSizeLow); /* EndOfFile */ + Stream_Write_UINT32(output, find_data->nFileSizeHigh); /* EndOfFile */ + Stream_Write_UINT32(output, find_data->nFileSizeLow); /* AllocationSize */ + Stream_Write_UINT32(output, find_data->nFileSizeHigh); /* AllocationSize */ + Stream_Write_UINT32(output, find_data->dwFileAttributes); /* FileAttributes */ Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */ - Stream_Write(output, file->find_data.cFileName, length); + Stream_Write(output, find_data->cFileName, length); break; case FileFullDirectoryInformation: @@ -919,30 +577,27 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, (UINT32)(68 + length)); /* Length */ Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ + Stream_Write_UINT32(output, find_data->ftCreationTime.dwLowDateTime); /* CreationTime */ Stream_Write_UINT32(output, - file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */ - Stream_Write_UINT32(output, - file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */ - Stream_Write_UINT32( - output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ - Stream_Write_UINT32( - output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ + find_data->ftCreationTime.dwHighDateTime); /* CreationTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ + find_data->ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + find_data->ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + find_data->ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */ - Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */ - Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */ - Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */ - Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */ - Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */ + find_data->ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, find_data->ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, find_data->ftLastWriteTime.dwHighDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, find_data->nFileSizeLow); /* EndOfFile */ + Stream_Write_UINT32(output, find_data->nFileSizeHigh); /* EndOfFile */ + Stream_Write_UINT32(output, find_data->nFileSizeLow); /* AllocationSize */ + Stream_Write_UINT32(output, find_data->nFileSizeHigh); /* AllocationSize */ + Stream_Write_UINT32(output, find_data->dwFileAttributes); /* FileAttributes */ Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */ Stream_Write_UINT32(output, 0); /* EaSize */ - Stream_Write(output, file->find_data.cFileName, length); + Stream_Write(output, find_data->cFileName, length); break; case FileBothDirectoryInformation: @@ -957,33 +612,30 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, (UINT32)(93 + length)); /* Length */ Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ + Stream_Write_UINT32(output, find_data->ftCreationTime.dwLowDateTime); /* CreationTime */ Stream_Write_UINT32(output, - file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */ + find_data->ftCreationTime.dwHighDateTime); /* CreationTime */ Stream_Write_UINT32(output, - file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */ - Stream_Write_UINT32( - output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ - Stream_Write_UINT32( - output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ + find_data->ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ + find_data->ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + find_data->ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */ - Stream_Write_UINT32(output, - file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */ - Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */ - Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */ - Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */ - Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */ - Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */ + find_data->ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, find_data->ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, find_data->ftLastWriteTime.dwHighDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, find_data->nFileSizeLow); /* EndOfFile */ + Stream_Write_UINT32(output, find_data->nFileSizeHigh); /* EndOfFile */ + Stream_Write_UINT32(output, find_data->nFileSizeLow); /* AllocationSize */ + Stream_Write_UINT32(output, find_data->nFileSizeHigh); /* AllocationSize */ + Stream_Write_UINT32(output, find_data->dwFileAttributes); /* FileAttributes */ Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */ Stream_Write_UINT32(output, 0); /* EaSize */ Stream_Write_UINT8(output, 0); /* ShortNameLength */ /* Reserved(1), MUST NOT be added! */ Stream_Zero(output, 24); /* ShortName */ - Stream_Write(output, file->find_data.cFileName, length); + Stream_Write(output, find_data->cFileName, length); break; case FileNamesInformation: @@ -999,7 +651,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ Stream_Write_UINT32(output, (UINT32)length); /* FileNameLength */ - Stream_Write(output, file->find_data.cFileName, length); + Stream_Write(output, find_data->cFileName, length); break; default: @@ -1015,7 +667,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT return FALSE; } -const uintptr_t drive_file_get_id(DRIVE_FILE* drive) +uintptr_t drive_file_get_id(DRIVE_FILE* drive) { WINPR_ASSERT(drive); return drive->id; @@ -1026,82 +678,25 @@ BOOL drive_file_is_dir_not_empty(DRIVE_FILE* drive) if (!drive) return FALSE; - if (!drive->is_dir) - return FALSE; - - return !PathIsDirectoryEmptyW(drive->fullpath); + WINPR_ASSERT(drive->context); + WINPR_ASSERT(drive->backend); + WINPR_ASSERT(drive->backend->empty); + return drive->backend->empty(drive->context); } -char* drive_file_resolve_path(const char* backend, const char* what) +WINPR_ATTR_MALLOC(free, 1) +char* drive_file_resolve_path(const rdpDriveDriver* backend, 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_ASSERT(backend); + WINPR_ASSERT(backend->resolve_path); + return backend->resolve_path(what); } -char* drive_file_resolve_name(const char* backend, const char* path, const char* suggested) +WINPR_ATTR_MALLOC(free, 1) +char* drive_file_resolve_name(const rdpDriveDriver* backend, 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_ASSERT(backend); + WINPR_ASSERT(backend->resolve_name); + return backend->resolve_name(path, suggested); } diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h index da0a35a4e231..164b48a5a471 100644 --- a/channels/drive/client/drive_file.h +++ b/channels/drive/client/drive_file.h @@ -27,8 +27,8 @@ #define FREERDP_CHANNEL_DRIVE_CLIENT_FILE_H #include -#include #include +#include #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); diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c index e62563547de6..d50da4a2f8ee 100644 --- a/channels/drive/client/drive_main.c +++ b/channels/drive/client/drive_main.c @@ -30,8 +30,6 @@ #include #include -#include -#include #include #include #include @@ -43,6 +41,7 @@ #include #include +#include #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); } +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 char* backend, const char* path, - const char* name, BOOL automount) +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 @@ static DRIVE_DEVICE* drive_new(rdpContext* rdpcontext, const char* backend, cons * @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) { diff --git a/channels/drive/client/file/CMakeLists.txt b/channels/drive/client/file/CMakeLists.txt new file mode 100644 index 000000000000..0ffedd268af3 --- /dev/null +++ b/channels/drive/client/file/CMakeLists.txt @@ -0,0 +1,30 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2024 Armin Novak +# 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 "") diff --git a/channels/drive/client/file/drive_backend_file.c b/channels/drive/client/file/drive_backend_file.c new file mode 100644 index 000000000000..73f44cbde85c --- /dev/null +++ b/channels/drive/client/file/drive_backend_file.c @@ -0,0 +1,579 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * File System Virtual Channel + * + * Copyright 2024 Armin Novak + * 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 +#include +#include + +#include +#include + +#include +#include +#include + +#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; +} diff --git a/include/freerdp/client/drive.h b/include/freerdp/client/drive.h new file mode 100644 index 000000000000..edb76f5d1772 --- /dev/null +++ b/include/freerdp/client/drive.h @@ -0,0 +1,154 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Drive Virtual Channel + * + * Copyright 2024 Armin Novak + * 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 + +#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