Skip to content

Commit 2d5b7b5

Browse files
committed
Make fs::extractZipFile thread-safe
1 parent 9fab5d5 commit 2d5b7b5

3 files changed

Lines changed: 65 additions & 51 deletions

File tree

src/filesys.cpp

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2828
#include "log.h"
2929
#include "config.h"
3030
#include "porting.h"
31+
#ifndef SERVER
32+
#include "irr_ptr.h"
33+
#endif
3134

3235
namespace fs
3336
{
3437

35-
#ifdef _WIN32 // WINDOWS
38+
#ifdef _WIN32
39+
40+
/***********
41+
* Windows *
42+
***********/
3643

3744
#define _WIN32_WINNT 0x0501
3845
#include <windows.h>
@@ -201,7 +208,11 @@ std::string CreateTempFile()
201208
return path;
202209
}
203210

204-
#else // POSIX
211+
#else
212+
213+
/*********
214+
* POSIX *
215+
*********/
205216

206217
#include <sys/types.h>
207218
#include <dirent.h>
@@ -392,6 +403,10 @@ std::string CreateTempFile()
392403

393404
#endif
394405

406+
/****************************
407+
* portable implementations *
408+
****************************/
409+
395410
void GetRecursiveDirs(std::vector<std::string> &dirs, const std::string &dir)
396411
{
397412
static const std::set<char> chars_to_ignore = { '_', '.' };
@@ -753,69 +768,66 @@ bool safeWriteToFile(const std::string &path, const std::string &content)
753768
return true;
754769
}
755770

