Make fs::extractZipFile thread-safe
This commit is contained in:
parent
1f4ff9cea6
commit
8c205f5bed
127
src/filesys.cpp
127
src/filesys.cpp
@ -27,11 +27,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "log.h"
|
||||
#include "config.h"
|
||||
#include "porting.h"
|
||||
#ifndef SERVER
|
||||
#include "irr_ptr.h"
|
||||
#endif
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
#ifdef _WIN32 // WINDOWS
|
||||
#ifdef _WIN32
|
||||
|
||||
/***********
|
||||
* Windows *
|
||||
***********/
|
||||
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include <windows.h>
|
||||
@ -185,7 +192,11 @@ std::string TempPath()
|
||||
return std::string(buf.begin(), buf.begin() + len);
|
||||
}
|
||||
|
||||
#else // POSIX
|
||||
#else
|
||||
|
||||
/*********
|
||||
* POSIX *
|
||||
*********/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
@ -388,6 +399,10 @@ std::string TempPath()
|
||||
|
||||
#endif
|
||||
|
||||
/****************************
|
||||
* portable implementations *
|
||||
****************************/
|
||||
|
||||
void GetRecursiveDirs(std::vector<std::string> &dirs, const std::string &dir)
|
||||
{
|
||||
static const std::set<char> chars_to_ignore = { '_', '.' };
|
||||
@ -749,78 +764,82 @@ bool safeWriteToFile(const std::string &path, const std::string &content)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef SERVER
|
||||
bool extractZipFile(io::IFileSystem *fs, const char *filename,
|
||||
const std::string &destination, const char *password, std::string *errorMessage)
|
||||
{
|
||||
if (!fs->addFileArchive(filename, false, false, io::EFAT_ZIP, password)) {
|
||||
// Be careful here not to touch the global file hierarchy in Irrlicht
|
||||
// since this function needs to be thread-safe!
|
||||
|
||||
io::IArchiveLoader *zip_loader = nullptr;
|
||||
for (u32 i = 0; i < fs->getArchiveLoaderCount(); i++) {
|
||||
if (fs->getArchiveLoader(i)->isALoadableFileFormat(io::EFAT_ZIP)) {
|
||||
zip_loader = fs->getArchiveLoader(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!zip_loader) {
|
||||
warningstream << "fs::extractZipFile(): Irrlicht said it doesn't support ZIPs." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
sanity_check(fs->getFileArchiveCount() > 0);
|
||||
|
||||
/**********************************************************************/
|
||||
/* WARNING this is not threadsafe!! */
|
||||
/**********************************************************************/
|
||||
io::IFileArchive* opened_zip = fs->getFileArchive(fs->getFileArchiveCount() - 1);
|
||||
irr_ptr<io::IFileArchive> opened_zip(zip_loader->createArchive(filename, false, false));
|
||||
if (opened_zip.get() == nullptr) {
|
||||
if (errorMessage != nullptr)
|
||||
*errorMessage = "failed to open zip file";
|
||||
return false;
|
||||
}
|
||||
|
||||
opened_zip->Password = core::stringc(password);
|
||||
const io::IFileList* files_in_zip = opened_zip->getFileList();
|
||||
|
||||
unsigned int number_of_files = files_in_zip->getFileCount();
|
||||
|
||||
for (unsigned int i=0; i < number_of_files; i++) {
|
||||
std::string fullpath = destination;
|
||||
fullpath += DIR_DELIM;
|
||||
for (u32 i = 0; i < files_in_zip->getFileCount(); i++) {
|
||||
std::string fullpath = destination + DIR_DELIM;
|
||||
fullpath += files_in_zip->getFullFileName(i).c_str();
|
||||
std::string fullpath_dir = fs::RemoveLastPathComponent(fullpath);
|
||||
|
||||
if (!files_in_zip->isDirectory(i)) {
|
||||
if (!fs::PathExists(fullpath_dir) && !fs::CreateAllDirs(fullpath_dir)) {
|
||||
fs->removeFileArchive(fs->getFileArchiveCount()-1);
|
||||
return false;
|
||||
}
|
||||
if (files_in_zip->isDirectory(i))
|
||||
continue; // ignore, we create dirs as necessary
|
||||
|
||||
io::IReadFile* toread = opened_zip->createAndOpenFile(i);
|
||||
if (!fs::PathExists(fullpath_dir) && !fs::CreateAllDirs(fullpath_dir))
|
||||
return false;
|
||||
|
||||
if (toread == nullptr) {
|
||||
// Wrong password
|
||||
fs->removeFileArchive(fs->getFileArchiveCount()-1);
|
||||
if (errorMessage != nullptr)
|
||||
*errorMessage = "invalid password";
|
||||
return false;
|
||||
}
|
||||
irr_ptr<io::IReadFile> toread(opened_zip->createAndOpenFile(i));
|
||||
|
||||
FILE *targetfile = fopen(fullpath.c_str(),"wb");
|
||||
|
||||
if (targetfile == NULL) {
|
||||
fs->removeFileArchive(fs->getFileArchiveCount()-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
char read_buffer[1024];
|
||||
long total_read = 0;
|
||||
|
||||
while (total_read < toread->getSize()) {
|
||||
|
||||
unsigned int bytes_read =
|
||||
toread->read(read_buffer,sizeof(read_buffer));
|
||||
if ((bytes_read == 0 ) ||
|
||||
(fwrite(read_buffer, 1, bytes_read, targetfile) != bytes_read))
|
||||
{
|
||||
fclose(targetfile);
|
||||
fs->removeFileArchive(fs->getFileArchiveCount() - 1);
|
||||
return false;
|
||||
}
|
||||
total_read += bytes_read;
|
||||
}
|
||||
|
||||
fclose(targetfile);
|
||||
if (toread.get() == nullptr) {
|
||||
// Wrong password
|
||||
fs->removeFileArchive(fs->getFileArchiveCount()-1);
|
||||
if (errorMessage != nullptr)
|
||||
*errorMessage = "invalid password";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ofstream os(fullpath.c_str(), std::ios::binary);
|
||||
if (!os.good())
|
||||
return false;
|
||||
|
||||
char buffer[4096];
|
||||
long total_read = 0;
|
||||
|
||||
while (total_read < toread->getSize()) {
|
||||
long bytes_read = toread->read(buffer, sizeof(buffer));
|
||||
bool error = true;
|
||||
if (bytes_read != 0) {
|
||||
os.write(buffer, bytes_read);
|
||||
error = os.fail();
|
||||
}
|
||||
if (error) {
|
||||
os.close();
|
||||
remove(fullpath.c_str());
|
||||
return false;
|
||||
}
|
||||
total_read += bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
fs->removeFileArchive(fs->getFileArchiveCount() - 1);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ReadFile(const std::string &path, std::string &out)
|
||||
{
|
||||
@ -834,7 +853,7 @@ bool ReadFile(const std::string &path, std::string &out)
|
||||
is.seekg(0);
|
||||
is.read(&out[0], size);
|
||||
|
||||
return true;
|
||||
return !is.fail();
|
||||
}
|
||||
|
||||
bool Rename(const std::string &from, const std::string &to)
|
||||
|
@ -24,12 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <vector>
|
||||
#include "exceptions.h"
|
||||
|
||||
#ifdef _WIN32 // WINDOWS
|
||||
#ifdef _WIN32
|
||||
#define DIR_DELIM "\\"
|
||||
#define DIR_DELIM_CHAR '\\'
|
||||
#define FILESYS_CASE_INSENSITIVE true
|
||||
#define PATH_DELIM ";"
|
||||
#else // POSIX
|
||||
#else
|
||||
#define DIR_DELIM "/"
|
||||
#define DIR_DELIM_CHAR '/'
|
||||
#define FILESYS_CASE_INSENSITIVE false
|
||||
@ -129,9 +129,11 @@ const char *GetFilenameFromPath(const char *path);
|
||||
|
||||
bool safeWriteToFile(const std::string &path, const std::string &content);
|
||||
|
||||
#ifndef SERVER
|
||||
bool extractZipFile(irr::io::IFileSystem *fs, const char *filename,
|
||||
const std::string &destination, const char *password = "",
|
||||
std::string *errorMessage = nullptr);
|
||||
#endif
|
||||
|
||||
bool ReadFile(const std::string &path, std::string &out);
|
||||
|
||||
|
@ -658,11 +658,10 @@ int ModApiMainMenu::l_extract_zip(lua_State *L)
|
||||
std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
|
||||
|
||||
if (ModApiMainMenu::mayModifyPath(absolute_destination)) {
|
||||
fs::CreateAllDirs(absolute_destination);
|
||||
|
||||
std::string errorMessage;
|
||||
lua_pushboolean(L, fs::extractZipFile(RenderingEngine::get_filesystem(),
|
||||
zipfile, destination, password, &errorMessage));
|
||||
auto fs = RenderingEngine::get_filesystem();
|
||||
bool ok = fs::extractZipFile(fs, zipfile, destination, password, &errorMessage);
|
||||
lua_pushboolean(L, ok);
|
||||
if (!errorMessage.empty()) {
|
||||
lua_pushstring(L, errorMessage.c_str());
|
||||
return 2;
|
||||
@ -967,7 +966,7 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
|
||||
API_FCT(delete_dir);
|
||||
API_FCT(copy_dir);
|
||||
API_FCT(is_dir);
|
||||
//API_FCT(extract_zip); //TODO remove dependency to GuiEngine
|
||||
API_FCT(extract_zip);
|
||||
API_FCT(may_modify_path);
|
||||
API_FCT(download_file);
|
||||
API_FCT(get_min_supp_proto);
|
||||
|
Loading…
x
Reference in New Issue
Block a user