Merge pull request #7481 from Robyt3/Storage-UTF8-Check

Ensure filenames are valid UTF-8 on Unix
This commit is contained in:
heinrich5991 2023-11-18 02:51:55 +00:00 committed by GitHub
commit 46679deddc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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) if(handle == INVALID_HANDLE_VALUE)
return; return;
/* add all the entries */
do do
{ {
const std::string current_entry = windows_wide_to_utf8(finddata.cFileName); 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); FindClose(handle);
#else #else
struct dirent *entry; DIR *dir_handle = opendir(dir);
char buffer[IO_MAX_PATH_LENGTH]; if(dir_handle == nullptr)
int length;
DIR *d = opendir(dir);
if(!d)
return; return;
char buffer[IO_MAX_PATH_LENGTH];
str_format(buffer, sizeof(buffer), "%s/", dir); str_format(buffer, sizeof(buffer), "%s/", dir);
length = str_length(buffer); size_t length = str_length(buffer);
while(true)
while((entry = readdir(d)) != NULL)
{ {
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)) if(cb(entry->d_name, fs_is_dir(buffer), type, user))
break; break;
} }
/* close the directory and return */ closedir(dir_handle);
closedir(d);
#endif #endif
} }
@ -2156,7 +2158,6 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t
if(handle == INVALID_HANDLE_VALUE) if(handle == INVALID_HANDLE_VALUE)
return; return;
/* add all the entries */
do do
{ {
const std::string current_entry = windows_wide_to_utf8(finddata.cFileName); 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); FindClose(handle);
#else #else
struct dirent *entry; DIR *dir_handle = opendir(dir);
time_t created = -1, modified = -1; if(dir_handle == nullptr)
char buffer[IO_MAX_PATH_LENGTH];
int length;
DIR *d = opendir(dir);
if(!d)
return; return;
char buffer[IO_MAX_PATH_LENGTH];
str_format(buffer, sizeof(buffer), "%s/", dir); 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; struct dirent *entry = readdir(dir_handle);
if(entry == nullptr)
str_copy(buffer + length, entry->d_name, (int)sizeof(buffer) - length); 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); fs_file_time(buffer, &created, &modified);
CFsFileInfo info;
info.m_pName = entry->d_name; info.m_pName = entry->d_name;
info.m_TimeCreated = created; info.m_TimeCreated = created;
info.m_TimeModified = modified; info.m_TimeModified = modified;
@ -2199,8 +2204,7 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t
break; break;
} }
/* close the directory and return */ closedir(dir_handle);
closedir(d);
#endif #endif
} }
@ -2209,17 +2213,27 @@ int fs_storage_path(const char *appname, char *path, int max)
#if defined(CONF_FAMILY_WINDOWS) #if defined(CONF_FAMILY_WINDOWS)
WCHAR *wide_home = _wgetenv(L"APPDATA"); WCHAR *wide_home = _wgetenv(L"APPDATA");
if(!wide_home) if(!wide_home)
{
path[0] = '\0';
return -1; return -1;
}
const std::string home = windows_wide_to_utf8(wide_home); const std::string home = windows_wide_to_utf8(wide_home);
str_format(path, max, "%s/%s", home.c_str(), appname); str_format(path, max, "%s/%s", home.c_str(), appname);
return 0; return 0;
#elif defined(CONF_PLATFORM_ANDROID)
// just use the data directory
return -1;
#else #else
char *home = getenv("HOME"); char *home = getenv("HOME");
if(!home) if(!home)
{
path[0] = '\0';
return -1; 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) #if defined(CONF_PLATFORM_HAIKU)
str_format(path, max, "%s/config/settings/%s", home, appname); 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"); char *data_home = getenv("XDG_DATA_HOME");
if(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); str_format(path, max, "%s/%s", data_home, appname);
}
else else
str_format(path, max, "%s/.local/share/%s", home, appname); 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 DWORD LastError = GetLastError();
const std::string ErrorMsg = windows_format_system_message(LastError); const std::string ErrorMsg = windows_format_system_message(LastError);
dbg_msg("filesystem", "GetCurrentDirectoryW failed: %ld %s", LastError, ErrorMsg.c_str()); dbg_msg("filesystem", "GetCurrentDirectoryW failed: %ld %s", LastError, ErrorMsg.c_str());
buffer[0] = '\0';
return nullptr; return nullptr;
} }
const std::string current_dir = windows_wide_to_utf8(wide_current_dir.c_str()); const std::string current_dir = windows_wide_to_utf8(wide_current_dir.c_str());
str_copy(buffer, current_dir.c_str(), buffer_size); str_copy(buffer, current_dir.c_str(), buffer_size);
return buffer; return buffer;
#else #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 #endif
} }

View file

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