771+
#ifndef SERVER
756772
bool extractZipFile(io::IFileSystem *fs, const char *filename, const std::string &destination)
757773
{
758-
if (!fs->addFileArchive(filename, false, false, io::EFAT_ZIP)) {
774+
// Be careful here not to touch the global file hierarchy in Irrlicht
775+
// since this function needs to be thread-safe!
776+
777+
io::IArchiveLoader *zip_loader = nullptr;
778+
for (u32 i = 0; i < fs->getArchiveLoaderCount(); i++) {
779+
if (fs->getArchiveLoader(i)->isALoadableFileFormat(io::EFAT_ZIP)) {
780+
zip_loader = fs->getArchiveLoader(i);
781+
break;
782+
}
783+
}
784+
if (!zip_loader) {
785+
warningstream << "fs::extractZipFile(): Irrlicht said it doesn't support ZIPs." << std::endl;
759786
return false;
760787
}
761788

762-
sanity_check(fs->getFileArchiveCount() > 0);
763-
764-
/**********************************************************************/
765-
/* WARNING this is not threadsafe!! */
766-
/**********************************************************************/
767-
io::IFileArchive* opened_zip = fs->getFileArchive(fs->getFileArchiveCount() - 1);
768-
789+
irr_ptr<io::IFileArchive> opened_zip(zip_loader->createArchive(filename, false, false));
769790
const io::IFileList* files_in_zip = opened_zip->getFileList();
770791

771-
unsigned int number_of_files = files_in_zip->getFileCount();
772-
773-
for (unsigned int i=0; i < number_of_files; i++) {
774-
std::string fullpath = destination;
775-
fullpath += DIR_DELIM;
792+
for (u32 i = 0; i < files_in_zip->getFileCount(); i++) {
793+
std::string fullpath = destination + DIR_DELIM;
776794
fullpath += files_in_zip->getFullFileName(i).c_str();
777795
std::string fullpath_dir = fs::RemoveLastPathComponent(fullpath);
778796

779-
if (!files_in_zip->isDirectory(i)) {
780-
if (!fs::PathExists(fullpath_dir) && !fs::CreateAllDirs(fullpath_dir)) {
781-
fs->removeFileArchive(fs->getFileArchiveCount()-1);
782-
return false;
783-
}
784-
785-
io::IReadFile* toread = opened_zip->createAndOpenFile(i);
797+
if (files_in_zip->isDirectory(i))
798+
continue; // ignore, we create dirs as necessary
786799

787-
FILE *targetfile = fopen(fullpath.c_str(),"wb");
800+
if (!fs::PathExists(fullpath_dir) && !fs::CreateAllDirs(fullpath_dir))
801+
return false;
788802

789-
if (targetfile == NULL) {
790-
fs->removeFileArchive(fs->getFileArchiveCount()-1);
791-
return false;
792-
}
803+
irr_ptr<io::IReadFile> toread(opened_zip->createAndOpenFile(i));
793804

794-
char read_buffer[1024];
795-
long total_read = 0;
805+
std::ofstream os(fullpath.c_str(), std::ios::binary);
806+
if (!os.good())
807+
return false;
796808

797-
while (total_read < toread->getSize()) {
809+
char buffer[4096];
810+
long total_read = 0;
798811

799-
unsigned int bytes_read =
800-
toread->read(read_buffer,sizeof(read_buffer));
801-
if ((bytes_read == 0 ) ||
802-
(fwrite(read_buffer, 1, bytes_read, targetfile) != bytes_read))
803-
{
804-
fclose(targetfile);
805-
fs->removeFileArchive(fs->getFileArchiveCount() - 1);
806-
return false;
807-
}
808-
total_read += bytes_read;
812+
while (total_read < toread->getSize()) {
813+
long bytes_read = toread->read(buffer, sizeof(buffer));
814+
bool error = true;
815+
if (bytes_read != 0) {
816+
os.write(buffer, bytes_read);
817+
error = os.fail();
809818
}
810-
811-
fclose(targetfile);
819+
if (error) {
820+
os.close();
821+
remove(fullpath.c_str());
822+
return false;
823+
}
824+
total_read += bytes_read;
812825
}
813-
814826
}
815827

816-
fs->removeFileArchive(fs->getFileArchiveCount() - 1);
817828
return true;
818829
}
830+
#endif
819831

820832
bool ReadFile(const std::string &path, std::string &out)
821833
{
@@ -829,7 +841,7 @@ bool ReadFile(const std::string &path, std::string &out)
829841
is.seekg(0);
830842
is.read(&out[0], size);
831843

832-
return true;
844+
return !is.fail();
833845
}
834846

835847
bool Rename(const std::string &from, const std::string &to)

src/filesys.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2424
#include <vector>
2525
#include "exceptions.h"
2626

27-
#ifdef _WIN32 // WINDOWS
27+
#ifdef _WIN32
2828
#define DIR_DELIM "\\"
2929
#define DIR_DELIM_CHAR '\\'
3030
#define FILESYS_CASE_INSENSITIVE true
3131
#define PATH_DELIM ";"
32-
#else // POSIX
32+
#else
3333
#define DIR_DELIM "/"
3434
#define DIR_DELIM_CHAR '/'
3535
#define FILESYS_CASE_INSENSITIVE false
@@ -133,7 +133,9 @@ const char *GetFilenameFromPath(const char *path);
133133

134134
bool safeWriteToFile(const std::string &path, const std::string &content);
135135

136+
#ifndef SERVER
136137
bool extractZipFile(irr::io::IFileSystem *fs, const char *filename, const std::string &destination);
138+
#endif
137139

138140
bool ReadFile(const std::string &path, std::string &out);
139141

src/script/lua_api/l_mainmenu.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,9 +644,9 @@ int ModApiMainMenu::l_extract_zip(lua_State *L)
644644
std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
645645

646646
if (ModApiMainMenu::mayModifyPath(absolute_destination)) {
647-
auto rendering_engine = getGuiEngine(L)->m_rendering_engine;
648-
fs::CreateAllDirs(absolute_destination);
649-
lua_pushboolean(L, fs::extractZipFile(rendering_engine->get_filesystem(), zipfile, destination));
647+
auto fs = RenderingEngine::get_raw_device()->getFileSystem();
648+
bool ok = fs::extractZipFile(fs, zipfile, destination);
649+
lua_pushboolean(L, ok);
650650
return 1;
651651
}
652652

@@ -916,7 +916,7 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
916916
API_FCT(delete_dir);
917917
API_FCT(copy_dir);
918918
API_FCT(is_dir);
919-
//API_FCT(extract_zip); //TODO remove dependency to GuiEngine
919+
API_FCT(extract_zip);
920920
API_FCT(may_modify_path);
921921
API_FCT(download_file);
922922
API_FCT(get_min_supp_proto);

0 commit comments

Comments
 (0)