[client,cliprdr] refactor file clipboard

This commit is contained in:
akallabeth
2025-05-23 14:19:56 +02:00
parent 4ab07eaff6
commit 5d088ce9c0

View File

@@ -54,7 +54,6 @@
#include <freerdp/client/client_cliprdr_file.h>
#define MAX_CLIP_DATA_DIR_LEN 10
#define NO_CLIP_DATA_ID (UINT64_C(1) << 32)
#define WIN32_FILETIME_TO_UNIX_EPOCH INT64_C(11644473600)
@@ -83,7 +82,10 @@ struct sCliprdrFuseFile
CliprdrFuseFile* parent;
wArrayList* children;
char* filename;
size_t filename_len;
const char* filename;
size_t filename_with_root_len;
char* filename_with_root;
UINT32 list_idx;
fuse_ino_t ino;
@@ -209,22 +211,40 @@ static void fuse_file_free(void* data)
free(fuse_file);
}
static CliprdrFuseFile* fuse_file_new(void)
WINPR_ATTR_FORMAT_ARG(1, 2)
WINPR_ATTR_MALLOC(fuse_file_free, 1)
static CliprdrFuseFile* fuse_file_new(WINPR_FORMAT_ARG const char* fmt, ...)
{
CliprdrFuseFile* fuse_file = NULL;
fuse_file = calloc(1, sizeof(CliprdrFuseFile));
if (!fuse_file)
CliprdrFuseFile* file = calloc(1, sizeof(CliprdrFuseFile));
if (!file)
return NULL;
fuse_file->children = ArrayList_New(FALSE);
if (!fuse_file->children)
file->children = ArrayList_New(FALSE);
if (!file->children)
goto fail;
WINPR_ASSERT(fmt);
va_list ap;
va_start(ap, fmt);
const int rc =
winpr_vasprintf(&file->filename_with_root, &file->filename_with_root_len, fmt, ap);
va_end(ap);
if (rc < 0)
goto fail;
if (file->filename_with_root && (file->filename_with_root_len > 0))
{
free(fuse_file);
return NULL;
file->filename_len = 0;
file->filename = strrchr(file->filename_with_root, '/') + 1;
if (file->filename)
file->filename_len = strnlen(file->filename, file->filename_with_root_len);
}
return fuse_file;
return file;
fail:
fuse_file_free(file);
return NULL;
}
static void clip_data_entry_free(void* data)
@@ -393,7 +413,7 @@ static BOOL notify_delete_child(void* data, WINPR_ATTR_UNUSED size_t index, va_l
WINPR_ASSERT(file_context->fuse_sess);
fuse_lowlevel_notify_delete(file_context->fuse_sess, parent->ino, child->ino, child->filename,
strlen(child->filename));
child->filename_len);
return TRUE;
}
@@ -478,7 +498,7 @@ static void clear_selection(CliprdrFileContext* file_context, BOOL all_selection
{
fuse_lowlevel_notify_delete(file_context->fuse_sess, file_context->root_dir->ino,
clip_data_dir->ino, clip_data_dir->filename,
strlen(clip_data_dir->filename));
clip_data_dir->filename_len);
}
}
ArrayList_Free(clear_context.fuse_files);
@@ -671,7 +691,7 @@ get_fuse_file_by_name_from_parent(WINPR_ATTR_UNUSED CliprdrFileContext* file_con
WINPR_ASSERT(child);
if (strcmp(name, child->filename) == 0)
if (strncmp(name, child->filename, child->filename_len + 1) == 0)
return child;
}
@@ -1696,34 +1716,17 @@ static CliprdrFuseFile* clip_data_dir_new(CliprdrFileContext* file_context, BOOL
UINT32 clip_data_id)
{
CliprdrFuseFile* root_dir = NULL;
CliprdrFuseFile* clip_data_dir = NULL;
size_t path_length = 0;
WINPR_ASSERT(file_context);
clip_data_dir = fuse_file_new();
UINT64 data_id = clip_data_id;
if (!has_clip_data_id)
data_id = NO_CLIP_DATA_ID;
CliprdrFuseFile* clip_data_dir = fuse_file_new("/%" PRIu64, data_id);
if (!clip_data_dir)
return NULL;
path_length = 1 + MAX_CLIP_DATA_DIR_LEN + 1;
clip_data_dir->filename_with_root = calloc(path_length, sizeof(char));
if (!clip_data_dir->filename_with_root)
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to allocate filename");
fuse_file_free(clip_data_dir);
return NULL;
}
if (has_clip_data_id)
(void)_snprintf(clip_data_dir->filename_with_root, path_length, "/%u",
(unsigned)clip_data_id);
else
(void)_snprintf(clip_data_dir->filename_with_root, path_length, "/%" PRIu64,
NO_CLIP_DATA_ID);
clip_data_dir->filename = strrchr(clip_data_dir->filename_with_root, '/') + 1;
clip_data_dir->ino = get_next_free_inode(file_context);
clip_data_dir->is_directory = TRUE;
clip_data_dir->is_readonly = TRUE;
@@ -1778,7 +1781,8 @@ static BOOL is_fuse_file_not_parent(WINPR_ATTR_UNUSED const void* key, void* val
if (!fuse_file->is_directory)
return TRUE;
if (strcmp(find_context->parent_path, fuse_file->filename_with_root) == 0)
if (strncmp(find_context->parent_path, fuse_file->filename_with_root,
fuse_file->filename_with_root_len + 1) == 0)
{
find_context->parent = fuse_file;
return FALSE;
@@ -1812,126 +1816,122 @@ static CliprdrFuseFile* get_parent_directory(CliprdrFileContext* file_context, c
return find_context.parent;
}
// NOLINTBEGIN(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
static BOOL selection_handle_file(CliprdrFileContext* file_context,
CliprdrFuseClipDataEntry* clip_data_entry, uint32_t index,
const FILEDESCRIPTORW* file)
{
WINPR_ASSERT(file_context);
WINPR_ASSERT(clip_data_entry);
WINPR_ASSERT(file);
CliprdrFuseFile* clip_data_dir = clip_data_entry->clip_data_dir;
WINPR_ASSERT(clip_data_dir);
char filename[ARRAYSIZE(file->cFileName) * 8] = { 0 };
const SSIZE_T filenamelen = ConvertWCharNToUtf8(file->cFileName, ARRAYSIZE(file->cFileName),
filename, ARRAYSIZE(filename) - 1);
if (filenamelen < 0)
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to convert filename");
return FALSE;
}
for (size_t j = 0; filename[j]; ++j)
{
if (filename[j] == '\\')
filename[j] = '/';
}
BOOL crc = FALSE;
CliprdrFuseFile* fuse_file = fuse_file_new(
"%.*s/%s", WINPR_ASSERTING_INT_CAST(int, clip_data_dir->filename_with_root_len),
clip_data_dir->filename_with_root, filename);
if (!fuse_file)
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to create FUSE file");
goto end;
}
fuse_file->parent = get_parent_directory(file_context, fuse_file->filename_with_root);
if (!fuse_file->parent)
{
WLog_Print(file_context->log, WLOG_ERROR, "Found no parent for FUSE file");
goto end;
}
if (!ArrayList_Append(fuse_file->parent->children, fuse_file))
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to append FUSE file");
goto end;
}
fuse_file->list_idx = index;
fuse_file->ino = get_next_free_inode(file_context);
fuse_file->has_clip_data_id = clip_data_entry->has_clip_data_id;
fuse_file->clip_data_id = clip_data_entry->clip_data_id;
if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
fuse_file->is_directory = TRUE;
if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
fuse_file->is_readonly = TRUE;
if (file->dwFlags & FD_FILESIZE)
{
fuse_file->size = ((UINT64)file->nFileSizeHigh << 32) + file->nFileSizeLow;
fuse_file->has_size = TRUE;
}
if (file->dwFlags & FD_WRITESTIME)
{
INT64 filetime = 0;
filetime = file->ftLastWriteTime.dwHighDateTime;
filetime <<= 32;
filetime += file->ftLastWriteTime.dwLowDateTime;
fuse_file->last_write_time_unix =
1LL * filetime / (10LL * 1000LL * 1000LL) - WIN32_FILETIME_TO_UNIX_EPOCH;
fuse_file->has_last_write_time = TRUE;
}
if (!HashTable_Insert(file_context->inode_table, (void*)(uintptr_t)fuse_file->ino, fuse_file))
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to insert inode into inode table");
goto end;
}
crc = TRUE;
end:
if (!crc)
{
fuse_file_free(fuse_file);
clear_entry_selection(clip_data_entry);
return FALSE;
}
return TRUE;
}
// NOLINTEND(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
static BOOL set_selection_for_clip_data_entry(CliprdrFileContext* file_context,
CliprdrFuseClipDataEntry* clip_data_entry,
const FILEDESCRIPTORW* files, UINT32 n_files)
{
CliprdrFuseFile* clip_data_dir = NULL;
WINPR_ASSERT(file_context);
WINPR_ASSERT(clip_data_entry);
WINPR_ASSERT(files);
clip_data_dir = clip_data_entry->clip_data_dir;
WINPR_ASSERT(clip_data_dir);
if (clip_data_entry->has_clip_data_id)
WLog_Print(file_context->log, WLOG_DEBUG, "Setting selection for clipDataId %u",
clip_data_entry->clip_data_id);
else
WLog_Print(file_context->log, WLOG_DEBUG, "Setting selection");
// NOLINTBEGIN(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
for (UINT32 i = 0; i < n_files; ++i)
{
const FILEDESCRIPTORW* file = &files[i];
CliprdrFuseFile* fuse_file = NULL;
char* filename = NULL;
size_t path_length = 0;
fuse_file = fuse_file_new();
if (!fuse_file)
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to create FUSE file");
clear_entry_selection(clip_data_entry);
if (!selection_handle_file(file_context, clip_data_entry, i, file))
return FALSE;
}
filename = ConvertWCharToUtf8Alloc(file->cFileName, NULL);
if (!filename)
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to convert filename");
fuse_file_free(fuse_file);
clear_entry_selection(clip_data_entry);
return FALSE;
}
for (size_t j = 0; filename[j]; ++j)
{
if (filename[j] == '\\')
filename[j] = '/';
}
path_length = strlen(clip_data_dir->filename_with_root) + 1 + strlen(filename) + 1;
fuse_file->filename_with_root = calloc(path_length, sizeof(char));
if (!fuse_file->filename_with_root)
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to allocate filename");
free(filename);
fuse_file_free(fuse_file);
clear_entry_selection(clip_data_entry);
return FALSE;
}
(void)_snprintf(fuse_file->filename_with_root, path_length, "%s/%s",
clip_data_dir->filename_with_root, filename);
free(filename);
fuse_file->filename = strrchr(fuse_file->filename_with_root, '/') + 1;
fuse_file->parent = get_parent_directory(file_context, fuse_file->filename_with_root);
if (!fuse_file->parent)
{
WLog_Print(file_context->log, WLOG_ERROR, "Found no parent for FUSE file");
fuse_file_free(fuse_file);
clear_entry_selection(clip_data_entry);
return FALSE;
}
if (!ArrayList_Append(fuse_file->parent->children, fuse_file))
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to append FUSE file");
fuse_file_free(fuse_file);
clear_entry_selection(clip_data_entry);
return FALSE;
}
fuse_file->list_idx = i;
fuse_file->ino = get_next_free_inode(file_context);
fuse_file->has_clip_data_id = clip_data_entry->has_clip_data_id;
fuse_file->clip_data_id = clip_data_entry->clip_data_id;
if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
fuse_file->is_directory = TRUE;
if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
fuse_file->is_readonly = TRUE;
if (file->dwFlags & FD_FILESIZE)
{
fuse_file->size = ((UINT64)file->nFileSizeHigh << 32) + file->nFileSizeLow;
fuse_file->has_size = TRUE;
}
if (file->dwFlags & FD_WRITESTIME)
{
INT64 filetime = 0;
filetime = file->ftLastWriteTime.dwHighDateTime;
filetime <<= 32;
filetime += file->ftLastWriteTime.dwLowDateTime;
fuse_file->last_write_time_unix =
1LL * filetime / (10LL * 1000LL * 1000LL) - WIN32_FILETIME_TO_UNIX_EPOCH;
fuse_file->has_last_write_time = TRUE;
}
if (!HashTable_Insert(file_context->inode_table, (void*)(uintptr_t)fuse_file->ino,
fuse_file))
{
WLog_Print(file_context->log, WLOG_ERROR, "Failed to insert inode into inode table");
fuse_file_free(fuse_file);
clear_entry_selection(clip_data_entry);
return FALSE;
}
}
// NOLINTEND(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
if (clip_data_entry->has_clip_data_id)
WLog_Print(file_context->log, WLOG_DEBUG, "Selection set for clipDataId %u",
@@ -2362,20 +2362,10 @@ static CliprdrFuseFile* fuse_file_new_root(CliprdrFileContext* file_context)
{
CliprdrFuseFile* root_dir = NULL;
root_dir = fuse_file_new();
root_dir = fuse_file_new("/");
if (!root_dir)
return NULL;
root_dir->filename_with_root = calloc(2, sizeof(char));
if (!root_dir->filename_with_root)
{
fuse_file_free(root_dir);
return NULL;
}
(void)_snprintf(root_dir->filename_with_root, 2, "/");
root_dir->filename = root_dir->filename_with_root;
root_dir->ino = FUSE_ROOT_ID;
root_dir->is_directory = TRUE;
root_dir->is_readonly = TRUE;