mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
wClipboard/posix: add directories to file list
text/uri-list contains only the files which were immediately selected by the user. However, we need to enumerate *all* files and directories to be pasted in CLIPRDR_FILELIST. Thus we need to walk through the directories and add their content to the file list as well. We use readdir() function to traverse the directory entries. It has more sane interface than readdir_r(), but lacks (standardized) thread-safety guarantees. However, most C liraries guarantee that so we can use it. There is no compile-time check because it cannot be made robust. You deserve a crash here if you are using a C library developed by people who cannot keep their unhealthy addiction to global state under control. Note that recursive traversal is also a good opportunity to maintain good remote names. We just need to concatenate the directory paths and file names correctly. However, this recursion has one caveat: it is not bounded, so if the file system contains a loop then we will crash due to a stack overflow. We could track symlink loops (and hardlinks too if we try hard) to avoid the crash, but I think it's not a common thing to do so we can ignore this possibility.
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -169,6 +170,153 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char* concat_local_name(const char* dir, const char* file)
|
||||
{
|
||||
size_t len_dir = 0;
|
||||
size_t len_file = 0;
|
||||
char* buffer = NULL;
|
||||
|
||||
len_dir = strlen(dir);
|
||||
len_file = strlen(file);
|
||||
|
||||
buffer = calloc(len_dir + 1 + len_file + 1, sizeof(char));
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
memcpy(buffer, dir, len_dir * sizeof(char));
|
||||
buffer[len_dir] = '/';
|
||||
memcpy(buffer + len_dir + 1, file, len_file * sizeof(char));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static WCHAR* concat_remote_name(const WCHAR* dir, const WCHAR* file)
|
||||
{
|
||||
size_t len_dir = 0;
|
||||
size_t len_file = 0;
|
||||
WCHAR* buffer = NULL;
|
||||
|
||||
len_dir = _wcslen(dir);
|
||||
len_file = _wcslen(file);
|
||||
|
||||
buffer = calloc(len_dir + 1 + len_file + 1, sizeof(WCHAR));
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
memcpy(buffer, dir, len_dir * sizeof(WCHAR));
|
||||
buffer[len_dir] = L'\\';
|
||||
memcpy(buffer + len_dir + 1, file, len_file * sizeof(WCHAR));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files);
|
||||
|
||||
static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* remote_dir_name,
|
||||
const struct dirent* entry, wArrayList* files)
|
||||
{
|
||||
BOOL result = FALSE;
|
||||
char* local_name = NULL;
|
||||
WCHAR* remote_name = NULL;
|
||||
WCHAR* remote_base_name = NULL;
|
||||
|
||||
/* Skip special directory entries. */
|
||||
if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0))
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
* As noted in add_file_to_list(), it is not always correct to assume
|
||||
* that that file names are encoded in UTF-8. However, this is the
|
||||
* most sane thing to do at the moment.
|
||||
*/
|
||||
if (!ConvertToUnicode(CP_UTF8, 0, entry->d_name, -1, &remote_base_name, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "Unicode conversion failed for %s", entry->d_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
local_name = concat_local_name(local_dir_name, entry->d_name);
|
||||
remote_name = concat_remote_name(remote_dir_name, remote_base_name);
|
||||
|
||||
if (local_name && remote_name)
|
||||
result = add_file_to_list(local_name, remote_name, files);
|
||||
|
||||
free(remote_base_name);
|
||||
free(remote_name);
|
||||
free(local_name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL do_add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name,
|
||||
DIR* dirp, wArrayList* files)
|
||||
{
|
||||
/*
|
||||
* For some reason POSIX does not require readdir() to be thread-safe.
|
||||
* However, readdir_r() has really insane interface and is pretty bad
|
||||
* replacement for it. Fortunately, most C libraries guarantee thread-
|
||||
* safety of readdir() when it is used for distinct directory streams.
|
||||
*
|
||||
* Thus we can use readdir() in multithreaded applications if we are
|
||||
* sure that it will not corrupt some global data. It would be nice
|
||||
* to have a compile-time check for this here, but some C libraries
|
||||
* do not provide a #define because of reasons (I'm looking at you,
|
||||
* musl). We should not be breaking people's builds because of that,
|
||||
* so we do nothing and proceed with fingers crossed.
|
||||
*/
|
||||
|
||||
for (;;)
|
||||
{
|
||||
struct dirent* entry = NULL;
|
||||
|
||||
errno = 0;
|
||||
entry = readdir(dirp);
|
||||
if (!entry)
|
||||
{
|
||||
int err = errno;
|
||||
if (!err)
|
||||
break;
|
||||
|
||||
WLog_ERR(TAG, "failed to read directory: %s", strerror(err));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!add_directory_entry_to_list(local_name, remote_name, entry, files))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name,
|
||||
wArrayList* files)
|
||||
{
|
||||
BOOL result = FALSE;
|
||||
DIR* dirp = NULL;
|
||||
|
||||
#ifdef WITH_DEBUG_WCLIPBOARD
|
||||
WLog_DBG(TAG, "adding directory: %s", local_name);
|
||||
#endif
|
||||
|
||||
dirp = opendir(local_name);
|
||||
if (!dirp)
|
||||
{
|
||||
int err = errno;
|
||||
WLog_ERR(TAG, "failed to open directory %s: %s", local_name, strerror(err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = do_add_directory_contents_to_list(local_name, remote_name, dirp, files);
|
||||
|
||||
if (closedir(dirp))
|
||||
{
|
||||
int err = errno;
|
||||
WLog_WARN(TAG, "failed to close directory: %s", strerror(err));
|
||||
}
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files)
|
||||
{
|
||||
struct posix_file* file = NULL;
|
||||
@@ -188,6 +336,16 @@ static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, w
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (file->is_directory)
|
||||
{
|
||||
/*
|
||||
* This is effectively a recursive call, but we do not track
|
||||
* recursion depth, thus filesystem loops can cause a crash.
|
||||
*/
|
||||
if (!add_directory_contents_to_list(local_name, remote_name, files))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user