client/cliprdr_file: Do not destroy FUSE session while using it

When invalidating inodes, it is obligatory, that the session was not
destroyed yet.
So, in case of the FUSE loop stops before the session stops wait with
the destroyal of the session, until it is clear, that it is not used
anymore.
This commit is contained in:
Pascal Nowack 2023-08-02 19:36:38 +02:00 committed by akallabeth
parent 8f1ba61553
commit c031e7eba6

View File

@ -161,6 +161,7 @@ struct cliprdr_file_context
{
#if defined(WITH_FUSE2) || defined(WITH_FUSE3)
/* FUSE related**/
HANDLE fuse_stop_sync;
HANDLE fuse_thread;
struct fuse_session* fuse_sess;
@ -387,6 +388,7 @@ static BOOL notify_delete_child(void* data, size_t index, va_list ap)
CliprdrFuseFile* parent = va_arg(ap, CliprdrFuseFile*);
WINPR_ASSERT(file_context);
WINPR_ASSERT(file_context->fuse_sess);
WINPR_ASSERT(parent);
fuse_lowlevel_notify_delete(file_context->fuse_sess, parent->ino, child->ino, child->filename,
@ -403,6 +405,7 @@ static BOOL invalidate_inode(void* data, size_t index, va_list ap)
CliprdrFileContext* file_context = va_arg(ap, CliprdrFileContext*);
WINPR_ASSERT(file_context);
WINPR_ASSERT(file_context->fuse_sess);
ArrayList_ForEach(fuse_file->children, notify_delete_child, file_context, fuse_file);
@ -459,20 +462,22 @@ static void clear_selection(CliprdrFileContext* file_context, BOOL all_selection
HashTable_Foreach(file_context->inode_table, maybe_steal_inode, &clear_context);
HashTable_Unlock(file_context->inode_table);
/*
* fuse_lowlevel_notify_inval_inode() is a blocking operation. If we receive a
* FUSE request (e.g. read()), then FUSE would block in read(), since the
* mutex of the inode_table would still be locked, if we wouldn't unlock it
* here.
* So, to avoid a deadlock here, unlock the mutex and reply all incoming
* operations with -ENOENT until the invalidation process is complete.
*/
ArrayList_ForEach(clear_context.fuse_files, invalidate_inode, file_context);
if (clip_data_dir)
fuse_lowlevel_notify_delete(file_context->fuse_sess, file_context->root_dir->ino,
clip_data_dir->ino, clip_data_dir->filename,
strlen(clip_data_dir->filename));
if (file_context->fuse_sess)
{
/*
* fuse_lowlevel_notify_inval_inode() is a blocking operation. If we receive a
* FUSE request (e.g. read()), then FUSE would block in read(), since the
* mutex of the inode_table would still be locked, if we wouldn't unlock it
* here.
* So, to avoid a deadlock here, unlock the mutex and reply all incoming
* operations with -ENOENT until the invalidation process is complete.
*/
ArrayList_ForEach(clear_context.fuse_files, invalidate_inode, file_context);
if (clip_data_dir)
fuse_lowlevel_notify_delete(file_context->fuse_sess, file_context->root_dir->ino,
clip_data_dir->ino, clip_data_dir->filename,
strlen(clip_data_dir->filename));
}
ArrayList_Free(clear_context.fuse_files);
HashTable_Lock(file_context->inode_table);
@ -1139,6 +1144,10 @@ static DWORD WINAPI cliprdr_file_fuse_thread(LPVOID arg)
fuse_session_unmount(file->fuse_sess);
}
freerdp_del_signal_cleanup_handler(file, fuse_abort);
WLog_Print(file->log, WLOG_DEBUG, "Waiting for FUSE stop sync");
if (WaitForSingleObject(file->fuse_stop_sync, INFINITE) == WAIT_FAILED)
WLog_Print(file->log, WLOG_ERROR, "Failed to wait for stop sync");
fuse_session_destroy(file->fuse_sess);
}
#else
@ -1154,10 +1163,20 @@ static DWORD WINAPI cliprdr_file_fuse_thread(LPVOID arg)
const int err = fuse_session_loop(file->fuse_sess);
if (err != 0)
WLog_Print(file->log, WLOG_WARN, "fuse_session_loop failed with %d", err);
}
WLog_Print(file->log, WLOG_DEBUG, "Waiting for FUSE stop sync");
if (WaitForSingleObject(file->fuse_stop_sync, INFINITE) == WAIT_FAILED)
WLog_Print(file->log, WLOG_ERROR, "Failed to wait for stop sync");
if (file->fuse_sess != NULL)
{
fuse_session_remove_chan(ch);
freerdp_del_signal_cleanup_handler(file, fuse_abort);
fuse_session_destroy(file->fuse_sess);
}
fuse_unmount(file->path, ch);
}
#endif
@ -2061,10 +2080,18 @@ void cliprdr_file_context_free(CliprdrFileContext* file)
if (file->fuse_thread)
{
WINPR_ASSERT(file->fuse_stop_sync);
WLog_Print(file->log, WLOG_DEBUG, "Stopping FUSE thread");
cliprdr_file_session_terminate(file);
SetEvent(file->fuse_stop_sync);
WLog_Print(file->log, WLOG_DEBUG, "Waiting on FUSE thread");
WaitForSingleObject(file->fuse_thread, INFINITE);
CloseHandle(file->fuse_thread);
}
if (file->fuse_stop_sync)
CloseHandle(file->fuse_stop_sync);
HashTable_Free(file->request_table);
HashTable_Free(file->clip_data_table);
@ -2411,6 +2438,8 @@ CliprdrFileContext* cliprdr_file_context_new(void* context)
goto fail;
#if defined(WITH_FUSE2) || defined(WITH_FUSE3)
if (!(file->fuse_stop_sync = CreateEvent(NULL, TRUE, FALSE, NULL)))
goto fail;
if (!(file->fuse_thread = CreateThread(NULL, 0, cliprdr_file_fuse_thread, file, 0, NULL)))
goto fail;
#endif