Repository: FreeRDP/FreeRDP
Pull request #11627: [client,cliprdr] refactor file clipboard
Author: login: akallabeth
Created: 2025-05-23T12:40:00Z | Merged: 2025-05-23T13:32:58Z



Diff:
diff --git a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
index 63e95311b277..15398fb83a6c 100644
--- a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
+++ b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
@@ -108,11 +108,13 @@ static BOOL tsmf_ffmpeg_init_context(ITSMFDecoder* decoder)
 static BOOL tsmf_ffmpeg_init_video_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
 {
 	TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
-	mdecoder->codec_context->width = media_type->Width;
-	mdecoder->codec_context->height = media_type->Height;
-	mdecoder->codec_context->bit_rate = media_type->BitRate;
-	mdecoder->codec_context->time_base.den = media_type->SamplesPerSecond.Numerator;
-	mdecoder->codec_context->time_base.num = media_type->SamplesPerSecond.Denominator;
+	mdecoder->codec_context->width = WINPR_ASSERTING_INT_CAST(int, media_type->Width);
+	mdecoder->codec_context->height = WINPR_ASSERTING_INT_CAST(int, media_type->Height);
+	mdecoder->codec_context->bit_rate = WINPR_ASSERTING_INT_CAST(int, media_type->BitRate);
+	mdecoder->codec_context->time_base.den =
+	    WINPR_ASSERTING_INT_CAST(int, media_type->SamplesPerSecond.Numerator);
+	mdecoder->codec_context->time_base.num =
+	    WINPR_ASSERTING_INT_CAST(int, media_type->SamplesPerSecond.Denominator);
 #if LIBAVCODEC_VERSION_MAJOR < 55
 	mdecoder->frame = avcodec_alloc_frame();
 #else
@@ -124,14 +126,16 @@ static BOOL tsmf_ffmpeg_init_video_stream(ITSMFDecoder* decoder, const TS_AM_MED
 static BOOL tsmf_ffmpeg_init_audio_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
 {
 	TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder;
-	mdecoder->codec_context->sample_rate = media_type->SamplesPerSecond.Numerator;
-	mdecoder->codec_context->bit_rate = media_type->BitRate;
+	mdecoder->codec_context->sample_rate =
+	    WINPR_ASSERTING_INT_CAST(int, media_type->SamplesPerSecond.Numerator);
+	mdecoder->codec_context->bit_rate = WINPR_ASSERTING_INT_CAST(int, media_type->BitRate);
 #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100)
-	mdecoder->codec_context->ch_layout.nb_channels = media_type->Channels;
+	mdecoder->codec_context->ch_layout.nb_channels =
+	    WINPR_ASSERTING_INT_CAST(int, media_type->Channels);
 #else
-	mdecoder->codec_context->channels = media_type->Channels;
+	mdecoder->codec_context->channels = WINPR_ASSERTING_INT_CAST(int, media_type->Channels);
 #endif
-	mdecoder->codec_context->block_align = media_type->BlockAlign;
+	mdecoder->codec_context->block_align = WINPR_ASSERTING_INT_CAST(int, media_type->BlockAlign);
 #if LIBAVCODEC_VERSION_MAJOR < 55
 #ifdef AV_CPU_FLAG_SSE2
 	mdecoder->codec_context->dsp_mask = AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMX2;
@@ -199,7 +203,8 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP
 	if (media_type->ExtraData)
 	{
 		/* Add a padding to avoid invalid memory read in some codec */
-		mdecoder->codec_context->extradata_size = media_type->ExtraDataSize + 8;
+		mdecoder->codec_context->extradata_size =
+		    WINPR_ASSERTING_INT_CAST(int, media_type->ExtraDataSize + 8);
 		mdecoder->codec_context->extradata = calloc(1, mdecoder->codec_context->extradata_size);
 
 		if (!mdecoder->codec_context->extradata)
@@ -387,7 +392,7 @@ static BOOL tsmf_ffmpeg_decode_video(ITSMFDecoder* decoder, const BYTE* data, UI
 		av_init_packet(&pkt);
 #endif
 		pkt.data = WINPR_CAST_CONST_PTR_AWAY(data, BYTE*);
-		pkt.size = data_size;
+		pkt.size = WINPR_ASSERTING_INT_CAST(int, data_size);
 
 		if (extensions & TSMM_SAMPLE_EXT_CLEANPOINT)
 			pkt.flags |= AV_PKT_FLAG_KEY;
@@ -501,9 +506,9 @@ static BOOL tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const BYTE* data, UI
 			dst += mdecoder->decoded_size;
 		}
 
-		frame_size = mdecoder->decoded_size_max - mdecoder->decoded_size;
 #if LIBAVCODEC_VERSION_MAJOR < 52 || \
     (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR <= 20)
+		frame_size = mdecoder->decoded_size_max - mdecoder->decoded_size;
 		len = avcodec_decode_audio2(mdecoder->codec_context, (int16_t*)dst, &frame_size, src,
 		                            src_size);
 #else
@@ -520,7 +525,7 @@ static BOOL tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const BYTE* data, UI
 #endif
 
 			pkt.data = WINPR_CAST_CONST_PTR_AWAY(src, BYTE*);
-			pkt.size = src_size;
+			pkt.size = WINPR_ASSERTING_INT_CAST(int, src_size);
 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)
 			len = avcodec_decode_audio4(mdecoder->codec_context, decoded_frame, &got_frame, &pkt);
 #else
diff --git a/channels/tsmf/client/oss/tsmf_oss.c b/channels/tsmf/client/oss/tsmf_oss.c
index ad8033604b76..9cc2119d8026 100644
--- a/channels/tsmf/client/oss/tsmf_oss.c
+++ b/channels/tsmf/client/oss/tsmf_oss.c
@@ -31,7 +31,6 @@
 #include <fcntl.h>
 #include <libgen.h>
 #include <limits.h>
-#include <unistd.h>
 #if defined(__OpenBSD__)
 #include <soundcard.h>
 #else
@@ -131,17 +130,17 @@ static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UIN
 	if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
 		OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
 
-	tmp = channels;
+	tmp = WINPR_ASSERTING_INT_CAST(int, channels);
 
 	if (ioctl(oss->pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
 		OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
 
-	tmp = sample_rate;
+	tmp = WINPR_ASSERTING_INT_CAST(int, sample_rate);
 
 	if (ioctl(oss->pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
 		OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
 
-	tmp = ((bits_per_sample / 8) * channels * sample_rate);
+	tmp = WINPR_ASSERTING_INT_CAST(int, ((bits_per_sample / 8) * channels * sample_rate));
 
 	if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
 		OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
diff --git a/channels/tsmf/client/tsmf_ifman.c b/channels/tsmf/client/tsmf_ifman.c
index dc97c56b1cb6..6fcb82b7279d 100644
--- a/channels/tsmf/client/tsmf_ifman.c
+++ b/channels/tsmf/client/tsmf_ifman.c
@@ -351,12 +351,14 @@ UINT tsmf_ifman_set_source_video_rect(TSMF_IFMAN* ifman)
 	}
 	else
 	{
+#ifdef WITH_DEBUG_TSMF
 		Left = tsmf_stream_read_float(ifman->input);   /* Left (4 bytes) */
 		Top = tsmf_stream_read_float(ifman->input);    /* Top (4 bytes) */
 		Right = tsmf_stream_read_float(ifman->input);  /* Right (4 bytes) */
 		Bottom = tsmf_stream_read_float(ifman->input); /* Bottom (4 bytes) */
 		DEBUG_TSMF("SetSourceVideoRect: Left: %f Top: %f Right: %f Bottom: %f", Left, Top, Right,
 		           Bottom);
+#endif
 	}
 
 	ifman->output_pending = TRUE;
diff --git a/client/X11/xf_tsmf.c b/client/X11/xf_tsmf.c
index 7d79c04b83e8..ff42dcfcc59f 100644
--- a/client/X11/xf_tsmf.c
+++ b/client/X11/xf_tsmf.c
@@ -127,8 +127,8 @@ static int xf_tsmf_xv_video_frame_event(TsmfClientContext* tsmf, TSMF_VIDEO_FRAM
 			width = event->visibleRects[i].right - event->visibleRects[i].left;
 			height = event->visibleRects[i].bottom - event->visibleRects[i].top;
 
-			xrects[i].x = x;
-			xrects[i].y = y;
+			xrects[i].x = WINPR_ASSERTING_INT_CAST(short, x);
+			xrects[i].y = WINPR_ASSERTING_INT_CAST(short, y);
 			xrects[i].width = width;
 			xrects[i].height = height;
 		}
@@ -188,8 +188,8 @@ static int xf_tsmf_xv_video_frame_event(TsmfClientContext* tsmf, TSMF_VIDEO_FRAM
 		return -1003;
 	}
 
-	image = XvShmCreateImage(xfc->display, xv->xv_port, xvpixfmt, 0, event->frameWidth,
-	                         event->frameHeight, &shminfo);
+	image = XvShmCreateImage(xfc->display, xv->xv_port, WINPR_ASSERTING_INT_CAST(int, xvpixfmt), 0,
+	                         event->frameWidth, event->frameHeight, &shminfo);
 
 	if (xv->xv_image_size != image->data_size)
 	{
diff --git a/client/common/client_cliprdr_file.c b/client/common/client_cliprdr_file.c
index 93a6b2651547..2e6bcba2fa4d 100644
--- a/client/common/client_cliprdr_file.c
+++ b/client/common/client_cliprdr_file.c
@@ -54,7 +54,6 @@
 
 #include <freerdp/client/client_cliprdr_file.h>
 
-#define MAX_CLIP_DATA_DIR_LEN 10
 #define NO_CLIP_DATA_ID (UINT64_C(1) << 32)
 #define WIN32_FILETIME_TO_UNIX_EPOCH INT64_C(11644473600)
 
@@ -83,7 +82,10 @@ struct sCliprdrFuseFile
 	CliprdrFuseFile* parent;
 	wArrayList* children;
 
-	char* filename;
+	size_t filename_len;
+	const char* filename;
+
+	size_t filename_with_root_len;
 	char* filename_with_root;
 	UINT32 list_idx;
 	fuse_ino_t ino;
@@ -209,22 +211,40 @@ static void fuse_file_free(void* data)
 	free(fuse_file);
 }
 
-static CliprdrFuseFile* fuse_file_new(void)
+WINPR_ATTR_FORMAT_ARG(1, 2)
+WINPR_ATTR_MALLOC(fuse_file_free, 1)
+static CliprdrFuseFile* fuse_file_new(WINPR_FORMAT_ARG const char* fmt, ...)
 {
-	CliprdrFuseFile* fuse_file = NULL;
-
-	fuse_file = calloc(1, sizeof(CliprdrFuseFile));
-	if (!fuse_file)
+	CliprdrFuseFile* file = calloc(1, sizeof(CliprdrFuseFile));
+	if (!file)
 		return NULL;
 
-	fuse_file->children = ArrayList_New(FALSE);
-	if (!fuse_file->children)
+	file->children = ArrayList_New(FALSE);
+	if (!file->children)
+		goto fail;
+
+	WINPR_ASSERT(fmt);
+
+	va_list ap;
+	va_start(ap, fmt);
+	const int rc =
+	    winpr_vasprintf(&file->filename_with_root, &file->filename_with_root_len, fmt, ap);
+	va_end(ap);
+
+	if (rc < 0)
+		goto fail;
+
+	if (file->filename_with_root && (file->filename_with_root_len > 0))
 	{
-		free(fuse_file);
-		return NULL;
+		file->filename_len = 0;
+		file->filename = strrchr(file->filename_with_root, '/') + 1;
+		if (file->filename)
+			file->filename_len = strnlen(file->filename, file->filename_with_root_len);
 	}
-
-	return fuse_file;
+	return file;
+fail:
+	fuse_file_free(file);
+	return NULL;
 }
 
 static void clip_data_entry_free(void* data)
@@ -393,7 +413,7 @@ static BOOL notify_delete_child(void* data, WINPR_ATTR_UNUSED size_t index, va_l
 
 	WINPR_ASSERT(file_context->fuse_sess);
 	fuse_lowlevel_notify_delete(file_context->fuse_sess, parent->ino, child->ino, child->filename,
-	                            strlen(child->filename));
+	                            child->filename_len);
 
 	return TRUE;
 }
@@ -478,7 +498,7 @@ static void clear_selection(CliprdrFileContext* file_context, BOOL all_selection
 		{
 			fuse_lowlevel_notify_delete(file_context->fuse_sess, file_context->root_dir->ino,
 			                            clip_data_dir->ino, clip_data_dir->filename,
-			                            strlen(clip_data_dir->filename));
+			                            clip_data_dir->filename_len);
 		}
 	}
 	ArrayList_Free(clear_context.fuse_files);
@@ -671,7 +691,7 @@ get_fuse_file_by_name_from_parent(WINPR_ATTR_UNUSED CliprdrFileContext* file_con
 
 		WINPR_ASSERT(child);
 
-		if (strcmp(name, child->filename) == 0)
+		if (strncmp(name, child->filename, child->filename_len + 1) == 0)
 			return child;
 	}
 
@@ -1696,33 +1716,16 @@ static CliprdrFuseFile* clip_data_dir_new(CliprdrFileContext* file_context, BOOL
                                           UINT32 clip_data_id)
 {
 	CliprdrFuseFile* root_dir = NULL;
-	CliprdrFuseFile* clip_data_dir = NULL;
-	size_t path_length = 0;
 
 	WINPR_ASSERT(file_context);
 
-	clip_data_dir = fuse_file_new();
-	if (!clip_data_dir)
-		return NULL;
-
-	path_length = 1 + MAX_CLIP_DATA_DIR_LEN + 1;
+	UINT64 data_id = clip_data_id;
+	if (!has_clip_data_id)
+		data_id = NO_CLIP_DATA_ID;
 
-	clip_data_dir->filename_with_root = calloc(path_length, sizeof(char));
-	if (!clip_data_dir->filename_with_root)
-	{
-		WLog_Print(file_context->log, WLOG_ERROR, "Failed to allocate filename");
-		fuse_file_free(clip_data_dir);
+	CliprdrFuseFile* clip_data_dir = fuse_file_new("/%" PRIu64, data_id);
+	if (!clip_data_dir)
 		return NULL;
-	}
-
-	if (has_clip_data_id)
-		(void)_snprintf(clip_data_dir->filename_with_root, path_length, "/%u",
-		                (unsigned)clip_data_id);
-	else
-		(void)_snprintf(clip_data_dir->filename_with_root, path_length, "/%" PRIu64,
-		                NO_CLIP_DATA_ID);
-
-	clip_data_dir->filename = strrchr(clip_data_dir->filename_with_root, '/') + 1;
 
 	clip_data_dir->ino = get_next_free_inode(file_context);
 	clip_data_dir->is_directory = TRUE;
@@ -1778,7 +1781,8 @@ static BOOL is_fuse_file_not_parent(WINPR_ATTR_UNUSED const void* key, void* val
 	if (!fuse_file->is_directory)
 		return TRUE;
 
-	if (strcmp(find_context->parent_path, fuse_file->filename_with_root) == 0)
+	if (strncmp(find_context->parent_path, fuse_file->filename_with_root,
+	            fuse_file->filename_with_root_len + 1) == 0)
 	{
 		find_context->parent = fuse_file;
 		return FALSE;
@@ -1812,126 +1816,122 @@ static CliprdrFuseFile* get_parent_directory(CliprdrFileContext* file_context, c
 	return find_context.parent;
 }
 
-static BOOL set_selection_for_clip_data_entry(CliprdrFileContext* file_context,
-                                              CliprdrFuseClipDataEntry* clip_data_entry,
-                                              const FILEDESCRIPTORW* files, UINT32 n_files)
+// NOLINTBEGIN(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
+static BOOL selection_handle_file(CliprdrFileContext* file_context,
+                                  CliprdrFuseClipDataEntry* clip_data_entry, uint32_t index,
+                                  const FILEDESCRIPTORW* file)
 {
-	CliprdrFuseFile* clip_data_dir = NULL;
 
 	WINPR_ASSERT(file_context);
 	WINPR_ASSERT(clip_data_entry);
-	WINPR_ASSERT(files);
+	WINPR_ASSERT(file);
 
-	clip_data_dir = clip_data_entry->clip_data_dir;
+	CliprdrFuseFile* clip_data_dir = clip_data_entry->clip_data_dir;
 	WINPR_ASSERT(clip_data_dir);
 
-	if (clip_data_entry->has_clip_data_id)
-		WLog_Print(file_context->log, WLOG_DEBUG, "Setting selection for clipDataId %u",
-		           clip_data_entry->clip_data_id);
-	else
-		WLog_Print(file_context->log, WLOG_DEBUG, "Setting selection");
+	char filename[ARRAYSIZE(file->cFileName) * 8] = { 0 };
+	const SSIZE_T filenamelen = ConvertWCharNToUtf8(file->cFileName, ARRAYSIZE(file->cFileName),
+	                                                filename, ARRAYSIZE(filename) - 1);
+	if (filenamelen < 0)
+	{
+		WLog_Print(file_context->log, WLOG_ERROR, "Failed to convert filename");
+		return FALSE;
+	}
 
-	// NOLINTBEGIN(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
-	for (UINT32 i = 0; i < n_files; ++i)
+	for (size_t j = 0; filename[j]; ++j)
 	{
-		const FILEDESCRIPTORW* file = &files[i];
-		CliprdrFuseFile* fuse_file = NULL;
-		char* filename = NULL;
-		size_t path_length = 0;
+		if (filename[j] == '\\')
+			filename[j] = '/';
+	}
 
-		fuse_file = fuse_file_new();
-		if (!fuse_file)
-		{
-			WLog_Print(file_context->log, WLOG_ERROR, "Failed to create FUSE file");
-			clear_entry_selection(clip_data_entry);
-			return FALSE;
-		}
+	BOOL crc = FALSE;
+	CliprdrFuseFile* fuse_file = fuse_file_new(
+	    "%.*s/%s", WINPR_ASSERTING_INT_CAST(int, clip_data_dir->filename_with_root_len),
+	    clip_data_dir->filename_with_root, filename);
+	if (!fuse_file)
+	{
+		WLog_Print(file_context->log, WLOG_ERROR, "Failed to create FUSE file");
+		goto end;
+	}
 
-		filename = ConvertWCharToUtf8Alloc(file->cFileName, NULL);
-		if (!filename)
-		{
-			WLog_Print(file_context->log, WLOG_ERROR, "Failed to convert filename");
-			fuse_file_free(fuse_file);
-			clear_entry_selection(clip_data_entry);
-			return FALSE;
-		}
+	fuse_file->parent = get_parent_directory(file_context, fuse_file->filename_with_root);
+	if (!fuse_file->parent)
+	{
+		WLog_Print(file_context->log, WLOG_ERROR, "Found no parent for FUSE file");
+		goto end;
+	}
 
-		for (size_t j = 0; filename[j]; ++j)
-		{
-			if (filename[j] == '\\')
-				filename[j] = '/';
-		}
+	if (!ArrayList_Append(fuse_file->parent->children, fuse_file))
+	{
+		WLog_Print(file_context->log, WLOG_ERROR, "Failed to append FUSE file");
+		goto end;
+	}
 
-		path_length = strlen(clip_data_dir->filename_with_root) + 1 + strlen(filename) + 1;
-		fuse_file->filename_with_root = calloc(path_length, sizeof(char));
-		if (!fuse_file->filename_with_root)
-		{
-			WLog_Print(file_context->log, WLOG_ERROR, "Failed to allocate filename");
-			free(filename);
-			fuse_file_free(fuse_file);
-			clear_entry_selection(clip_data_entry);
-			return FALSE;
-		}
+	fuse_file->list_idx = index;
+	fuse_file->ino = get_next_free_inode(file_context);
+	fuse_file->has_clip_data_id = clip_data_entry->has_clip_data_id;
+	fuse_file->clip_data_id = clip_data_entry->clip_data_id;
+	if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		fuse_file->is_directory = TRUE;
+	if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+		fuse_file->is_readonly = TRUE;
+	if (file->dwFlags & FD_FILESIZE)
+	{
+		fuse_file->size = ((UINT64)file->nFileSizeHigh << 32) + file->nFileSizeLow;
+		fuse_file->has_size = TRUE;
+	}
+	if (file->dwFlags & FD_WRITESTIME)
+	{
+		INT64 filetime = 0;
 
-		(void)_snprintf(fuse_file->filename_with_root, path_length, "%s/%s",
-		                clip_data_dir->filename_with_root, filename);
-		free(filename);
+		filetime = file->ftLastWriteTime.dwHighDateTime;
+		filetime <<= 32;
+		filetime += file->ftLastWriteTime.dwLowDateTime;
 
-		fuse_file->filename = strrchr(fuse_file->filename_with_root, '/') + 1;
+		fuse_file->last_write_time_unix =
+		    1LL * filetime / (10LL * 1000LL * 1000LL) - WIN32_FILETIME_TO_UNIX_EPOCH;
+		fuse_file->has_last_write_time = TRUE;
+	}
 
-		fuse_file->parent = get_parent_directory(file_context, fuse_file->filename_with_root);
-		if (!fuse_file->parent)
-		{
-			WLog_Print(file_context->log, WLOG_ERROR, "Found no parent for FUSE file");
-			fuse_file_free(fuse_file);
-			clear_entry_selection(clip_data_entry);
-			return FALSE;
-		}
+	if (!HashTable_Insert(file_context->inode_table, (void*)(uintptr_t)fuse_file->ino, fuse_file))
+	{
+		WLog_Print(file_context->log, WLOG_ERROR, "Failed to insert inode into inode table");
+		goto end;
+	}
 
-		if (!ArrayList_Append(fuse_file->parent->children, fuse_file))
-		{
-			WLog_Print(file_context->log, WLOG_ERROR, "Failed to append FUSE file");
-			fuse_file_free(fuse_file);
-			clear_entry_selection(clip_data_entry);
-			return FALSE;
-		}
+	crc = TRUE;
 
-		fuse_file->list_idx = i;
-		fuse_file->ino = get_next_free_inode(file_context);
-		fuse_file->has_clip_data_id = clip_data_entry->has_clip_data_id;
-		fuse_file->clip_data_id = clip_data_entry->clip_data_id;
-		if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-			fuse_file->is_directory = TRUE;
-		if (file->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
-			fuse_file->is_readonly = TRUE;
-		if (file->dwFlags & FD_FILESIZE)
-		{
-			fuse_file->size = ((UINT64)file->nFileSizeHigh << 32) + file->nFileSizeLow;
-			fuse_file->has_size = TRUE;
-		}
-		if (file->dwFlags & FD_WRITESTIME)
-		{
-			INT64 filetime = 0;
+end:
+	if (!crc)
+	{
+		fuse_file_free(fuse_file);
+		clear_entry_selection(clip_data_entry);
+		return FALSE;
+	}
+	return TRUE;
+}
+// NOLINTEND(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
 
-			filetime = file->ftLastWriteTime.dwHighDateTime;
-			filetime <<= 32;
-			filetime += file->ftLastWriteTime.dwLowDateTime;
+static BOOL set_selection_for_clip_data_entry(CliprdrFileContext* file_context,
+                                              CliprdrFuseClipDataEntry* clip_data_entry,
+                                              const FILEDESCRIPTORW* files, UINT32 n_files)
+{
+	WINPR_ASSERT(file_context);
+	WINPR_ASSERT(clip_data_entry);
+	WINPR_ASSERT(files);
 
-			fuse_file->last_write_time_unix =
-			    1LL * filetime / (10LL * 1000LL * 1000LL) - WIN32_FILETIME_TO_UNIX_EPOCH;
-			fuse_file->has_last_write_time = TRUE;
-		}
+	if (clip_data_entry->has_clip_data_id)
+		WLog_Print(file_context->log, WLOG_DEBUG, "Setting selection for clipDataId %u",
+		           clip_data_entry->clip_data_id);
+	else
+		WLog_Print(file_context->log, WLOG_DEBUG, "Setting selection");
 
-		if (!HashTable_Insert(file_context->inode_table, (void*)(uintptr_t)fuse_file->ino,
-		                      fuse_file))
-		{
-			WLog_Print(file_context->log, WLOG_ERROR, "Failed to insert inode into inode table");
-			fuse_file_free(fuse_file);
-			clear_entry_selection(clip_data_entry);
+	for (UINT32 i = 0; i < n_files; ++i)
+	{
+		const FILEDESCRIPTORW* file = &files[i];
+		if (!selection_handle_file(file_context, clip_data_entry, i, file))
 			return FALSE;
-		}
 	}
-	// NOLINTEND(clang-analyzer-unix.Malloc) HashTable_Insert owns fuse_file
 
 	if (clip_data_entry->has_clip_data_id)
 		WLog_Print(file_context->log, WLOG_DEBUG, "Selection set for clipDataId %u",
@@ -2362,20 +2362,10 @@ static CliprdrFuseFile* fuse_file_new_root(CliprdrFileContext* file_context)
 {
 	CliprdrFuseFile* root_dir = NULL;
 
-	root_dir = fuse_file_new();
+	root_dir = fuse_file_new("/");
 	if (!root_dir)
 		return NULL;
 
-	root_dir->filename_with_root = calloc(2, sizeof(char));
-	if (!root_dir->filename_with_root)
-	{
-		fuse_file_free(root_dir);
-		return NULL;
-	}
-
-	(void)_snprintf(root_dir->filename_with_root, 2, "/");
-	root_dir->filename = root_dir->filename_with_root;
-
 	root_dir->ino = FUSE_ROOT_ID;
 	root_dir->is_directory = TRUE;
 	root_dir->is_readonly = TRUE;
diff --git a/libfreerdp/codec/dsp_fdk_aac.c b/libfreerdp/codec/dsp_fdk_aac.c
index 2165863cea0a..94fd23fd1321 100644
--- a/libfreerdp/codec/dsp_fdk_aac.c
+++ b/libfreerdp/codec/dsp_fdk_aac.c
@@ -34,7 +34,7 @@ static void write_log(unsigned log_level, const char* fmt, ...)
 
 		va_list ap = { 0 };
 		va_start(ap, fmt);
-		vsnprintf(buffer, sizeof(buffer), fmt, ap);
+		(void)vsnprintf(buffer, sizeof(buffer), fmt, ap);
 		va_end(ap);
 
 		WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, __LINE__, __FILE__, __func__, "%s",
diff --git a/libfreerdp/codec/dsp_fdk_impl.c b/libfreerdp/codec/dsp_fdk_impl.c
index 7a757903474a..d2f3442f72a7 100644
--- a/libfreerdp/codec/dsp_fdk_impl.c
+++ b/libfreerdp/codec/dsp_fdk_impl.c
@@ -480,7 +480,7 @@ int fdk_aac_dsp_impl_config(void* handle, size_t* pbuffersize, int encoder, unsi
 		if (err != AACENC_OK)
 			log(WLOG_WARN, "aacEncClose failed with %s", enc_err_str(err));
 
-		*pbuffersize = info.frameLength * info.inputChannels * sizeof(INT_PCM);
+		*pbuffersize = sizeof(INT_PCM) * info.frameLength * info.inputChannels;
 
 		HANDLE_AACDECODER aacdec = (HANDLE_AACDECODER)handle;
 
@@ -555,14 +555,15 @@ ssize_t fdk_aac_dsp_impl_stream_info(void* handle, int encoder, fdk_log_fkt_t lo
 			return -1;
 		}
 
-		return sizeof(INT_PCM) * info->numChannels * info->frameSize;
+		const size_t rsize = sizeof(INT_PCM) * info->numChannels * info->frameSize;
+		return (ssize_t)rsize;
 	}
 }
 
 ssize_t fdk_aac_dsp_impl_encode(void* handle, const void* data, size_t size, void* dst,
                                 size_t dstSize, fdk_log_fkt_t log)
 {
-	INT inSizes[] = { size };
+	INT inSizes[] = { (INT)size };
 	INT inElSizes[] = { sizeof(INT_PCM) };
 	INT inIdentifiers[] = { IN_AUDIO_DATA };
 	union
@@ -581,7 +582,7 @@ ssize_t fdk_aac_dsp_impl_encode(void* handle, const void* data, size_t size, voi
 		.bufElSizes = inElSizes /* TODO: 8/16 bit input? */
 	};
 
-	INT outSizes[] = { dstSize };
+	INT outSizes[] = { (INT)dstSize };
 	INT outElSizes[] = { 1 };
 	INT outIdentifiers[] = { OUT_BITSTREAM_DATA };
 	void* outBuffers[] = { dst };
@@ -592,7 +593,7 @@ ssize_t fdk_aac_dsp_impl_encode(void* handle, const void* data, size_t size, voi
 		                                .bufElSizes = outElSizes };
 
 	const AACENC_InArgs inArgs = { .numInSamples =
-		                               size / sizeof(INT_PCM), /* TODO: 8/16 bit input? */
+		                               (INT)(size / sizeof(INT_PCM)), /* TODO: 8/16 bit input? */
 		                           .numAncBytes = 0 };
 	AACENC_OutArgs outArgs = { 0 };
 
diff --git a/winpr/libwinpr/clipboard/clipboard.c b/winpr/libwinpr/clipboard/clipboard.c
index 965be4df060d..ea6c0e05bd87 100644
--- a/winpr/libwinpr/clipboard/clipboard.c
+++ b/winpr/libwinpr/clipboard/clipboard.c
@@ -32,7 +32,7 @@
 #include "../log.h"
 #define TAG WINPR_TAG("clipboard")
 
-const char* mime_text_plain = "text/plain";
+const char* const mime_text_plain = "text/plain";
 
 /**
  * Clipboard (Windows):
diff --git a/winpr/libwinpr/clipboard/clipboard.h b/winpr/libwinpr/clipboard/clipboard.h
index b3dc4d07e309..14071d04a1db 100644
--- a/winpr/libwinpr/clipboard/clipboard.h
+++ b/winpr/libwinpr/clipboard/clipboard.h
@@ -72,6 +72,6 @@ WINPR_LOCAL BOOL ClipboardInitSynthesizers(wClipboard* clipboard);
 
 WINPR_LOCAL char* parse_uri_to_local_file(const char* uri, size_t uri_len);
 
-extern const char* mime_text_plain;
+extern const char* const mime_text_plain;
 
 #endif /* WINPR_CLIPBOARD_PRIVATE_H */
diff --git a/winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c b/winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c
index 60b2c4572a0b..fbd87016b40d 100644
--- a/winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c
+++ b/winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c
@@ -50,7 +50,8 @@ krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_
 
 	krb5glue_keys_free(ctx, keyset);
 
-	if (!(rv = krb5_auth_con_getkey(ctx, auth_ctx, &keyblock)))
+	rv = krb5_auth_con_getkey(ctx, auth_ctx, &keyblock);
+	if (!rv)
 	{
 		krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->session_key);
 		krb5_free_keyblock(ctx, keyblock);


Reviews:
user: login: github-actions[bot]
body: clang-tidy made some suggestions
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T13:23:09Z

Review comments:
diff_hunk: @@ -32,7 +32,7 @@
 #include "../log.h"
 #define TAG WINPR_TAG("clipboard")
 
-const char* mime_text_plain = "text/plain";
+const char* const mime_text_plain = "text/plain";
path: winpr/libwinpr/clipboard/clipboard.c
user: login: github-actions[bot]
body: warning: redefinition of 'mime_text_plain' with a different type: 'const char *const' vs 'const char *' [clang-diagnostic-error]
```cpp
const char* const mime_text_plain = "text/plain";
                  ^
```
<details>
<summary>Additional context</summary>

**winpr/libwinpr/clipboard/clipboard.h:74:** previous declaration is here
```cpp
extern const char* mime_text_plain;
                   ^
```

</details>

created_at: 2025-05-23T13:23:09Z
author_association: NONE
diff_hunk: @@ -50,7 +50,8 @@ krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_
 
 	krb5glue_keys_free(ctx, keyset);
 
-	if (!(rv = krb5_auth_con_getkey(ctx, auth_ctx, &keyblock)))
+	rv = krb5_auth_con_getkey(ctx, auth_ctx, &keyblock);
+	if (!rv)
 	{
 		krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->session_key);
path: winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c
user: login: github-actions[bot]
body: warning: call to undeclared function 'krb5_crypto_init'; ISO C99 and later do not support implicit function declarations [clang-diagnostic-implicit-function-declaration]
```cpp
		krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->session_key);
  ^
```
<details>
<summary>Additional context</summary>

**winpr/libwinpr/sspi/Kerberos/krb5glue_heimdal.c:55:** did you mean 'krb5_pac_init'?
```cpp
		krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->session_key);
  ^
```
**/usr/include/mit-krb5/krb5/krb5.h:8219:** 'krb5_pac_init' declared here
```cpp
krb5_pac_init(krb5_context context, krb5_pac *pac);
^
```

</details>

created_at: 2025-05-23T13:23:09Z
author_association: NONE

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-23T12:43:12Z
body: clang-tidy review says "All clean, LGTM! :+1:"
author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-23T12:51:52Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14931/

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-23T13:02:32Z
body: clang-tidy review says "All clean, LGTM! :+1:"
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-23T13:07:42Z
body: clang-tidy review says "All clean, LGTM! :+1:"
author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-23T13:11:09Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14932/

author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-23T13:22:50Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14933/

author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-23T13:24:57Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14934/

author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-23T13:37:14Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14935/

author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-23T13:39:22Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14936/

author_association: NONE