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 = 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);
1112 entry_size = fuse_add_direntry(fuse_req, buf + written_size, max_size - written_size,
1113 child->filename, &attr, i + 1);
1114 if (entry_size > max_size - written_size)
1117 written_size += entry_size;
1119 HashTable_Unlock(file_context->inode_table);
1121 fuse_reply_buf(fuse_req, buf, written_size);
1125 static void fuse_abort(
int sig,
const char* signame,
void* context)
1127 CliprdrFileContext* file = (CliprdrFileContext*)context;
1131 WLog_Print(file->log, WLOG_INFO,
"signal %s [%d] aborting session", signame, sig);
1132 cliprdr_file_session_terminate(file, FALSE);
1136 static DWORD WINAPI cliprdr_file_fuse_thread(LPVOID arg)
1138 CliprdrFileContext* file = (CliprdrFileContext*)arg;
1142 DEBUG_CLIPRDR(file->log,
"Starting fuse with mountpoint '%s'", file->path);
1144 struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
1145 fuse_opt_add_arg(&args, file->path);
1146 file->fuse_sess = fuse_session_new(&args, &cliprdr_file_fuse_oper,
1147 sizeof(cliprdr_file_fuse_oper), (
void*)file);
1148 (void)SetEvent(file->fuse_start_sync);
1150 if (file->fuse_sess != NULL)
1152 freerdp_add_signal_cleanup_handler(file, fuse_abort);
1153 if (0 == fuse_session_mount(file->fuse_sess, file->path))
1155 fuse_session_loop(file->fuse_sess);
1156 fuse_session_unmount(file->fuse_sess);
1158 freerdp_del_signal_cleanup_handler(file, fuse_abort);
1160 WLog_Print(file->log, WLOG_DEBUG,
"Waiting for FUSE stop sync");
1161 if (WaitForSingleObject(file->fuse_stop_sync, INFINITE) == WAIT_FAILED)
1162 WLog_Print(file->log, WLOG_ERROR,
"Failed to wait for stop sync");
1163 fuse_session_destroy(file->fuse_sess);
1165 fuse_opt_free_args(&args);
1167 DEBUG_CLIPRDR(file->log,
"Quitting fuse with mountpoint '%s'", file->path);
1173 static UINT cliprdr_file_context_server_file_contents_response(
1174 CliprdrClientContext* cliprdr_context,
1177 CliprdrFileContext* file_context = NULL;
1178 CliprdrFuseRequest* fuse_request = NULL;
1179 struct fuse_entry_param entry = { 0 };
1181 WINPR_ASSERT(cliprdr_context);
1182 WINPR_ASSERT(file_contents_response);
1184 file_context = cliprdr_context->custom;
1185 WINPR_ASSERT(file_context);
1187 HashTable_Lock(file_context->inode_table);
1188 fuse_request = HashTable_GetItemValue(file_context->request_table,
1189 (
void*)(uintptr_t)file_contents_response->streamId);
1192 HashTable_Unlock(file_context->inode_table);
1193 return CHANNEL_RC_OK;
1196 if (!(file_contents_response->common.msgFlags & CB_RESPONSE_OK))
1198 WLog_Print(file_context->log, WLOG_WARN,
1199 "FileContentsRequests for file \"%s\" was unsuccessful",
1200 fuse_request->fuse_file->filename);
1202 fuse_reply_err(fuse_request->fuse_req, EIO);
1203 HashTable_Remove(file_context->request_table,
1204 (
void*)(uintptr_t)file_contents_response->streamId);
1205 HashTable_Unlock(file_context->inode_table);
1206 return CHANNEL_RC_OK;
1209 if ((fuse_request->operation_type == FUSE_LL_OPERATION_LOOKUP ||
1210 fuse_request->operation_type == FUSE_LL_OPERATION_GETATTR) &&
1211 file_contents_response->cbRequested !=
sizeof(UINT64))
1213 WLog_Print(file_context->log, WLOG_WARN,
1214 "Received invalid file size for file \"%s\" from the client",
1215 fuse_request->fuse_file->filename);
1216 fuse_reply_err(fuse_request->fuse_req, EIO);
1217 HashTable_Remove(file_context->request_table,
1218 (
void*)(uintptr_t)file_contents_response->streamId);
1219 HashTable_Unlock(file_context->inode_table);
1220 return CHANNEL_RC_OK;
1223 if (fuse_request->operation_type == FUSE_LL_OPERATION_LOOKUP ||
1224 fuse_request->operation_type == FUSE_LL_OPERATION_GETATTR)
1226 DEBUG_CLIPRDR(file_context->log,
"Received file size for file \"%s\" with stream id %u",
1227 fuse_request->fuse_file->filename, file_contents_response->streamId);
1229 fuse_request->fuse_file->size = *((
const UINT64*)file_contents_response->requestedData);
1230 fuse_request->fuse_file->has_size = TRUE;
1232 entry.ino = fuse_request->fuse_file->ino;
1233 write_file_attributes(fuse_request->fuse_file, &entry.attr);
1234 entry.attr_timeout = 1.0;
1235 entry.entry_timeout = 1.0;
1237 else if (fuse_request->operation_type == FUSE_LL_OPERATION_READ)
1239 DEBUG_CLIPRDR(file_context->log,
"Received file range for file \"%s\" with stream id %u",
1240 fuse_request->fuse_file->filename, file_contents_response->streamId);
1242 HashTable_Unlock(file_context->inode_table);
1244 switch (fuse_request->operation_type)
1246 case FUSE_LL_OPERATION_NONE:
1248 case FUSE_LL_OPERATION_LOOKUP:
1249 fuse_reply_entry(fuse_request->fuse_req, &entry);
1251 case FUSE_LL_OPERATION_GETATTR:
1252 fuse_reply_attr(fuse_request->fuse_req, &entry.attr, entry.attr_timeout);
1254 case FUSE_LL_OPERATION_READ:
1255 fuse_reply_buf(fuse_request->fuse_req,
1256 (
const char*)file_contents_response->requestedData,
1257 file_contents_response->cbRequested);
1263 HashTable_Remove(file_context->request_table,
1264 (
void*)(uintptr_t)file_contents_response->streamId);
1266 return CHANNEL_RC_OK;
1270 static UINT cliprdr_file_context_send_file_contents_failure(
1276 WINPR_ASSERT(fileContentsRequest);
1278 const UINT64 offset = (((UINT64)fileContentsRequest->nPositionHigh) << 32) |
1279 ((UINT64)fileContentsRequest->nPositionLow);
1280 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1281 "server file contents request [lockID %" PRIu32
", streamID %" PRIu32
1282 ", index %" PRIu32
"] offset %" PRIu64
", size %" PRIu32
" failed",
1283 fileContentsRequest->clipDataId, fileContentsRequest->streamId,
1284 fileContentsRequest->listIndex, offset, fileContentsRequest->cbRequested);
1286 response.common.msgFlags = CB_RESPONSE_FAIL;
1287 response.streamId = fileContentsRequest->streamId;
1289 WINPR_ASSERT(file->context);
1290 WINPR_ASSERT(file->context->ClientFileContentsResponse);
1291 return file->context->ClientFileContentsResponse(file->context, &response);
1295 cliprdr_file_context_send_contents_response(CliprdrFileContext* file,
1297 const void* data,
size_t size)
1299 if (size > UINT32_MAX)
1300 return ERROR_INVALID_PARAMETER;
1303 .requestedData = data,
1304 .cbRequested = (UINT32)size,
1305 .common.msgFlags = CB_RESPONSE_OK };
1307 WINPR_ASSERT(request);
1310 WLog_Print(file->log, WLOG_DEBUG,
"send contents response streamID=%" PRIu32
", size=%" PRIu32,
1311 response.streamId, response.cbRequested);
1312 WINPR_ASSERT(file->context);
1313 WINPR_ASSERT(file->context->ClientFileContentsResponse);
1314 return file->context->ClientFileContentsResponse(file->context, &response);
1317 static BOOL dump_streams(
const void* key,
void* value,
void* arg)
1319 const UINT32* ukey = key;
1320 CliprdrLocalStream* cur = value;
1322 writelog(cur->context->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1323 "[key %" PRIu32
"] lockID %" PRIu32
", count %" PRIuz
", locked %d", *ukey,
1324 cur->lockId, cur->count, cur->locked);
1325 for (
size_t x = 0; x < cur->count; x++)
1327 const CliprdrLocalFile* file = &cur->files[x];
1328 writelog(cur->context->log, WLOG_WARN, __FILE__, __func__, __LINE__,
"file [%" PRIuz
"] ",
1329 x, file->name, file->size);
1334 static CliprdrLocalFile* file_info_for_request(CliprdrFileContext* file, UINT32 lockId,
1339 CliprdrLocalStream* cur = HashTable_GetItemValue(file->local_streams, &lockId);
1342 if (listIndex < cur->count)
1344 CliprdrLocalFile* f = &cur->files[listIndex];
1349 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1350 "invalid entry index for lockID %" PRIu32
", index %" PRIu32
" [count %" PRIu32
1352 lockId, listIndex, cur->count, cur->locked);
1357 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1358 "missing entry for lockID %" PRIu32
", index %" PRIu32, lockId, listIndex);
1359 HashTable_Foreach(file->local_streams, dump_streams, file);
1365 static CliprdrLocalFile* file_for_request(CliprdrFileContext* file, UINT32 lockId, UINT32 listIndex)
1367 CliprdrLocalFile* f = file_info_for_request(file, lockId, listIndex);
1372 const char* name = f->name;
1373 f->fp = winpr_fopen(name,
"rb");
1377 char ebuffer[256] = { 0 };
1378 writelog(file->log, WLOG_WARN, __FILE__, __func__, __LINE__,
1379 "[lockID %" PRIu32
", index %" PRIu32
1380 "] failed to open file '%s' [size %" PRId64
"] %s [%d]",
1381 lockId, listIndex, f->name, f->size,
1382 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
1390 static void cliprdr_local_file_try_close(CliprdrLocalFile* file, UINT res, UINT64 offset,
1397 WINPR_ASSERT(file->context);
1398 WLog_Print(file->context->log, WLOG_DEBUG,
"closing file %s after error %" PRIu32,
1401 else if (((file->size > 0) && (offset + size >= (UINT64)file->size)))
1403 WINPR_ASSERT(file->context);
1404 WLog_Print(file->context->log, WLOG_DEBUG,
"closing file %s after read", file->name);
1412 (void)fclose(file->fp);
1416 static UINT cliprdr_file_context_server_file_size_request(
1419 WINPR_ASSERT(fileContentsRequest);
1421 if (fileContentsRequest->cbRequested !=
sizeof(UINT64))
1423 WLog_Print(file->log, WLOG_WARN,
"unexpected FILECONTENTS_SIZE request: %" PRIu32
" bytes",
1424 fileContentsRequest->cbRequested);
1427 HashTable_Lock(file->local_streams);
1429 UINT res = CHANNEL_RC_OK;
1430 CliprdrLocalFile* rfile =
1431 file_for_request(file, fileContentsRequest->clipDataId, fileContentsRequest->listIndex);
1433 res = cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1436 if (_fseeki64(rfile->fp, 0, SEEK_END) < 0)
1437 res = cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1440 const INT64 size = _ftelli64(rfile->fp);
1442 cliprdr_local_file_try_close(rfile, res, 0, 0);
1444 res = cliprdr_file_context_send_contents_response(file, fileContentsRequest, &size,
1449 HashTable_Unlock(file->local_streams);
1453 static UINT cliprdr_file_context_server_file_range_request(
1458 WINPR_ASSERT(fileContentsRequest);
1460 HashTable_Lock(file->local_streams);
1461 const UINT64 offset = (((UINT64)fileContentsRequest->nPositionHigh) << 32) |
1462 ((UINT64)fileContentsRequest->nPositionLow);
1464 CliprdrLocalFile* rfile =
1465 file_for_request(file, fileContentsRequest->clipDataId, fileContentsRequest->listIndex);
1469 if (_fseeki64(rfile->fp, offset, SEEK_SET) < 0)
1472 data = malloc(fileContentsRequest->cbRequested);
1476 const size_t r = fread(data, 1, fileContentsRequest->cbRequested, rfile->fp);
1477 const UINT rc = cliprdr_file_context_send_contents_response(file, fileContentsRequest, data, r);
1480 cliprdr_local_file_try_close(rfile, rc, offset, fileContentsRequest->cbRequested);
1481 HashTable_Unlock(file->local_streams);
1485 cliprdr_local_file_try_close(rfile, ERROR_INTERNAL_ERROR, offset,
1486 fileContentsRequest->cbRequested);
1488 HashTable_Unlock(file->local_streams);
1489 return cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1492 static void cliprdr_local_stream_free(
void* obj);
1494 static UINT change_lock(CliprdrFileContext* file, UINT32 lockId, BOOL lock)
1496 UINT rc = CHANNEL_RC_OK;
1500 HashTable_Lock(file->local_streams);
1503 CliprdrLocalStream* stream = HashTable_GetItemValue(file->local_streams, &lockId);
1504 if (lock && !stream)
1506 stream = cliprdr_local_stream_new(file, lockId, NULL, 0);
1507 if (!HashTable_Insert(file->local_streams, &lockId, stream))
1509 rc = ERROR_INTERNAL_ERROR;
1510 cliprdr_local_stream_free(stream);
1513 file->local_lock_id = lockId;
1517 stream->locked = lock;
1518 stream->lockId = lockId;
1525 if (!HashTable_Foreach(file->local_streams, local_stream_discard, file))
1526 rc = ERROR_INTERNAL_ERROR;
1529 HashTable_Unlock(file->local_streams);
1533 static UINT cliprdr_file_context_lock(CliprdrClientContext* context,
1536 WINPR_ASSERT(context);
1537 WINPR_ASSERT(lockClipboardData);
1538 CliprdrFileContext* file = (context->custom);
1539 return change_lock(file, lockClipboardData->clipDataId, TRUE);
1542 static UINT cliprdr_file_context_unlock(CliprdrClientContext* context,
1545 WINPR_ASSERT(context);
1546 WINPR_ASSERT(unlockClipboardData);
1547 CliprdrFileContext* file = (context->custom);
1548 return change_lock(file, unlockClipboardData->clipDataId, FALSE);
1551 static UINT cliprdr_file_context_server_file_contents_request(
1554 UINT error = NO_ERROR;
1556 WINPR_ASSERT(context);
1557 WINPR_ASSERT(fileContentsRequest);
1559 CliprdrFileContext* file = (context->custom);
1566 if ((fileContentsRequest->dwFlags & (FILECONTENTS_SIZE | FILECONTENTS_RANGE)) ==
1567 (FILECONTENTS_SIZE | FILECONTENTS_RANGE))
1569 WLog_Print(file->log, WLOG_ERROR,
"invalid CLIPRDR_FILECONTENTS_REQUEST.dwFlags");
1570 return cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1573 if (fileContentsRequest->dwFlags & FILECONTENTS_SIZE)
1574 error = cliprdr_file_context_server_file_size_request(file, fileContentsRequest);
1576 if (fileContentsRequest->dwFlags & FILECONTENTS_RANGE)
1577 error = cliprdr_file_context_server_file_range_request(file, fileContentsRequest);
1581 WLog_Print(file->log, WLOG_ERROR,
"failed to handle CLIPRDR_FILECONTENTS_REQUEST: 0x%08X",
1583 return cliprdr_file_context_send_file_contents_failure(file, fileContentsRequest);
1586 return CHANNEL_RC_OK;
1589 BOOL cliprdr_file_context_init(CliprdrFileContext* file, CliprdrClientContext* cliprdr)
1592 WINPR_ASSERT(cliprdr);
1594 cliprdr->custom = file;
1595 file->context = cliprdr;
1597 cliprdr->ServerLockClipboardData = cliprdr_file_context_lock;
1598 cliprdr->ServerUnlockClipboardData = cliprdr_file_context_unlock;
1599 cliprdr->ServerFileContentsRequest = cliprdr_file_context_server_file_contents_request;
1600 #if defined(WITH_FUSE)
1601 cliprdr->ServerFileContentsResponse = cliprdr_file_context_server_file_contents_response;
1607 #if defined(WITH_FUSE)
1608 static void clear_all_selections(CliprdrFileContext* file_context)
1610 WINPR_ASSERT(file_context);
1611 WINPR_ASSERT(file_context->inode_table);
1613 HashTable_Lock(file_context->inode_table);
1614 clear_selection(file_context, TRUE, NULL);
1616 HashTable_Clear(file_context->clip_data_table);
1617 HashTable_Unlock(file_context->inode_table);
1621 BOOL cliprdr_file_context_uninit(CliprdrFileContext* file, CliprdrClientContext* cliprdr)
1624 WINPR_ASSERT(cliprdr);
1628 #if defined(WITH_FUSE)
1629 if (file->inode_table)
1631 clear_no_cdi_entry(file);
1632 clear_all_selections(file);
1636 HashTable_Clear(file->local_streams);
1638 file->context = NULL;
1639 #if defined(WITH_FUSE)
1640 cliprdr->ServerFileContentsResponse = NULL;
1646 static BOOL cliprdr_file_content_changed_and_update(
void* ihash,
size_t hsize,
const void* data,
1650 BYTE hash[WINPR_SHA256_DIGEST_LENGTH] = { 0 };
1652 if (hsize <
sizeof(hash))
1655 if (!winpr_Digest(WINPR_MD_SHA256, data, size, hash,
sizeof(hash)))
1658 const BOOL changed = memcmp(hash, ihash,
sizeof(hash)) != 0;
1660 memcpy(ihash, hash,
sizeof(hash));
1664 static BOOL cliprdr_file_server_content_changed_and_update(CliprdrFileContext* file,
1665 const void* data,
size_t size)
1668 return cliprdr_file_content_changed_and_update(file->server_data_hash,
1669 sizeof(file->server_data_hash), data, size);
1672 static BOOL cliprdr_file_client_content_changed_and_update(CliprdrFileContext* file,
1673 const void* data,
size_t size)
1676 return cliprdr_file_content_changed_and_update(file->client_data_hash,
1677 sizeof(file->client_data_hash), data, size);
1680 #if defined(WITH_FUSE)
1681 static fuse_ino_t get_next_free_inode(CliprdrFileContext* file_context)
1685 WINPR_ASSERT(file_context);
1687 ino = file_context->next_ino;
1688 while (ino == 0 || ino == FUSE_ROOT_ID ||
1689 HashTable_GetItemValue(file_context->inode_table, (
void*)(uintptr_t)ino))
1692 file_context->next_ino = ino + 1;
1697 static CliprdrFuseFile* clip_data_dir_new(CliprdrFileContext* file_context, BOOL has_clip_data_id,
1698 UINT32 clip_data_id)
1700 CliprdrFuseFile* root_dir = NULL;
1701 CliprdrFuseFile* clip_data_dir = NULL;
1702 size_t path_length = 0;
1704 WINPR_ASSERT(file_context);
1706 clip_data_dir = fuse_file_new();
1710 path_length = 1 + MAX_CLIP_DATA_DIR_LEN + 1;
1712 clip_data_dir->filename_with_root = calloc(path_length,
sizeof(
char));
1713 if (!clip_data_dir->filename_with_root)
1715 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to allocate filename");
1716 fuse_file_free(clip_data_dir);
1720 if (has_clip_data_id)
1721 (void)_snprintf(clip_data_dir->filename_with_root, path_length,
"/%u",
1722 (
unsigned)clip_data_id);
1724 (
void)_snprintf(clip_data_dir->filename_with_root, path_length,
"/%" PRIu64,
1727 clip_data_dir->filename = strrchr(clip_data_dir->filename_with_root,
'/') + 1;
1729 clip_data_dir->ino = get_next_free_inode(file_context);
1730 clip_data_dir->is_directory = TRUE;
1731 clip_data_dir->is_readonly = TRUE;
1732 clip_data_dir->has_clip_data_id = has_clip_data_id;
1733 clip_data_dir->clip_data_id = clip_data_id;
1735 root_dir = file_context->root_dir;
1736 if (!ArrayList_Append(root_dir->children, clip_data_dir))
1738 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to append FUSE file");
1739 fuse_file_free(clip_data_dir);
1742 clip_data_dir->parent = root_dir;
1744 if (!HashTable_Insert(file_context->inode_table, (
void*)(uintptr_t)clip_data_dir->ino,
1747 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to insert inode into inode table");
1748 ArrayList_Remove(root_dir->children, clip_data_dir);
1749 fuse_file_free(clip_data_dir);
1753 return clip_data_dir;
1756 static char* get_parent_path(
const char* filepath)
1759 size_t parent_path_length = 0;
1760 char* parent_path = NULL;
1762 base = strrchr(filepath,
'/');
1765 while (base > filepath && *base ==
'/')
1768 parent_path_length = 1 + base - filepath;
1769 parent_path = calloc(parent_path_length + 1,
sizeof(
char));
1773 memcpy(parent_path, filepath, parent_path_length);
1778 static BOOL is_fuse_file_not_parent(
const void* key,
void* value,
void* arg)
1780 CliprdrFuseFile* fuse_file = value;
1781 CliprdrFuseFindParentContext* find_context = arg;
1783 if (!fuse_file->is_directory)
1786 if (strcmp(find_context->parent_path, fuse_file->filename_with_root) == 0)
1788 find_context->parent = fuse_file;
1795 static CliprdrFuseFile* get_parent_directory(CliprdrFileContext* file_context,
const char* path)
1797 CliprdrFuseFindParentContext find_context = { 0 };
1799 WINPR_ASSERT(file_context);
1802 find_context.parent_path = get_parent_path(path);
1803 if (!find_context.parent_path)
1806 WINPR_ASSERT(!find_context.parent);
1808 if (HashTable_Foreach(file_context->inode_table, is_fuse_file_not_parent, &find_context))
1810 free(find_context.parent_path);
1813 WINPR_ASSERT(find_context.parent);
1815 free(find_context.parent_path);
1817 return find_context.parent;
1820 static BOOL set_selection_for_clip_data_entry(CliprdrFileContext* file_context,
1821 CliprdrFuseClipDataEntry* clip_data_entry,
1824 CliprdrFuseFile* clip_data_dir = NULL;
1826 WINPR_ASSERT(file_context);
1827 WINPR_ASSERT(clip_data_entry);
1828 WINPR_ASSERT(files);
1830 clip_data_dir = clip_data_entry->clip_data_dir;
1831 WINPR_ASSERT(clip_data_dir);
1833 if (clip_data_entry->has_clip_data_id)
1834 WLog_Print(file_context->log, WLOG_DEBUG,
"Setting selection for clipDataId %u",
1835 clip_data_entry->clip_data_id);
1837 WLog_Print(file_context->log, WLOG_DEBUG,
"Setting selection");
1840 for (UINT32 i = 0; i < n_files; ++i)
1843 CliprdrFuseFile* fuse_file = NULL;
1844 char* filename = NULL;
1845 size_t path_length = 0;
1847 fuse_file = fuse_file_new();
1850 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to create FUSE file");
1851 clear_entry_selection(clip_data_entry);
1855 filename = ConvertWCharToUtf8Alloc(file->cFileName, NULL);
1858 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to convert filename");
1859 fuse_file_free(fuse_file);
1860 clear_entry_selection(clip_data_entry);
1864 for (
size_t j = 0; filename[j]; ++j)
1866 if (filename[j] ==
'\\')
1870 path_length = strlen(clip_data_dir->filename_with_root) + 1 + strlen(filename) + 1;
1871 fuse_file->filename_with_root = calloc(path_length,
sizeof(
char));
1872 if (!fuse_file->filename_with_root)
1874 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to allocate filename");
1876 fuse_file_free(fuse_file);
1877 clear_entry_selection(clip_data_entry);
1881 (void)_snprintf(fuse_file->filename_with_root, path_length,
"%s/%s",
1882 clip_data_dir->filename_with_root, filename);
1885 fuse_file->filename = strrchr(fuse_file->filename_with_root,
'/') + 1;
1887 fuse_file->parent = get_parent_directory(file_context, fuse_file->filename_with_root);
1888 if (!fuse_file->parent)
1890 WLog_Print(file_context->log, WLOG_ERROR,
"Found no parent for FUSE file");
1891 fuse_file_free(fuse_file);
1892 clear_entry_selection(clip_data_entry);
1896 if (!ArrayList_Append(fuse_file->parent->children, fuse_file))
1898 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to append FUSE file");
1899 fuse_file_free(fuse_file);
1900 clear_entry_selection(clip_data_entry);
1904 fuse_file->list_idx = i;
1905 fuse_file->ino = get_next_free_inode(file_context);
1906 fuse_file->has_clip_data_id = clip_data_entry->has_clip_data_id;
1907 fuse_file->clip_data_id = clip_data_entry->clip_data_id;
1908 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1909 fuse_file->is_directory = TRUE;
1910 if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1911 fuse_file->is_readonly = TRUE;
1912 if (file->dwFlags & FD_FILESIZE)
1914 fuse_file->size = ((UINT64)file->nFileSizeHigh << 32) + file->nFileSizeLow;
1915 fuse_file->has_size = TRUE;
1917 if (file->dwFlags & FD_WRITESTIME)
1921 filetime = file->ftLastWriteTime.dwHighDateTime;
1923 filetime += file->ftLastWriteTime.dwLowDateTime;
1925 fuse_file->last_write_time_unix =
1926 1LL * filetime / (10LL * 1000LL * 1000LL) - WIN32_FILETIME_TO_UNIX_EPOCH;
1927 fuse_file->has_last_write_time = TRUE;
1930 if (!HashTable_Insert(file_context->inode_table, (
void*)(uintptr_t)fuse_file->ino,
1933 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to insert inode into inode table");
1934 fuse_file_free(fuse_file);
1935 clear_entry_selection(clip_data_entry);
1941 if (clip_data_entry->has_clip_data_id)
1942 WLog_Print(file_context->log, WLOG_DEBUG,
"Selection set for clipDataId %u",
1943 clip_data_entry->clip_data_id);
1945 WLog_Print(file_context->log, WLOG_DEBUG,
"Selection set");
1950 static BOOL update_exposed_path(CliprdrFileContext* file_context, wClipboard* clip,
1951 CliprdrFuseClipDataEntry* clip_data_entry)
1953 wClipboardDelegate* delegate = NULL;
1954 CliprdrFuseFile* clip_data_dir = NULL;
1956 WINPR_ASSERT(file_context);
1958 WINPR_ASSERT(clip_data_entry);
1960 delegate = ClipboardGetDelegate(clip);
1961 WINPR_ASSERT(delegate);
1963 clip_data_dir = clip_data_entry->clip_data_dir;
1964 WINPR_ASSERT(clip_data_dir);
1966 free(file_context->exposed_path);
1967 file_context->exposed_path = GetCombinedPath(file_context->path, clip_data_dir->filename);
1968 if (file_context->exposed_path)
1969 WLog_Print(file_context->log, WLOG_DEBUG,
"Updated exposed path to \"%s\"",
1970 file_context->exposed_path);
1972 delegate->basePath = file_context->exposed_path;
1974 return delegate->basePath != NULL;
1978 BOOL cliprdr_file_context_update_server_data(CliprdrFileContext* file_context, wClipboard* clip,
1979 const void* data,
size_t size)
1981 #if defined(WITH_FUSE)
1982 CliprdrFuseClipDataEntry* clip_data_entry = NULL;
1987 WINPR_ASSERT(file_context);
1989 if (size > UINT32_MAX)
1992 if (cliprdr_parse_file_list(data, (UINT32)size, &files, &n_files))
1994 WLog_Print(file_context->log, WLOG_ERROR,
"Failed to parse file list");
1998 HashTable_Lock(file_context->inode_table);
1999 HashTable_Lock(file_context->clip_data_table);
2000 if (does_server_support_clipdata_locking(file_context))
2001 clip_data_entry = HashTable_GetItemValue(
2002 file_context->clip_data_table, (
void*)(uintptr_t)file_context->current_clip_data_id);
2004 clip_data_entry = file_context->clip_data_entry_without_id;
2006 WINPR_ASSERT(clip_data_entry);
2008 clear_entry_selection(clip_data_entry);
2009 WINPR_ASSERT(!clip_data_entry->clip_data_dir);
2011 clip_data_entry->clip_data_dir =
2012 clip_data_dir_new(file_context, does_server_support_clipdata_locking(file_context),
2013 file_context->current_clip_data_id);
2014 if (!clip_data_entry->clip_data_dir)
2016 if (!update_exposed_path(file_context, clip, clip_data_entry))
2018 if (!set_selection_for_clip_data_entry(file_context, clip_data_entry, files, n_files))
2024 HashTable_Unlock(file_context->clip_data_table);
2025 HashTable_Unlock(file_context->inode_table);
2033 void* cliprdr_file_context_get_context(CliprdrFileContext* file)
2036 return file->clipboard;
2039 void cliprdr_file_session_terminate(CliprdrFileContext* file, BOOL stop_thread)
2044 #if defined(WITH_FUSE)
2045 WINPR_ASSERT(file->fuse_stop_sync);
2047 WLog_Print(file->log, WLOG_DEBUG,
"Setting FUSE exit flag");
2048 if (file->fuse_sess)
2049 fuse_session_exit(file->fuse_sess);
2053 WLog_Print(file->log, WLOG_DEBUG,
"Setting FUSE stop event");
2054 (void)SetEvent(file->fuse_stop_sync);
2060 #if defined(WITH_FUSE)
2061 WLog_Print(file->log, WLOG_DEBUG,
"Forcing FUSE to check exit flag");
2063 (void)winpr_PathFileExists(file->path);
2066 void cliprdr_file_context_free(CliprdrFileContext* file)
2071 #if defined(WITH_FUSE)
2072 if (file->inode_table)
2074 clear_no_cdi_entry(file);
2075 clear_all_selections(file);
2078 if (file->fuse_thread)
2080 WINPR_ASSERT(file->fuse_stop_sync);
2082 WLog_Print(file->log, WLOG_DEBUG,
"Stopping FUSE thread");
2083 cliprdr_file_session_terminate(file, TRUE);
2085 WLog_Print(file->log, WLOG_DEBUG,
"Waiting on FUSE thread");
2086 (void)WaitForSingleObject(file->fuse_thread, INFINITE);
2087 (void)CloseHandle(file->fuse_thread);
2089 if (file->fuse_stop_sync)
2090 (void)CloseHandle(file->fuse_stop_sync);
2091 if (file->fuse_start_sync)
2092 (void)CloseHandle(file->fuse_start_sync);
2094 HashTable_Free(file->request_table);
2095 HashTable_Free(file->clip_data_table);
2096 HashTable_Free(file->inode_table);
2098 HashTable_Free(file->local_streams);
2099 winpr_RemoveDirectory(file->path);
2101 free(file->exposed_path);
2105 static BOOL create_base_path(CliprdrFileContext* file)
2109 char base[64] = { 0 };
2110 (void)_snprintf(base,
sizeof(base),
"com.freerdp.client.cliprdr.%" PRIu32,
2111 GetCurrentProcessId());
2113 file->path = GetKnownSubPath(KNOWN_PATH_TEMP, base);
2117 if (!winpr_PathFileExists(file->path) && !winpr_PathMakePath(file->path, 0))
2119 WLog_Print(file->log, WLOG_ERROR,
"Failed to create directory '%s'", file->path);
2125 static void cliprdr_local_file_free(CliprdrLocalFile* file)
2127 const CliprdrLocalFile empty = { 0 };
2132 WLog_Print(file->context->log, WLOG_DEBUG,
"closing file %s, discarding entry", file->name);
2133 (void)fclose(file->fp);
2139 static BOOL cliprdr_local_file_new(CliprdrFileContext* context, CliprdrLocalFile* f,
2142 const CliprdrLocalFile empty = { 0 };
2144 WINPR_ASSERT(context);
2148 f->context = context;
2149 f->name = winpr_str_url_decode(path, strlen(path));
2155 cliprdr_local_file_free(f);
2159 static void cliprdr_local_files_free(CliprdrLocalStream* stream)
2161 WINPR_ASSERT(stream);
2163 for (
size_t x = 0; x < stream->count; x++)
2164 cliprdr_local_file_free(&stream->files[x]);
2165 free(stream->files);
2167 stream->files = NULL;
2171 static void cliprdr_local_stream_free(
void* obj)
2173 CliprdrLocalStream* stream = (CliprdrLocalStream*)obj;
2175 cliprdr_local_files_free(stream);
2180 static BOOL append_entry(CliprdrLocalStream* stream,
const char* path)
2182 CliprdrLocalFile* tmp = realloc(stream->files,
sizeof(CliprdrLocalFile) * (stream->count + 1));
2185 stream->files = tmp;
2186 CliprdrLocalFile* f = &stream->files[stream->count++];
2188 return cliprdr_local_file_new(stream->context, f, path);
2191 static BOOL is_directory(
const char* path)
2193 WCHAR* wpath = ConvertUtf8ToWCharAlloc(path, NULL);
2198 CreateFileW(wpath, 0, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2201 if (hFile == INVALID_HANDLE_VALUE)
2205 const BOOL status = GetFileInformationByHandle(hFile, &fileInformation);
2206 (void)CloseHandle(hFile);
2210 return (fileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? TRUE : FALSE;
2213 static BOOL add_directory(CliprdrLocalStream* stream,
const char* path)
2215 char* wildcardpath = GetCombinedPath(path,
"*");
2218 WCHAR* wpath = ConvertUtf8ToWCharAlloc(wildcardpath, NULL);
2224 HANDLE hFind = FindFirstFileW(wpath, &FindFileData);
2227 if (hFind == INVALID_HANDLE_VALUE)
2233 WCHAR dotbuffer[6] = { 0 };
2234 WCHAR dotdotbuffer[6] = { 0 };
2235 const WCHAR* dot = InitializeConstWCharFromUtf8(
".", dotbuffer, ARRAYSIZE(dotbuffer));
2236 const WCHAR* dotdot = InitializeConstWCharFromUtf8(
"..", dotdotbuffer, ARRAYSIZE(dotdotbuffer));
2239 if (_wcscmp(FindFileData.cFileName, dot) == 0)
2241 if (_wcscmp(FindFileData.cFileName, dotdot) == 0)
2244 char cFileName[MAX_PATH] = { 0 };
2245 (void)ConvertWCharNToUtf8(FindFileData.cFileName, ARRAYSIZE(FindFileData.cFileName),
2246 cFileName, ARRAYSIZE(cFileName));
2249 next = GetCombinedPath(path, cFileName);
2253 if (!append_entry(stream, next))
2255 if (is_directory(next))
2257 if (!add_directory(stream, next))
2260 }
while (FindNextFileW(hFind, &FindFileData));
2270 static BOOL cliprdr_local_stream_update(CliprdrLocalStream* stream,
const char* data,
size_t size)
2273 WINPR_ASSERT(stream);
2277 cliprdr_local_files_free(stream);
2279 stream->files = calloc(size,
sizeof(CliprdrLocalFile));
2283 char* copy = strndup(data, size);
2287 char* saveptr = NULL;
2288 char* ptr = strtok_s(copy,
"\r\n", &saveptr);
2291 const char* name = ptr;
2292 if (strncmp(
"file:///", ptr, 8) == 0)
2294 else if (strncmp(
"file:/", ptr, 6) == 0)
2297 if (!append_entry(stream, name))
2300 if (is_directory(name))
2302 const BOOL res = add_directory(stream, name);
2306 ptr = strtok_s(NULL,
"\r\n", &saveptr);
2315 CliprdrLocalStream* cliprdr_local_stream_new(CliprdrFileContext* context, UINT32 streamID,
2316 const char* data,
size_t size)
2318 WINPR_ASSERT(context);
2319 CliprdrLocalStream* stream = calloc(1,
sizeof(CliprdrLocalStream));
2323 stream->context = context;
2324 if (!cliprdr_local_stream_update(stream, data, size))
2327 stream->lockId = streamID;
2331 cliprdr_local_stream_free(stream);
2335 static UINT32 UINTPointerHash(
const void*
id)
2338 return *((
const UINT32*)
id);
2341 static BOOL UINTPointerCompare(
const void* pointer1,
const void* pointer2)
2343 if (!pointer1 || !pointer2)
2344 return pointer1 == pointer2;
2346 const UINT32* a = pointer1;
2347 const UINT32* b = pointer2;
2351 static void* UINTPointerClone(
const void* other)
2353 const UINT32* src = other;
2357 UINT32* copy = calloc(1,
sizeof(UINT32));
2365 #if defined(WITH_FUSE)
2366 static CliprdrFuseFile* fuse_file_new_root(CliprdrFileContext* file_context)
2368 CliprdrFuseFile* root_dir = NULL;
2370 root_dir = fuse_file_new();
2374 root_dir->filename_with_root = calloc(2,
sizeof(
char));
2375 if (!root_dir->filename_with_root)
2377 fuse_file_free(root_dir);
2381 (void)_snprintf(root_dir->filename_with_root, 2,
"/");
2382 root_dir->filename = root_dir->filename_with_root;
2384 root_dir->ino = FUSE_ROOT_ID;
2385 root_dir->is_directory = TRUE;
2386 root_dir->is_readonly = TRUE;
2388 if (!HashTable_Insert(file_context->inode_table, (
void*)(uintptr_t)root_dir->ino, root_dir))
2390 fuse_file_free(root_dir);
2398 CliprdrFileContext* cliprdr_file_context_new(
void* context)
2400 CliprdrFileContext* file = calloc(1,
sizeof(CliprdrFileContext));
2404 file->log = WLog_Get(CLIENT_TAG(
"common.cliprdr.file"));
2405 file->clipboard = context;
2407 file->local_streams = HashTable_New(FALSE);
2408 if (!file->local_streams)
2411 if (!HashTable_SetHashFunction(file->local_streams, UINTPointerHash))
2414 wObject* hkobj = HashTable_KeyObject(file->local_streams);
2415 WINPR_ASSERT(hkobj);
2416 hkobj->fnObjectEquals = UINTPointerCompare;
2417 hkobj->fnObjectFree = free;
2418 hkobj->fnObjectNew = UINTPointerClone;
2420 wObject* hobj = HashTable_ValueObject(file->local_streams);
2422 hobj->fnObjectFree = cliprdr_local_stream_free;
2424 #if defined(WITH_FUSE)
2425 file->inode_table = HashTable_New(FALSE);
2426 file->clip_data_table = HashTable_New(FALSE);
2427 file->request_table = HashTable_New(FALSE);
2428 if (!file->inode_table || !file->clip_data_table || !file->request_table)
2432 wObject* ctobj = HashTable_ValueObject(file->request_table);
2433 WINPR_ASSERT(ctobj);
2434 ctobj->fnObjectFree = free;
2437 wObject* ctobj = HashTable_ValueObject(file->clip_data_table);
2438 WINPR_ASSERT(ctobj);
2439 ctobj->fnObjectFree = clip_data_entry_free;
2442 file->root_dir = fuse_file_new_root(file);
2443 if (!file->root_dir)
2447 if (!create_base_path(file))
2450 #if defined(WITH_FUSE)
2451 if (!(file->fuse_start_sync = CreateEvent(NULL, TRUE, FALSE, NULL)))
2453 if (!(file->fuse_stop_sync = CreateEvent(NULL, TRUE, FALSE, NULL)))
2455 if (!(file->fuse_thread = CreateThread(NULL, 0, cliprdr_file_fuse_thread, file, 0, NULL)))
2458 if (WaitForSingleObject(file->fuse_start_sync, INFINITE) == WAIT_FAILED)
2459 WLog_Print(file->log, WLOG_ERROR,
"Failed to wait for start sync");
2464 WINPR_PRAGMA_DIAG_PUSH
2465 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
2466 cliprdr_file_context_free(file);
2467 WINPR_PRAGMA_DIAG_POP
2471 BOOL local_stream_discard(
const void* key,
void* value,
void* arg)
2473 CliprdrFileContext* file = arg;
2474 CliprdrLocalStream* stream = value;
2476 WINPR_ASSERT(stream);
2478 if (!stream->locked)
2479 HashTable_Remove(file->local_streams, key);
2483 BOOL cliprdr_file_context_clear(CliprdrFileContext* file)
2487 WLog_Print(file->log, WLOG_DEBUG,
"clear file clipboard...");
2489 HashTable_Lock(file->local_streams);
2490 HashTable_Foreach(file->local_streams, local_stream_discard, file);
2491 HashTable_Unlock(file->local_streams);
2493 memset(file->server_data_hash, 0,
sizeof(file->server_data_hash));
2494 memset(file->client_data_hash, 0,
sizeof(file->client_data_hash));
2498 BOOL cliprdr_file_context_update_client_data(CliprdrFileContext* file,
const char* data,
2504 if (!cliprdr_file_client_content_changed_and_update(file, data, size))
2507 if (!cliprdr_file_context_clear(file))
2510 UINT32 lockId = file->local_lock_id;
2512 HashTable_Lock(file->local_streams);
2513 CliprdrLocalStream* stream = HashTable_GetItemValue(file->local_streams, &lockId);
2515 WLog_Print(file->log, WLOG_DEBUG,
"update client file list (stream=%p)...", stream);
2517 rc = cliprdr_local_stream_update(stream, data, size);
2520 stream = cliprdr_local_stream_new(file, lockId, data, size);
2521 rc = HashTable_Insert(file->local_streams, &stream->lockId, stream);
2523 cliprdr_local_stream_free(stream);
2527 HashTable_Unlock(file->local_streams);
2531 UINT32 cliprdr_file_context_current_flags(CliprdrFileContext* file)
2535 if ((file->file_capability_flags & CB_STREAM_FILECLIP_ENABLED) == 0)
2538 if (!file->file_formats_registered)
2541 return CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS |
2542 CB_HUGE_FILE_SUPPORT_ENABLED;
2545 BOOL cliprdr_file_context_set_locally_available(CliprdrFileContext* file, BOOL available)
2548 file->file_formats_registered = available;
2552 BOOL cliprdr_file_context_remote_set_flags(CliprdrFileContext* file, UINT32 flags)
2555 file->file_capability_flags = flags;
2559 UINT32 cliprdr_file_context_remote_get_flags(CliprdrFileContext* file)
2562 return file->file_capability_flags;
2565 BOOL cliprdr_file_context_has_local_support(CliprdrFileContext* file)
2569 #if defined(WITH_FUSE)
This struct contains function pointer to initialize/free objects.