Ensure filenames are valid UTF-8 on Unix

On Unix, the encoding of filenames is unspecified, although UTF-8 is likely used in most cases. Detecting and converting from several possible encodings to UTF-8 would be too much effort, considering that most systems already use UTF-8 per default. Therefore, any filenames which are not valid UTF-8 will be ignored when listing directories with `fs_listdir(_fileinfo)`, `fs_storage_path` will fail if the storage location is not valid UTF-8 and `fs_getcwd` will fail if the current working directory is not valid UTF-8. Paths specified in `storage.cfg` must also be valid UTF-8.

On Windows, we already ensure that all filenames are converted to UTF-8.
This commit is contained in:
Robert Müller 2023-11-02 18:09:55 +01:00
parent 55098cd313
commit 5c7bb58457
2 changed files with 98 additions and 46 deletions

View file

@ -2111,7 +2111,6 @@ void fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user)
if(handle == INVALID_HANDLE_VALUE)
return;
/* add all the entries */
do
{
const std::string current_entry = windows_wide_to_utf8(finddata.cFileName);
@ -2121,26 +2120,29 @@ void fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user)
FindClose(handle);
#else
struct dirent *entry;
char buffer[IO_MAX_PATH_LENGTH];
int length;
DIR *d = opendir(dir);
if(!d)
DIR *dir_handle = opendir(dir);
if(dir_handle == nullptr)
return;
char buffer[IO_MAX_PATH_LENGTH];
str_format(buffer, sizeof(buffer), "%s/", dir);
length = str_length(buffer);
while((entry = readdir(d)) != NULL)
size_t length = str_length(buffer);
while(true)
{
str_copy(buffer + length, entry->d_name, (int)sizeof(buffer) - length);
struct dirent *entry = readdir(dir_handle);
if(entry == nullptr)
break;
if(!str_utf8_check(entry->d_name))
{
log_error("filesystem", "ERROR: file/folder name containing invalid UTF-8 found in folder '%s'", dir);
continue;
}
str_copy(buffer + length, entry->d_name, sizeof(buffer) - length);
if(cb(entry->d_name, fs_is_dir(buffer), type, user))
break;
}
/* close the directory and return */
closedir(d);
closedir(dir_handle);
#endif
}
@ -2156,7 +2158,6 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t
if(handle == INVALID_HANDLE_VALUE)
return;
/* add all the entries */
do
{
const std::string current_entry = windows_wide_to_utf8(finddata.cFileName);
@ -2172,25 +2173,29 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t
FindClose(handle);
#else
struct dirent *entry;
time_t created = -1, modified = -1;
char buffer[IO_MAX_PATH_LENGTH];
int length;
DIR *d = opendir(dir);
if(!d)
DIR *dir_handle = opendir(dir);
if(dir_handle == nullptr)
return;
char buffer[IO_MAX_PATH_LENGTH];
str_format(buffer, sizeof(buffer), "%s/", dir);
length = str_length(buffer);
size_t length = str_length(buffer);
while((entry = readdir(d)) != NULL)
while(true)
{
CFsFileInfo info;
str_copy(buffer + length, entry->d_name, (int)sizeof(buffer) - length);
struct dirent *entry = readdir(dir_handle);
if(entry == nullptr)
break;
if(!str_utf8_check(entry->d_name))
{
log_error("filesystem", "ERROR: file/folder name containing invalid UTF-8 found in folder '%s'", dir);
continue;
}
str_copy(buffer + length, entry->d_name, sizeof(buffer) - length);
time_t created = -1, modified = -1;
fs_file_time(buffer, &created, &modified);
CFsFileInfo info;
info.m_pName = entry->d_name;
info.m_TimeCreated = created;
info.m_TimeModified = modified;
@ -2199,8 +2204,7 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t
break;
}
/* close the directory and return */
closedir(d);
closedir(dir_handle);
#endif
}
@ -2209,17 +2213,27 @@ int fs_storage_path(const char *appname, char *path, int max)
#if defined(CONF_FAMILY_WINDOWS)
WCHAR *wide_home = _wgetenv(L"APPDATA");
if(!wide_home)
{
path[0] = '\0';
return -1;
}
const std::string home = windows_wide_to_utf8(wide_home);
str_format(path, max, "%s/%s", home.c_str(), appname);
return 0;
#elif defined(CONF_PLATFORM_ANDROID)
// just use the data directory
return -1;
#else
char *home = getenv("HOME");
if(!home)
{
path[0] = '\0';
return -1;
}
if(!str_utf8_check(home))
{
log_error("filesystem", "ERROR: the HOME environment variable contains invalid UTF-8");
path[0] = '\0';
return -1;
}
#if defined(CONF_PLATFORM_HAIKU)
str_format(path, max, "%s/config/settings/%s", home, appname);
@ -2235,7 +2249,15 @@ int fs_storage_path(const char *appname, char *path, int max)
{
char *data_home = getenv("XDG_DATA_HOME");
if(data_home)
{
if(!str_utf8_check(data_home))
{
log_error("filesystem", "ERROR: the XDG_DATA_HOME environment variable contains invalid UTF-8");
path[0] = '\0';
return -1;
}
str_format(path, max, "%s/%s", data_home, appname);
}
else
str_format(path, max, "%s/.local/share/%s", home, appname);
}
@ -2366,13 +2388,20 @@ char *fs_getcwd(char *buffer, int buffer_size)
const DWORD LastError = GetLastError();
const std::string ErrorMsg = windows_format_system_message(LastError);
dbg_msg("filesystem", "GetCurrentDirectoryW failed: %ld %s", LastError, ErrorMsg.c_str());
buffer[0] = '\0';
return nullptr;
}
const std::string current_dir = windows_wide_to_utf8(wide_current_dir.c_str());
str_copy(buffer, current_dir.c_str(), buffer_size);
return buffer;
#else
return getcwd(buffer, buffer_size);
char *result = getcwd(buffer, buffer_size);
if(result == nullptr || !str_utf8_check(result))
{
buffer[0] = '\0';
return nullptr;
}
return result;
#endif
}

