24 #include <freerdp/config.h>
30 #define FUSE_USE_VERSION 30
31 #include <fuse_lowlevel.h>
34 #if defined(WITH_FUSE)
35 #include <sys/mount.h>
41 #include <winpr/crt.h>
42 #include <winpr/string.h>
43 #include <winpr/assert.h>
44 #include <winpr/image.h>
45 #include <winpr/stream.h>
46 #include <winpr/clipboard.h>
47 #include <winpr/path.h>
49 #include <freerdp/utils/signal.h>
50 #include <freerdp/log.h>
51 #include <freerdp/client/cliprdr.h>
52 #include <freerdp/channels/channels.h>
53 #include <freerdp/channels/cliprdr.h>
55 #include <freerdp/client/client_cliprdr_file.h>
57 #define MAX_CLIP_DATA_DIR_LEN 10
58 #define NO_CLIP_DATA_ID (UINT64_C(1) << 32)
59 #define WIN32_FILETIME_TO_UNIX_EPOCH INT64_C(11644473600)
61 #ifdef WITH_DEBUG_CLIPRDR
62 #define DEBUG_CLIPRDR(log, ...) WLog_Print(log, WLOG_DEBUG, __VA_ARGS__)
64 #define DEBUG_CLIPRDR(log, ...) \
70 #if defined(WITH_FUSE)
71 typedef enum eFuseLowlevelOperationType
73 FUSE_LL_OPERATION_NONE,
74 FUSE_LL_OPERATION_LOOKUP,
75 FUSE_LL_OPERATION_GETATTR,
76 FUSE_LL_OPERATION_READ,
77 } FuseLowlevelOperationType;
79 typedef struct sCliprdrFuseFile CliprdrFuseFile;
81 struct sCliprdrFuseFile
83 CliprdrFuseFile* parent;
87 char* filename_with_root;
97 BOOL has_last_write_time;
98 INT64 last_write_time_unix;
100 BOOL has_clip_data_id;
106 CliprdrFileContext* file_context;
108 CliprdrFuseFile* clip_data_dir;
110 BOOL has_clip_data_id;
112 } CliprdrFuseClipDataEntry;
116 CliprdrFileContext* file_context;
118 wArrayList* fuse_files;
121 BOOL has_clip_data_id;
123 } FuseFileClearContext;
127 FuseLowlevelOperationType operation_type;
128 CliprdrFuseFile* fuse_file;
131 } CliprdrFuseRequest;
135 CliprdrFuseFile* parent;
137 } CliprdrFuseFindParentContext;
145 CliprdrFileContext* context;
153 CliprdrLocalFile* files;
154 CliprdrFileContext* context;
155 } CliprdrLocalStream;
157 struct cliprdr_file_context
159 #if defined(WITH_FUSE)
161 HANDLE fuse_start_sync;
162 HANDLE fuse_stop_sync;
164 struct fuse_session* fuse_sess;
165 #if FUSE_USE_VERSION < 30
166 struct fuse_chan* ch;
169 wHashTable* inode_table;
170 wHashTable* clip_data_table;
171 wHashTable* request_table;
173 CliprdrFuseFile* root_dir;
174 CliprdrFuseClipDataEntry* clip_data_entry_without_id;
175 UINT32 current_clip_data_id;
178 UINT32 next_clip_data_id;
179 UINT32 next_stream_id;
183 BOOL file_formats_registered;
184 UINT32 file_capability_flags;
186 UINT32 local_lock_id;
188 wHashTable* local_streams;
191 CliprdrClientContext* context;
194 BYTE server_data_hash[WINPR_SHA256_DIGEST_LENGTH];
195 BYTE client_data_hash[WINPR_SHA256_DIGEST_LENGTH];
198 #if defined(WITH_FUSE)
199 static void fuse_file_free(
void* data)
201 CliprdrFuseFile* fuse_file = data;
206 ArrayList_Free(fuse_file->children);
207 free(fuse_file->filename_with_root);
212 static CliprdrFuseFile* fuse_file_new(
void)
214 CliprdrFuseFile* fuse_file = NULL;
216 fuse_file = calloc(1,
sizeof(CliprdrFuseFile));
220 fuse_file->children = ArrayList_New(FALSE);
221 if (!fuse_file->children)
230 static void clip_data_entry_free(
void* data)
232 CliprdrFuseClipDataEntry* clip_data_entry = data;
234 if (!clip_data_entry)
237 if (clip_data_entry->has_clip_data_id)
239 CliprdrFileContext* file_context = clip_data_entry->file_context;
242 WINPR_ASSERT(file_context);
244 unlock_clipboard_data.common.msgType = CB_UNLOCK_CLIPDATA;
245 unlock_clipboard_data.clipDataId = clip_data_entry->clip_data_id;
247 file_context->context->ClientUnlockClipboardData(file_context->context,
248 &unlock_clipboard_data);
249 clip_data_entry->has_clip_data_id = FALSE;
251 WLog_Print(file_context->log, WLOG_DEBUG,
"Destroyed ClipDataEntry with id %u",
252 clip_data_entry->clip_data_id);
255 free(clip_data_entry);
258 static BOOL does_server_support_clipdata_locking(CliprdrFileContext* file_context)
260 WINPR_ASSERT(file_context);
262 if (cliprdr_file_context_remote_get_flags(file_context) & CB_CAN_LOCK_CLIPDATA)
268 static UINT32 get_next_free_clip_data_id(CliprdrFileContext* file_context)
270 UINT32 clip_data_id = 0;
272 WINPR_ASSERT(file_context);
274 HashTable_Lock(file_context->inode_table);
275 clip_data_id = file_context->next_clip_data_id;
276 while (clip_data_id == 0 ||
277 HashTable_GetItemValue(file_context->clip_data_table, (
void*)(uintptr_t)clip_data_id))
280 file_context->next_clip_data_id = clip_data_id + 1;
281 HashTable_Unlock(file_context->inode_table);
286 static CliprdrFuseClipDataEntry* clip_data_entry_new(CliprdrFileContext* file_context,
287 BOOL needs_clip_data_id)
289 CliprdrFuseClipDataEntry* clip_data_entry = NULL;
292 WINPR_ASSERT(file_context);
294 clip_data_entry = calloc(1,
sizeof(CliprdrFuseClipDataEntry));
295 if (!clip_data_entry)
298 clip_data_entry->file_context = file_context;
299 clip_data_entry->clip_data_id = get_next_free_clip_data_id(file_context);
301 if (!needs_clip_data_id)
302 return clip_data_entry;
304 lock_clipboard_data.common.msgType = CB_LOCK_CLIPDATA;
305 lock_clipboard_data.clipDataId = clip_data_entry->clip_data_id;
307 if (file_context->context->ClientLockClipboardData(file_context->context, &lock_clipboard_data))
309 HashTable_Lock(file_context->inode_table);
310 clip_data_entry_free(clip_data_entry);
311 HashTable_Unlock(file_context->inode_table);
314 clip_data_entry->has_clip_data_id = TRUE;
316 WLog_Print(file_context->log, WLOG_DEBUG,
"Created ClipDataEntry with id %u",
317 clip_data_entry->clip_data_id);
319 return clip_data_entry;
322 static BOOL should_remove_fuse_file(CliprdrFuseFile* fuse_file, BOOL all_files,
323 BOOL has_clip_data_id, UINT32 clip_data_id)
328 if (fuse_file->ino == FUSE_ROOT_ID)
330 if (!fuse_file->has_clip_data_id && !has_clip_data_id)
332 if (fuse_file->has_clip_data_id && has_clip_data_id &&
333 (fuse_file->clip_data_id == clip_data_id))
339 static BOOL maybe_clear_fuse_request(
const void* key,
void* value,
void* arg)
341 CliprdrFuseRequest* fuse_request = value;
342 FuseFileClearContext* clear_context = arg;
343 CliprdrFileContext* file_context = clear_context->file_context;
344 CliprdrFuseFile* fuse_file = fuse_request->fuse_file;
346 WINPR_ASSERT(file_context);
348 if (!should_remove_fuse_file(fuse_file, clear_context->all_files,
349 clear_context->has_clip_data_id, clear_context->clip_data_id))
352 DEBUG_CLIPRDR(file_context->log,
"Clearing FileContentsRequest for file \"%s\"",
353 fuse_file->filename_with_root);
355 fuse_reply_err(fuse_request->fuse_req, EIO);
356 HashTable_Remove(file_context->request_table, key);
361 static BOOL maybe_steal_inode(
const void* key,
void* value,
void* arg)
363 CliprdrFuseFile* fuse_file = value;
364 FuseFileClearContext* clear_context = arg;
365 CliprdrFileContext* file_context = clear_context->file_context;
367 WINPR_ASSERT(file_context);
369 if (should_remove_fuse_file(fuse_file, clear_context->all_files,
370 clear_context->has_clip_data_id, clear_context->clip_data_id))
372 if (!ArrayList_Append(clear_context->fuse_files, fuse_file))
373 WLog_Print(file_context->log, WLOG_ERROR,
374 "Failed to append FUSE file to list for deletion");
376 HashTable_Remove(file_context->inode_table, key);
382 static BOOL notify_delete_child(
void* data,
size_t index, va_list ap)
384 CliprdrFuseFile* child = data;
388 CliprdrFileContext* file_context = va_arg(ap, CliprdrFileContext*);
389 CliprdrFuseFile* parent = va_arg(ap, CliprdrFuseFile*);
391 WINPR_ASSERT(file_context);
392 WINPR_ASSERT(parent);
394 WINPR_ASSERT(file_context->fuse_sess);
395 fuse_lowlevel_notify_delete(file_context->fuse_sess, parent->ino, child->ino, child->filename,
396 strlen(child->filename));
401 static BOOL invalidate_inode(
void* data,
size_t index, va_list ap)
403 CliprdrFuseFile* fuse_file = data;
405 WINPR_ASSERT(fuse_file);
407 CliprdrFileContext* file_context = va_arg(ap, CliprdrFileContext*);
408 WINPR_ASSERT(file_context);
409 WINPR_ASSERT(file_context->fuse_sess);
411 ArrayList_ForEach(fuse_file->children, notify_delete_child, file_context, fuse_file);
413 DEBUG_CLIPRDR(file_context->log,
"Invalidating inode %lu for file \"%s\"", fuse_file->ino,
414 fuse_file->filename);
415 fuse_lowlevel_notify_inval_inode(file_context->fuse_sess, fuse_file->ino, 0, 0);
416 WLog_Print(file_context->log, WLOG_DEBUG,
"Inode %lu invalidated", fuse_file->ino);
421 static void clear_selection(CliprdrFileContext* file_context, BOOL all_selections,
422 CliprdrFuseClipDataEntry* clip_data_entry)
424 FuseFileClearContext clear_context = { 0 };
425 CliprdrFuseFile* root_dir = NULL;
426 CliprdrFuseFile* clip_data_dir = NULL;
428 WINPR_ASSERT(file_context);
430 root_dir = file_context->root_dir;
431 WINPR_ASSERT(root_dir);
433 clear_context.file_context = file_context;
434 clear_context.fuse_files = ArrayList_New(FALSE);
435 WINPR_ASSERT(clear_context.fuse_files);
437 wObject* aobj = ArrayList_Object(clear_context.fuse_files);
439 aobj->fnObjectFree = fuse_file_free;
443 clip_data_dir = clip_data_entry->clip_data_dir;
444 clip_data_entry->clip_data_dir = NULL;
446 WINPR_ASSERT(clip_data_dir);
448 ArrayList_Remove(root_dir->children, clip_data_dir);
450 clear_context.has_clip_data_id = clip_data_dir->has_clip_data_id;
451 clear_context.clip_data_id = clip_data_dir->clip_data_id;
453 clear_context.all_files = all_selections;
455 if (clip_data_entry && clip_data_entry->has_clip_data_id)
456 WLog_Print(file_context->log, WLOG_DEBUG,
"Clearing selection for clipDataId %u",
457 clip_data_entry->clip_data_id);
459 WLog_Print(file_context->log, WLOG_DEBUG,
"Clearing selection%s",
460 all_selections ?
"s" :
"");
462 HashTable_Foreach(file_context->request_table, maybe_clear_fuse_request, &clear_context);
463 HashTable_Foreach(file_context->inode_table, maybe_steal_inode, &clear_context);
464 HashTable_Unlock(file_context->inode_table);
466 if (file_context->fuse_sess)
476 ArrayList_ForEach(clear_context.fuse_files, invalidate_inode, file_context);
479 fuse_lowlevel_notify_delete(file_context->fuse_sess, file_context->root_dir->ino,
480 clip_data_dir->ino, clip_data_dir->filename,
481 strlen(clip_data_dir->filename));
484 ArrayList_Free(clear_context.fuse_files);
486 HashTable_Lock(file_context->inode_table);
487 if (clip_data_entry && clip_data_entry->has_clip_data_id)
488 WLog_Print(file_context->log, WLOG_DEBUG,
"Selection cleared for clipDataId %u",
489 clip_data_entry->clip_data_id);
491 WLog_Print(file_context->log, WLOG_DEBUG,
"Selection%s cleared", all_selections ?
"s" :
"");
494 static void clear_entry_selection(CliprdrFuseClipDataEntry* clip_data_entry)
496 WINPR_ASSERT(clip_data_entry);
498 if (!clip_data_entry->clip_data_dir)
501 clear_selection(clip_data_entry->file_context, FALSE, clip_data_entry);
504 static void clear_no_cdi_entry(CliprdrFileContext* file_context)
506 WINPR_ASSERT(file_context);
508 WINPR_ASSERT(file_context->inode_table);
509 HashTable_Lock(file_context->inode_table);
510 if (file_context->clip_data_entry_without_id)
512 clear_entry_selection(file_context->clip_data_entry_without_id);
514 clip_data_entry_free(file_context->clip_data_entry_without_id);
515 file_context->clip_data_entry_without_id = NULL;
517 HashTable_Unlock(file_context->inode_table);
520 static BOOL clear_clip_data_entries(
const void* key,
void* value,
void* arg)
522 clear_entry_selection(value);
527 static void clear_cdi_entries(CliprdrFileContext* file_context)
529 WINPR_ASSERT(file_context);
531 HashTable_Lock(file_context->inode_table);
532 HashTable_Foreach(file_context->clip_data_table, clear_clip_data_entries, NULL);
534 HashTable_Clear(file_context->clip_data_table);
535 HashTable_Unlock(file_context->inode_table);
538 static UINT prepare_clip_data_entry_with_id(CliprdrFileContext* file_context)
540 CliprdrFuseClipDataEntry* clip_data_entry = NULL;
542 WINPR_ASSERT(file_context);
544 clip_data_entry = clip_data_entry_new(file_context, TRUE);
545 if (!clip_data_entry)
547 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to create clipDataEntry");
548 return ERROR_INTERNAL_ERROR;
551 HashTable_Lock(file_context->inode_table);
552 if (!HashTable_Insert(file_context->clip_data_table,
553 (
void*)(uintptr_t)clip_data_entry->clip_data_id, clip_data_entry))
555 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to insert clipDataEntry");
556 clip_data_entry_free(clip_data_entry);
557 HashTable_Unlock(file_context->inode_table);
558 return ERROR_INTERNAL_ERROR;
560 HashTable_Unlock(file_context->inode_table);
564 file_context->current_clip_data_id = clip_data_entry->clip_data_id;
566 return CHANNEL_RC_OK;
569 static UINT prepare_clip_data_entry_without_id(CliprdrFileContext* file_context)
571 WINPR_ASSERT(file_context);
572 WINPR_ASSERT(!file_context->clip_data_entry_without_id);
574 file_context->clip_data_entry_without_id = clip_data_entry_new(file_context, FALSE);
575 if (!file_context->clip_data_entry_without_id)
577 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to create clipDataEntry");
578 return ERROR_INTERNAL_ERROR;
581 return CHANNEL_RC_OK;
585 UINT cliprdr_file_context_notify_new_server_format_list(CliprdrFileContext* file_context)
587 WINPR_ASSERT(file_context);
588 WINPR_ASSERT(file_context->context);
590 #if defined(WITH_FUSE)
591 clear_no_cdi_entry(file_context);
593 clear_cdi_entries(file_context);
595 if (does_server_support_clipdata_locking(file_context))
596 return prepare_clip_data_entry_with_id(file_context);
598 return prepare_clip_data_entry_without_id(file_context);
600 return CHANNEL_RC_OK;
604 UINT cliprdr_file_context_notify_new_client_format_list(CliprdrFileContext* file_context)
606 WINPR_ASSERT(file_context);
607 WINPR_ASSERT(file_context->context);
609 #if defined(WITH_FUSE)
610 clear_no_cdi_entry(file_context);
612 clear_cdi_entries(file_context);
615 return CHANNEL_RC_OK;
618 static CliprdrLocalStream* cliprdr_local_stream_new(CliprdrFileContext* context, UINT32 streamID,
619 const char* data,
size_t size);
620 static void cliprdr_file_session_terminate(CliprdrFileContext* file, BOOL stop_thread);
621 static BOOL local_stream_discard(
const void* key,
void* value,
void* arg);
623 static void writelog(wLog* log, DWORD level,
const char* fname,
const char* fkt,
size_t line, ...)
625 if (!WLog_IsLevelActive(log, level))
630 WLog_PrintMessageVA(log, WLOG_MESSAGE_TEXT, level, line, fname, fkt, ap);
634 #if defined(WITH_FUSE)
635 static void cliprdr_file_fuse_lookup(fuse_req_t req, fuse_ino_t parent,
const char* name);
636 static void cliprdr_file_fuse_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info* fi);
637 static void cliprdr_file_fuse_readdir(fuse_req_t req, fuse_ino_t ino,
size_t size, off_t off,
638 struct fuse_file_info* fi);
639 static void cliprdr_file_fuse_read(fuse_req_t req, fuse_ino_t ino,
size_t size, off_t off,
640 struct fuse_file_info* fi);
641 static void cliprdr_file_fuse_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info* fi);
642 static void cliprdr_file_fuse_opendir(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info* fi);
644 static const struct fuse_lowlevel_ops cliprdr_file_fuse_oper = {
645 .lookup = cliprdr_file_fuse_lookup,
646 .getattr = cliprdr_file_fuse_getattr,
647 .readdir = cliprdr_file_fuse_readdir,
648 .open = cliprdr_file_fuse_open,
649 .read = cliprdr_file_fuse_read,
650 .opendir = cliprdr_file_fuse_opendir,
653 static CliprdrFuseFile* get_fuse_file_by_ino(CliprdrFileContext* file_context, fuse_ino_t fuse_ino)
655 WINPR_ASSERT(file_context);
657 return HashTable_GetItemValue(file_context->inode_table, (
void*)(uintptr_t)fuse_ino);
660 static CliprdrFuseFile* get_fuse_file_by_name_from_parent(CliprdrFileContext* file_context,
661 CliprdrFuseFile* parent,
const char* name)
663 WINPR_ASSERT(file_context);
664 WINPR_ASSERT(parent);
666 for (
size_t i = 0; i < ArrayList_Count(parent->children); ++i)
668 CliprdrFuseFile* child = ArrayList_GetItem(parent->children, i);
672 if (strcmp(name, child->filename) == 0)
676 DEBUG_CLIPRDR(file_context->log,
"Requested file \"%s\" in directory \"%s\" does not exist",
677 name, parent->filename);
682 static CliprdrFuseRequest* cliprdr_fuse_request_new(CliprdrFileContext* file_context,
683 CliprdrFuseFile* fuse_file, fuse_req_t fuse_req,
684 FuseLowlevelOperationType operation_type)
686 CliprdrFuseRequest* fuse_request = NULL;
687 UINT32 stream_id = file_context->next_stream_id;
689 WINPR_ASSERT(file_context);
690 WINPR_ASSERT(fuse_file);
692 fuse_request = calloc(1,
sizeof(CliprdrFuseRequest));
695 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to allocate FUSE request for file \"%s\"",
696 fuse_file->filename_with_root);
700 fuse_request->fuse_file = fuse_file;
701 fuse_request->fuse_req = fuse_req;
702 fuse_request->operation_type = operation_type;
704 while (stream_id == 0 ||
705 HashTable_GetItemValue(file_context->request_table, (
void*)(uintptr_t)stream_id))
707 fuse_request->stream_id = stream_id;
709 file_context->next_stream_id = stream_id + 1;
711 if (!HashTable_Insert(file_context->request_table, (
void*)(uintptr_t)fuse_request->stream_id,
714 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to track FUSE request for file \"%s\"",
715 fuse_file->filename_with_root);
723 static BOOL request_file_size_async(CliprdrFileContext* file_context, CliprdrFuseFile* fuse_file,
724 fuse_req_t fuse_req, FuseLowlevelOperationType operation_type)
726 CliprdrFuseRequest* fuse_request = NULL;
729 WINPR_ASSERT(file_context);
730 WINPR_ASSERT(fuse_file);
732 fuse_request = cliprdr_fuse_request_new(file_context, fuse_file, fuse_req, operation_type);
736 file_contents_request.common.msgType = CB_FILECONTENTS_REQUEST;
737 file_contents_request.streamId = fuse_request->stream_id;
738 file_contents_request.listIndex = fuse_file->list_idx;
739 file_contents_request.dwFlags = FILECONTENTS_SIZE;
740 file_contents_request.cbRequested = 0x8;
741 file_contents_request.haveClipDataId = fuse_file->has_clip_data_id;
742 file_contents_request.clipDataId = fuse_file->clip_data_id;
744 if (file_context->context->ClientFileContentsRequest(file_context->context,
745 &file_contents_request))
747 WLog_Print(file_context->log, WLOG_ERROR,
748 "Failed to send FileContentsRequest for file \"%s\"",
749 fuse_file->filename_with_root);
750 HashTable_Remove(file_context->request_table, (
void*)(uintptr_t)fuse_request->stream_id);
753 DEBUG_CLIPRDR(file_context->log,
"Requested file size for file \"%s\" with stream id %u",
754 fuse_file->filename, fuse_request->stream_id);
759 static void write_file_attributes(CliprdrFuseFile* fuse_file,
struct stat* attr)
761 memset(attr, 0,
sizeof(
struct stat));
766 attr->st_ino = fuse_file->ino;
767 if (fuse_file->is_directory)
769 attr->st_mode = S_IFDIR | (fuse_file->is_readonly ? 0555 : 0755);
774 attr->st_mode = S_IFREG | (fuse_file->is_readonly ? 0444 : 0644);
776 attr->st_size = WINPR_ASSERTING_INT_CAST(__off_t, fuse_file->size);
778 attr->st_uid = getuid();
779 attr->st_gid = getgid();
780 attr->st_atime = attr->st_mtime = attr->st_ctime =
781 (fuse_file->has_last_write_time ? fuse_file->last_write_time_unix : time(NULL));
784 static void cliprdr_file_fuse_lookup(fuse_req_t fuse_req, fuse_ino_t parent_ino,
const char* name)
786 CliprdrFileContext* file_context = fuse_req_userdata(fuse_req);
787 CliprdrFuseFile* parent = NULL;
788 CliprdrFuseFile* fuse_file = NULL;
789 struct fuse_entry_param entry = { 0 };
791 WINPR_ASSERT(file_context);
793 HashTable_Lock(file_context->inode_table);
794 if (!(parent = get_fuse_file_by_ino(file_context, parent_ino)) || !parent->is_directory)
796 HashTable_Unlock(file_context->inode_table);
797 fuse_reply_err(fuse_req, ENOENT);
800 if (!(fuse_file = get_fuse_file_by_name_from_parent(file_context, parent, name)))
802 HashTable_Unlock(file_context->inode_table);
803 fuse_reply_err(fuse_req, ENOENT);
807 DEBUG_CLIPRDR(file_context->log,
"lookup() has been called for \"%s\"", name);
808 DEBUG_CLIPRDR(file_context->log,
"Parent is \"%s\", child is \"%s\"",
809 parent->filename_with_root, fuse_file->filename_with_root);
811 if (!fuse_file->is_directory && !fuse_file->has_size)
816 request_file_size_async(file_context, fuse_file, fuse_req, FUSE_LL_OPERATION_LOOKUP);
817 HashTable_Unlock(file_context->inode_table);
820 fuse_reply_err(fuse_req, EIO);
825 entry.ino = fuse_file->ino;
826 write_file_attributes(fuse_file, &entry.attr);
827 entry.attr_timeout = 1.0;
828 entry.entry_timeout = 1.0;
829 HashTable_Unlock(file_context->inode_table);
831 fuse_reply_entry(fuse_req, &entry);
834 static void cliprdr_file_fuse_getattr(fuse_req_t fuse_req, fuse_ino_t fuse_ino,
835 struct fuse_file_info* file_info)
837 CliprdrFileContext* file_context = fuse_req_userdata(fuse_req);
838 CliprdrFuseFile* fuse_file = NULL;
839 struct stat attr = { 0 };
841 WINPR_ASSERT(file_context);
843 HashTable_Lock(file_context->inode_table);
844 if (!(fuse_file = get_fuse_file_by_ino(file_context, fuse_ino)))
846 HashTable_Unlock(file_context->inode_table);
847 fuse_reply_err(fuse_req, ENOENT);
851 DEBUG_CLIPRDR(file_context->log,
"getattr() has been called for file \"%s\"",
852 fuse_file->filename_with_root);
854 if (!fuse_file->is_directory && !fuse_file->has_size)
859 request_file_size_async(file_context, fuse_file, fuse_req, FUSE_LL_OPERATION_GETATTR);
860 HashTable_Unlock(file_context->inode_table);
863 fuse_reply_err(fuse_req, EIO);
868 write_file_attributes(fuse_file, &attr);
869 HashTable_Unlock(file_context->inode_table);
871 fuse_reply_attr(fuse_req, &attr, 1.0);
874 static void cliprdr_file_fuse_open(fuse_req_t fuse_req, fuse_ino_t fuse_ino,
875 struct fuse_file_info* file_info)
877 CliprdrFileContext* file_context = fuse_req_userdata(fuse_req);
878 CliprdrFuseFile* fuse_file = NULL;
880 WINPR_ASSERT(file_context);
882 HashTable_Lock(file_context->inode_table);
883 if (!(fuse_file = get_fuse_file_by_ino(file_context, fuse_ino)))
885 HashTable_Unlock(file_context->inode_table);
886 fuse_reply_err(fuse_req, ENOENT);
889 if (fuse_file->is_directory)
891 HashTable_Unlock(file_context->inode_table);
892 fuse_reply_err(fuse_req, EISDIR);
895 HashTable_Unlock(file_context->inode_table);
897 if ((file_info->flags & O_ACCMODE) != O_RDONLY)
899 fuse_reply_err(fuse_req, EACCES);
904 file_info->direct_io = 1;
906 fuse_reply_open(fuse_req, file_info);
909 static BOOL request_file_range_async(CliprdrFileContext* file_context, CliprdrFuseFile* fuse_file,
910 fuse_req_t fuse_req, off_t offset,
size_t requested_size)
914 WINPR_ASSERT(file_context);
915 WINPR_ASSERT(fuse_file);
917 if (requested_size > UINT32_MAX)
920 CliprdrFuseRequest* fuse_request =
921 cliprdr_fuse_request_new(file_context, fuse_file, fuse_req, FUSE_LL_OPERATION_READ);
925 file_contents_request.common.msgType = CB_FILECONTENTS_REQUEST;
926 file_contents_request.streamId = fuse_request->stream_id;
927 file_contents_request.listIndex = fuse_file->list_idx;
928 file_contents_request.dwFlags = FILECONTENTS_RANGE;
929 file_contents_request.nPositionLow = (UINT32)(offset & 0xFFFFFFFF);
930 file_contents_request.nPositionHigh = (UINT32)((offset >> 32) & 0xFFFFFFFF);
931 file_contents_request.cbRequested = (UINT32)requested_size;
932 file_contents_request.haveClipDataId = fuse_file->has_clip_data_id;
933 file_contents_request.clipDataId = fuse_file->clip_data_id;
935 if (file_context->context->ClientFileContentsRequest(file_context->context,
936 &file_contents_request))
938 WLog_Print(file_context->log, WLOG_ERROR,
939 "Failed to send FileContentsRequest for file \"%s\"",
940 fuse_file->filename_with_root);
941 HashTable_Remove(file_context->request_table, (
void*)(uintptr_t)fuse_request->stream_id);
949 "Requested file range (%zu Bytes at offset %lu) for file \"%s\" with stream id %u",
950 requested_size, offset, fuse_file->filename, fuse_request->stream_id);
956 static void cliprdr_file_fuse_read(fuse_req_t fuse_req, fuse_ino_t fuse_ino,
size_t size,
957 off_t offset,
struct fuse_file_info* file_info)
959 CliprdrFileContext* file_context = fuse_req_userdata(fuse_req);
960 CliprdrFuseFile* fuse_file = NULL;
963 WINPR_ASSERT(file_context);
965 HashTable_Lock(file_context->inode_table);
966 if (!(fuse_file = get_fuse_file_by_ino(file_context, fuse_ino)))
968 HashTable_Unlock(file_context->inode_table);
969 fuse_reply_err(fuse_req, ENOENT);
972 if (fuse_file->is_directory)
974 HashTable_Unlock(file_context->inode_table);
975 fuse_reply_err(fuse_req, EISDIR);
978 if (!fuse_file->has_size || (offset < 0) || ((
size_t)offset > fuse_file->size))
980 HashTable_Unlock(file_context->inode_table);
981 fuse_reply_err(fuse_req, EINVAL);
985 size = MIN(size, 8ULL * 1024ULL * 1024ULL);
987 result = request_file_range_async(file_context, fuse_file, fuse_req, offset, size);
988 HashTable_Unlock(file_context->inode_table);
991 fuse_reply_err(fuse_req, EIO);
994 static void cliprdr_file_fuse_opendir(fuse_req_t fuse_req, fuse_ino_t fuse_ino,
995 struct fuse_file_info* file_info)
997 CliprdrFileContext* file_context = fuse_req_userdata(fuse_req);
998 CliprdrFuseFile* fuse_file = NULL;
1000 WINPR_ASSERT(file_context);
1002 HashTable_Lock(file_context->inode_table);
1003 if (!(fuse_file = get_fuse_file_by_ino(file_context, fuse_ino)))
1005 HashTable_Unlock(file_context->inode_table);
1006 fuse_reply_err(fuse_req, ENOENT);
1009 if (!fuse_file->is_directory)
1011 HashTable_Unlock(file_context->inode_table);
1012 fuse_reply_err(fuse_req, ENOTDIR);
1015 HashTable_Unlock(file_context->inode_table);
1017 if ((file_info->flags & O_ACCMODE) != O_RDONLY)
1019 fuse_reply_err(fuse_req, EACCES);
1023 fuse_reply_open(fuse_req, file_info);
1026 static void cliprdr_file_fuse_readdir(fuse_req_t fuse_req, fuse_ino_t fuse_ino,
size_t max_size,
1027 off_t offset,
struct fuse_file_info* file_info)
1029 CliprdrFileContext* file_context = fuse_req_userdata(fuse_req);
1030 CliprdrFuseFile* fuse_file = NULL;
1031 CliprdrFuseFile* child = NULL;
1032 struct stat attr = { 0 };
1033 size_t written_size = 0;
1034 size_t entry_size = 0;
1035 char* filename = NULL;
1038 WINPR_ASSERT(file_context);
1040 HashTable_Lock(file_context->inode_table);
1041 if (!(fuse_file = get_fuse_file_by_ino(file_context, fuse_ino)))
1043 HashTable_Unlock(file_context->inode_table);
1044 fuse_reply_err(fuse_req, ENOENT);
1047 if (!fuse_file->is_directory)
1049 HashTable_Unlock(file_context->inode_table);
1050 fuse_reply_err(fuse_req, ENOTDIR);
1054 DEBUG_CLIPRDR(file_context->log,
"Reading directory \"%s\" at offset %lu",
1055 fuse_file->filename_with_root, offset);
1057 if ((offset < 0) || ((
size_t)offset >= ArrayList_Count(fuse_file->children)))
1059 HashTable_Unlock(file_context->inode_table);
1060 fuse_reply_buf(fuse_req, NULL, 0);
1064 buf = calloc(max_size,
sizeof(
char));
1067 HashTable_Unlock(file_context->inode_table);
1068 fuse_reply_err(fuse_req, ENOMEM);
1073 for (off_t i = offset; i < 2; ++i)
1077 write_file_attributes(fuse_file, &attr);
1082 write_file_attributes(fuse_file->parent, &attr);
1083 attr.st_ino = fuse_file->parent ? attr.st_ino : FUSE_ROOT_ID;
1084 attr.st_mode = fuse_file->parent ? attr.st_mode : 0555;
1089 WINPR_ASSERT(FALSE);
1096 entry_size = fuse_add_direntry(fuse_req, buf + written_size, max_size - written_size,
1097 filename, &attr, i + 1);
1098 if (entry_size > max_size - written_size)
1101 written_size += entry_size;
1104 for (
size_t j = 0, i = 2; j < ArrayList_Count(fuse_file->children); ++j, ++i)
1106 if (i < (
size_t)offset)
1109 child = ArrayList_GetItem(fuse_file->children, j);
1111 write_file_attributes(child, &attr);
1113 fuse_add_direntry(fuse_req, buf + written_size, max_size - written_size,
1114 child->filename, &attr, WINPR_ASSERTING_INT_CAST(off_t, i + 1));
1115 if (entry_size > max_size - written_size)
1118 written_size += entry_size;
1120 HashTable_Unlock(file_context->inode_table);
1122 fuse_reply_buf(fuse_req, buf, written_size);
1126 static void fuse_abort(
int sig,
const char* signame,
void* context)
1128 CliprdrFileContext* file = (CliprdrFileContext*)context;
1132 WLog_Print(file->log, WLOG_INFO,
"signal %s [%d] aborting session", signame, sig);
1133 cliprdr_file_session_terminate(file, FALSE);
1137 static DWORD WINAPI cliprdr_file_fuse_thread(LPVOID arg)
1139 CliprdrFileContext* file = (CliprdrFileContext*)arg;
1143 DEBUG_CLIPRDR(file->log,
"Starting fuse with mountpoint '%s'", file->path);
1145 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
1146 fuse_opt_add_arg(&args, file->path);
1147 file->fuse_sess = fuse_session_new(&args, &cliprdr_file_fuse_oper,
1148 sizeof(cliprdr_file_fuse_oper), (
void*)file);
1149 (void)SetEvent(file->fuse_start_sync);
1151 if (file->fuse_sess != NULL)
1153 freerdp_add_signal_cleanup_handler(file, fuse_abort);
1154 if (0 == fuse_session_mount(file->fuse_sess, file->path))
1156 fuse_session_loop(file->fuse_sess);
1157 fuse_session_unmount(file->fuse_sess);
1159 freerdp_del_signal_cleanup_handler(file, fuse_abort);
1161 WLog_Print(file->log, WLOG_DEBUG,
"Waiting for FUSE stop sync");
1162 if (WaitForSingleObject(file->fuse_stop_sync, INFINITE) == WAIT_FAILED)
1163 WLog_Print(file->log, WLOG_ERROR,
"Failed to wait for stop sync");
1164 fuse_session_destroy(file->fuse_sess);
1166 fuse_opt_free_args(&args);
1168 DEBUG_CLIPRDR(file->log,
"Quitting fuse with mountpoint '%s'", file->path);
1174 static UINT cliprdr_file_context_server_file_contents_response(
1175 CliprdrClientContext* cliprdr_context,
1178 CliprdrFileContext* file_context = NULL;
1179 CliprdrFuseRequest* fuse_request = NULL;
1180 struct fuse_entry_param entry = { 0 };
1182 WINPR_ASSERT(cliprdr_context);
1183 WINPR_ASSERT(file_contents_response);
1185 file_context = cliprdr_context->custom;
1186 WINPR_ASSERT(file_context);
1188 HashTable_Lock(file_context->inode_table);
1189 fuse_request = HashTable_GetItemValue(file_context->request_table,
1190 (
void*)(uintptr_t)file_contents_response->streamId);
1193 HashTable_Unlock(file_context->inode_table);
1194 return CHANNEL_RC_OK;
1197 if (!(file_contents_response->common.msgFlags & CB_RESPONSE_OK))
1199 WLog_Print(file_context->log, WLOG_WARN,
1200 "FileContentsRequests for file \"%s\" was unsuccessful",
1201 fuse_request->fuse_file->filename);
1203 fuse_reply_err(fuse_request->fuse_req, EIO);
1204 HashTable_Remove(file_context->request_table,
1205 (
void*)(uintptr_t)file_contents_response->streamId);
1206 HashTable_Unlock(file_context->inode_table);
1207 return CHANNEL_RC_OK;
1210 if ((fuse_request->operation_type == FUSE_LL_OPERATION_LOOKUP ||
1211 fuse_request->operation_type == FUSE_LL_OPERATION_GETATTR) &&
1212 file_contents_response->cbRequested !=
sizeof(UINT64))
1214 WLog_Print(file_context->log, WLOG_WARN,
1215 "Received invalid file size for file \"%s\" from the client",
1216 fuse_request->fuse_file->filename);
1217 fuse_reply_err(fuse_request->fuse_req, EIO);
1218 HashTable_Remove(file_context->request_table,
1219 (
void*)(uintptr_t)file_contents_response->streamId);
1220 HashTable_Unlock(file_context->inode_table);
1221 return CHANNEL_RC_OK;
1224 if (fuse_request->operation_type == FUSE_LL_OPERATION_LOOKUP ||
1225 fuse_request->operation_type == FUSE_LL_OPERATION_GETATTR)
1227 DEBUG_CLIPRDR(file_context->log,
"Received file size for file \"%s\" with stream id %u",
1228 fuse_request->fuse_file->filename, file_contents_response->streamId);
1230 fuse_request->fuse_file->size = *((
const UINT64*)file_contents_response->requestedData);
1231 fuse_request->fuse_file->has_size = TRUE;
1233 entry.ino = fuse_request->fuse_file->ino;
1234 write_file_attributes(fuse_request->fuse_file, &entry.attr);
1235 entry.attr_timeout = 1.0;
1236 entry.entry_timeout = 1.0;
1238 else if (fuse_request->operation_type == FUSE_LL_OPERATION_READ)
1240 DEBUG_CLIPRDR(file_context->log,
"Received file range for file \"%s\" with stream id %u",
1241 fuse_request->fuse_file->filename, file_contents_response->streamId);
1243 HashTable_Unlock(file_context->inode_table);
1245 switch (fuse_request->operation_type)
1247 case FUSE_LL_OPERATION_NONE:
1249 case FUSE_LL_OPERATION_LOOKUP:
1250 fuse_reply_entry(fuse_request->fuse_req, &entry);
1252 case FUSE_LL_OPERATION_GETATTR:
1253 fuse_reply_attr(fuse_request->fuse_req, &entry.attr, entry.attr_timeout);
1255 case FUSE_LL_OPERATION_READ:
1256 fuse_reply_buf(fuse_request->fuse_req,
1257 (
const char*)file_contents_response->requestedData,
1258 file_contents_response->cbRequested);
1264 HashTable_Remove(file_context->request_table,
1265 (
void*)(uintptr_t)file_contents_response->streamId);
1267 return CHANNEL_RC_OK;
1271 static UINT cliprdr_file_context_send_file_contents_failure(
1277 WINPR_ASSERT(fileContentsRequest);
1279 const UINT64 offset = (((UINT64)fileContentsRequest->nPositionHigh) << 32) |
1280 ((UINT64)fileContentsRequest->nPositionLow);
1281 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1282 "server file contents request [lockID %" PRIu32
", streamID %" PRIu32
1283 ", index %" PRIu32
"] offset %" PRIu64
", size %" PRIu32
" failed",
1284 fileContentsRequest->clipDataId, fileContentsRequest->streamId,
1285 fileContentsRequest->listIndex, offset, fileContentsRequest->cbRequested);
1287 response.common.msgFlags = CB_RESPONSE_FAIL;
1288 response.streamId = fileContentsRequest->streamId;
1290 WINPR_ASSERT(file->context);
1291 WINPR_ASSERT(file->context->ClientFileContentsResponse);
1292 return file->context->ClientFileContentsResponse(file->context, &response);
1296 cliprdr_file_context_send_contents_response(CliprdrFileContext* file,
1298 const void* data,
size_t size)
1300 if (size > UINT32_MAX)
1301 return ERROR_INVALID_PARAMETER;
1304 .requestedData = data,
1305 .cbRequested = (UINT32)size,
1306 .common.msgFlags = CB_RESPONSE_OK };
1308 WINPR_ASSERT(request);
1311 WLog_Print(file->log, WLOG_DEBUG,
"send contents response streamID=%" PRIu32
", size=%" PRIu32,
1312 response.streamId, response.cbRequested);
1313 WINPR_ASSERT(file->context);
1314 WINPR_ASSERT(file->context->ClientFileContentsResponse);
1315 return file->context->ClientFileContentsResponse(file->context, &response);
1318 static BOOL dump_streams(
const void* key,
void* value,
void* arg)
1320 const UINT32* ukey = key;
1321 CliprdrLocalStream* cur = value;
1323 writelog(cur->context->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1324 "[key %" PRIu32
"] lockID %" PRIu32
", count %" PRIuz
", locked %d", *ukey,
1325 cur->lockId, cur->count, cur->locked);
1326 for (
size_t x = 0; x < cur->count; x++)
1328 const CliprdrLocalFile* file = &cur->files[x];
1329 writelog(cur->context->log, WLOG_WARN, __FILE__, __func__, __LINE__,
"file [%" PRIuz
"] ",
1330 x, file->name, file->size);
1335 static CliprdrLocalFile* file_info_for_request(CliprdrFileContext* file, UINT32 lockId,
1340 CliprdrLocalStream* cur = HashTable_GetItemValue(file->local_streams, &lockId);
1343 if (listIndex < cur->count)
1345 CliprdrLocalFile* f = &cur->files[listIndex];
1350 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1351 "invalid entry index for lockID %" PRIu32
", index %" PRIu32
" [count %" PRIu32
1353 lockId, listIndex, cur->count, cur->locked);
1358 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1359 "missing entry for lockID %" PRIu32
", index %" PRIu32, lockId, listIndex);
1360 HashTable_Foreach(file->local_streams, dump_streams, file);
1366 static CliprdrLocalFile* file_for_request(CliprdrFileContext* file, UINT32 lockId, UINT32 listIndex)
1368 CliprdrLocalFile* f = file_info_for_request(file, lockId, listIndex);
1373 const char* name = f->name;
1374 f->fp = winpr_fopen(name,
"rb");
1378 char ebuffer[256] = { 0 };
1379 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1380 "[lockID %" PRIu32
", index %" PRIu32
1381 "] failed to open file '%s' [size %" PRId64
"] %s [%d]",
1382 lockId, listIndex, f->name, f->size,
1383 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
1391 static void cliprdr_local_file_try_close(CliprdrLocalFile* file, UINT res, UINT64 offset,
1398 WINPR_ASSERT(file->context);
1399 WLog_Print(file->context->log, WLOG_DEBUG,
"closing file %s after error %" PRIu32,
1402 else if (((file->size > 0) && (offset + size >= (UINT64)file->size)))
1404 WINPR_ASSERT(file->context);
1405 WLog_Print(file->context->log, WLOG_DEBUG,
"closing file %s after read", file->name);
1413 (void)fclose(file->fp);
1417 static UINT cliprdr_file_context_server_file_size_request(
1420 WINPR_ASSERT(fileContentsRequest);
1422 if (fileContentsRequest->cbRequested !=
sizeof(UINT64))
1424 WLog_Print(file->log, WLOG_WARN,
"unexpected FILECONTENTS_SIZE request: %" PRIu32
" bytes",
1425 fileContentsRequest->cbRequested);
1428 HashTable_Lock(file->local_streams);
1430 UINT res = CHANNEL_RC_OK;
1431 CliprdrLocalFile* rfile =
1432 file_for_request(file, fileContentsRequest->clipDataId, fileContentsRequest->listIndex);
1434 res = cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1437 if (_fseeki64(rfile->fp, 0, SEEK_END) < 0)
1438 res = cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1441 const INT64 size = _ftelli64(rfile->fp);
1443 cliprdr_local_file_try_close(rfile, res, 0, 0);
1445 res = cliprdr_file_context_send_contents_response(file, fileContentsRequest, &size,
1450 HashTable_Unlock(file->local_streams);
1454 static UINT cliprdr_file_context_server_file_range_request(
1459 WINPR_ASSERT(fileContentsRequest);
1461 HashTable_Lock(file->local_streams);
1462 const UINT64 offset = (((UINT64)fileContentsRequest->nPositionHigh) << 32) |
1463 ((UINT64)fileContentsRequest->nPositionLow);
1465 CliprdrLocalFile* rfile =
1466 file_for_request(file, fileContentsRequest->clipDataId, fileContentsRequest->listIndex);
1470 if (_fseeki64(rfile->fp, WINPR_ASSERTING_INT_CAST(int64_t, offset), SEEK_SET) < 0)
1473 data = malloc(fileContentsRequest->cbRequested);
1477 const size_t r = fread(data, 1, fileContentsRequest->cbRequested, rfile->fp);
1478 const UINT rc = cliprdr_file_context_send_contents_response(file, fileContentsRequest, data, r);
1481 cliprdr_local_file_try_close(rfile, rc, offset, fileContentsRequest->cbRequested);
1482 HashTable_Unlock(file->local_streams);
1486 cliprdr_local_file_try_close(rfile, ERROR_INTERNAL_ERROR, offset,
1487 fileContentsRequest->cbRequested);
1489 HashTable_Unlock(file->local_streams);
1490 return cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1493 static void cliprdr_local_stream_free(
void* obj);
1495 static UINT change_lock(CliprdrFileContext* file, UINT32 lockId, BOOL lock)
1497 UINT rc = CHANNEL_RC_OK;
1501 HashTable_Lock(file->local_streams);
1504 CliprdrLocalStream* stream = HashTable_GetItemValue(file->local_streams, &lockId);
1505 if (lock && !stream)
1507 stream = cliprdr_local_stream_new(file, lockId, NULL, 0);
1508 if (!HashTable_Insert(file->local_streams, &lockId, stream))
1510 rc = ERROR_INTERNAL_ERROR;
1511 cliprdr_local_stream_free(stream);
1514 file->local_lock_id = lockId;
1518 stream->locked = lock;
1519 stream->lockId = lockId;
1526 if (!HashTable_Foreach(file->local_streams, local_stream_discard, file))
1527 rc = ERROR_INTERNAL_ERROR;
1530 HashTable_Unlock(file->local_streams);
1534 static UINT cliprdr_file_context_lock(CliprdrClientContext* context,
1537 WINPR_ASSERT(context);
1538 WINPR_ASSERT(lockClipboardData);
1539 CliprdrFileContext* file = (context->custom);
1540 return change_lock(file, lockClipboardData->clipDataId, TRUE);
1543 static UINT cliprdr_file_context_unlock(CliprdrClientContext* context,
1546 WINPR_ASSERT(context);
1547 WINPR_ASSERT(unlockClipboardData);
1548 CliprdrFileContext* file = (context->custom);
1549 return change_lock(file, unlockClipboardData->clipDataId, FALSE);
1552 static UINT cliprdr_file_context_server_file_contents_request(
1555 UINT error = NO_ERROR;
1557 WINPR_ASSERT(context);
1558 WINPR_ASSERT(fileContentsRequest);
1560 CliprdrFileContext* file = (context->custom);
1567 if ((fileContentsRequest->dwFlags & (FILECONTENTS_SIZE | FILECONTENTS_RANGE)) ==
1568 (FILECONTENTS_SIZE | FILECONTENTS_RANGE))
1570 WLog_Print(file->log, WLOG_ERROR,
"invalid CLIPRDR_FILECONTENTS_REQUEST.dwFlags");
1571 return cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1574 if (fileContentsRequest->dwFlags & FILECONTENTS_SIZE)
1575 error = cliprdr_file_context_server_file_size_request(file, fileContentsRequest);
1577 if (fileContentsRequest->dwFlags & FILECONTENTS_RANGE)
1578 error = cliprdr_file_context_server_file_range_request(file, fileContentsRequest);
1582 WLog_Print(file->log, WLOG_ERROR,
"failed to handle CLIPRDR_FILECONTENTS_REQUEST: 0x%08X",
1584 return cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1587 return CHANNEL_RC_OK;
1590 BOOL cliprdr_file_context_init(CliprdrFileContext* file, CliprdrClientContext* cliprdr)
1593 WINPR_ASSERT(cliprdr);
1595 cliprdr->custom = file;
1596 file->context = cliprdr;
1598 cliprdr->ServerLockClipboardData = cliprdr_file_context_lock;
1599 cliprdr->ServerUnlockClipboardData = cliprdr_file_context_unlock;
1600 cliprdr->ServerFileContentsRequest = cliprdr_file_context_server_file_contents_request;
1601 #if defined(WITH_FUSE)
1602 cliprdr->ServerFileContentsResponse = cliprdr_file_context_server_file_contents_response;
1608 #if defined(WITH_FUSE)
1609 static void clear_all_selections(CliprdrFileContext* file_context)
1611 WINPR_ASSERT(file_context);
1612 WINPR_ASSERT(file_context->inode_table);
1614 HashTable_Lock(file_context->inode_table);
1615 clear_selection(file_context, TRUE, NULL);
1617 HashTable_Clear(file_context->clip_data_table);
1618 HashTable_Unlock(file_context->inode_table);
1622 BOOL cliprdr_file_context_uninit(CliprdrFileContext* file, CliprdrClientContext* cliprdr)
1625 WINPR_ASSERT(cliprdr);
1629 #if defined(WITH_FUSE)
1630 if (file->inode_table)
1632 clear_no_cdi_entry(file);
1633 clear_all_selections(file);
1637 HashTable_Clear(file->local_streams);
1639 file->context = NULL;
1640 #if defined(WITH_FUSE)
1641 cliprdr->ServerFileContentsResponse = NULL;
1647 static BOOL cliprdr_file_content_changed_and_update(
void* ihash,
size_t hsize,
const void* data,
1651 BYTE hash[WINPR_SHA256_DIGEST_LENGTH] = { 0 };
1653 if (hsize <
sizeof(hash))
1656 if (!winpr_Digest(WINPR_MD_SHA256, data, size, hash,
sizeof(hash)))
1659 const BOOL changed = memcmp(hash, ihash,
sizeof(hash)) != 0;
1661 memcpy(ihash, hash,
sizeof(hash));
1665 static BOOL cliprdr_file_server_content_changed_and_update(CliprdrFileContext* file,
1666 const void* data,
size_t size)
1669 return cliprdr_file_content_changed_and_update(file->server_data_hash,
1670 sizeof(file->server_data_hash), data, size);
1673 static BOOL cliprdr_file_client_content_changed_and_update(CliprdrFileContext* file,
1674 const void* data,
size_t size)
1677 return cliprdr_file_content_changed_and_update(file->client_data_hash,
1678 sizeof(file->client_data_hash), data, size);
1681 #if defined(WITH_FUSE)
1682 static fuse_ino_t get_next_free_inode(CliprdrFileContext* file_context)
1686 WINPR_ASSERT(file_context);
1688 ino = file_context->next_ino;
1689 while (ino == 0 || ino == FUSE_ROOT_ID ||
1690 HashTable_GetItemValue(file_context->inode_table, (
void*)(uintptr_t)ino))
1693 file_context->next_ino = ino + 1;
1698 static CliprdrFuseFile* clip_data_dir_new(CliprdrFileContext* file_context, BOOL has_clip_data_id,
1699 UINT32 clip_data_id)
1701 CliprdrFuseFile* root_dir = NULL;
1702 CliprdrFuseFile* clip_data_dir = NULL;
1703 size_t path_length = 0;
1705 WINPR_ASSERT(file_context);
1707 clip_data_dir = fuse_file_new();
1711 path_length = 1 + MAX_CLIP_DATA_DIR_LEN + 1;
1713 clip_data_dir->filename_with_root = calloc(path_length,
sizeof(
char));
1714 if (!clip_data_dir->filename_with_root)
1716 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to allocate filename");
1717 fuse_file_free(clip_data_dir);
1721 if (has_clip_data_id)
1722 (void)_snprintf(clip_data_dir->filename_with_root, path_length,
"/%u",
1723 (
unsigned)clip_data_id);
1725 (
void)_snprintf(clip_data_dir->filename_with_root, path_length,
"/%" PRIu64,
1728 clip_data_dir->filename = strrchr(clip_data_dir->filename_with_root,
'/') + 1;
1730 clip_data_dir->ino = get_next_free_inode(file_context);
1731 clip_data_dir->is_directory = TRUE;
1732 clip_data_dir->is_readonly = TRUE;
1733 clip_data_dir->has_clip_data_id = has_clip_data_id;
1734 clip_data_dir->clip_data_id = clip_data_id;
1736 root_dir = file_context->root_dir;
1737 if (!ArrayList_Append(root_dir->children, clip_data_dir))
1739 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to append FUSE file");
1740 fuse_file_free(clip_data_dir);
1743 clip_data_dir->parent = root_dir;
1745 if (!HashTable_Insert(file_context->inode_table, (
void*)(uintptr_t)clip_data_dir->ino,
1748 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to insert inode into inode table");
1749 ArrayList_Remove(root_dir->children, clip_data_dir);
1750 fuse_file_free(clip_data_dir);
1754 return clip_data_dir;
1757 static char* get_parent_path(
const char* filepath)
1759 const char* base = strrchr(filepath,
'/');
1762 while ((base > filepath) && (*base ==
'/'))
1765 WINPR_ASSERT(base >= filepath);
1766 const size_t parent_path_length = 1ULL + (size_t)(base - filepath);
1767 char* parent_path = calloc(parent_path_length + 1,
sizeof(
char));
1771 memcpy(parent_path, filepath, parent_path_length);
1776 static BOOL is_fuse_file_not_parent(
const void* key,
void* value,
void* arg)
1778 CliprdrFuseFile* fuse_file = value;
1779 CliprdrFuseFindParentContext* find_context = arg;
1781 if (!fuse_file->is_directory)
1784 if (strcmp(find_context->parent_path, fuse_file->filename_with_root) == 0)
1786 find_context->parent = fuse_file;
1793 static CliprdrFuseFile* get_parent_directory(CliprdrFileContext* file_context,
const char* path)
1795 CliprdrFuseFindParentContext find_context = { 0 };
1797 WINPR_ASSERT(file_context);
1800 find_context.parent_path = get_parent_path(path);
1801 if (!find_context.parent_path)
1804 WINPR_ASSERT(!find_context.parent);
1806 if (HashTable_Foreach(file_context->inode_table, is_fuse_file_not_parent, &find_context))
1808 free(find_context.parent_path);
1811 WINPR_ASSERT(find_context.parent);
1813 free(find_context.parent_path);
1815 return find_context.parent;
1818 static BOOL set_selection_for_clip_data_entry(CliprdrFileContext* file_context,
1819 CliprdrFuseClipDataEntry* clip_data_entry,
1822 CliprdrFuseFile* clip_data_dir = NULL;
1824 WINPR_ASSERT(file_context);
1825 WINPR_ASSERT(clip_data_entry);
1826 WINPR_ASSERT(files);
1828 clip_data_dir = clip_data_entry->clip_data_dir;
1829 WINPR_ASSERT(clip_data_dir);
1831 if (clip_data_entry->has_clip_data_id)
1832 WLog_Print(file_context->log, WLOG_DEBUG,
"Setting selection for clipDataId %u",
1833 clip_data_entry->clip_data_id);
1835 WLog_Print(file_context->log, WLOG_DEBUG,
"Setting selection");
1838 for (UINT32 i = 0; i < n_files; ++i)
1841 CliprdrFuseFile* fuse_file = NULL;
1842 char* filename = NULL;
1843 size_t path_length = 0;
1845 fuse_file = fuse_file_new();
1848 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to create FUSE file");
1849 clear_entry_selection(clip_data_entry);
1853 filename = ConvertWCharToUtf8Alloc(file->cFileName, NULL);
1856 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to convert filename");
1857 fuse_file_free(fuse_file);
1858 clear_entry_selection(clip_data_entry);
1862 for (
size_t j = 0; filename[j]; ++j)
1864 if (filename[j] ==
'\\')
1868 path_length = strlen(clip_data_dir->filename_with_root) + 1 + strlen(filename) + 1;
1869 fuse_file->filename_with_root = calloc(path_length,
sizeof(
char));
1870 if (!fuse_file->filename_with_root)
1872 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to allocate filename");
1874 fuse_file_free(fuse_file);
1875 clear_entry_selection(clip_data_entry);
1879 (void)_snprintf(fuse_file->filename_with_root, path_length,
"%s/%s",
1880 clip_data_dir->filename_with_root, filename);
1883 fuse_file->filename = strrchr(fuse_file->filename_with_root,
'/') + 1;
1885 fuse_file->parent = get_parent_directory(file_context, fuse_file->filename_with_root);
1886 if (!fuse_file->parent)
1888 WLog_Print(file_context->log, WLOG_ERROR,
"Found no parent for FUSE file");
1889 fuse_file_free(fuse_file);
1890 clear_entry_selection(clip_data_entry);
1894 if (!ArrayList_Append(fuse_file->parent->children, fuse_file))
1896 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to append FUSE file");
1897 fuse_file_free(fuse_file);
1898 clear_entry_selection(clip_data_entry);
1902 fuse_file->list_idx = i;
1903 fuse_file->ino = get_next_free_inode(file_context);
1904 fuse_file->has_clip_data_id = clip_data_entry->has_clip_data_id;
1905 fuse_file->clip_data_id = clip_data_entry->clip_data_id;
1906 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1907 fuse_file->is_directory = TRUE;
1908 if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1909 fuse_file->is_readonly = TRUE;
1910 if (file->dwFlags & FD_FILESIZE)
1912 fuse_file->size = ((UINT64)file->nFileSizeHigh << 32) + file->nFileSizeLow;
1913 fuse_file->has_size = TRUE;
1915 if (file->dwFlags & FD_WRITESTIME)
1919 filetime = file->ftLastWriteTime.dwHighDateTime;
1921 filetime += file->ftLastWriteTime.dwLowDateTime;
1923 fuse_file->last_write_time_unix =
1924 1LL * filetime / (10LL * 1000LL * 1000LL) - WIN32_FILETIME_TO_UNIX_EPOCH;
1925 fuse_file->has_last_write_time = TRUE;
1928 if (!HashTable_Insert(file_context->inode_table, (
void*)(uintptr_t)fuse_file->ino,
1931 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to insert inode into inode table");
1932 fuse_file_free(fuse_file);
1933 clear_entry_selection(clip_data_entry);
1939 if (clip_data_entry->has_clip_data_id)
1940 WLog_Print(file_context->log, WLOG_DEBUG,
"Selection set for clipDataId %u",
1941 clip_data_entry->clip_data_id);
1943 WLog_Print(file_context->log, WLOG_DEBUG,
"Selection set");
1948 static BOOL update_exposed_path(CliprdrFileContext* file_context, wClipboard* clip,
1949 CliprdrFuseClipDataEntry* clip_data_entry)
1951 wClipboardDelegate* delegate = NULL;
1952 CliprdrFuseFile* clip_data_dir = NULL;
1954 WINPR_ASSERT(file_context);
1956 WINPR_ASSERT(clip_data_entry);
1958 delegate = ClipboardGetDelegate(clip);
1959 WINPR_ASSERT(delegate);
1961 clip_data_dir = clip_data_entry->clip_data_dir;
1962 WINPR_ASSERT(clip_data_dir);
1964 free(file_context->exposed_path);
1965 file_context->exposed_path = GetCombinedPath(file_context->path, clip_data_dir->filename);
1966 if (file_context->exposed_path)
1967 WLog_Print(file_context->log, WLOG_DEBUG,
"Updated exposed path to \"%s\"",
1968 file_context->exposed_path);
1970 delegate->basePath = file_context->exposed_path;
1972 return delegate->basePath != NULL;
1976 BOOL cliprdr_file_context_update_server_data(CliprdrFileContext* file_context, wClipboard* clip,
1977 const void* data,
size_t size)
1979 #if defined(WITH_FUSE)
1980 CliprdrFuseClipDataEntry* clip_data_entry = NULL;
1985 WINPR_ASSERT(file_context);
1987 if (size > UINT32_MAX)
1990 if (cliprdr_parse_file_list(data, (UINT32)size, &files, &n_files))
1992 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to parse file list");
1996 HashTable_Lock(file_context->inode_table);
1997 HashTable_Lock(file_context->clip_data_table);
1998 if (does_server_support_clipdata_locking(file_context))
1999 clip_data_entry = HashTable_GetItemValue(
2000 file_context->clip_data_table, (
void*)(uintptr_t)file_context->current_clip_data_id);
2002 clip_data_entry = file_context->clip_data_entry_without_id;
2004 WINPR_ASSERT(clip_data_entry);
2006 clear_entry_selection(clip_data_entry);
2007 WINPR_ASSERT(!clip_data_entry->clip_data_dir);
2009 clip_data_entry->clip_data_dir =
2010 clip_data_dir_new(file_context, does_server_support_clipdata_locking(file_context),
2011 file_context->current_clip_data_id);
2012 if (!clip_data_entry->clip_data_dir)
2014 if (!update_exposed_path(file_context, clip, clip_data_entry))
2016 if (!set_selection_for_clip_data_entry(file_context, clip_data_entry, files, n_files))
2022 HashTable_Unlock(file_context->clip_data_table);
2023 HashTable_Unlock(file_context->inode_table);
2031 void* cliprdr_file_context_get_context(CliprdrFileContext* file)
2034 return file->clipboard;
2037 void cliprdr_file_session_terminate(CliprdrFileContext* file, BOOL stop_thread)
2042 #if defined(WITH_FUSE)
2043 WINPR_ASSERT(file->fuse_stop_sync);
2045 WLog_Print(file->log, WLOG_DEBUG,
"Setting FUSE exit flag");
2046 if (file->fuse_sess)
2047 fuse_session_exit(file->fuse_sess);
2051 WLog_Print(file->log, WLOG_DEBUG,
"Setting FUSE stop event");
2052 (void)SetEvent(file->fuse_stop_sync);
2058 #if defined(WITH_FUSE)
2059 WLog_Print(file->log, WLOG_DEBUG,
"Forcing FUSE to check exit flag");
2061 (void)winpr_PathFileExists(file->path);
2064 void cliprdr_file_context_free(CliprdrFileContext* file)
2069 #if defined(WITH_FUSE)
2070 if (file->inode_table)
2072 clear_no_cdi_entry(file);
2073 clear_all_selections(file);
2076 if (file->fuse_thread)
2078 WINPR_ASSERT(file->fuse_stop_sync);
2080 WLog_Print(file->log, WLOG_DEBUG,
"Stopping FUSE thread");
2081 cliprdr_file_session_terminate(file, TRUE);
2083 WLog_Print(file->log, WLOG_DEBUG,
"Waiting on FUSE thread");
2084 (void)WaitForSingleObject(file->fuse_thread, INFINITE);
2085 (void)CloseHandle(file->fuse_thread);
2087 if (file->fuse_stop_sync)
2088 (void)CloseHandle(file->fuse_stop_sync);
2089 if (file->fuse_start_sync)
2090 (void)CloseHandle(file->fuse_start_sync);
2092 HashTable_Free(file->request_table);
2093 HashTable_Free(file->clip_data_table);
2094 HashTable_Free(file->inode_table);
2096 HashTable_Free(file->local_streams);
2097 winpr_RemoveDirectory(file->path);
2099 free(file->exposed_path);
2103 static BOOL create_base_path(CliprdrFileContext* file)
2107 char base[64] = { 0 };
2108 (void)_snprintf(base,
sizeof(base),
"com.freerdp.client.cliprdr.%" PRIu32,
2109 GetCurrentProcessId());
2111 file->path = GetKnownSubPath(KNOWN_PATH_TEMP, base);
2115 if (!winpr_PathFileExists(file->path) && !winpr_PathMakePath(file->path, 0))
2117 WLog_Print(file->log, WLOG_ERROR,
"Failed to create directory '%s'", file->path);
2123 static void cliprdr_local_file_free(CliprdrLocalFile* file)
2125 const CliprdrLocalFile empty = { 0 };
2130 WLog_Print(file->context->log, WLOG_DEBUG,
"closing file %s, discarding entry", file->name);
2131 (void)fclose(file->fp);
2137 static BOOL cliprdr_local_file_new(CliprdrFileContext* context, CliprdrLocalFile* f,
2140 const CliprdrLocalFile empty = { 0 };
2142 WINPR_ASSERT(context);
2146 f->context = context;
2147 f->name = winpr_str_url_decode(path, strlen(path));
2153 cliprdr_local_file_free(f);
2157 static void cliprdr_local_files_free(CliprdrLocalStream* stream)
2159 WINPR_ASSERT(stream);
2161 for (
size_t x = 0; x < stream->count; x++)
2162 cliprdr_local_file_free(&stream->files[x]);
2163 free(stream->files);
2165 stream->files = NULL;
2169 static void cliprdr_local_stream_free(
void* obj)
2171 CliprdrLocalStream* stream = (CliprdrLocalStream*)obj;
2173 cliprdr_local_files_free(stream);
2178 static BOOL append_entry(CliprdrLocalStream* stream,
const char* path)
2180 CliprdrLocalFile* tmp = realloc(stream->files,
sizeof(CliprdrLocalFile) * (stream->count + 1));
2183 stream->files = tmp;
2184 CliprdrLocalFile* f = &stream->files[stream->count++];
2186 return cliprdr_local_file_new(stream->context, f, path);
2189 static BOOL is_directory(
const char* path)
2191 WCHAR* wpath = ConvertUtf8ToWCharAlloc(path, NULL);
2196 CreateFileW(wpath, 0, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2199 if (hFile == INVALID_HANDLE_VALUE)
2203 const BOOL status = GetFileInformationByHandle(hFile, &fileInformation);
2204 (void)CloseHandle(hFile);
2208 return (fileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? TRUE : FALSE;
2211 static BOOL add_directory(CliprdrLocalStream* stream,
const char* path)
2213 char* wildcardpath = GetCombinedPath(path,
"*");
2216 WCHAR* wpath = ConvertUtf8ToWCharAlloc(wildcardpath, NULL);
2222 HANDLE hFind = FindFirstFileW(wpath, &FindFileData);
2225 if (hFind == INVALID_HANDLE_VALUE)
2231 WCHAR dotbuffer[6] = { 0 };
2232 WCHAR dotdotbuffer[6] = { 0 };
2233 const WCHAR* dot = InitializeConstWCharFromUtf8(
".", dotbuffer, ARRAYSIZE(dotbuffer));
2234 const WCHAR* dotdot = InitializeConstWCharFromUtf8(
"..", dotdotbuffer, ARRAYSIZE(dotdotbuffer));
2237 if (_wcscmp(FindFileData.cFileName, dot) == 0)
2239 if (_wcscmp(FindFileData.cFileName, dotdot) == 0)
2242 char cFileName[MAX_PATH] = { 0 };
2243 (void)ConvertWCharNToUtf8(FindFileData.cFileName, ARRAYSIZE(FindFileData.cFileName),
2244 cFileName, ARRAYSIZE(cFileName));
2247 next = GetCombinedPath(path, cFileName);
2251 if (!append_entry(stream, next))
2253 if (is_directory(next))
2255 if (!add_directory(stream, next))
2258 }
while (FindNextFileW(hFind, &FindFileData));
2268 static BOOL cliprdr_local_stream_update(CliprdrLocalStream* stream,
const char* data,
size_t size)
2271 WINPR_ASSERT(stream);
2275 cliprdr_local_files_free(stream);
2277 stream->files = calloc(size,
sizeof(CliprdrLocalFile));
2281 char* copy = strndup(data, size);
2285 char* saveptr = NULL;
2286 char* ptr = strtok_s(copy,
"\r\n", &saveptr);
2289 const char* name = ptr;
2290 if (strncmp(
"file:///", ptr, 8) == 0)
2292 else if (strncmp(
"file:/", ptr, 6) == 0)
2295 if (!append_entry(stream, name))
2298 if (is_directory(name))
2300 const BOOL res = add_directory(stream, name);
2304 ptr = strtok_s(NULL,
"\r\n", &saveptr);
2313 CliprdrLocalStream* cliprdr_local_stream_new(CliprdrFileContext* context, UINT32 streamID,
2314 const char* data,
size_t size)
2316 WINPR_ASSERT(context);
2317 CliprdrLocalStream* stream = calloc(1,
sizeof(CliprdrLocalStream));
2321 stream->context = context;
2322 if (!cliprdr_local_stream_update(stream, data, size))
2325 stream->lockId = streamID;
2329 cliprdr_local_stream_free(stream);
2333 static UINT32 UINTPointerHash(
const void*
id)
2336 return *((
const UINT32*)
id);
2339 static BOOL UINTPointerCompare(
const void* pointer1,
const void* pointer2)
2341 if (!pointer1 || !pointer2)
2342 return pointer1 == pointer2;
2344 const UINT32* a = pointer1;
2345 const UINT32* b = pointer2;
2349 static void* UINTPointerClone(
const void* other)
2351 const UINT32* src = other;
2355 UINT32* copy = calloc(1,
sizeof(UINT32));
2363 #if defined(WITH_FUSE)
2364 static CliprdrFuseFile* fuse_file_new_root(CliprdrFileContext* file_context)
2366 CliprdrFuseFile* root_dir = NULL;
2368 root_dir = fuse_file_new();
2372 root_dir->filename_with_root = calloc(2,
sizeof(
char));
2373 if (!root_dir->filename_with_root)
2375 fuse_file_free(root_dir);
2379 (void)_snprintf(root_dir->filename_with_root, 2,
"/");
2380 root_dir->filename = root_dir->filename_with_root;
2382 root_dir->ino = FUSE_ROOT_ID;
2383 root_dir->is_directory = TRUE;
2384 root_dir->is_readonly = TRUE;
2386 if (!HashTable_Insert(file_context->inode_table, (
void*)(uintptr_t)root_dir->ino, root_dir))
2388 fuse_file_free(root_dir);
2396 CliprdrFileContext* cliprdr_file_context_new(
void* context)
2398 CliprdrFileContext* file = calloc(1,
sizeof(CliprdrFileContext));
2402 file->log = WLog_Get(CLIENT_TAG(
"common.cliprdr.file"));
2403 file->clipboard = context;
2405 file->local_streams = HashTable_New(FALSE);
2406 if (!file->local_streams)
2409 if (!HashTable_SetHashFunction(file->local_streams, UINTPointerHash))
2412 wObject* hkobj = HashTable_KeyObject(file->local_streams);
2413 WINPR_ASSERT(hkobj);
2414 hkobj->fnObjectEquals = UINTPointerCompare;
2415 hkobj->fnObjectFree = free;
2416 hkobj->fnObjectNew = UINTPointerClone;
2418 wObject* hobj = HashTable_ValueObject(file->local_streams);
2420 hobj->fnObjectFree = cliprdr_local_stream_free;
2422 #if defined(WITH_FUSE)
2423 file->inode_table = HashTable_New(FALSE);
2424 file->clip_data_table = HashTable_New(FALSE);
2425 file->request_table = HashTable_New(FALSE);
2426 if (!file->inode_table || !file->clip_data_table || !file->request_table)
2430 wObject* ctobj = HashTable_ValueObject(file->request_table);
2431 WINPR_ASSERT(ctobj);
2432 ctobj->fnObjectFree = free;
2435 wObject* ctobj = HashTable_ValueObject(file->clip_data_table);
2436 WINPR_ASSERT(ctobj);
2437 ctobj->fnObjectFree = clip_data_entry_free;
2440 file->root_dir = fuse_file_new_root(file);
2441 if (!file->root_dir)
2445 if (!create_base_path(file))
2448 #if defined(WITH_FUSE)
2449 if (!(file->fuse_start_sync = CreateEvent(NULL, TRUE, FALSE, NULL)))
2451 if (!(file->fuse_stop_sync = CreateEvent(NULL, TRUE, FALSE, NULL)))
2453 if (!(file->fuse_thread = CreateThread(NULL, 0, cliprdr_file_fuse_thread, file, 0, NULL)))
2456 if (WaitForSingleObject(file->fuse_start_sync, INFINITE) == WAIT_FAILED)
2457 WLog_Print(file->log, WLOG_ERROR,
"Failed to wait for start sync");
2462 WINPR_PRAGMA_DIAG_PUSH
2463 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
2464 cliprdr_file_context_free(file);
2465 WINPR_PRAGMA_DIAG_POP
2469 BOOL local_stream_discard(
const void* key,
void* value,
void* arg)
2471 CliprdrFileContext* file = arg;
2472 CliprdrLocalStream* stream = value;
2474 WINPR_ASSERT(stream);
2476 if (!stream->locked)
2477 HashTable_Remove(file->local_streams, key);
2481 BOOL cliprdr_file_context_clear(CliprdrFileContext* file)
2485 WLog_Print(file->log, WLOG_DEBUG,
"clear file clipboard...");
2487 HashTable_Lock(file->local_streams);
2488 HashTable_Foreach(file->local_streams, local_stream_discard, file);
2489 HashTable_Unlock(file->local_streams);
2491 memset(file->server_data_hash, 0,
sizeof(file->server_data_hash));
2492 memset(file->client_data_hash, 0,
sizeof(file->client_data_hash));
2496 BOOL cliprdr_file_context_update_client_data(CliprdrFileContext* file,
const char* data,
2502 if (!cliprdr_file_client_content_changed_and_update(file, data, size))
2505 if (!cliprdr_file_context_clear(file))
2508 UINT32 lockId = file->local_lock_id;
2510 HashTable_Lock(file->local_streams);
2511 CliprdrLocalStream* stream = HashTable_GetItemValue(file->local_streams, &lockId);
2513 WLog_Print(file->log, WLOG_DEBUG,
"update client file list (stream=%p)...", stream);
2515 rc = cliprdr_local_stream_update(stream, data, size);
2518 stream = cliprdr_local_stream_new(file, lockId, data, size);
2519 rc = HashTable_Insert(file->local_streams, &stream->lockId, stream);
2521 cliprdr_local_stream_free(stream);
2525 HashTable_Unlock(file->local_streams);
2529 UINT32 cliprdr_file_context_current_flags(CliprdrFileContext* file)
2533 if ((file->file_capability_flags & CB_STREAM_FILECLIP_ENABLED) == 0)
2536 if (!file->file_formats_registered)
2539 return CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS |
2540 CB_HUGE_FILE_SUPPORT_ENABLED;
2543 BOOL cliprdr_file_context_set_locally_available(CliprdrFileContext* file, BOOL available)
2546 file->file_formats_registered = available;
2550 BOOL cliprdr_file_context_remote_set_flags(CliprdrFileContext* file, UINT32 flags)
2553 file->file_capability_flags = flags;
2557 UINT32 cliprdr_file_context_remote_get_flags(CliprdrFileContext* file)
2560 return file->file_capability_flags;
2563 BOOL cliprdr_file_context_has_local_support(CliprdrFileContext* file)
2567 #if defined(WITH_FUSE)
This struct contains function pointer to initialize/free objects.