Merge pull request #1930 from pfirsich/physfs_skip_codesign

Add PHYSFS_Io StripSuffixIo to skip codesign signatures
This commit is contained in:
Sasha Szpakowski
2023-05-29 09:42:25 -03:00
committed by GitHub
4 changed files with 390 additions and 4 deletions
+2
View File
@@ -477,6 +477,8 @@ set(LOVE_SRC_MODULE_FILESYSTEM_PHYSFS
src/modules/filesystem/physfs/File.h
src/modules/filesystem/physfs/Filesystem.cpp
src/modules/filesystem/physfs/Filesystem.h
src/modules/filesystem/physfs/PhysfsIo.h
src/modules/filesystem/physfs/PhysfsIo.cpp
)
set(LOVE_SRC_MODULE_FILESYSTEM
+25 -4
View File
@@ -27,6 +27,7 @@
#include "Filesystem.h"
#include "File.h"
#include "PhysfsIo.h"
// PhysFS
#include "libraries/physfs/physfs.h"
@@ -152,7 +153,7 @@ bool Filesystem::isFused() const
return fused;
}
bool Filesystem::setIdentity(const char *ident, bool appendToPath)
bool Filesystem::setIdentity(const char *ident, bool appendToPath)
{
if (!PHYSFS_isInit())
return false;
@@ -172,8 +173,8 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath)
else
save_path_full += save_path_relative;
save_path_full = normalize(save_path_full);
save_path_full = normalize(save_path_full);
#ifdef LOVE_ANDROID
if (save_identity == "")
save_identity = "unnamed";
@@ -292,7 +293,27 @@ bool Filesystem::setSource(const char *source)
#else
// Add the directory.
if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1))
return false;
{
// It's possible there is additional data at the end of the fused executable,
// e.g. for signed windows executables (the signature).
// In this case let's try a little bit harder to find the zip file.
// This is not used by default because I assume that the physfs IOs are probably
// more robust and more performant, so they should be favored, if possible.
auto io = StripSuffixIo::create(new_search_path);
if (!io->determineStrippedLength())
{
delete io;
return false;
}
if (!PHYSFS_mountIo(io, io->filename.c_str(), nullptr, 1))
{
// If PHYSFS_mountIo fails, io->destroy(io) is not called and we have
// to delete ourselves.
delete io;
return false;
}
return true;
}
#endif
// Save the game source.
+203
View File
@@ -0,0 +1,203 @@
/**
* Copyright (c) 2006-2023 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include <cassert>
#include <algorithm>
#include "PhysfsIo.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
bool StripSuffixIo::determineStrippedLength()
{
if (!file) {
return false;
}
const int64_t fullSize = fullLength();
int64_t chunkSize = std::min(fullSize, (int64_t)8192);
std::string buffer;
buffer.reserve(chunkSize);
int64_t i = fullSize - chunkSize;
// I don't think we really need to go through the whole file. The main known use
// case for this functionality is to skip windows codesign signatures, which are
// from what I have seen ~12KB or so, but trying is better than just failing.
while (i >= 0)
{
buffer.resize(chunkSize);
if (seek(i) == 0)
return false;
const auto n = read(&buffer[0], chunkSize);
if (n <= 0)
return false;
buffer.resize(n);
// We are looking for the magic bytes that indicate the start
// of the "End of cental directory record (EOCD)".
// As this is most likely not a multi-disk zip, we could include 4 bytes of 0x00,
// but I'd rather make fewer assumptions.
const auto endOfCentralDirectory = buffer.rfind("\x50\x4B\x05\x06");
if (endOfCentralDirectory != std::string::npos)
{
i = i + endOfCentralDirectory;
break;
}
if (i == 0)
break;
i = std::max((int64_t)0, i - chunkSize);
}
if (i > 0)
{
// The EOCD record is at least 22 bytes but may include a comment
if (i + 22 > fullSize)
return false; // Incomplete central directory
// The comment length (u16) is located 20 bytes from the start of the EOCD record
if (seek(i + 20) == 0)
return false;
uint8_t buffer[2];
const auto n = read(buffer, 2);
if (n <= 0)
return false;
const auto commentSize = (buffer[1] << 8) | buffer[0];
if (i + 22 + commentSize > fullSize) // Comment incomplete
return false;
// We pretend the file ends just after the comment
// (which should be the end of the embedded zip file)
strippedLength_ = i + 22 + commentSize;
}
else
{
strippedLength_ = fullSize;
}
if (seek(0) == 0)
return false;
return true;
}
int64_t StripSuffixIo::read(void* buf, uint64_t len)
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
const auto ret = std::fread(buf, 1, len, file);
if (ret == 0)
{
if (std::feof(file))
{
PHYSFS_setErrorCode(PHYSFS_ERR_OK);
return 0;
}
else
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
}
else if (ret < len && std::ferror(file))
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
PHYSFS_setErrorCode(PHYSFS_ERR_OK);
return ret;
}
int64_t StripSuffixIo::write(const void* /*buf*/, uint64_t /*len*/)
{
PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY);
return -1;
}
int64_t StripSuffixIo::seek(uint64_t offset)
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return 0;
}
const auto ret = std::fseek(file, offset, SEEK_SET);
PHYSFS_setErrorCode(ret != 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK);
return ret == 0 ? 1 : 0;
}
int64_t StripSuffixIo::tell()
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
return std::ftell(file);
}
int64_t StripSuffixIo::length()
{
return strippedLength_;
}
int64_t StripSuffixIo::flush()
{
if (!file)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return 0;
}
return std::fflush(file) == 0 ? 1 : 0;
}
int64_t StripSuffixIo::fullLength()
{
assert(file);
const auto cur = std::ftell(file);
if (cur == -1)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
if (std::fseek(file, 0, SEEK_END) != 0)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
const auto len = std::ftell(file);
if (len == -1)
{
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
if (std::fseek(file, cur, SEEK_SET) != 0)
{
// We do have the length now, but something is wrong, so we return an error anyways
PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR);
return -1;
}
return len;
}
} // physfs
} // filesystem
} // love
+160
View File
@@ -0,0 +1,160 @@
/**
* Copyright (c) 2006-2023 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_PHYSFS_PHYSFSIO_H
#define LOVE_FILESYSTEM_PHYSFS_PHYSFSIO_H
#include <cstdint>
#include <cstdio>
#include <string>
#include "libraries/physfs/physfs.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
template <typename Derived>
struct PhysfsIo : PHYSFS_Io {
protected:
PhysfsIo()
: PHYSFS_Io
{
/*.version =*/ Derived::version,
/*.opaque =*/ this,
/*.read =*/ staticRead, // may be NULL
/*.write =*/ staticWrite, // may be NULL
/*.seek =*/ staticSeek,
/*.tell =*/ staticTell,
/*.length =*/ staticLength,
/*.duplicate =*/ staticDuplicate,
/*.flush =*/ staticFlush, // may be NULL
/*.destroy =*/ staticDestroy,
}
{
}
private:
// Returns: number of bytes read, 0 on EOF, -1 on failure
static PHYSFS_sint64 staticRead(struct PHYSFS_Io* io, void* buf, PHYSFS_uint64 len)
{
return derived(io)->read(buf, len);
}
// Returns: number of bytes written, -1 on failure
static PHYSFS_sint64 staticWrite(struct PHYSFS_Io* io, const void* buf, PHYSFS_uint64 len)
{
return derived(io)->write(buf, len);
}
// Returns: non-zero on success, zero on error
static int staticSeek(struct PHYSFS_Io* io, PHYSFS_uint64 offset)
{
return derived(io)->seek(offset);
}
// Returns: current offset from start, -1 on error
static PHYSFS_sint64 staticTell(struct PHYSFS_Io* io)
{
return derived(io)->tell();
}
// Returns: total size in bytes, -1 on error
static PHYSFS_sint64 staticLength(struct PHYSFS_Io* io)
{
return derived(io)->length();
}
static struct PHYSFS_Io* staticDuplicate(struct PHYSFS_Io* io)
{
// Just use copy constructor
return new Derived(*derived(io));
}
// Returns: non-zero on success, zero on error
static int staticFlush(struct PHYSFS_Io* io)
{
return derived(io)->flush();
}
static void staticDestroy(struct PHYSFS_Io* io)
{
// Just use destructor
delete derived(io);
}
static Derived* derived(PHYSFS_Io* io)
{
return static_cast<Derived*>(reinterpret_cast<PhysfsIo*>(io->opaque));
}
};
struct StripSuffixIo : public PhysfsIo<StripSuffixIo> {
static const uint32_t version = 0;
std::string filename;
FILE* file = nullptr;
// The constructor is private in favor of this function to prevent stack allocation
// because Physfs will take ownership of this object and call destroy on it later.
static StripSuffixIo* create(std::string f) { return new StripSuffixIo(f); }
~StripSuffixIo()
{
if (file)
{
std::fclose(file);
}
}
StripSuffixIo(const StripSuffixIo& other)
: StripSuffixIo(other.filename)
{
}
bool determineStrippedLength();
int64_t read(void* buf, uint64_t len);
int64_t write(const void* buf, uint64_t len);
int64_t seek(uint64_t offset);
int64_t tell();
int64_t length();
int64_t flush();
private:
StripSuffixIo(std::string f)
: filename(std::move(f))
, file(std::fopen(filename.c_str(), "rb"))
{
}
int64_t fullLength();
int64_t strippedLength_ = -1;
};
} // physfs
} // filesystem
} // love
#endif