View file

@ -1,6 +1,7 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/hash_ctxt.h>
#include <base/log.h>
#include <base/math.h>
#include <base/system.h>
@ -30,21 +31,31 @@ public:
{
mem_zero(m_aaStoragePaths, sizeof(m_aaStoragePaths));
m_NumPaths = 0;
m_aDatadir[0] = 0;
m_aUserdir[0] = 0;
m_aDatadir[0] = '\0';
m_aUserdir[0] = '\0';
m_aCurrentdir[0] = '\0';
m_aBinarydir[0] = '\0';
}
int Init(int StorageType, int NumArgs, const char **ppArguments)
{
// get userdir
#if !defined(CONF_PLATFORM_ANDROID)
// get userdir, just use data directory on android
char aFallbackUserdir[IO_MAX_PATH_LENGTH];
fs_storage_path("DDNet", m_aUserdir, sizeof(m_aUserdir));
fs_storage_path("Teeworlds", aFallbackUserdir, sizeof(aFallbackUserdir));
if(fs_storage_path("DDNet", m_aUserdir, sizeof(m_aUserdir)))
{
log_error("storage", "could not determine user directory");
}
if(fs_storage_path("Teeworlds", aFallbackUserdir, sizeof(aFallbackUserdir)))
{
log_error("storage", "could not determine fallback user directory");
}
if(!fs_is_dir(m_aUserdir) && fs_is_dir(aFallbackUserdir))
if((m_aUserdir[0] == '\0' || !fs_is_dir(m_aUserdir)) && aFallbackUserdir[0] != '\0' && fs_is_dir(aFallbackUserdir))
{
str_copy(m_aUserdir, aFallbackUserdir);
}
#endif
// get datadir
FindDatadir(ppArguments[0]);
@ -54,7 +65,9 @@ public:
// get currentdir
if(!fs_getcwd(m_aCurrentdir, sizeof(m_aCurrentdir)))
m_aCurrentdir[0] = 0;
{
log_error("storage", "could not determine current directory");
}
// load paths from storage.cfg
LoadPaths(ppArguments[0]);
@ -161,12 +174,12 @@ public:
{
if(!pPath[0])
{
dbg_msg("storage", "cannot add empty path");
log_error("storage", "cannot add empty path");
return;
}
if(m_NumPaths >= MAX_PATHS)
{
dbg_msg("storage", "cannot add path '%s', the maximum number of paths is %d", pPath, MAX_PATHS);
log_error("storage", "cannot add path '%s', the maximum number of paths is %d", pPath, MAX_PATHS);
return;
}
@ -177,6 +190,10 @@ public:
str_copy(m_aaStoragePaths[m_NumPaths++], m_aUserdir);
dbg_msg("storage", "added path '$USERDIR' ('%s')", m_aUserdir);
}
else
{
log_error("storage", "cannot add path '$USERDIR' because it could not be determined");
}
}
else if(!str_comp(pPath, "$DATADIR"))
{
@ -185,13 +202,17 @@ public:
str_copy(m_aaStoragePaths[m_NumPaths++], m_aDatadir);
dbg_msg("storage", "added path '$DATADIR' ('%s')", m_aDatadir);
}
else
{
log_error("storage", "cannot add path '$DATADIR' because it could not be determined");
}
}
else if(!str_comp(pPath, "$CURRENTDIR"))
{
m_aaStoragePaths[m_NumPaths++][0] = 0;
m_aaStoragePaths[m_NumPaths++][0] = '\0';
dbg_msg("storage", "added path '$CURRENTDIR' ('%s')", m_aCurrentdir);
}
else
else if(str_utf8_check(pPath))
{
if(fs_is_dir(pPath))
{
@ -200,9 +221,13 @@ public:
}
else
{
dbg_msg("storage", "cannot add path '%s', which is not a directory", pPath);
log_error("storage", "cannot add path '%s', which is not a directory", pPath);
}
}
else
{
log_error("storage", "cannot add path containing invalid UTF-8");
}
}
void FindDatadir(const char *pArgv0)
@ -817,8 +842,6 @@ public:
str_append(pBuffer, "/", BufferSize);
str_append(pBuffer, aBinaryPath, BufferSize);
}
else
pBuffer[0] = '\0';
}
else
str_copy(pBuffer, aBinaryPath, BufferSize);