mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-17 09:51:02 +03:00
clipboard: declare file sizes in the Windows file list
Pasting files from a Windows peer got slower with the number of files and then stopped working, while the total size made no difference: one 32 MB file pasted at once, 45 small files took seconds, and 50 small files totalling 8.3 MB left the shell spinning and copied nothing. The sender never set FD_FILESIZE, although it had already read the size and filled nFileSizeLow/nFileSizeHigh. Without that flag the receiver cannot trust those fields, so CliprdrStream_New() asks for the size of each file with its own FILECONTENTS_SIZE request and blocks on the reply for up to CLIPBOARD_RESPONSE_WAIT_TIMEOUT_SECS. Those streams are all built up front, in the loop that answers the shell's request for the file group descriptor, so the round trips run one after another inside IDataObject::GetData() and the shell waits for every one of them before the paste can begin. The cost is therefore per file, not per byte, which is what the reports describe. The size is now declared, and only when it is known: GetFileSizeEx() replaces GetFileSize(), whose INVALID_FILE_SIZE return cannot be told from a genuine 4GB-1 file without GetLastError(), and directories are skipped because the handle opened with FILE_FLAG_BACKUP_SEMANTICS above is not a file handle. A regular file whose size cannot be read is rejected instead of publishing an ambiguous zero size. This is required because the Unix receiver currently consumes the descriptor size fields regardless of FD_FILESIZE for compatibility with older Windows senders. Upstream FreeRDP, which this file comes from, sets FD_FILESIZE here. It has been commented out in our copy since the file was first added in6672087f7, with no recorded reason; the "for compatibility" note above it was written later, in55005f812, about code that already looked this way. The Unix receiver carries the other half of the same workaround in filetype.rs, where the size is trusted whether or not the flag is set, explicitly "for compatibility with Windows". This is a sender-side fix: the paste gets faster once the machine the files come from runs it, whichever version does the pasting. Fixes #16238 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
This commit is contained in:
@@ -2443,9 +2443,6 @@ static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR *file_name, size_t
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// to-do: use `fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI`.
|
||||
// We keep `fd->dwFlags = FD_ATTRIBUTES | FD_WRITESTIME | FD_PROGRESSUI` for compatibility.
|
||||
// fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
|
||||
fd->dwFlags = FD_ATTRIBUTES | FD_WRITESTIME | FD_PROGRESSUI;
|
||||
fd->dwFileAttributes = GetFileAttributesW(file_name);
|
||||
if (fd->dwFileAttributes == INVALID_FILE_ATTRIBUTES)
|
||||
@@ -2458,7 +2455,34 @@ static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR *file_name, size_t
|
||||
fd->dwFlags &= ~FD_WRITESTIME;
|
||||
}
|
||||
|
||||
fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh);
|
||||
// Announce the size in the file list. Without FD_FILESIZE the receiving side cannot
|
||||
// trust the size fields, so CliprdrStream_New() asks for each file's size with its own
|
||||
// FILECONTENTS_SIZE request and blocks on the reply. Those requests are made for every
|
||||
// entry up front, while the shell is inside IDataObject::GetData(), so the cost grows
|
||||
// with the number of files and not with their size.
|
||||
//
|
||||
// GetFileSize() reports failure as INVALID_FILE_SIZE, which cannot be told apart from a
|
||||
// genuine 4GB-1 file without GetLastError(), and it fails outright on the directory
|
||||
// handles FILE_FLAG_BACKUP_SEMANTICS lets us open above. A directory gets no size. A
|
||||
// file whose size cannot be read is rejected rather than sent with the flag off: the
|
||||
// Unix receiver reads the size fields whether or not the flag is set, for compatibility
|
||||
// with older Windows senders, and would take the zero for an empty file.
|
||||
if ((fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
||||
{
|
||||
LARGE_INTEGER file_size = {0};
|
||||
|
||||
if (!GetFileSizeEx(hFile, &file_size) || file_size.QuadPart < 0)
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
free(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fd->nFileSizeLow = file_size.LowPart;
|
||||
fd->nFileSizeHigh = (DWORD)file_size.HighPart;
|
||||
fd->dwFlags |= FD_FILESIZE;
|
||||
}
|
||||
|
||||
if ((wcslen(file_name + pathLen) + 1) > sizeof(fd->cFileName) / sizeof(fd->cFileName[0]))
|
||||
{
|
||||
// The file name is too long, which is not a normal case.
|
||||
|
||||
Reference in New Issue
Block a user